iosswiftquartz-core

Recognizing touch events only inside the masked view


I am creating this structured through masks:

enter image description here

Each hexagon should be clickable. This is the code I used:

    // To create one masked hexagun

    let hex = UIImage(named: "hexum")
    let mask = CALayer()
    mask.contents = hexum!.CGImage
    mask.frame = CGRectMake(0, 0, hex!.size.width, hex!.size.height)

    let img = UIImageView(image: UIImage(named: "img"))
    img.layer.mask = mask
    img.layer.masksToBounds = true

    // Gesture Recognizer

    let singleTap = UITapGestureRecognizer(target: self, action: "tapDetected")
    singleTap.numberOfTapsRequired = 1
    img.addGestureRecognizer(singleTap)
    img.userInteractionEnabled = true

    func tapDetected() {
        print("Clicked!")
    }

The problem is that the click region is larger than the mask, which will cause the inconvenience of a region overlapping each other. Something like this:

Not what I want!

The yellow border shows the clickable region (not actually visible)

I am a beginner, it may be a trivial problem, but can you help me solve? Thank you.


Solution

  • If you want to do this perfectly, use the UIGestureRecognizerDelegate method gestureRecognizer(gesture, shouldReceiveTouch: touch) -> Bool. You will need to map the given gesture recogniser to a particular hexagon and then do pixel precise hit-testing on the image for that hexagon. This latter part is achieved by rendering the mask image to a graphics context and finding the pixel at the point corresponding to the touch location.

    However, this is likely overkill. You can simplify the problem by hit-testing each shape as a circle, not a hexagon. The circle shape roughly approximates the hexagon so it will work almost the same for a user and avoids messy pixel-level alpha equality. The inaccuracy of touch input will cover up the inaccurate regions.

    Another option is to rework your views to be based on CAShapeLayer masks. CAShapeLayer includes a path property. Bezier paths in UIKit include their own rolled versions of path-contains-point methods so you can just use that for this purpose.