Preserve Ripple effect over UIImageView - iphone

I want to preserve Ripple effect on my UIImageView. I know we can animate image for ripple effect , but preserve. In other words I want a rippled image.
I know we can animate image using
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:25.0];
[UIView setAnimationTransition:(UIViewAnimationTransition) 110 forView:imgRipple cache:NO];
[UIView commitAnimations];
But it animates , doesn't keep the ripple. I think we can get rippled image if we Pause or Stop animation before it ends. Is it possible? how can we Pause or Stop UIView Animation?
If there is any alternate to this trick ,kindly mention it.
Thanks.

I got my solution,
used this code
CFTimeInterval pausedTime = [imgRipple.layer convertTime:CACurrentMediaTime() fromLayer:nil];
imgRipple.layer.speed = 0.0;
imgRipple.layer.timeOffset = pausedTime;
and it paused

I'm not sure if it gives you what you want, but you can stop animations using those:
#import <QuartzCore/QuartzCore.h>
[CATransaction begin];
[myView.layer removeAllAnimations];
[CATransaction commit];
You can get the current state before stopping animation using presentationLayer:
CALayer* myPreLayer = [myView.layer presentationLayer];
CATransform3D currentTransform = [myPreLayer transform];
//if you need some specific info, you can use key-value pairs
float currentAngle = [[myPreLayer valueForKeyPath:#"transform.rotation.z"] floatValue];

swift 5
Imageview set ripple animation its working fine......
#IBOutlet weak var imageview: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageview.layer.cornerRadius = imageview.bounds.width / 2
self.animateImage()
}
func animateImage() {
addRippleEffect(to: viewAnimation)
}
func addRippleEffect(to referenceView: UIView) {
/*! Creates a circular path around the view*/
let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: referenceView.bounds.size.width, height: referenceView.bounds.size.height))
/*! Position where the shape layer should be */
let shapePosition = CGPoint(x: referenceView.bounds.size.width / 2.0, y: referenceView.bounds.size.height / 2.0)
let rippleShape = CAShapeLayer()
rippleShape.bounds = CGRect(x: 0, y: 0, width: referenceView.bounds.size.width, height: referenceView.bounds.size.height)
rippleShape.path = path.cgPath
rippleShape.fillColor = UIColor.clear.cgColor
rippleShape.strokeColor = UIColor.black.cgColor
rippleShape.lineWidth = 5
rippleShape.position = shapePosition
rippleShape.opacity = 0
/*! Add the ripple layer as the sublayer of the reference view */
referenceView.layer.addSublayer(rippleShape)
/*! Create scale animation of the ripples */
let scaleAnim = CABasicAnimation(keyPath: "transform.scale")
scaleAnim.fromValue = NSValue(caTransform3D: CATransform3DIdentity)
scaleAnim.toValue = NSValue(caTransform3D: CATransform3DMakeScale(2, 2, 1))
/*! Create animation for opacity of the ripples */
let opacityAnim = CABasicAnimation(keyPath: "opacity")
opacityAnim.fromValue = 1
opacityAnim.toValue = 0
/*! Group the opacity and scale animations */
let animation = CAAnimationGroup()
animation.animations = [scaleAnim, opacityAnim]
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.duration = CFTimeInterval(1.0)
animation.repeatCount = .infinity
animation.isRemovedOnCompletion = true
rippleShape.add(animation, forKey: "rippleEffect")
}

Related

Animate roundedrect with CoreAnimation

Guys I have the following code to produce a bezierpath
firstLine.path = UIBezierPath(roundedRect: CGRect(x: frameSize.width / 2.2, y: frameSize.height / 2.6, width: 4, height: 4), cornerRadius: 10).cgPath
I try to animate the firstLine by moving it to the right of the screen by x value and back to the original position. But I don't know what to use for the keyPath in the animation.
let animation = CABasicAnimation(keyPath: "??")
animation.toValue = // this should be the position to move?
animation.duration = 0.5 // duration of the animation
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) // animation curve is Ease Out
animation.fillMode = kCAFillModeBoth // keep to value after finishing
animation.isRemovedOnCompletion = false // don't remove after finishing
animation.autoreverses = true
animation.repeatCount = HUGE
Can someone help me out on this :)
From: https://developer.apple.com/reference/quartzcore/cabasicanimation
You create an instance of CABasicAnimation using the inherited init(keyPath:) method, specifying the key path of the property to be animated in the render tree.
Example of what you want:
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = [startX, startY]
animation.toValue = [destinationX, destinationY]

