Animate CAShapeLayer path on animated bounds change Animate CAShapeLayer path on animated bounds change ios ios

Animate CAShapeLayer path on animated bounds change


I know this is an old question but I can provide you a solution which appears similar to Mr Lockwoods approach. Sadly the source code here is swift so you will need to convert it to ObjC.

As mentioned before if the layer is a backing layer for a view you can intercept the CAAction's in the view itself. This however isn't convenient for example if the backing layer is used in more then one view.

The good news is actionForLayer:forKey: actually calls actionForKey: in the backing layer.

It's in the actionForKey: in the backing layer where we can intercept these calls and provide an animation for when the path is changed.

An example layer written in swift is as follows:

class AnimatedBackingLayer: CAShapeLayer{    override var bounds: CGRect    {        didSet        {            if !CGRectIsEmpty(bounds)            {                path = UIBezierPath(roundedRect: CGRectInset(bounds, 10, 10), cornerRadius: 5).CGPath            }        }    }    override func actionForKey(event: String) -> CAAction?    {        if event == "path"        {            if let action = super.actionForKey("backgroundColor") as? CABasicAnimation            {                let animation = CABasicAnimation(keyPath: event)                animation.fromValue = path                // Copy values from existing action                animation.autoreverses = action.autoreverses                animation.beginTime = action.beginTime                animation.delegate = action.delegate                animation.duration = action.duration                animation.fillMode = action.fillMode                animation.repeatCount = action.repeatCount                animation.repeatDuration = action.repeatDuration                animation.speed = action.speed                animation.timingFunction = action.timingFunction                animation.timeOffset = action.timeOffset                return animation            }        }        return super.actionForKey(event)    }}


I think you have problems because you play with the frame of a layer and it's path at the same time.

I would just go with CustomView that has custom drawRect: that draws what you need, and then just do

[UIView animateWithDuration:1.0 delay:0.0 usingSpringWithDamping:0.3 initialSpringVelocity:0.0 options:0 animations:^{    self.customView.bounds = newBounds;} completion:nil];

Sor the is no need to use pathes at all

Here is what i've got using this approachhttps://dl.dropboxusercontent.com/u/73912254/triangle.mov


Found solution for animating path of CAShapeLayer while bounds is animated:

typedef UIBezierPath *(^PathGeneratorBlock)();@interface AnimatedPathShapeLayer : CAShapeLayer@property (copy, nonatomic) PathGeneratorBlock pathGenerator;@end@implementation AnimatedPathShapeLayer- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key {    if ([key rangeOfString:@"bounds.size"].location == 0) {        CAShapeLayer *presentationLayer = self.presentationLayer;        CABasicAnimation *pathAnim = [anim copy];        pathAnim.keyPath = @"path";        pathAnim.fromValue = (id)[presentationLayer path];        pathAnim.toValue = (id)self.pathGenerator().CGPath;        self.path = [presentationLayer path];        [super addAnimation:pathAnim forKey:@"path"];    }    [super addAnimation:anim forKey:key];}- (void)removeAnimationForKey:(NSString *)key {    if ([key rangeOfString:@"bounds.size"].location == 0) {        [super removeAnimationForKey:@"path"];    }    [super removeAnimationForKey:key];}@end//@interface ShapeLayeredView : UIView@property (strong, nonatomic) AnimatedPathShapeLayer *layer;@end@implementation ShapeLayeredView@dynamic layer;+ (Class)layerClass {    return [AnimatedPathShapeLayer class];}- (instancetype)initWithGenerator:(PathGeneratorBlock)pathGenerator {    self = [super init];    if (self) {        self.layer.pathGenerator = pathGenerator;    }    return self;}@end