1

Hi I have a file where the view is buildt up of 10 layered images that is pngs with big transparent parts. I like this to be clickable only on the visible parts, but how ever I try it's only the top image/button that register clicks. I've tried with both UIbuttons and tappable UIImage views. I work in swift 2.2. Does anyone have an idea on how to solve this?

2 Answers2

2
  1. Sort the image views by z-position in the view hierarchy. This ensures correct behaviour when multiple opaque regions overlap.
  2. Iterate through all of the images.
  3. For each image, check the alpha component of the pixel at the selected location.
  4. The alpha will be between some range (usually between 0 ... 100). If the alpha is greater than some threshold amount, then respond to the tap and exit, otherwise check the next image.

Below is an example of how this would work. The images and tap gesture have been added in interface builder. This example just brings the tapped view to the front, although it can be easily modified for your required behaviour.

class ClickViewController: UIViewController {

    @IBOutlet var images: [UIImageView]!

    @IBAction internal func tapGestureHandler(gesture: UITapGestureRecognizer) {

        let sorted = images.sort() { a, b in
            return a.layer.zPosition < b.layer.zPosition
        }

        for (i, image) in sorted.enumerate() {

            let location = gesture.locationInView(image)
            let alpha = image.alphaAtPoint(location)

            if alpha > 50.0 {

                print("selected image #\(i) \(image.layer.zPosition)")
                view.addSubview(image)

                return
            }
        }
    }
}

Here is an example of how to set up the storyboard. Note the image views and the tap gesture recogniser. The views can also be added programatically.

Views and gesture recogniser

Link each image to the images IBCollection, under the Referencing Outlet Collections in the Connections Inspector.

Images added to the IBCollection

Here is the code to determine the alpha component of the pixel in image at the tapped location. This code below was provided as an answer to another similar question on SO.

extension UIImageView {

    //
    // Return the alpha component of a pixel in the UIImageView.
    //
    // See: https://stackoverflow.com/questions/27923232/how-to-know-that-if-the-only-visible-area-of-a-png-is-touched-in-xcode-swift-o?rq=1
    //
    func alphaAtPoint(point: CGPoint) -> CGFloat {

        var pixel: [UInt8] = [0, 0, 0, 0]
        let colorSpace = CGColorSpaceCreateDeviceRGB();
        let alphaInfo = CGImageAlphaInfo.PremultipliedLast.rawValue

        guard let context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, colorSpace, alphaInfo) else {
            return 0
        }

        CGContextTranslateCTM(context, -point.x, -point.y);

        layer.renderInContext(context)

        let floatAlpha = CGFloat(pixel[3])

        return floatAlpha
    }
}

See the example app available on GitHub.

Community
  • 1
  • 1
Luke Van In
  • 5,215
  • 2
  • 24
  • 45
  • Hi and big thanks for your help! I acctually ended up using another solution that so far seems to work fine for this project, https://gist.github.com/clmntcrl/8e4cab3ed7d1e7da47e7 But again, thanks for that very instructionally answer! – user2801834 Jul 14 '16 at 11:20
0

@Luke Van In Thanks for this answer, it helped me out a lot.

Here is the Swift 3 version: (Some syntax changes)

import UIKit

class ViewController: UIViewController {

@IBOutlet var images: [UIImageView]!

@IBAction internal func tapGestureHandler(gesture: UITapGestureRecognizer) {

    let sorted = images.sorted() { a, b in
        return a.layer.zPosition < b.layer.zPosition
    }

    for (i, image) in sorted.enumerated() {

        let location = gesture.location(in: image)
        let alpha = image.alphaAtPoint(point: location)

        if alpha > 50.0 {

            print("selected image #\(i) \(image.layer.zPosition)")
            view.addSubview(image)

            return
        }
    }
}



}

extension UIImageView {

func alphaAtPoint(point: CGPoint) -> CGFloat {

    var pixel: [UInt8] = [0, 0, 0, 0]
    let colorSpace = CGColorSpaceCreateDeviceRGB();
    let alphaInfo = CGImageAlphaInfo.premultipliedLast.rawValue

    guard let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: alphaInfo) else {
        return 0
    }

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

    layer.render(in: context)

    let floatAlpha = CGFloat(pixel[3])

    return floatAlpha
 }
}
otto
  • 111
  • 1
  • 5