5

I have imported a .png image into UIImageView in Xcode and what I want to make is when the image is touched, it will be hidden.

But my problem is that the png image contains transparent parts and when I touch on the transparent parts, the action goes on. I want the action to go on only when the visible part of the image is touched. How to solve the problem?

Swift or Objective-C

Cœur
  • 37,241
  • 25
  • 195
  • 267
thura oo
  • 493
  • 1
  • 6
  • 18

4 Answers4

21

I have created a custom UIButton subclass that behaves exactly as you describe, have a look: https://github.com/spagosx/iOS-Shaped-Button-Swift

It's written in Swift, but it's easily convertible to Objective-c.

The approach is to get the pixel data from the touch point and to access the RGBA values, in this case we read A (alpha) and check if it is higher than our threshold.

Looking at a bit of code:

func alphaFromPoint(point: CGPoint) -> CGFloat {
    var pixel: [UInt8] = [0, 0, 0, 0]
    let colourSpace = CGColorSpaceCreateDeviceRGB()
    let alphaInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
    let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colourSpace, bitmapInfo: alphaInfo.rawValue)

    context?.translateBy(x: -point.x, y: -point.y)

    self.layer.render(in: context!)

    let floatAlpha = CGFloat(pixel[3])
    return floatAlpha
}

You can than take the floatAlpha value and compare it with your acceptable value of alpha:

    override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
        return self.alphaFromPoint(point) >= 100
    }
idrougge
  • 633
  • 5
  • 12
Danny S
  • 1,291
  • 8
  • 12
  • Much better answer than the currently highest voted one. – Fogmeister Jan 13 '15 at 13:41
  • Very nice solution! You should update your answer to provide a sentence or two on your algorithm (which is a great solution!) as answers with just links in them are not looked on kindly here :-) – David H Jan 13 '15 at 16:32
  • Thanks a lot. This has been to great help for me – Ole Haugset Aug 12 '15 at 09:42
  • Swift 2.2: `let alphaInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue) let context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, colorSpace, alphaInfo.rawValue)` – Twitter khuong291 Jun 07 '16 at 12:44
2

I have taken the liberty of updating 'Danny S's' answer to Swift 5 and removed extraneous code, bug fixed and added some additional clarity to the UX.

Here's the code:

https://github.com/ZoeESummers/SOXShapedTapView-Updated.git

0
  -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
    {
        UITouch *touch = [touches anyObject]; 
        CGPoint touch_point = [touch locationInView:self.view];

        if (![imageView pointInside:touch_point withEvent:event]) 
        {
            NSLog(@"you inside imageview");
// write here what you want
        }
    } 
Sport
  • 8,570
  • 6
  • 46
  • 65
  • Please note this question http://stackoverflow.com/questions/2958808/how-to-check-whether-a-cgpoint-is-inside-a-uiimageview Imo you should convert the coordinates ! – Coldsteel48 Jan 13 '15 at 13:35
  • @thura look at above link also.if your issue not solved. – Sport Jan 13 '15 at 13:37
  • 1
    @Sport How does this answer the question? The question asks how to determine if the tap is in a visible vs transparent part of the image. All you have done here is check that the tap is inside the image view. But this doesn't answer the question in any way. Even the link in the comment doesn't add anything relevant. – Fogmeister Jan 13 '15 at 13:40
  • @Fogmeister He has a CGPoint he can then check inside the image view if the point is in transparent or not area – Coldsteel48 Jan 13 '15 at 14:39
  • 3
    Yes, but that is the whole question. HOW does he do this. His question is "how do I check if the tap is in the transparent part" and your answer is "get the tap. Then check if it's in the transparent part". You haven't actually answered the question. – Fogmeister Jan 13 '15 at 14:41
0

Combining Danny's and Sport's answer in Swift 4.2 as an extension.

extension UIButton{

    open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = event!.touches(for: self)?.first {
            let location = touch.location(in: self)
            if alphaFromPoint(point: location) == 0 {
                self.cancelTracking(with: nil)
                print("cancelled!")
            } else{
                super.touchesBegan(touches, with: event)
            }
        }
    }

    func alphaFromPoint(point: CGPoint) -> CGFloat {
        var pixel: [UInt8] = [0, 0, 0, 0]
        let colorSpace = CGColorSpaceCreateDeviceRGB();
        let alphaInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
        let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: alphaInfo.rawValue)

        context!.translateBy(x: -point.x, y: -point.y)
        self.layer.render(in: context!)

        let floatAlpha = CGFloat(pixel[3])
        return floatAlpha
    }
}
ilkayaktas
  • 188
  • 1
  • 7