CABasicAnimation resets to initial value after animation completes
Here's the answer, it's a combination of my answer and Krishnan's.
cabasicanimation.fillMode = kCAFillModeForwards;
cabasicanimation.removedOnCompletion = NO;
The default value is kCAFillModeRemoved
. (Which is the reset behavior you're seeing.)
CABasicAnimation resets after completion
Since you're animating transform
property of a layer, you should check what else could reset it to CATransform3DIdentity
(default) state. But why not use simple [UIView animateWithDuration:]
method to animate view's frame?
How to restart a CABasicAnimation after it is completed
I was able to make your code work just by making a new animation each time the button is tapped:
let shapeLayer = CAShapeLayer()
var basicAnimation: CABasicAnimation!
override func viewDidLoad() {
super.viewDidLoad()
let circularPath = UIBezierPath(arcCenter: view.center, radius: 100, startAngle: -.pi/2, endAngle: 3 * .pi/2, clockwise: true)
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 10
shapeLayer.strokeEnd = 0
view.layer.addSublayer(shapeLayer)
}
@IBAction func buttonTapped(_ sender: Any) {
basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
removeAnimation()
basicAnimation.delegate = self
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.default)
basicAnimation.fromValue = 0
basicAnimation.toValue = 1
basicAnimation.duration = CFTimeInterval(2)
basicAnimation.fillMode = CAMediaTimingFillMode.forwards
basicAnimation.isRemovedOnCompletion = false
shapeLayer.add(basicAnimation, forKey: "myAnimation")
}
func removeAnimation() {
shapeLayer.removeAnimation(forKey: "myAnimation")
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
print("animation done")
}
CABasicAnimation - Change animation start value during pause
If you want to pick up from where you left off, when you stop the animation, you want to:
- capture how much it has rotated (see https://stackoverflow.com/a/9712319/1271826);
- determine the in-flight value by examining the presentation layer;
- set the
transform
of the layer accordingly (so that it stays there when you remove the animation); and - remove the animation
Thus:
var angle: CGFloat?
func stopRotate() {
let transform = cdImage.layer.presentation()!.transform
angle = atan2(transform.m12, transform.m11);
cdImage.layer.transform = transform
cdImage.layer.removeAnimation(forKey: "rotationAnimation")
}
You can then start the animation from that angle
when you restart it.
Is CABasicAnimation's toValue relative to initial position?
The issue is that you're moving the frame
of the layer, but the blue dot path
of this CAShapeLayer
has been offset within that layer's frame
by self.view.center.x - 60 - 5, 163.5
, the coordinates you specified when creating the path
for that CAShapeLayer
. So when you animate the position
of the layer, the blue dot will still be offset by that amount from this new position
.
The fix is to define the path
of the CAShapeLayer
to be in the upper left corner of the layer, e.g.
CAShapeLayer *blueDot = [CAShapeLayer layer];
blueDot.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(5, 5) radius:5 startAngle:0 endAngle:M_PI * 2 clockwise:true].CGPath; // note that using arc often generates a better looking circle, probably not noticeable at this size, just probably good practice
blueDot.fillColor = [UIColor blueColor].CGColor;
Now when you move the position
, that's where the blue dot's origin will move.
As an aside, I'd generally create a separate UIView
and add this CAShapeLayer
to that. This way, you can enjoy a variety of different features:
- you can enjoy
UIView
block-based animation; - you can use
center
to move it around so that when you move itscenter
to thecenter
of its superview, it will really be centered (right now, if you move theposition
of the layer toself.view.center
, it's not really quite centered, as it will be the upper left corner of the blue dot's layer that will be in the center); - you can use UIKit Dynamics to snap it to location or define other behaviors,
- you can use auto-layout constraints to dictate placement if you want,
- you could define a
UIView
subclass that isIBDesignable
with this dot as a view so that you could add it on a storyboard and see what it looks like; etc.
Related Topics
Passing Variables Between View Controllers
How to Identify iOS Device Uniquely
Replace a Particular Color Inside an Image with Another Color
How to Access an Xcassets Directory on the Filesystem
How to Add Text Using Paths Drawing
Intercepting Phone Call - iPhone (Correct Method to Hook in Coretelephony)
Calling Performseguewithidentifier Doesn't Call Shouldperformseguewithidentifier
Why Force Unwrapping Is Required in Case of Enum and Switch
Swift Invalidate Timer Doesn't Work
How to Pass Information Between Storyboard Segues
How to Scroll to a Particluar Index in Collection View in Swift
Invalid Signature (Invalid Binary) on Itunes Connect
Is There a Simple Way to Edit/Modify a Uilocalnotification
iOS - Is Motion Activity Enabled in Settings > Privacy > Motion Activity
Proper Usage of the Alamofire's Urlrequestconvertible
How to Change the Blue Highlight Color of a Uitableviewcell