How can I animate the movement of a view or image along a curved path?
Solution 1
To expand upon what Nikolai said, the best way to handle this is to use Core Animation to animate the motion of the image or view along a Bezier path. This is accomplished using a CAKeyframeAnimation. For example, I've used the following code to animate an image of a view into an icon to indicate saving (as can be seen in the video for this application):
First of all import QuartzCore header file
#import <QuartzCore/QuartzCore.h>
UIImageView *imageViewForAnimation = [[UIImageView alloc] initWithImage:imageToAnimate];
imageViewForAnimation.alpha = 1.0f;
CGRect imageFrame = imageViewForAnimation.frame;
//Your image frame.origin from where the animation need to get start
CGPoint viewOrigin = imageViewForAnimation.frame.origin;
viewOrigin.y = viewOrigin.y + imageFrame.size.height / 2.0f;
viewOrigin.x = viewOrigin.x + imageFrame.size.width / 2.0f;
imageViewForAnimation.frame = imageFrame;
imageViewForAnimation.layer.position = viewOrigin;
[self.view addSubview:imageViewForAnimation];
// Set up fade out effect
CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[fadeOutAnimation setToValue:[NSNumber numberWithFloat:0.3]];
fadeOutAnimation.fillMode = kCAFillModeForwards;
fadeOutAnimation.removedOnCompletion = NO;
// Set up scaling
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size"];
[resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(40.0f, imageFrame.size.height * (40.0f / imageFrame.size.width))]];
resizeAnimation.fillMode = kCAFillModeForwards;
resizeAnimation.removedOnCompletion = NO;
// Set up path movement
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.calculationMode = kCAAnimationPaced;
pathAnimation.fillMode = kCAFillModeForwards;
pathAnimation.removedOnCompletion = NO;
//Setting Endpoint of the animation
CGPoint endPoint = CGPointMake(480.0f - 30.0f, 40.0f);
//to end animation in last tab use
//CGPoint endPoint = CGPointMake( 320-40.0f, 480.0f);
CGMutablePathRef curvedPath = CGPathCreateMutable();
CGPathMoveToPoint(curvedPath, NULL, viewOrigin.x, viewOrigin.y);
CGPathAddCurveToPoint(curvedPath, NULL, endPoint.x, viewOrigin.y, endPoint.x, viewOrigin.y, endPoint.x, endPoint.y);
pathAnimation.path = curvedPath;
CGPathRelease(curvedPath);
CAAnimationGroup *group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
[group setAnimations:[NSArray arrayWithObjects:fadeOutAnimation, pathAnimation, resizeAnimation, nil]];
group.duration = 0.7f;
group.delegate = self;
[group setValue:imageViewForAnimation forKey:@"imageViewBeingAnimated"];
[imageViewForAnimation.layer addAnimation:group forKey:@"savingAnimation"];
[imageViewForAnimation release];
Solution 2
The way to animate along CGPath using UIView.animateKeyframes
(Swift 4)
private func animateNew() {
let alphaFrom: CGFloat = 1
let alphaTo: CGFloat = 0.3
let sizeFrom = CGSize(width: 40, height: 20)
let sizeTo = CGSize(width: 80, height: 60)
let originFrom = CGPoint(x: 40, y: 40)
let originTo = CGPoint(x: 240, y: 480)
let deltaWidth = sizeTo.width - sizeFrom.width
let deltaHeight = sizeTo.height - sizeFrom.height
let deltaAlpha = alphaTo - alphaFrom
// Setting default values
imageViewNew.alpha = alphaFrom
imageViewNew.frame = CGRect(origin: originFrom, size: sizeFrom)
// CGPath setup for calculating points on curve.
let curvedPath = CGMutablePath()
curvedPath.move(to: originFrom)
curvedPath.addQuadCurve(to: originTo, control: CGPoint(x: originFrom.x, y: originTo.y))
let path = Math.BezierPath(cgPath: curvedPath, approximationIterations: 10)
// Calculating timing parameters
let duration: TimeInterval = 0.7
let numberOfKeyFrames = 16
let curvePoints = Math.Easing.timing(numberOfSteps: numberOfKeyFrames, .easeOutQuad)
UIView.animateKeyframes(withDuration: duration, delay: 0, options: [.calculationModeCubic], animations: {
// Iterating curve points and adding key frames
for point in curvePoints {
let origin = path.point(atPercentOfLength: point.end)
let size = CGSize(width: sizeFrom.width + deltaWidth * point.end,
height: sizeFrom.height + deltaHeight * point.end)
let alpha = alphaFrom + deltaAlpha * point.end
UIView.addKeyframe(withRelativeStartTime: TimeInterval(point.start), relativeDuration: TimeInterval(point.duration)) {
self.imageViewNew.frame = CGRect(origin: origin, size: size)
self.imageViewNew.alpha = alpha
}
}
}, completion: nil)
}
File: Math.Easing.swift
// Inspired by: RBBAnimation/RBBEasingFunction.m: https://github.com/robb/RBBAnimation/blob/master/RBBAnimation/RBBEasingFunction.m
extension Math { public struct Easing { } }
extension Math.Easing {
public enum Algorithm: Int {
case linear, easeInQuad, easeOutQuad, easeInOutQuad
}
@inline(__always)
public static func linear(_ t: CGFloat) -> CGFloat {
return t
}
@inline(__always)
public static func easeInQuad(_ t: CGFloat) -> CGFloat {
return t * t
}
@inline(__always)
public static func easeOutQuad(_ t: CGFloat) -> CGFloat {
return t * (2 - t)
}
@inline(__always)
public static func easeInOutQuad(_ t: CGFloat) -> CGFloat {
if t < 0.5 {
return 2 * t * t
} else {
return -1 + (4 - 2 * t) * t
}
}
}
extension Math.Easing {
public struct Timing {
public let start: CGFloat
public let end: CGFloat
public let duration: CGFloat
init(start: CGFloat, end: CGFloat) {
self.start = start
self.end = end
self.duration = end - start
}
public func multiplying(by: CGFloat) -> Timing {
return Timing(start: start * by, end: end * by)
}
}
public static func process(_ t: CGFloat, _ algorithm: Algorithm) -> CGFloat {
switch algorithm {
case .linear:
return linear(t)
case .easeInQuad:
return easeInQuad(t)
case .easeOutQuad:
return easeOutQuad(t)
case .easeInOutQuad:
return easeInOutQuad(t)
}
}
public static func timing(numberOfSteps: Int, _ algorithm: Algorithm) -> [Timing] {
var result: [Timing] = []
let linearStepSize = 1 / CGFloat(numberOfSteps)
for step in (0 ..< numberOfSteps).reversed() {
let linearValue = CGFloat(step) * linearStepSize
let processedValue = process(linearValue, algorithm) // Always in range 0 ... 1
let lastValue = result.last?.start ?? 1
result.append(Timing(start: processedValue, end: lastValue))
}
result = result.reversed()
return result
}
}
File: Math.BezierPath.swift
. Look on this SO answer: https://stackoverflow.com/a/50782971/1418981
Solution 3
You can animate a UIView's center property using a CAKeyframeAnimation. See the CoreAnimation programming guide.
Related videos on Youtube
Maksim
Lead iOS Applications Developer in Nuday Games, Stockholm, Sweden. Twitter (@maxmikheev) LinkedIn
Updated on July 05, 2022Comments
-
Maksim almost 2 years
I am developing a commerce application. When I add an item to the shopping cart, I want to create an effect where an image of the item follows a curved path and ends up at the cart tab.
How can I create an animation of an image along a curve like this?
-
SALMAN over 12 yearsAnimated images on the iPhone Refer this link. It will help you surely. Thanks.
-
-
Admin almost 15 yearsI got it.But i want to create animation effect something like i am having big image i should get resized image to big image's center then add animation path.How to get it.
-
Brad Larson almost 15 yearsI'm not sure what you're asking, but the animation above does three things: moves the image along a curved path, shrinks it as it moves so that it's tiny by the time it hits the end point, and fades it out as it moves. You can tweak the resizing by changing the from or to values, as needed.
-
Thanks almost 15 yearsWhat do I have to include so that Xcode recognizes all the CAKey...stuff? It says it doesnt find the symbols.
-
Thanks almost 15 yearsThat was the solution. Perfect!
-
Andrey Chernukha about 12 yearsi just can't get what is viewOrigin in your code. it has not been declared. is it CGPoint?
-
rml about 12 yearsI get it. Thanks, @Brad Larson. The viewOrigin must be the original position of the image view to be animated.
-
Alex G over 11 years@BradLarson I know this an older question but Can you please explain what I need to define viewOrigin as?:) Thanks!
-
khaled over 9 years@BradLarson I try to use your Code but it crash because of Bad Access Exception i think the problem because of CAKeyframeAnimation can you try it on ios8