How to make a video pause when a calayer appears

I am trying to make a calayer pause the video when it appears. I do not know how to go about doing this whether I add it as another calayer or animation. Here is my code when I add the calayer before exporting it. So when the animation happens I would like to have the video pause when it appears and then resume once the animation stops.
let titleLayer = CATextLayer()
titleLayer.backgroundColor = NSColor.clearColor().CGColor
titleLayer.string = "Dummy text"
titleLayer.font = NSFont(name: "Helvetica", size: 28)
titleLayer.shadowOpacity = 0.5
titleLayer.alignmentMode = kCAAlignmentCenter
titleLayer.frame = CGRectMake(0, 50, size.width, size.height / 6)
let animation: CABasicAnimation = CABasicAnimation(keyPath: "opacity")
animation.duration = 0
animation.fromValue = Int(1.0)
animation.toValue = Int(0.0)
animation.beginTime = 5
animation.removedOnCompletion = false
animation.fillMode = kCAFillModeForwards
titleLayer.addAnimation(animation, forKey: "animateOpacity")
You can pause the video before you add your animation to the layer and then resume it when the animation finishes. To know when the animation finishes, you can have an object be a delegate of animation and then resume playback in the animationDidStop:finished: delegate method.
func showTitle() {
// configure animation { .. }
animation.delegate = self
// pause video
titleLayer.addAnimation(animation, forKey: "animateOpacity")
}
func animationDidStop(anim: CAAnimation, finished flag: Bool) {
// play video
}

Animating UILabel Font Size Change

