sborra sborra - 7 months ago 95
Swift Question

CAShapeLayer detect touch during animation Swift

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?

Answer

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. 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()