Adding easing to animateKeyframes - ios

I'm animating the scale of a button using animateKeyframes in Swift 3 using the following code:
UIView.animateKeyframes(withDuration: 4, delay: 1, options: [.repeat, .allowUserInteraction, .calculationModeCubic], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.1, animations: {
self.myButton.transform = CGAffineTransform(scaleX: 1,y: 1)
})
UIView.addKeyframe(withRelativeStartTime: 0.1, relativeDuration: 0.1, animations: {
self.myButton.transform = CGAffineTransform(scaleX: 0.9,y: 0.9)
})
}, completion: nil)
What I need to do is add easing to each keyframe. I looked up something that uses the following code that can then be passed to animateKeyframes
let animationOptions: UIViewAnimationOptions = .curveEaseIn
let keyframeAnimationOptions: UIViewKeyframeAnimationOptions = UIViewKeyframeAnimationOptions(rawValue: animationOptions.rawValue)
However, it doesn't quite say exactly how to pass the keyframeAnimationOptions variable, and I can't seem to figure it out myself! Can anybody help?

You can use below code to call the keyframeAnimationOptions variable.
let keyframeAnimationOption=UIViewKeyframeAnimationOptions.CalculationModeCubic
UIView.animateKeyframesWithDuration(duration, delay: delay, options: keyframeAnimationOption, animations:
{
/////Your code //////
}, completion: nil)

Related

Understanding animateKeyframes relative start times/delays

I'm either being stupid, or misunderstanding how keyframe animations work on iOS (or both!). The two animation blocks below produce different results but I would expect them to be the same:
let duration: TimeInterval = 2
UIView.animateKeyframes(withDuration: duration, delay: 0, animations: {
UIView.addKeyframe(withRelativeStartTime: 0.9, relativeDuration: 0.1, animations: {
self.someView.transform = CGAffineTransform(translationX: 0, y: 150)
})
})
UIView.animateKeyframes(withDuration: duration * 0.1, delay: duration * 0.9, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1, animations: {
self.someView.transform = CGAffineTransform(translationX: 0, y: 150)
})
})
Can anyone help me understand why these are different when executed? The first seems to do what I expect, but the second one seems to execute the animation earlier than expected.
You're seeing the effects of the default timing curve on your keyframe animations. The first animation is the last 0.2 seconds of a 2 second animation, and the second animation is the entirety of a 0.2 second animation. The default ease-in-ease-out means that the first animation will be done entirely in the ease-out portion of the curve.
To force a linear curve on both animations you can wrap them inside another animation and set a couple of options:
UIView.animate(withDuration: duration, delay: 0, options: [.curveLinear], animations: {
UIView.animateKeyframes(withDuration: duration, delay: 0, options: [.overrideInheritedDuration, .calculationModeLinear], animations: {
UIView.addKeyframe(withRelativeStartTime: 0.9, relativeDuration: 0.1, animations: {
view1.transform = CGAffineTransform(translationX: 0, y: 150)
})
})
UIView.animateKeyframes(withDuration: duration * 0.1, delay: duration * 0.9, options: [.overrideInheritedDuration, .calculationModeLinear], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1, animations: {
view2.transform = CGAffineTransform(translationX: 0, y: 150)
})
})
}, completion: nil)
Which I think you'll agree is horrible-looking code, but I'm assuming this is an intellectual exercise :)

Second keyframe in UIView.animateKeyframes not getting called

Currently there are 2 CGAffineTransform key frames added to the UIView.animateKeyframes. For some strange reason the second keyframe's animation doesn't appear to happen. It just jumps right back to the starting position after the animation completes.
UIView.animateKeyframes(withDuration: 3, delay: 0, options: [], animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1, animations: {
let scaledBy = CGAffineTransform(scaleX: 1.0, y: 1.0)
self.firstView.transform = scaledBy.translatedBy(x: 120, y: 20).rotated(by: CGFloat.pi/2)
})
UIView.addKeyframe(withRelativeStartTime: 1, relativeDuration: 1, animations: {
let scaledBy = CGAffineTransform(scaleX: 1.5, y: 1.5)
self.firstView.transform = scaledBy.translatedBy(x: 40, y: 60).rotated(by: -CGFloat.pi/2)
self.firstView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi/2)
})
}, completion: {_ in})
It seems like this is very old question. I hope my answer will be helpful though. The issue here is because you didn't get the meaning of relativeStartTime and relativeDuration parameters. The key word here is relative. It means that all your inner animations, added via UIView.addKeyframe function should happen between 0 and 1.
Because this call will create a kind of timeline
UIView.animateKeyframes(withDuration: 3, delay: 0, options: [], animations: {
}, completion: {_ in})
This timeline runs from 0 to 1 (0% to 100%). Which means that relative duration of any animation can't be more than 1 and relative start time also can't be more than 1.

