24

I'm new to the iPhone SDK. Right now I'm programming with CALayers which I really like a lot – not as expensive as UIViews, and a lot less code than OpenGL ES sprites.

I have this question: is it possible to get a touch event on a CALayer? I understand how to get a touch event on a UIView with

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 

but I can't find anywhere about how to get a touch event on a CALayer object, for instance, an orange square floating in 3D space. I refuse to believe I'm the only one who's curious about this.

I appreciate any help!

DarkDust
  • 90,870
  • 19
  • 190
  • 224

4 Answers4

30

ok- answered my own question! let's say you've got a bunch of CALayers in your view controller's main layer, and you want them to go to opacity 0.5 when you touch them. implement this code in the .m file of your view controller class:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([touches count] == 1) {
        for (UITouch *touch in touches) {
            CGPoint point = [touch locationInView:[touch view]];
            point = [[touch view] convertPoint:point toView:nil];

            CALayer *layer = [(CALayer *)self.view.layer.presentationLayer hitTest:point];

            layer = layer.modelLayer;
            layer.opacity = 0.5;
        }
    }
}
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • 1
    Notes for clarity: 1. The for loop is a little awkward, since you verified there is only one touch. The usual way of doing this is to make a pointer to a touch using [touches anyObject] or [touches objectAtIndex:0]. 2. Instead of using convertPoint: , just pass "nil" to locationInView: in the first place. That gives the touch location in the window's coordinate system, so the next line is redundant. – Rab Nov 01 '11 at 17:05
  • 1
    I Know this is quite old questions. But just for those who curious. @Rab Mostly right except [touches objectAtIndex:0] NSset which is represent set of touches does not have objectAtIndex selector. – Eugene P May 23 '14 at 09:43
  • +1 Good point @EugeneProkoshev. It's possible someone might put touch events into an NSArray, but in this case we just have the NSSet which is unindexed. Thanks for the clarification of a clarification. :) – Rab May 23 '14 at 13:57
8

Similar to the first Answer.

- (CALayer *)layerForTouch:(UITouch *)touch {
    UIView *view = self.view;

    CGPoint location = [touch locationInView:view];
    location = [view convertPoint:location toView:nil];

    CALayer *hitPresentationLayer = [view.layer.presentationLayer hitTest:location];
    if (hitPresentationLayer) {
        return hitPresentationLayer.modelLayer;
    }

    return nil;
} 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CALayer *hitLayer = [self layerForTouch:touch];

    // do layer processing...
}
Jun
  • 3,422
  • 3
  • 28
  • 58
2

Egor T answer updated for Swift 4:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
    super.touchesBegan(touches, with: event)

    if let touch = touches.first, let touchedLayer = self.layerFor(touch)
    {
        //Here you will have the layer as "touchedLayer"
    }
}

private func layerFor(_ touch: UITouch) -> CALayer?
{
    let view = self.view
    let touchLocation = touch.location(in: view)
    let locationInView = view.convert(touchLocation, to: nil)

    let hitPresentationLayer = view.layer.presentation()?.hitTest(locationInView)
    return hitPresentationLayer?.model()
}
tf.alves
  • 919
  • 8
  • 15
1

I found that I was getting the wrong coordinates with

point = [[touch view] convertPoint:point toView:nil];

I had to change it to

point = [[touch view] convertPoint:point toView:self.view];

To get the correct layer

Siddharth Ram
  • 546
  • 5
  • 15