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.
Related
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.
What is the best way to implement a smooth reversing animation which tracks touches? I am referring to those animations in which, for example, if the user executes a swipe gesture some elements smoothly animate on screen, and others off, but if the user instead slowly drags a pan gesture back and forth the same objects will move forward/backward as a percent in accordance with the touch position. This is seen in many app intros and also in transitions. I have found
One tutorial which discusses the built-in facility for this but it is only between view controller transitions, not providing the full granular control I see in many apps (http://www.doubleencore.com/2013/09/ios-7-custom-transitions/)
Jazzhands, which is a kit by IFTTT, but this is a packaged solution that might not cover how the solution is best implemented at a lower level (https://github.com/IFTTT/JazzHands)
A question here for which one answer shows how you might execute an animation after a gesture ends (iOS Touch, Gestures, Animation)
What I don't grasp - and I'm comfortable using CAAnimations and gestures - is how something can be both animated and interactive.
Typically, when I create an animation, I commit the animation and it goes from start to finish. While I could interrupt the animation as touches continue, that seems like it would be stilted.
On the other hand, moving things in response to user input is easy, but that is not animated.
How is the effect achieved where something can change according to an animation, but also have that exact same animation occur tied to touches, and yet still also have it so that although the animation reaches completion it doesn't really "finish" (become irreversible) unless the user releases touch, while at any point during interaction if the user releases panning then the animation either reverts backwards to its starting position or animates to completion depending on the last touch location and velocity. These requirements are baffling.
The glimpses of this technique I see all involve keyframe animations, but what I don't understand is where the touch events intersect with an animation to create these smooth effects I see.
Any tips, examples, or tutorials are most welcome.
What I don't grasp - and I'm comfortable using CAAnimations and gestures - is how something can be both animated and interactive.
It is because, having set up an animation, you can set that animation to any "frame" you wish. Thus you can track the animation in correspondence to the movement of a gesture.
The way this works is that an animation is a feature of the render tree, belonging to a CALayer. CALayer implements the CAMediaTiming protocol. The timeOffset of a CALayer thus determines what "frame" of an animation that layer displays. If a complex animation involves many different layers, no problem; just set the timeOffset of their mutual superlayer, to control the frame of the entire animation.
That in fact is exactly how the new iOS 7 interactive custom transition feature works (to which you rightly refer in your question). For instance, in this example code:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/iOS7bookExamples/bk2ch06p296customAnimation2/ch19p620customAnimation1/AppDelegate.m
... I keep updating the UIPercentDrivenInteractiveTransition to tell it how far through the gesture the user is, and the animation therefore tracks the gesture. Now ask yourself: how the heck is this possible???
Well, the UIPercentDrivenInteractiveTransition, in turn, behind the scenes, keeps adjusting the layer's timeOffset to portray the animation at the corresponding frame. (You can actually add logging code to my example to see that this is true.)
Moreover, when I end the gesture at an incomplete point, the animation either hurries to its end or runs backwards to its beginning - again, this is because of the CAMediaTiming protocol, which lets you change the speed of the animation, including a negative value to run it backwards.
Update: It really was as simple as not animating the UI element when utilizing touches. It perfectly follows touches now with no lag.
I'm currently attempting to implement a UI feature by implementing a CALayer subclass inside of a UIView subclass. I receive touch events in the custom UIVIew's corresponding view controller, notify the UIView about the touches, which in turn notifies the CALayer in order to animate the UI elements drawn in the layer.
It all works, but I have noticed that when there is a big delta in movement (as in when quickly scrolling a finger), the CABasicAnimation lags behind. Ideally I want the animation to stay perfectly aligned with the user's finger.
I've come up with a hacky way of just setting the animation's speed arbitrarily high as in
anim.speed = 10.0f;
which essentially keeps up with the user's finger, but I feel that this is a total hack and not a shippable solution. Should I be artificially limiting how many touch events are processed in order to solve this problem? Is there some sort of calculation I should be doing for the speed/duration of the animation that I'm not aware of?
Thanks for any help with this!
During the continuous gesture, one shouldn’t animate movements, but rather just move directly to the gesture’s location. When the gesture finishes, if you want it to settle in some other position, then animate that final, post-gesture, destination. But don’t animate during the gesture itself.
In rare cases, where rendering of a single frame is incredibly slow, there can still be perceived lagginess. Obviously, one should optimize the draw(_:) process so that it isn’t slow (or take a snapshot and animate the snapshot view rather than the complicated view). But during the gesture, you can also use “predictive touches,” where the OS estimates where user’s gesture is going to be in the future. For example, you can implement touchesMoved(_:with:) and then call predictedTouches(for:). By moving the view to the predicted touch location, it reduces perceived lagginess.
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!