I have often noticed that UIView animations are often not smooth during the first becomeFirstResponder event when the keyboard appears for the first time. I am referring to animations that occur with the keyboard animation, such as manually scrolling the UIView to make a textField visible. The animation is always smooth after the first time it is executed.
Is there a technical reason why this would be the case? I was thinking that there might be some lazy loading or optimization that happens with UIView animations on the first run, then gets stored in cache for reuse. Are there lessons learned around this? If this is not clear to this audience, I can try to recreate the issue in a test project.
While this does not answer the question WHY this happens, it explains how to fix it.
Why are iOS animations slow the first time they are run?
Basically, you need to do animations on "DID" events rather than "WILL" or "SHOULD". The system performs it's animations during the "will/should" events, so apparently there is some colluding happening. This does not explain why the behavior is inconsistent between the first run and all other runs.
I thought, as may some of you, that I should put the animation in the "textFieldWillBeginEditing" because I wanted the animation to run concurrently with the keyboard animation. Luckily, putting the animation code in "DID" actually still ensures that the animation happens concurrently. Fantastic.
If anyone still has an explanation of the inconsistency between the first and latter runs, I'll still hold his question open and award you with an upvote and question answer. Thanks!
Related
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.
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.
This question has been asked at least as many times as I've searched for the answer but never to my satisfaction. I have a view with 52 subviews representing a deck of cards. The cards appear in the center of the screen, split into two stacks of cards and then merge together as a shuffled deck.
If I call self.standardShuffle() from viewDidAppear(), after instantiating the card images, it works just fine.
But if I call self.standardShuffle() 2 times in a row, the second call catches the first animation in flight and and the animation is ruined. There has to be a way to block the calling thread until the animation chain completes. Timers get too unwieldy and I can't put all of my code inside animation blocks (and it doesn't work, anyway.)
Anybody have the real answer? Please.
There has to be a way to block the calling thread
No there is not. Never block. Never.
If you think the answers you've seen are not satisfactory, you have not read them carefully. Read them and believe them. You can easily (and in many different ways) arrange to be called back after the first animation; that is the moment to begin the second animation. Or, create a single grouped animation that performs two animations in succession.
Those are your choices — your only choices. If you're finding that difficult to reconcile with your code, it's because you've architected your code incorrectly. Rearchitect it. Use the framework, don't fight it.
Facing a really, really weird problem with an OpenGL View we are using in our app to perform some custom animations. As soon as the Open GL View is added into the project, all native View animations slow down. And by slow down I don't mean a drop in frame rate. The animations are stutter-free, except much much MUCH slower than normal (like someone enabled "Toggle Slow Animations" in the Simulator).
This is affecting only view transitions animations, for example:
a. transitionFromView
b. presentViewController (iOS6, or presentModalViewController on earlier)
While regular UIView Animations, CABasicAnimation etc proceed at the regular pace.
I haven't seen anything like this, and the results honestly have to be seen to be believed. :) But any idea what the problem could be (I'm not sure which piece of the code would help you debug, and I'm unfortunately not in a position to share screenshots or video)
It seems like the animation gets stuck while loading the OpenGL View, debug the
Lifecycle methods like loadView, viewWillLoad. The loadview, etc. happens within the
transition animation, maybe you can solve the slow animation by sending most of the loading
code in to a custom method which you call in viewdidload or viewdidappear.
It does not interrupt any other animation because thats a totally new action in the queue.
Hope this helps!
Found the solution accidentally several days later.
The problem turned out to be much sillier and unrelated. It so happens that if you have a UIView beginAnimation block that is not closed properly, future animations get all wonky. This faultily coded animation happened to be triggered at more or less the same time as the OpenGL view was being initialized, which led to my erroneous belief that the OpenGL View was at the root of this.
Thanks for the help!
My app consists of an animation of about 25 frames. The animations is triggered by a button, and all is working. However, upon first invoking the animation, there is a few second delay.
I am aware that this is because the images need to be cached upon their first run in an animation, and I have ~2mb to be cached.
I wish Apple would implement an [animation prepareToPlay] method for this reason.
In the meantime, I'm running the animation within viewDidLoad. This caches the images during the splash screen, but then displays the animation.
There are two alternatives that I would like to consider.
This first is to forcibly cache the images upon initialization during the splash screen, without the animation playing upon loading the view. (read: custom UIImageView prepareToPlay method) --- This solution would be ideal.
The second alternative would be to keep my current code, running the animation at start-up once the view has been loaded, but disabling the invoking button until this first animation completes. Since UIImageView doesn't have it's own completion notification, would the best solution be to set up a timer equal to the animation duration and enable the button and the end of said timer?
Thanks in advance,
Greg
TL;DR --- How to forcibly cache animation images upon initialization.
I also faced problem as yours. I had created my own question here, and I got good answers. Check it out here: Does UIImageView cache images?
Good luck!