I am currently making an application that uses a custom View Controller container. Multiple views are on the screen at one time and when one is tapped, the selected view controller animates to full screen. In doing so, the selected view controllers subviews scale as well (frame, font size, etc.) Though, UILabel's font property is not animatable leading to issues. I have tried multiple solutions but all flat out suck.
The solutions I have tried are:
Take a screenshot of the larger view and animating the change (similar to how Flipboard does)
Animate by using the transform property
Zooming out a UIScrollView and zooming it in when brought to full screen.
Setting adjustsFontSizeToFitWidth to YES and setting the fontSize prior to animation
One has been the best solution so far but I am not satisfied with it.
I'm looking for other suggestions if anyone has any or a UILabel substitue that animates smoothly using [UIView animate..].
Here is a good example that is similar to what I would like my UILabel to do:
http://www.cocoawithlove.com/2010/09/zoomingviewcontroller-to-animate-uiview.html
EDIT: This code works
// Load View
self.label = [[UILabel alloc] init];
self.label.text = #"TEXT";
self.label.font = [UIFont boldSystemFontOfSize:20.0];
self.label.backgroundColor = [UIColor clearColor];
[self.label sizeToFit];
[self.view addSubview:self.label];
// Animation
self.label.font = [UIFont boldSystemFontOfSize:80.0];
self.label.transform = CGAffineTransformScale(self.label.transform, .25, .25);
[self.label sizeToFit];
[UIView animateWithDuration:1.0 animations:^{
self.label.transform = CGAffineTransformScale(self.label.transform, 4.0, 4.0);
self.label.center = self.view.center;
} completion:^(BOOL finished) {
self.label.font = [UIFont boldSystemFontOfSize:80.0];
self.label.transform = CGAffineTransformScale(self.label.transform, 1.0, 1.0);
[self.label sizeToFit];
}];
You can change the size and font of your UILabel with animation like below .. here I just put the example of how to change the font of UILabel with transform Animation ..
yourLabel.font = [UIFont boldSystemFontOfSize:35]; // set font size which you want instead of 35
yourLabel.transform = CGAffineTransformScale(yourLabel.transform, 0.35, 0.35);
[UIView animateWithDuration:1.0 animations:^{
yourLabel.transform = CGAffineTransformScale(yourLabel.transform, 5, 5);
}];
For 2017 onwards....
Swift 3.0, 4.0
UIView.animate(withDuration: 0.5) {
label.transform = CGAffineTransform(scaleX: 1.1, y: 1.1) //Scale label area
}
Critical:
The critical point to avoid blurring is you must begin with the biggest size, and shrink it. Then expand to "1" when needed.
For quick "pops" (like a highlight animation) it's OK to expand beyond 1 but if you are transitioning between two sizes, make the larger size the "correct" normal one.
I've created UILabel extension in Swift.
import UIKit
extension UILabel {
func animate(font: UIFont, duration: TimeInterval) {
// let oldFrame = frame
let labelScale = self.font.pointSize / font.pointSize
self.font = font
let oldTransform = transform
transform = transform.scaledBy(x: labelScale, y: labelScale)
// let newOrigin = frame.origin
// frame.origin = oldFrame.origin // only for left aligned text
// frame.origin = CGPoint(x: oldFrame.origin.x + oldFrame.width - frame.width, y: oldFrame.origin.y) // only for right aligned text
setNeedsUpdateConstraints()
UIView.animate(withDuration: duration) {
//L self.frame.origin = newOrigin
self.transform = oldTransform
self.layoutIfNeeded()
}
}
}
Uncomment lines if the label text is left or right aligned.
You could also use CATextLayer which has fontSize as an animatable property.
let startFontSize: CGFloat = 20
let endFontSize: CGFloat = 80
let textLayer = CATextLayer()
textLayer.string = "yourText"
textLayer.font = yourLabel.font.fontName as CFTypeRef?
textLayer.fontSize = startFontSize
textLayer.foregroundColor = UIColor.black.cgColor
textLayer.contentsScale = UIScreen.main.scale //for some reason CATextLayer by default only works for 1x screen resolution and needs this line to work properly on 2x, 3x, etc. ...
textLayer.frame = parentView.bounds
parentView.layer.addSublayer(textLayer)
//animation:
let duration: TimeInterval = 1
textLayer.fontSize = endFontSize //because upon completion of the animation CABasicAnimation resets the animated CALayer to its original state (as opposed to changing its properties to the end state of the animation), setting fontSize to endFontSize right BEFORE the animation starts ensures the fontSize doesn't jump back right after the animation.
let fontSizeAnimation = CABasicAnimation(keyPath: "fontSize")
fontSizeAnimation.fromValue = startFontSize
fontSizeAnimation.toValue = endFontSize
fontSizeAnimation.duration = duration
fontSizeAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
textLayer.add(fontSizeAnimation, forKey: nil)
I used it in my project: https://github.com/yinanq/AngelListJobs
This animation keeps the font top left aligned (unlike CGAffineTransformScale scaling the label from center), pro or con depending on your needs. A disadvantage of CATextLayer is that CALayers don't work with autolayout constraint animation (which I happened to need and solved it by making a UIView containing just the CATextLayer and animating its constraints).
For those not looking for a transform, but actual value change:
UIView.transition(with: label, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.label.font = UIFont.systemFont(ofSize: 15)
}) { isFinished in }
For someone who wants to adjust direction of animation
I have created an extension for UILabel to animate font size change
extension UILabel {
func animate(fontSize: CGFloat, duration: TimeInterval) {
let startTransform = transform
let oldFrame = frame
var newFrame = oldFrame
let scaleRatio = fontSize / font.pointSize
newFrame.size.width *= scaleRatio
newFrame.size.height *= scaleRatio
newFrame.origin.x = oldFrame.origin.x - (newFrame.size.width - oldFrame.size.width) * 0.5
newFrame.origin.y = oldFrame.origin.y - (newFrame.size.height - oldFrame.size.height) * 0.5
frame = newFrame
font = font.withSize(fontSize)
transform = CGAffineTransform.init(scaleX: 1 / scaleRatio, y: 1 / scaleRatio);
layoutIfNeeded()
UIView.animate(withDuration: duration, animations: {
self.transform = startTransform
newFrame = self.frame
}) { (Bool) in
self.frame = newFrame
}
}
If you want to adjust direction of animation, use below method and put a suitable anchor point.
SWIFT
struct LabelAnimateAnchorPoint {
// You can add more suitable archon point for your needs
static let leadingCenterY = CGPoint.init(x: 0, y: 0.5)
static let trailingCenterY = CGPoint.init(x: 1, y: 0.5)
static let centerXCenterY = CGPoint.init(x: 0.5, y: 0.5)
static let leadingTop = CGPoint.init(x: 0, y: 0)
}
extension UILabel {
func animate(fontSize: CGFloat, duration: TimeInterval, animateAnchorPoint: CGPoint) {
let startTransform = transform
let oldFrame = frame
var newFrame = oldFrame
let archorPoint = layer.anchorPoint
let scaleRatio = fontSize / font.pointSize
layer.anchorPoint = animateAnchorPoint
newFrame.size.width *= scaleRatio
newFrame.size.height *= scaleRatio
newFrame.origin.x = oldFrame.origin.x - (newFrame.size.width - oldFrame.size.width) * animateAnchorPoint.x
newFrame.origin.y = oldFrame.origin.y - (newFrame.size.height - oldFrame.size.height) * animateAnchorPoint.y
frame = newFrame
font = font.withSize(fontSize)
transform = CGAffineTransform.init(scaleX: 1 / scaleRatio, y: 1 / scaleRatio);
layoutIfNeeded()
UIView.animate(withDuration: duration, animations: {
self.transform = startTransform
newFrame = self.frame
}) { (Bool) in
self.layer.anchorPoint = archorPoint
self.frame = newFrame
}
}
}
OBJECTIVE-C
// You can add more suitable archon point for your needs
#define kLeadingCenterYAnchorPoint CGPointMake(0.f, .5f)
#define kTrailingCenterYAnchorPoint CGPointMake(1.f, .5f)
#define kCenterXCenterYAnchorPoint CGPointMake(.5f, .5f)
#define kLeadingTopAnchorPoint CGPointMake(0.f, 0.f)
#implementation UILabel (FontSizeAnimating)
- (void)animateWithFontSize:(CGFloat)fontSize duration:(NSTimeInterval)duration animateAnchorPoint:(CGPoint)animateAnchorPoint {
CGAffineTransform startTransform = self.transform;
CGRect oldFrame = self.frame;
__block CGRect newFrame = oldFrame;
CGPoint archorPoint = self.layer.anchorPoint;
CGFloat scaleRatio = fontSize / self.font.pointSize;
self.layer.anchorPoint = animateAnchorPoint;
newFrame.size.width *= scaleRatio;
newFrame.size.height *= scaleRatio;
newFrame.origin.x = oldFrame.origin.x - (newFrame.size.width - oldFrame.size.width) * animateAnchorPoint.x;
newFrame.origin.y = oldFrame.origin.y - (newFrame.size.height - oldFrame.size.height) * animateAnchorPoint.y;
self.frame = newFrame;
self.font = [self.font fontWithSize:fontSize];
self.transform = CGAffineTransformScale(self.transform, 1.f / scaleRatio, 1.f / scaleRatio);
[self layoutIfNeeded];
[UIView animateWithDuration:duration animations:^{
self.transform = startTransform;
newFrame = self.frame;
} completion:^(BOOL finished) {
self.layer.anchorPoint = archorPoint;
self.frame = newFrame;
}];
}
#end
For example, to animate changing label font size to 30, duration 1s from center and scale bigger. Simply call
SWIFT
YOUR_LABEL.animate(fontSize: 30, duration: 1, animateAnchorPoint: LabelAnimateAnchorPoint.centerXCenterY)
OBJECTIVE-C
[YOUR_LABEL animateWithFontSize:30
duration:1
animateAnchorPoint:kCenterXCenterYAnchorPoint];
Swift 3.0 & Swift 4.0
UIView.animate(withDuration: 0.5, delay: 0.1, options: .curveLinear, animations: {
label.transform = label.transform.scaledBy(x:4,y:4) //Change x,y to get your desired effect.
} ) { (completed) in
//Animation Completed
}
I found each of the suggestions here inadequate for these reasons:
They don't actually change the font size.
They don't play well with frame sizing & auto layout.
Their interface is non-trivial and/or doesn't play nice inside animation blocks.
In order to retain all of these features & still get a smooth animation transition I've combined the transform approach and the font approach.
The interface is simple. Just update the fontSize property and you'll update the font's size. Do this inside an animation block and it'll animate.
#interface UILabel(MPFontSize)
#property(nonatomic) CGFloat fontSize;
#end
As for the implementation, there's the simple way, and there's the better way.
Simple:
#implementation UILabel(MPFontSize)
- (void)setFontSize:(CGFloat)fontSize {
CGAffineTransform originalTransform = self.transform;
UIFont *targetFont = [self.font fontWithSize:fontSize];
[UIView animateWithDuration:0 delay:0 options:0 animations:^{
self.transform = CGAffineTransformScale( originalTransform,
fontSize / self.fontSize, fontSize / self.fontSize );
} completion:^(BOOL finished) {
self.transform = originalTransform;
if (finished)
self.font = targetFont;
}];
}
- (CGFloat)fontSize {
return self.font.pointSize;
};
#end
Now, the problem with this is that the layout can stutter upon completion, because the view's frame is sized based on the original font all the way until the animation completion, at which point the frame updates to accommodate the target font without animation.
Fixing this problem is a little harder because we need to override intrinsicContentSize. You can do this either by subclassing UILabel or by swizzling the method. I personally swizzle the method, because it lets me keep a generic fontSize property available to all UILabels, but that depends on some library code I can't share here. Here is how you would go about this using subclassing.
Interface:
#interface AnimatableLabel : UILabel
#property(nonatomic) CGFloat fontSize;
#end
Implementation:
#interface AnimatableLabel()
#property(nonatomic) UIFont *targetFont;
#property(nonatomic) UIFont *originalFont;
#end
#implementation AnimatableLabel
- (void)setFontSize:(CGFloat)fontSize {
CGAffineTransform originalTransform = self.transform;
self.originalFont = self.font;
self.targetFont = [self.font fontWithSize:fontSize];
[self invalidateIntrinsicContentSize];
[UIView animateWithDuration:0 delay:0 options:0 animations:^{
self.transform = CGAffineTransformScale( originalTransform,
fontSize / self.fontSize, fontSize / self.fontSize );
} completion:^(BOOL finished) {
self.transform = originalTransform;
if (self.targetFont) {
if (finished)
self.font = self.targetFont;
self.targetFont = self.originalFont = nil;
[self invalidateIntrinsicContentSize];
}
}];
}
- (CGFloat)fontSize {
return self.font.pointSize;
};
- (CGSize)intrinsicContentSize {
#try {
if (self.targetFont)
self.font = self.targetFont;
return self.intrinsicContentSize;
}
#finally {
if (self.originalFont)
self.font = self.originalFont;
}
}
#end
If you want to animate the text size from another anchor point, here is the Swift 5 solution:
How to apply:
yourLabel.setAnimatedFont(.systemFont(ofSize: 48), duration: 0.2, anchorPointX: 0, anchorPointY: 1)
Extensions:
extension UILabel {
/// Animate font size from a given anchor point of the label.
/// - Parameters:
/// - duration: Animation measured in seconds
/// - anchorPointX: 0 = left, 0.5 = center, 1 = right
/// - anchorPointY: 0 = top, 0.5 = center, 1 = bottom
func setAnimatedFont(_ font: UIFont, duration: TimeInterval, anchorPointX: CGFloat, anchorPointY: CGFloat) {
guard let oldFont = self.font else { return }
setAnchorPoint(CGPoint(x: anchorPointX, y: anchorPointY))
self.font = font
let scaleFactor = oldFont.pointSize / font.pointSize
let oldTransform = transform
transform = transform.scaledBy(x: scaleFactor, y: scaleFactor)
setNeedsUpdateConstraints()
UIView.animate(withDuration: duration) {
self.transform = oldTransform
self.layoutIfNeeded()
}
}
}
extension UIView {
/// Change the anchor point without moving the view's position.
/// - Parameters:
/// - point: The layer's bounds rectangle.
func setAnchorPoint(_ point: CGPoint) {
let oldOrigin = frame.origin
layer.anchorPoint = point
let newOrigin = frame.origin
let translation = CGPoint(x: newOrigin.x - oldOrigin.x, y: newOrigin.y - oldOrigin.y)
translatesAutoresizingMaskIntoConstraints = true
center = CGPoint(x: center.x - translation.x, y: center.y - translation.y)
}
}

