CAShapeLayer detect touch during animation Swift

2019-07-17 07:37发布

问题:

I can detect a touch of a CAShapeLayer like this (touchesEnded):

let touchLocation : CGPoint = (touch as! UITouch).locationInView(self.view)

for shape in shapes{
    if CGPathContainsPoint(shape.path, nil, touchLocation, false){
        print("Layer touch")
    }
}

And I can animate the path of a CAShapeLayer like this:

let newShapePath = UIBezierPath(arcCenter: toPoint, radius: 20, startAngle: CGFloat(0), endAngle: CGFloat(M_PI * 2), clockwise: true).CGPath

// animate the `path`
let animation = CABasicAnimation(keyPath: "path")
animation.toValue = newShapePath
animation.duration = CFTimeInterval(duration)

animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
animation.fillMode = kCAFillModeBoth
animation.removedOnCompletion = false

shape.addAnimation(animation, forKey: animation.keyPath)

But while the animation is happening, touches aren't detected on the CAShapeLayer. Is it possible to detect a touch on the a CAShapeLayer while animating the path?

回答1:

You can access the layer's presentationLayer in order to do this. This will provide you with a rough approximation to the 'in flight' values of a given layer while animating. For example:

for shape in shapes {

    // gets the layer's presentation layer if it exists – else fallback on the model layer
    let presentationLayer = shape.presentationLayer() as? CAShapeLayer ?? shape

    if CGPathContainsPoint(presentationLayer.path, nil, touchLocation, false){
        print("Layer touch")
    }
}

Also, as a side note, it's generally considered bad practice to use removedOnCompletion = false if you're not using the animation delegate. Instead of leaving the animation lingering, you should just update the layer's model values to represent its new state. You can do this through a CATransaction to ensure that no implicit animations are generated. For example:

let animation = CABasicAnimation(keyPath: "path")
animation.fromValue = shape.path
animation.toValue = newShapePath
animation.duration = CFTimeInterval(duration)

animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)

shape.addAnimation(animation, forKey: animation.keyPath)

// update the layer's model values
CATransaction.begin()
CATransaction.setDisableActions(true)
shape.path = newShapePath
CATransaction.commit()