ambiguous reference to member animate

I use this code in viewDidAppear method
UIView.animate(withDuration: 0,5, delay: 0.5,
usingSpringWithDamping: 0.5, initialSpringVelocity: 0.0,
options: [], animations: {
self.loginButton.alpha = CGFloat(1.0)
}
, completion: nil)
in the picture below I have a problem which I can't fix
problem
Little mistake withDuration parameter 0,5 would be 0.5.

iOS Swift - Cannot Change Animation Options with Array [UIViewAnimationOptions]

I have some problem about animation options with array in Swift 2. I can add multiple options in array like this : [.Repeat, UIViewAnimationOptions.Autoreverse] and this is if I add in animation function (first example) :
UIView.animateWithDuration(1, delay: 0, options: [.Repeat, UIViewAnimationOptions.Autoreverse], animations: {
}) { (true) in
}
But I cannot add animation options in array like this (second example) :
var animationOptions = [UIViewAnimationOptions]()
animationOptions.append(UIViewAnimationOptions.Repeat)
animationOptions.append(UIViewAnimationOptions.Autoreverse)
UIView.animateWithDuration(1, delay: 0, options: animationOptions, animations: {
}) { (true) in
}
Can someone help me make animation options in array like second example ?
var animationOptions:UIViewAnimationOptions = .repeat
animationOptions.insert(.autoreverse)
UIView.animate(withDuration: 0.1, delay: 0.1, options: animationOptions, animations: {
}) { (success:Bool) in
}
You need to use the insert from the SetAlgebra Protocol, which the OptionSet conforms to. In the question you are using the Array Object instead of the UIViewAnimationOptions.
You can use animation option like this:
Swift 2
UIView.animateWithDuration(0.2, delay: 0.0, options: [.Repeat,
.Autoreverse, .CurveLinear, .CurveEaseOut, .CurveEaseInOut,
.TransitionCurlUp, .TransitionCurlDown,
.TransitionFlipFromBottom,.TransitionFlipFromLeft,.TransitionFlipFromRight,
.BeginFromCurrentState, .CurveEaseIn], animations: {}, completion: nil)
Swift 3, 4, 5
UIView.animate(withDuration: 0.2, delay: 0.0, options: [.repeat,
.autoreverse, .curveLinear, .curveEaseOut, .curveEaseInOut,
.transitionCurlUp, .transitionCurlDown,
.transitionFlipFromBottom,.transitionFlipFromLeft,.transitionFlipFromRight,
.beginFromCurrentState, .curveEaseIn], animations: {}, completion: nil)

Consecutive fade in fade out animations

I'm trying to implement this animation where a UIButton fades out when a user saves a file. While the button fades out, a UIImageView of a check mark image fades in place of the button. Once the imageview has fully faded in, then it fades out and the button fades back in.
UIView.animateWithDuration(0.60, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.recButton.alpha = 0.0
}, completion: {
(value: Bool) in
UIView.animateWithDuration(0.60, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.checkView.alpha = 1.0
}, completion: {
(value: Bool) in
UIView.animateWithDuration(0.60, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.checkView.alpha = 0.0
}, completion: {
(value: Bool) in
UIView.animateWithDuration(0.60, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.recButton.alpha = 1.0
self.recButton.enabled = true
}, completion: nil)
})
})
})
While the method above does work, its not as smooth as I would like. Is there a better way to go about this?
You need four keyframes for the animation you want: button fade out, image fade in, image fade out, button fade in.
let button: UIButton
let checkmarkImage: UIImageView
button.alpha = 1.0
checkmarkImage = 0.0
UIView.animateKeyframesWithDuration(2.4, delay: 0, options: .CalculationModeLinear, animations: {
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.25, animations: {
button.alpha = 0.0
})
UIView.addKeyframeWithRelativeStartTime(0.25, relativeDuration: 0.25, animations: {
checkmarkImage.alpha = 1.0
})
UIView.addKeyframeWithRelativeStartTime(0.5, relativeDuration: 0.25, animations: {
checkmarkImage.alpha = 0.0
})
UIView.addKeyframeWithRelativeStartTime(0.75, relativeDuration: 0.25, animations: {
button.alpha = 1.0
})
}, completion: nil)
This question raised my curiosity - this keyframe thing is pretty cool.

Resources