adding bounce effect to appearance of UIImageView

How can I add a bounce effect when I am about to show an UIImageView as a subview? Do I have to use CoreAnimation to do this? My only guess right now is to use CAKeyframeAnimation, please let me know if there's a better way. Here's my current code:
CABasicAnimation * theAnimation=[CABasicAnimation animationWithKeyPath:#"transform.translation.y"];
theAnimation.delegate = self;
theAnimation.duration = 1.0;
theAnimation.fromValue = [NSNumber numberWithFloat:notif.center.y];
theAnimation.toValue = [NSNumber numberWithFloat:notif.center.y-20];
theAnimation.repeatCount = 3;
y-axis animation using CABasicAnimation:
CGPoint origin = self.imageView.center;
CGPoint target = CGPointMake(self.imageView.center.x, self.imageView.center.y+100);
CABasicAnimation *bounce = [CABasicAnimation animationWithKeyPath:#"position.y"];
bounce.duration = 0.5;
bounce.fromValue = [NSNumber numberWithInt:origin.y];
bounce.toValue = [NSNumber numberWithInt:target.y];
bounce.repeatCount = 2;
bounce.autoreverses = YES;
[self.imageView.layer addAnimation:bounce forKey:#"position"];
If you want to implement shrink and grow you have to add a CGAffineTransformMakeScale, eg:
// grow
CGAffineTransform transform = CGAffineTransformMakeScale(1.3, 1.3);
imageView.transform = transform;
Bouncy (expand/shrink) animation in Swift:
var selected: Bool {
willSet(selected) {
let expandTransform:CGAffineTransform = CGAffineTransformMakeScale(1.2, 1.2);
if (!self.selected && selected) {
self.imageView.image = SNStockCellSelectionAccessoryViewImage(selected)
self.imageView.transform = expandTransform
UIView.animateWithDuration(0.4,
delay:0.0,
usingSpringWithDamping:0.40,
initialSpringVelocity:0.2,
options: .CurveEaseOut,
animations: {
self.imageView.transform = CGAffineTransformInvert(expandTransform)
}, completion: {
//Code to run after animating
(value: Bool) in
})
}
}
}
var imageView:UIImageView
If imageView is correctly added to the view as a subview, toggling between selected = false to selected = true should swap the image with a bouncy animation. SNStockCellSelectionAccessoryViewImage just returns a different image based on the current selection state, see below:
private let SNStockCellSelectionAccessoryViewPlusIconSelected:UIImage = UIImage(named:"PlusIconSelected")!
private let SNStockCellSelectionAccessoryViewPlusIcon:UIImage = UIImage(named:"PlusIcon")!
private func SNStockCellSelectionAccessoryViewImage(selected:Bool) -> UIImage {
return selected ? SNStockCellSelectionAccessoryViewPlusIconSelected : SNStockCellSelectionAccessoryViewPlusIcon
}
The GIF example below is a bit slowed down, the actual animation happens faster:
[UIView animateWithDuration:0.8
delay:0
usingSpringWithDamping:0.5
initialSpringVelocity:0.5
options:(UIViewAnimationOptionAutoreverse|
UIViewAnimationOptionRepeat)
animations:^{
CGRect frame = view.frame;
frame.origin.y -= 8;
view.frame = frame;
} completion:nil];
Play with the values to get different effects.
#Jano's answer in Swift
let origin:CGPoint = self.image.center
let target:CGPoint = CGPointMake(self.image.center.x, self.image.center.y+100)
let bounce = CABasicAnimation(keyPath: "position.y")
bounce.duration = 1
bounce.fromValue = origin.y
bounce.toValue = target.y
bounce.repeatCount = 2
bounce.autoreverses = true
self.image.layer.addAnimation(bounce, forKey: "position")
I followed this link
func imageViewBounceEffect() {
yourImageView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
UIView.animate(withDuration: 1.35, delay: 0,
usingSpringWithDamping: 0.25,
initialSpringVelocity: 5,
options: .curveEaseOut,
animations: {
self.yourImageView.transform = .identity
})
}

Moving an object along a curve in iPhone development

I wanted to animate an image object by moving it along a particular curve. It is not a general or random curve but rather a curve which follows a particular path on screen.
Currently, Im manually specifying the list of x and y co-ordinates of the path along which i want the image object to move by setting its frame each time. This is a laborious process in the sense that im setting the specific x and y coordinates of the path and moving the image along it. Is there a more efficient way to do this?
Is there a way that i can specify,say, just about 15 - 20 points and have a curve traced along those to move the object? Any other way to acheive this? Any help would be much appreciated. Thanks.
You could use a combination of UIBezierPath and CAKeyFrameAnimation.
I found a very useful blog post dealing with this subject.
http://oleb.net/blog/2010/12/animating-drawing-of-cgpath-with-cashapelayer/
Here's a simplified version of what I used (it just animates the drawing of a square):
UIBezierPath *customPath = [UIBezierPath bezierPath];
[customPath moveToPoint:CGPointMake(100,100)];
[customPath addLineToPoint:CGPointMake(200,100)];
[customPath addLineToPoint:CGPointMake(200,200)];
[customPath addLineToPoint:CGPointMake(100,200)];
[customPath addLineToPoint:CGPointMake(100,100)];
UIImage *movingImage = [UIImage imageNamed:#"foo.png"];
CALayer *movingLayer = [CALayer layer];
movingLayer.contents = (id)movingImage.CGImage;
movingLayer.anchorPoint = CGPointZero;
movingLayer.frame = CGRectMake(0.0f, 0.0f, movingImage.size.width, movingImage.size.height);
[self.view.layer addSublayer:movingLayer];
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
pathAnimation.duration = 4.0f;
pathAnimation.path = customPath.CGPath;
pathAnimation.calculationMode = kCAAnimationLinear;
[movingLayer addAnimation:pathAnimation forKey:#"movingAnimation"];
In Swift-3 version of #Jilouc :-
override func viewDidLoad() {
super.viewDidLoad()
addAdditiveAnimation()
initiateAnimation()
}
//curve which follows a particular path
func pathToTrace() -> UIBezierPath {
let path = UIBezierPath(ovalIn: CGRect(x: 120 , y: 120, width: 100, height: 100))
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 1.0
self.view.layer.addSublayer(shapeLayer)
return path
}
func addAdditiveAnimation() {
let movement = CAKeyframeAnimation(keyPath: "position")
movement.path = pathToTrace().cgPath
movement.duration = 5
movement.repeatCount = HUGE
movement.calculationMode = kCAAnimationPaced
movement.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]
self.movement = movement
}
func createLayer() -> CALayer {
let layer = CALayer()
let image = UIImage(named: "launch.png")
layer.frame = CGRect(x: 0 , y: 0, width: (image?.size.width),height: (image?.size.height))
layer.position = CGPoint(x: 5, y: 5)
layer.contents = image?.cgImage
layer.anchorPoint = .zero
layer.backgroundColor = UIColor.red.cgColor
//layer.cornerRadius = 5
self.view.layer.addSublayer(layer)
return layer
}
func initiateAnimation() {
let layer = createLayer()
layer.add(self.movement, forKey: "Object Movement")
}
Github Demo