CAShapeLayer Animating Path Glitches / Flickers (From Ellipse to Rect and back)
Unfortunately this is a limitation of the otherwise awesome animatable path property of CAShapeLayers.
Basically it tries to interpolate between the two paths. It hits trouble when the destination path and start path have a different number of control points - and curves and straight edges will have this problem.
You can try to minimise the effect by drawing your ellipse as 4 curves instead of a single ellipse, but it still isn't quite right. I haven't found a way to go smoothly from curves to polygons.
You may be able to get most of the way there, then transfer to a fade animation for the last part - this won't look as nice, though.
CAShapeLayer, animating path with Transactions
The answer is simple but a bit unsatisfactory: while the path
property is animatable, it doesn't support implicit animations. This is called out in the Discussion section of the documentation for the path property:
Unlike most animatable properties,
path
(as with allCGPathRef
animatable properties) does not support implicit animation.
Explicit vs. Implicit animations
An "explicit" animation is an animation object (e.g. CABasicAnimation
) that is explicitly added to the layer by calling -addAnimation:forKey:
on the layer.
An "implicit" animation is an animation that happens implicitly as a result of changing an animatable property.
The animation is considered implicit even if the property is changed within a transaction.
CAShapeLayer Animation without having to update the path
You can simplify your code and omit the spring effect:
[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
[CATransaction setAnimationTimingFunction: kCAMediaTimingFunctionEaseInEaseOut];
layer.strokeEnd = 0.0;
[CATransaction commit];
The transaction will create and add an implicit animation object for you.
Animating between two bezier path shapes
The first important point is to construct the two bezier paths similarly, so the rectangle is a (trivial) analogue to the more complex shape.
// the complex bezier path
let initialPoint = CGPoint(x: 0, y: 0)
let curveStart = CGPoint(x: 0, y: (rect.size.height) * (0.2))
let curveControl = CGPoint(x: (rect.size.width) * (0.6), y: (rect.size.height) * (0.5))
let curveEnd = CGPoint(x: 0, y: (rect.size.height) * (0.8))
let firstCorner = CGPoint(x: 0, y: rect.size.height)
let secondCorner = CGPoint(x: rect.size.width, y: rect.size.height)
let thirdCorner = CGPoint(x: rect.size.width, y: 0)
var myBezierArc = UIBezierPath()
myBezierArc.moveToPoint(initialPoint)
myBezierArc.addLineToPoint(curveStart)
myBezierArc.addQuadCurveToPoint(curveEnd, controlPoint: curveControl)
myBezierArc.addLineToPoint(firstCorner)
myBezierArc.addLineToPoint(secondCorner)
myBezierArc.addLineToPoint(thirdCorner)
The simpler 'trivial' bezier path, that creates a rectangle, is exactly the same but the controlPoint
is set so that it appears to not be there:
let curveControl = CGPoint(x: 0, y: (rect.size.height) * (0.5))
( Try removing the addQuadCurveToPoint
line to get a very strange animation! )
And finally, the animation commands:
let myAnimation = CABasicAnimation(keyPath: "path")
if (isArcVisible == true) {
myAnimation.fromValue = myBezierArc.CGPath
myAnimation.toValue = myBezierTrivial.CGPath
} else {
myAnimation.fromValue = myBezierTrivial.CGPath
myAnimation.toValue = myBezierArc.CGPath
}
myAnimation.duration = 0.4
myAnimation.fillMode = kCAFillModeForwards
myAnimation.removedOnCompletion = false
myImageView.layer.mask.addAnimation(myAnimation, forKey: "animatePath")
If anyone is interested, the project is here.
Animating a CGMutablePathRef with CAShapeLayer
You can animate the path
of the shape layer from one path to another.
In your case you would create the paths for the initial state and for the end state as two different paths.
Note: if your two paths have a different number of segments or control points, the animation will look very strange.
Then you would create an animation for the "path"
key path with those to and from values and add it to the layer you are animating. If you also want the property to change to the end state you should also update the path
property to reflect the end state of the animation.
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.duration = 1.0; // however long your duration should be
animation.fromValue = (__bridge id)fromPath;
animation.toValue = (__bridge id)toPath;
shapeLayer.path = toPath; // the property should reflect the end state of the animation
[shapeLayer addAnimation:animation forKey:@"myPathAnimation"];
CoreAnimation CAShapeLayer Animating Bezier Path
You can take current animated value using presentationLayer
CGFloat val = [[bezier.presentationLayer valueForKey:@"strokeEnd"] floatValue];
Animate line segment in iOS
You can use a CAShapeLayer and create a CGPath containing only two control points. The path property of CAShapeLayer itself actually is animatable (as long as the new path has the same number of points) plus you get all the transform capabilities of CALayer. And as Tommy just mentioned, you can play with strokeStart
and strokeEnd
for some pretty cool animations (there also is lineDashPattern
which animates nicely with lineDashPhase
but i guess you won't need that).
Code Sample from this question:
CAShapeLayer *lineShape = nil;
CGMutablePathRef linePath = nil;
linePath = CGPathCreateMutable();
lineShape = [CAShapeLayer layer];
lineShape.lineWidth = 1.0f;
lineShape.lineCap = kCALineJoinMiter;
lineShape.strokeColor = [[UIColor redColor] CGColor];
CGPathMoveToPoint(linePath, NULL, x, y);
CGPathAddLineToPoint(linePath, NULL, toX, toY);
lineShape.path = linePath;
CGPathRelease(linePath);
Related Topics
Youtube Video Autoplay Inside Uiwebview
How to Find Out the Objective-C Generics Type
How to Add Firebase to Today Extension iOS
Swift 3- How to Get Button in Uicollectionviewcell Work
Why Was Uitextfield's Text Property Changed to an Optional in Swift 2
Collectionview Duplicate Cell When Loading More Data
iOS Uimenucontroller Uimenuitem, How to Determine Item Selected with Generic Selector Method
Create a Tabbar Controller with a Master-Detail Template
Accessing Objective-C Base Class's Instance Variables from a Swift Class
How to Retrieve a File/Resource in a iOS Framework Bundle from a Main Program
iOS Web App: Showing Content Only If the Application Is Standalone
How to Access the Accelerometer from the Apple Watch
How to Create a Uiimage with Uibezierpath
Type 'Nsattributedstringkey' (Aka 'Nsstring') Has No Member 'Font'