Im struck with Animation. I would like to animate in below sequence as shown in picture.
Please click here for Image
All are views i.e., outerView, dot1, dot2, dot3 . I've implemented code to animate dots but need your help to animate outerview and adding everything in sequence
let transition = CATransition()
transition.duration = 2;
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault)
transition.speed = 1.0
dot3?.layer.add(transition, forKey: nil)
transition.beginTime = CACurrentMediaTime() + 0.11
dot2?.layer.add(transition, forKey: nil)
transition.beginTime = CACurrentMediaTime() + 0.22
dot1?.layer.add(transition, forKey: nil)
Please help me animating in sequence - outerView starting, dots and closing outerView like shown
You're on the right path, except obviously there will be a lot more animations than the few that you've shown in the snippet. There's no reason why you can't continue building this animation using the CAAnimation classes, but I suspect that using the newer UIViewPropertyAnimator classes (will need to target iOS10) will be useful because they allow you to 'scrub' the steps in the animation which will be useful debugging. Here's a good intro: dzone.com/articles/ios-10-day-by-day-uiviewpropertyanimator
Expanding on this comment to a proper answer...
Using animateWithKeyframes is a pretty decent solution to create this animation in code. Here's a snippet of what this could look like:
let capsule: UIView // ... the container view
let capsuleDots [UIView] //... the three dots
let capsuleFrameWide, capsuleFrameNarrow: CGRect //.. frames for the capsule
let offstageLeft, offstageRight: CGAffineTransform // transforms to move dots to left or right
let animator = UIViewPropertyAnimator(duration: 2, curve: .easeIn)
// the actual animation occurs in 4 steps
animator.addAnimations {
UIView.animateKeyframes(withDuration: 2, delay: 0, options: [.calculationModeLinear], animations: {
// step 1: make the capsule grow to large size
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.1) {
capsule.bounds = capsuleFrameWide
}
// step 2: move the dots to their default positions, and fade in
UIView.addKeyframe(withRelativeStartTime: 0.1, relativeDuration: 0.1) {
capsuleDots.forEach({ dot in
dot.transform = .identity
dot.alpha = 1.0
})
}
// step 3: fade out dots and translate to the right
UIView.addKeyframe(withRelativeStartTime: 0.8, relativeDuration: 0.1) {
capsuleDots.forEach({ dot in
dot.alpha = 0.0
dot.transform = offstageRight
})
}
// step4: make capsure move to narrow width
UIView.addKeyframe(withRelativeStartTime: 0.9, relativeDuration: 0.1) {
capsule.bounds = capsuleFrameNarrow
}
})
}
Wrapping the keyframes in a UIViewPropertyAnimator makes it easy to scrub the animation (among other things).
In case it's useful for anyone, I've pushed a small project to GitHub that allows you to jump in an explore/refine/debug animations with UIViewPropertyAnimator. It includes boilerplate for connecting the UISlider to the animation so all you have to focus on is the animation itself.
This is all for debugging the animation, for production of course you'll probably want to remove hard coded sizes so it can be potentially reused at different scales etc.
It very easy to implement
animatedImage(with:duration:)
or
var animationImages: [UIImage]?
example:
UIImageView.animationImages = [image1, image2, image3, image4,...]
UIImageView.animationDuration = 5
UIImageView.startAnimating()
You will get ordered animation with couple of lines only
Related
I'm following this tutorial. The code snippet can be found here.In some animations, the animation being messaged directly onto the UIView. Like the code snippet below.
Question1: What property of what class is this UIView? Or is it that we are messaging onto the UIView of the everything on the screen do what's in the code block? I'm just trying to understand which property we are messaging here...
protocol Flashable {}
extension Flashable where Self: UIView {
func flash() {
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn, animations: {
self.alpha = 1.0
}) { (animationComplete) in
if animationComplete == true {
UIView.animate(withDuration: 0.3, delay: 2.0, options: .curveEaseOut, animations: {
self.alpha = 0.0
}, completion: nil)
}
}
}
}
^^^I don't get UIView.animate part. ^^^
And for some it's not directly messaged to the UIView.
extension Jitterable where Self: UIView {
func jitter() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.05
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint.init(x: self.center.x - 5.0, y: self.center.y))
animation.toValue = NSValue(cgPoint: CGPoint.init(x: self.center.x + 5.0, y: self.center.y))
layer.add(animation, forKey: "position")
}
}
^^^The above code is more understandable^^^
However when you want to call them both you do:
errorLabel.flash()
errorLabel.jitter()
Question2: what's the difference between the way both animations are written?
An animation block is global over all views. A given animation block is free (and encouraged) to animate as many views as it would like at the same time. So this is naturally a "global" function, not tied to any specific view.
In object oriented programming, it is common to implement "global" functions as a static/class method on some relevant class. This improves name spacing (and occasionally has other benefits, such as access to class data). That's all that's happening here. UIView is a convenient and expressive place to nest the global animate(withDuration:...) methods. But these methods are really just global functions. There's no strict rule that they even have to animate views.
In your CABasicAnimation code, there's an implicit global CATransaction created as well that wraps up all the current animations. So the "more understandable" version is also a bit more magical and is hiding some of the more complicated parts from you (in a way that can bite you if you don't understand it). The UIView version is actually a bit more explicit in what's going on, especially if you animated multiple things together. That's one reason the UIView wrapper was added, and why it is typically the better approach for animating views.
The animate(withDuration:animations:completion:) function is a type method of UIView.
This means you can just call the function on the Type UIView as you can see in your first code snippet. You can find more information on type methods in the Swift Language Guide
The second argument to this function is the animation you want to be performed. This can be an animation on a single view (in this case on self) or any other amount of views and animations.
The second version of animation is a CABasicAnimation. You set up the animation and then attach it to the specific layer you want.
As the documentation says, a CABasicAnimation is
An object that provides basic, single-keyframe animation capabilities for a layer property.
I'm trying to animate the heading line of a naval-style radar (the spinning part below) in iOS, like this:
My current slow, laggy, and high-overhead solution (pseudocode because the real Swift code is a little lengthy):
create NSTimer to call animate() every 1/60 sec
currentAngle = 0
animate():
create UIBezierPath from center of circle to outside edge at currentAngle
add path to new CAShapeLayer, add layer to views layer
add fade out CABasicAnimation to layer (reduces opacity over time)
increase currentAngle
What's the best way to do this without using a .gif? I have implemented the solution above, but the performance (frame rate and CPU use) is awful. How would you approach this problem?
Your approach is too complex. Don't try to re-draw your image during the animation.
Separate out the part that rotates from the part that fades in and out and animate them separately. Make each animation draw partly transparent so the other animation shows through.
Either create a static image of the part that rotates, or build a layer that creates that image.
If you create an image, you can use UIView animation to animate the rotation on the image view.
If you create a layer that draws the heading line and gradient around it, then use a CABasicAnimation that animates the z rotation of the layer's transform.
I've come to a working Swift 3 solution, thanks to Animations in Swift / iOS 8 article :
let scanInProgress = UIImageView()
scanInProgress.image = UIImage(named: "scanInProgress.png")
scanInProgress.frame = CGRect(x: 150, y: 200, width: 80, height: 200)
view.addSubview(scanInProgress)
let fullRotation = CGFloat(Double.pi * 2)
let duration = 2.0
let delay = 0.0
UIView.animateKeyframes(withDuration: duration, delay: delay, options: [.repeat, .calculationModeLinear], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1/3, animations: {
scanInProgress.transform = CGAffineTransform(rotationAngle: 1/3 * fullRotation)
})
UIView.addKeyframe(withRelativeStartTime: 1/3, relativeDuration: 1/3, animations: {
scanInProgress.transform = CGAffineTransform(rotationAngle: 2/3 * fullRotation)
})
UIView.addKeyframe(withRelativeStartTime: 2/3, relativeDuration: 1/3, animations: {
scanInProgress.transform = CGAffineTransform(rotationAngle: 3/3 * fullRotation)
})
}, completion: {
})
I am creating an application in which I am showing two textfields at login page. In that if user enters incorrect input then it will show Cross symbol in front of the textfields. For showing this symbol I am reducing width of UITextField. I want to do some animation while reducing the width. I have done the following :
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.1
animation.repeatCount = 1
animation.autoreverses = true
animation.fromValue = NSValue(CGPoint: CGPointMake(textFieldEmail.center.x - 5, textFieldEmail.center.y))
animation.toValue = NSValue(CGPoint: CGPointMake(textFieldEmail.center.x + 5, textFieldEmail.center.y))
textFieldEmail.layer.addAnimation(animation, forKey: "position")
self.imageViewCrossPassword.hidden = false
self.passwordRightMarginContraint.constant = 25
The above code is applying shake animation for the same. But I want something different which I can apply to only right side of UITextField. Suggestions are always appreciated. :-)
You can add a width constraint on textFieldEmail and animate it's change with the code like this
self.emailWidthConstraint.constant = self.textFieldEmail.bounds.size.width + 5;
UIView.animateWithDuration(0.1, delay: 0, options:.Autoreverse, animations: { () -> Void in
self.textFieldEmail.layoutIfNeeded()
}, completion: nil);
I am trying to animate the opacity of a CALayer but the values are more than one value to begin with and one to end with.
For example: I want it to animate throw these values: 0.0, 0.7, 0.3, 1.0, 0.5, 0.0
I also want the animation to repeat with auto reverse.
This is what I have for now:
let redLineAnimation = CABasicAnimation(keyPath: "opacity")
redLineAnimation.duration = 0.4
redLineAnimation.autoreverses = true
redLineAnimation.fromValue = 0.0
redLineAnimation.toValue = 1.0
redLineAnimation.repeatCount = Float.infinity
movingRedLineLayer.addAnimation(redLineAnimation, forKey: nil)
I am new to iOS development. What can I do? Thanks!
Take a look at CAKeyframeAnimation. That will give you what you want.
And if what you are animating is a view's layer, you could use the easier-to-use UIView keyframe based animation methods. (Core Animation is pretty tricky, and the docs are spotty)
How can I save each frame of a CoreAnimation based animation (as image files)?
Here's my little playground scene. The animation lasts 2.4 seconds.
let stage = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 300, height: 300))
stage.backgroundColor = UIColor.blueColor();
var dot = UIView(frame: CGRectMake(0, 0, 10, 10))
dot.backgroundColor = UIColor.redColor()
dot.center = stage.center
UIView.animateWithDuration(2.4, animations: { () -> Void in
dot.center.y = dot.center.y + 50
})
I know how to save a static UIView frame as PDF but I am not sure how to hook into the animation sequence while it happens and capture/save the view frame by frame.
Option 1
As far as I see it I need to hook into the animation block and save the stage view for each frame (at a given frame rate?). How can I do this? Is there some sort of callback I can use?
Option 2
While looking for a solution I came across another option that looks even more promising (based on this question and this awesome blog post). Using a CBAnimation, setting a timingFunction and then setting the timeOffset to the progress states I want to render.
Here's an example for the frame at 50% progress (using a different example here).
var drawAnimation = CABasicAnimation(keyPath: "strokeEnd")
…
drawAnimation.fromValue = NSNumber(float: 0.0)
drawAnimation.toValue = NSNumber(float: 1.0)
drawAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
circle.addAnimation(drawAnimation, forKey: "drawCircleAnimation")
circle.speed = 0.0;
circle.timeOffset = 0.5
The problem is that I cannot capture this timeOffset based animation state. When I render the view I only get the final state instead of the frozen animation frame at 0.5.
Any help to resolve this is appreciated.