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!
Related
I want to make a view/header collaspe/expand animation, but I also want to be able to pause or stop from expanding/collapsing while the user takes his finger off the screen.
I have a current implementation for this, but the animation is not very smooth.
I will also add some images to explain better what I want to do.
So the first image is how it should look when expanded.
The second image is how it should look if the user decides to take take his finger off the screen while he would scroll slowly so the animation/the view should stop in a state similar to that.
The third image is how it should look when it is collapsed.
If someone has any recommendation how I could achieve this I would be very grateful.
If you are targeting iOS 10+, use UIViewPropertyAnimator which encapsulates an animation and allows scrubbing and controlling the animation:
Start, pause, resume, and stop animations; see the methods of the UIViewAnimating protocol.
Add animation blocks after the original animations start using the addAnimations(_:) and addAnimations(_:delayFactor:) methods.
Scrub through a paused animation by modifying the fractionComplete property.
Change the animation’s direction using the isReversed property.
Modify the timing and duration of a partially complete animation by pausing the animation and using the continueAnimation(withTimingParameters:durationFactor:) method to finish it.
You just need to wire up touches to UIViewPropertyAnimator object encapsulating the animation.
There are many good tutorials on how to start with it, e.g. this one.
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.
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!
I've created a custom button because I wanted to be able to compose it of multiple different images. It actually subclasses UIControl instead of UIButton. This led to the issue of highlighting the images while it was being tapped.
So, I followed the advice in this question by creating a category on UIImage to emulate the highlighting of a standard UIButton: How to implement highlighting on UIImage like UIButton does when tapped?
I'm triggering the image tinting based on the UIControlEventTouchDown, UIControlEventTouchUpInside, and UIControlEventTouchUpOutside events.
This mostly works, except the timing is a bit off. With a standard UIButton, no matter how long or short the user taps down, the highlighting always happens, but with my implementation, if the user taps very quickly (which is most times), the highlighting doesn't happen.
I'm assuming this may be because the screen isn't getting re-drawn between the time the user taps down and up, but I'm not totally sure.
What I've tried:
Calling setNeedsDisplay right after swapping out the image - doesn't help
Over-riding touchesBegan and touchesEnded and putting the image swapping code there - doesn't help
Executing the image swapping code asynchronously inside a dispatch_async call - doesn't help
At this point, the only other thing I can think of is to set up a timer that manually fires off the image change after a slight delay if it detects that the user hasn't pressed down for longer than a certain time period.
This feels wrong, and I'm wondering if there's a better way to achieve this. Is there some other event I should be over-riding?
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!