简体   繁体   中英

iOS: CAShapeLayer, CALayer mask, hitTest:WithEvents, hitTest:

Thanks in advance: I have a view hierarchy like this:

A UIView within this I set: CALayer * layer = (CALayer*) self.layer

And within this class I have a CAShapeLayer , which is set as the mask of the CALayer .

It animates fine, no problem.

I have a UIViewController that init the above UIView by:

myView = [myAnimationView alloc] initWithFrame:(CGRect){{0, 0}, 320, 450}]; [self.view addSubview myView];

So what I have is: a UIView class and the above UIViewController class. Nothing else.

As the CAShapeLayer animates (it is only a basic ca animation, scaling from a small circle to a larger one), I hope to be able to get the touch point within UIViewController that inits this UIView

Should I use hitTest:WithEvents: here? I have tried it, but it get called three times. The returned points are correct, but I was hoping to find a way to know whether or not an animated view is being touched from the container view. Other words, I would like to know if the subView /subLayer is being touched.

In summary, here is my view hierarchy:

UIViewController initializes the UIView class and adds it as subView. Within this UIView class its layer is set to be CALayer by CALayer * layer = (CALayer *) self.layer and the CAShapeLayer is set to be the mask of the CALayer and animation is set on the shape layer's path.

And I would like to to able to get the layer touched within UIViewController

Is it possible to get the touch point from a layer that is being animated by CAShapeLayer within a view whose layer is set to be CALayer from a UIViewController that adds it as its subview? Please cite with examples.

Thanks very much in advance. Regards.

I think CALayer isn't designed to receive/handle touch events (which is one big difference to UIView ). I tested for a (long press) touch within a CAShapeLayer.path from the ViewController with something like

- (void)handleLongPress:(UILongPressGestureRecognizer *)recognizer {
    CGPoint currentPoint = [recognizer locationInView:self.overlayView];
    bool didSelect = [self selectPathContainingPoint:currentPoint];
    if (didSelect) {
        [self updateViews];
    }
}

- (BOOL)selectPathContainingPoint:(CGPoint)point {
    CALayer * layer = (CALayer *) self.layer;
    CAShapeLayer *mask = (CAShapeLayer *)layer.mask;
    return CGPathContainsPoint(mask.path, nil, point);
}

There's one caveat however: my path isn't animated. In the docs for CAShapeLayer it also doesn't say anything if the CAShapeLayer.path property updates during the animation.

Try something like this:

- (void)setup
{
    _maskLayer = [CAShapeLayer layer];
    _maskLayer.fillColor = [UIColor whiteColor].CGColor;
    _maskLayer.path = somePath;

    _theLayer.frame = CGRectMake(0, 0, size.width, size.height);

    // Apply a mask
    _maskLayer.frame = _theLayer.frame;
    _theLayer.mask = _maskLayer;
}

- (IBAction)tapOnView:(UITapGestureRecognizer *)sender
{
    CGPoint point = [sender locationInView:theView];
    CALayer *subLayer = [_theLayer.presentationLayer hitTest:point].modelLayer;

    if (subLayer != _theLayer && subLayer != nil) {
        // Do something with the sublayer which the user touched
    }
}

You will have to setup your TapGestureRecognizer, I did this using Interface Builder but you can do it in code if you want. Be sure that your layers have proper bounds setup, and you also need to set the bounds on your mask.

Notice that I am getting the presentationLayer and the converting it back to the real layer. This will cause it to work with animations.

I hope this works for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM