Search code examples
iosuigesturerecognizercalayer

iOS CALayer and TapGestureRecognizer


I'm developing an app on iOS 6.1 for iPad. I've a problem with the CALayer and a TapGestureRecognizer.

I've 7 CALayers forming a rainbow (every layer is a colour). Every layer is built using a CAShapeLayer generate from a CGMutablePathRef. Everything works fine. All the layers are drawn on screen and I can see a beautiful rainbow.

Now I want to detect the tap above a single color/layer. I try this way:

- (void)tap:(UITapGestureRecognizer*)recognizer
{
   //I've had the tapGestureRecognizer to rainbowView (that is an UIView) in viewDidLoad
  CGLayer* tappedLayer = [rainbowView.layer.presentationlayer hitTest:[recognizer locationInView:rainbowView];

  if (tappedLayer == purpleLayer) //for example
         NSLog(@"PURPLE!");
}

I don't understand why this code won't work! I've already red other topics in here: all suggest the hitTest: method for solving problems like this. But in my case I can't obtain the desired result. Can anyone help me? Thanks!!

EDIT:

Here's the code for the creation of paths and layers:

- (void)viewDidLoad
{
     //Other layers
     ...
     ...
     //Purple Arc
     purplePath = CGPathCreateMutable();
     CGPathMoveToPoint(purplePath, NULL, 150, 400);
     CGPathAddCurveToPoint(purplePath, NULL, 150, 162, 550, 162, 550, 400);
     purpletrack = [CAShapeLayer layer];
     purpletrack.path = purplePath;
     purpletrack.strokeColor = [UIColor colorWithRed:134.0/255.0f green:50.0/255.0f blue:140.0/255.0f alpha:1.0].CGColor;
     purpletrack.fillColor = nil;
     purpletrack.lineWidth = 25.0;
     [rainbowView.layer insertSublayer:purpletrack above:bluetrack];
}

This was my first approach to the problem. And the touch didn't work.

I also tried to create a RainbowView class where the rainbow was drawing in drawRect method using UIBezierPaths. Then I follow the "Doing Hit-Detection on a Path" section in http://developer.apple.com/library/ios/#documentation/2ddrawing/conceptual/drawingprintingios/BezierPaths/BezierPaths.html In this case the problem was the path variable passed to the method. I try to compare the UIBezierPath passed with the paths in RainbowView but with no results.

I could try to create curves instead of paths. In this case maybe there isn't a fill part of figure and the touching area is limited to the stroke. But then how can I recognize the touch on a curve?

I'm so confused about all of these stuff!!! :D


Solution

  • The problem you are facing is that you are checking agains the frame/bounds of the layer when hit testing and not agains the path of the shape layer.

    If your paths are filled you should instead use CGPathContainsPoint() to determine if the tap was inside the path. If your paths aren't filled but instead stroked I refer you to Ole Begemann's article about CGPath Hit Testing.

    To make your code cleaner you could do the hit testing in your own subclass. Also, unless the layer is animating when hit testing it makes no sense using the presentationLayer.