How to call another method during key frame animation in iOS? - ios

For CAKeyframeAnimation, there are delegates for animation start and animation stop. But I want to call a method in each time frame (to update some subview information on-the-fly). Is there any way I can do it?

I do not believe there is any way to do this. The closest you can come is running a timer with a very short period that queries the currently-visible layer properties by calling -presentationLayer on the layer and querying the result. However this is not guaranteed to match precisely with what's on-screen, as it effectively generates a copy of the model layer with the animations applied to the current time rather than querying the actual presentation layer used internally.

Related

Observe progress of UIView.animateWithDuration/CABasicAnimation?

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

How to create complex animation with better performance in iOS?

All the animations mean change of layer.transform or frame properties. There are 2 main questions about performance:
I have multiple views which I should animate simultaneously. What is better: to create multiple animateWithDuration blocks or to create one such block and iterate views in it?
My animation requires complex calculations and I need to call animateWithDuration enough often. Should I perform all the calculations before the animation block or is there no difference for performance?
Nobody has answered yet. So let I answer my own question and you will correct it if I'm mistaken.
I recommend one block only. The usual animateWithDuration:animations: doesn't support simultaneous animations properly (at least in my case it caused some troubles). I suggest to use RZViewActions instead - it uses more complex approach with dispatch_group_t for simultaneous animations (and actions in general). In the last version you don't even need to prepare groups/sequences before the animation start.
The only difference I see is the code outside animateWithDuration:animations: is performed in the same NSRunLoop cycle and if I place it inside the animation block it will be calculated in the next cycle. So for example, it will be useful to place the calculations outside the block if sometimes you don't want to start the animation according to the results of these calculations.
On the other hand it may slow down the current cycle which is usually more critical than than the slower performance of the next cycle. Additionally you should think about variables you need to pass into the block correctly.
In both cases the animation will not start until the end of the animation block. So if you set some visual variable (for example, view's frame) it is set as usually and you can use it at once. But this value is not rendered until the beginning of the completion block. So you with animateWithDuration:animations: you will see the starting and the ending value only. Even if the animation is interrupted it jumps to the ending value at once (it is default behaviour if you don't specify additional options).

How can I get a callback when an arbitrary UIView redraws itself?

I would like to write a class that applies an effect to a UIView, similar to UIVisualEffectView. I would also like this view to update live -- if the "target" view redraws itself, my view should as well. Obviously I can just redraw on every frame using a CADisplayLink or NSTimer, but I would prefer not to waste the user's battery unnecessarily.
I see that CALayer/UIView objects have a needsDisplay boolean object, but this seems to be something that the client sets, rather than the system exposing it, so it doesn't seem to be any help.
Is it possible to do this?

CoreAnimation confusion: CATransaction vs CATransition vs CAAnimationGroup?

I've used these three classes several times by separate several times.
For example when I want to group several animations (i.e.: CABasicAnimations, etc) to happen simultaneously I first think of CAAnimationGroup, when I want to see a layer change from one state to another (i.e.: appear, slide in, etc) I think of CATransition.
But at the same time CATransaction is meant to execute animations in batches (WTH?)
CATransaction and CATransition seem to be different beasts but I usually see CATransitionsinside CATransactions? I've read the docs several times but they are never compared side by side and when/what they should be used for.
It would be great if someone could point what is their relationship/usage :)
CATransaction and CATransition are indeed different beasts...
It seems that the missing bit in your understanding is about CATransaction; once you get that, then maybe all the pieces will fall into place by themselves.
A CATransaction is always created every time you have a Core animation going on.
Every modification to a layer is part of a transaction. CATransaction is the Core Animation class responsible for batching multiple layer-tree modifications into atomic updates to the render tree.
(source)
What happens is that if you do not specify one explicitly, then an implicit CATransaction is created for you.
You can create an explicit transaction (by means of [CATransaction begin/commit]) to tune several parameters of an animation, like whether default animations should be used, how long they are etc. Those are all described in CATransaction reference.
Explicit transactions are particularly useful when setting the properties of many layers at the same time (for example, while laying out multiple layers), temporarily disabling layer actions, or temporarily changing the duration of resulting implied animations.
So, resuming it all, CATransaction is the "big umbrella" under which a core animation animation is run, wether it is a CABasicAnimation, a CATransition, or group animation. It allows you to set some general parameters affecting the way the animation/transition takes place and if you do not provide one, a default (implicit) one is used.
Hope this helps.

iOS class that allows an image to "drift" across screen?

I've seen a lot of helpful tutorials that show one how to:
make an image move according to a predefined path, or
move the image, a few pixels at a time, in response to a UIButton.
What I want to do is have the image "drift" arbitrarily according to an Vxy velocity I define, then have the button(s) change the velocity. (Yes, I'd have it slow down with time if no action made).
In other languages there might have been a way to do Change Pxy position by Vxy (to ad infinitum) unless button pushed. I believe GET was the command. I can think of a way to do that in iOS I suppose but that would involve setting up a series of 1 sec CGMutablePathRef anims. Alternatively, I have seen some talk of NSTimer: would it be a good practice to introduce some sort of delay: draw, delay, draw, delay.
Request: specific classes or terms I can search in the manuals for myself.
Iirc using uiview's animateWithDuration:completion is cheaper than using core animation. frame is an animatable property. So, yeah I think I would use an NSTimer to call your method for default calculation of the end frame of your view and then call animateWithDuration:completion there.
[deleted bad idea]
I ran across a wonderful tutorial for anyone considering such a project;
http://www.youtube.com/watch?v=nH_Rj152DRM
I believe the key "noob" problem I was having was in not realizing I should declare the instance variable for my sprite/ image in the
-(void) viewDidLoad{
then work on other properties of the animation in touches/ other user events. Once I figured that out, I am now capable of doing the heavy lifting for the rest of the project myself.

Resources