I'm developing an iOS application and I have a simple question.
If you start an animation for an image (like rotating it) for an unlimited time, and then after a few seconds set the alpha to zero to hide it [[self image]setAlpha:0];. Is the animation still working in the background or not? Do you always have to set [layer removeAllAnimations];? I'm asking this because I don't wan't to slow down the cpu.
Thanks!
You really want to use removeAllAnimations, not just setting the alpha to zero. If you use a timer or display link to monitor the presentationLayer (which indicates the current properties of the view) of the animated object, you'll see that the animation is still in progress, even if the alpha is zero.
Related
How do I produce an animation that simulates the burning effect of fire consuming an UIView from top to bottom in Swift?
I found Fireworks, an app that allows users to tweak and try out different settings of CAEmitterLayer with instant results. The effects are great for applying to a whole screen but how would I use it for my purpose - where the UIView must disappear as the fire consumes it from one end to the other?
Is there some tutorial on consuming UIViews with fire using the particle emitter anywhere? I know that I’m supposed to show some code but anything I put here would be irrelevant. I’ve also exhausted my search engine looking for something similar. That’s how I found the Fireworks app actually.
This seems to be a use case that shouldn't be uncommon.
I haven't done much with CAEmitterLayer, so I decided to try my hand at this.
I created a project that does this an posted it on Github. It uses the approach in this Youtube video as a starting point. You can download it here:
FireEmitter project
Here is a small thumbnail of what it looks like:
The project includes a custom subclass of UIView called BurnItDownView
The BurnItDownView is meant to contain other views.
It has one public method, burnItDown(). That triggers the animation.
There are multiple parts to the animation:
A CAEmitterLayer set up to simulate flames burning off a flat surface:
An animation that lowers the emitter layer from the top of the view to the bottom,
A CAGradientView applied as a mask to the view that starts ot fully opaque (with colors of [.clear, .white, .white] and locations of [-0.5, 0, 1] (where the clear color is above the top of the view) and animates the locations property of the gradient view to mask away the view contents from top to bottom. (Animating the locations property to [0, 0, 0], so the entire gradient layer is filled with clear color, fully masking the view's layer.)
Once the view is fully masked, it starts lowering the "birthRate" of the emitter layer in steps until the birth rate is 0. It then holds this step for 2 seconds until all the flame particles have animated away.
Once the flame is fully "extinguised", it resets the locations array to the original value of [-0.5, 0, 1]. This causes an "implicit animation" so the view animates back from the bottom, but quickly
Finally, it resets the emitter layer and emitter cells back to newly a newly created emitter layer and emitter cell to get it ready for the next pass of the animation. (I couldn't figure out how to restore the emitter back to its original state. It was simpler to just create new ones.) It also invokes an Optional completion handler passed to the burnItDown() method. (The app's view controller uses the closure to re-enable the "Burn it down" button.
I was once in your shoe before and came across this Open source library called particle animations.
I would NOT recommend using the library itself since it's deprecated. But I would recommend referring to its source code to get an idea of how to use CAEmitterLayer and CAEmitterCell to make the looks of a Fire!
As you could see from its readme, it has direct examples of Fire. It also states that even Apple and Facebook uses CAEmitterLayer and CAEmitterCell to produce the effect of a fire.
Feel free to ask for more questions.
I have this small view V
v: UICrazyView
which has sundry animations, which run often, and which follow various annoying states and inputs.
Now on top of that, from time to time the whole thing is just hidden
var slideAwaySomePanel: Bool {
didSet {
.. do many other things
v.isHidden = slideAwaySomePanel
}
}
It might be hidden for a minute, an hour, forever, or never.
It occurred to me, while V is hidden ... are the animations still running?
Do they still use a lot of battery/performance?
I was about to override isHidden and start writing a whole lot of fragile PITA code that would know what to do as isHidden is toggled, but maybe that is pointless.
I wish to know
When you isHidden, do all the calculations (and even drawing?) continue for the ongoing animations? Are you still using battery? Should we carefully stop everything during isHidden to save battery / performance. Or, does isHidden stop everything anyway? Or does it still do the timers and curves, but not waste any power on drawing?
Do all the timers and so on actually "pause" when you go in to isHidden? If you have an endless repeating animation, or, a 10 second fade or such, does it "hold" at an exact position, and continue when you not isHidden? Or what happens?
In short should we carefully and tediously stop by-hand animations, when isHidden is in effect? Or is there no point?
(It occurs to me, this is very much like in cg where you either do or don't have animations or other physics ongoing when objects are occluded or out of the frustrum. For this reason game engines simply have a toggle to decide on exactly that behavior "keep going or not when I'm offscreen?")
I'm pretty sure, even though I have no reference, that hidden views are not animated because Core Animation was implemented very efficiently in terms of performance.
Core animation layers and animations have their own clock. The animation state is calculated from this time. The clock continues to run when the view is not visible. Since neither the layer nor the animation object are destroyed by hiding the view, the animation has exactly the same state after reappearing that it would have had if the view had not been hidden.
Apple gives some nice examples how to modify the animation timing for some use cases.
I have made a CAAnimation (CAAnimationGroup to be precise) that scale and fade (opacity) from 0 -> 1 and then back 0 -> 1. The animation also repeats forever
since I have set animationGroup.repeatCount = .greatestFiniteMagnitude. This animation is applied to a UILabel and makes the label appear and disappear with a nice animation over and over.
I now want to change the text between each repetition of the animation (after it disappears but before it appears again). What is the correct way of doing this?
I managed to get this working by removing the animationGroup.repeatCount = .greatestFiniteMagnitude line and manually restarting the animation in the animation delegate method animationDidStop(_:finished:). I also change the text of the label in the delegate method before restarting the animation. I'm not sure if this is the best solution though.
Is there a way to observe the "time progress" of UIView.animateWithDuration...family of methods from UIView /alternatively CA animations?
I am animating a view's frame and I need to be informed how it is progressing.
My line of thinking was I can either
1) tap into CAAnimation related stuff or
2) observe the animated properties (like frame) and do my own calculations each screen frame.
Approach 1) turns out to be a dead end, inspecting the internal of how CAAnimations work told me absolutely nothing...and 2) is flawed as the "model layer tree is updated immediately and tapping into the presentation tree is difficult as the presentation layer is nil when you start.
I am pretty desperate, I was thinking that hooking into CADisplayLink will give me a tick and then I simply check something that is affected by the animation but there is nothing to tap to.
Do you think going the NSTimer way that is launched in the same scope as the animation method is ok? If I know animation duration then I can generate the progress myself.
If all you want is the time value, then you can do math on the CACurrentMediaTime() minus the animation start time. I have a sample project on Github called KeyframeViewAnimations that does exactly that.
That project supports pausing and resuming and scrubbing both UIView and CAAnimation based animations. In both cases it digs into the underlying CAAnimations.
I have another project that uses the values of the animated layer's presentationLayer in order to do hit testing so you can tap on an in-flight view and start/pause the animation. That one can be found here:
iOS-CAAnimation-group-demo
My code uses an NSTimer to update the progress of the animation. It would be better to use a CADisplayLink timer, as you mentioned.
I am also looking at the new UIViewPropertyAnimator class that was added to iOS 10. That makes pausing, reversing, and scrubbing UIView animations easy without having to dig into the underlying CAAnimations. See this thread I just posted:
Is there a way to observe changes to fractionComplete in UIViewPropertyAnimator
So I'm making a simple trivia game and I have a timerView that shrinks as time passes. When the user selects an answer, it needs to stop shrinking immediately - it must be very responsive. I give the user 10 seconds per question. Originally I would animate 10 times (with a duration of 1.0f), calling the next "segment" of animation in the completion block of the previous animation. In the completion block I would check to see if the user has tapped an answer, and if so I don't continue the chain. That solution works fine except that it's not very responsive because it's on a per second basis-- user taps an answer at the start of the second segment and the bar has a noticeable continuation.
My solution to THAT problem was to instead have 1000 animation calls with a duration of 0.01f. After doing that, the responsiveness was on point - the view stops animating as soon as I tap an answer -- the issue though, is that it's not actually 10 seconds, it takes more like 20.
So question number 1: what's the smallest time interval animateWithDuration can actually process properly?
Question number 2: is there a better way to accomplish what I'm trying to do accomplish?
ill answer question two: yes there definitely is a better way, have a look at CADisplayLink
use it to shrink your view a little bit each frame, and end the display link when you need to
the most responsive way is: the user taps an answer, you response in the touch callback, remove animations. you can remove animations by CALayer's removeAllAnimations method
Another way to do it is to set the view to shrinking using a single animation with linear timing, and then set the speed of the view's layer to 0 to pause the animation. When you set the speed on the layer to 0 the animation pauses instantly.
This works because under the covers, UIView animation actually creates and installs CAAnimation objects on the view's layers. It's possible to pause and continue an in-flight UIView animation just like you can a CAAnimation.
I have a project called KeyframeViewAnimations (link) on github that allows you to pause, continue, or "scrub" UIView and CAAnimations back and forth with a slider. You could use that technique. The tricky bit will be figuring out how far along the animation is.