I've written some code that moves some objects around on the screen in sequence. As each object finishes moving, the next starts.
The code is structured similarly to this:
Model:
moveObject
{
// Code to move the object
...
[delegate moved:self];
}
Delegate (Controller):
moved:(MyClass *)o
{
UIView* v = [self viewForObject:o];
[UIView animateWithDuration:1.0
animations:^{
[v setCenter: [model center]];
}
completion:^(BOOL finished){
// Move object
[model moveObject];
}];
}
Aside from the fact that this doesn't actually work correctly, it doesn't seem conceptually right to me. The logic in the model should be free to carry on moving things around without having to wait to be nudged by the controller telling it that the views are ready for more updates.
My plan is to create a queue of pending actions-requiring-animation, and each time the model updates something which needs animating, just add it to the queue and let it carry on. Then I could pluck actions off the queue and animate them one at a time.
This would also mean that I could use the model without any views at all, without having to write a separate controller that just keeps calling back into the model when objects are moved.
So before I start working on my own solution for this, is there already code to handle this in Apple's APIs? It seems like it would be a common enough problem that there would be a standard method of handling it.
Alternatively, is there a better way to handle building chains of animations at run-time? (As opposed to simply hard coding them using the completion block.)
Related
Ever since their introduction in iOS 4, I have been wondering about the internal implementation of the UIView's block-based animation methods. In particular I would like to understand what mystical features of Objective C are used there to capture all the relevant layer state changes before and after execution of the animation block.
Observing the black-box implementation, I gather that it needs to capture the before-state of all layer properties modified in the animation block, to create all the relevant CAAnimations. I guess it does not do a snapshot of whole view hierarchy, as that would be horribly inefficient. The animation block is opaque code blob during runtime, so I don't think it can analyze that directly. Does it replace the implementation of property setters on CALayer with some kind of recoding versions? Or is the support for this property change recoding baked-in somewhere deep inside the CALayers?
To generalize the question a little bit, is it possible do create similar block-based API for recording state changes using some Objective C dark magic, or does this rely on knowing and having the access to the internals of the objects being changed in the block?
It is actually a very elegant solution that is built around the fact that the view is the layers delegate and that stand-alone layers implicitly do animate on property changes.
It just happens to be that I gave a BLITZ talk about this at NSConference just a couple of days ago and I posted my slides on GitHub and tried to write down more or less what I said in the presenter notes.
That said: it is a very interesting question that I don't see being asked very often. It may be a bit to broad but I really like curiosity.
UIView animations existed before iOS 4
Ever since their introduction in iOS 4, I have been wondering about the internal implementation of the UIView's block-based animation methods.
UIView animations existed before iOS 4 but in a different style (that is no longer recommended to use because it is more cumbersome to use). For example, animating position and color of a view with a delay could be done like this. Disclaimer: I did not run this code so it may contains bugs.
// Setup
static void *myAnimationContext = &myAnimationContext;
[UIView beginAnimations:#"My Animation ID" context:myAnimationContext];
// Configure
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelay:0.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
// Make changes
myView.center = newCenter;
myView.backgroundColor = newColor;
// Commit
[UIView commitAnimations];
The view-layer synergy is very elegant
In particular I would like to understand what mystical features of Objective C are used there to capture all the relevant layer state changes before and after execution of the animation block.
It is actually the other way around. The view is built on top of the layer and they work together very closely. When you set a property on the view it sets the corresponding property on the layer. You can for example see that the view doesn't even have it's own variable for the frame, bounds or position.
Observing the black-box implementation, I gather that it needs to capture the before-state of all layer properties modified in the animation block, to create all the relevant CAAnimations.
It does not need to do that and this is where it all gets very elegant. Whenever a layer property changes, the layer looks for the action (a more general term for an animation) to execute. Since setting most properties on a view actually sets the property on the layer, you are implicitly setting a bunch of layer properties.
The first place that the layer goes looking for an action is that it asks the layer delegate (it is documented behaviour that the view is the layers delegate). This means that when the layer property changes, the layers asks the view to provide an animation object for that each property change. So the view doesn't need to keep track of any state since the layer has the state and the layer asks the view to provide an animation when the properties change.
Actually, that's not entirely true. The view needs to keep track of some state such as: if you are inside of the block or not, what duration to use for the animation, etc.
You could imagine that the API looks something like this.
Note: I don't know what the actual implementation does and this is obviously a huge simplification to prove a point
// static variables since this is a class method
static NSTimeInterval _durationToUseWhenAsked;
static BOOL _isInsideAnimationBlock;
// Oversimplified example implementation of how it _could_ be done
+ (void)animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations
{
_durationToUseWhenAsked = duration;
_isInsideAnimationBlock = YES;
animations();
_isInsideAnimationBlock = NO;
}
// Running the animations block is going to change a bunch of properties
// which result in the delegate method being called for each property change
- (id<CAAction>)actionForLayer:(CALayer *)layer
forKey:(NSString *)event
{
// Don't animate outside of an animation block
if (!_isInsideAnimationBlock)
return (id)[NSNull null]; // return NSNull to don't animate
// Only animate certain properties
if (![[[self class] arrayOfPropertiesThatSupportAnimations] containsObject:event])
return (id)[NSNull null]; // return NSNull to don't animate
CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:event];
theAnimation.duration = _durationToUseWhenAsked;
// Get the value that is currently seen on screen
id oldValue = [[layer presentationLayer] valueForKeyPath:event];
theAnimation.fromValue = oldValue;
// Only setting the from value means animating form that value to the model value
return theAnimation;
}
Does it replace the implementation of property setters on CALayer with some kind of recoding versions?
No (see above)
Or is the support for this property change recoding baked-in somewhere deep inside the CALayers?
Yes, sort of (see above)
Creating similar API yourself
To generalize the question a little bit, is it possible do create similar block-based API for recording state changes using some Objective C dark magic, or does this rely on knowing and having the access to the internals of the objects being changed in the block?
You can definitely create a similar block based API if you want to provide your own animations based on property changes. If you look at the techniques I showed in my talk at NSConference for inspecting UIView animations (directly asking the layer for the actionForLayer:forKey: and using layerClass to create a layer that logs all addAnimation:forKey: information) then you should be able to learn enough about how the view is using the layer to create this abstraction.
I'm not sure if recording state changes is you end goal or not. If you only want to do your own animation API then you shouldn't have to. If you really want to do it, You could probably could, but there wouldn't be as much communication infrastructure (delegate methods and callbacks between the view and the layer) available to you as there is for animations.
David's answer is awesome. You should accept it as the definitive answer.
I do have a minor contribution. I created a markdown file in one of my github projects called "Sleuthing UIView Animations." (link) It goes into more detail on how you can watch the CAAnimation objects that the system creates in response to UIView animations. The project is called KeyframeViewAnimations. (link)
It also shows working code that logs the CAAnimations that are created when you submit UIView animations.
And, to give credit where credit is due, it was David who suggested the technique I use.
UPATE start
Correct answer is here:
Is it posible to do multiple animations on UIView without using completion block
No need to read this.
UPATE end
I have similar problem as UIView animateWithDuration returns immediately, but I can not use completion block because my animations are in different functions.
But do want to animate same object.
I am making card game, so I am moving card across the screen, but betwean moving I also have some game logic. So that why it is convenient for me to do animations as separate.
How to solve this ?
#Fogmeister
Problem with completion is following:
after method1, method2 is called.
If I want to call method1 52 times (because I have 52 cards), then method2 will also be called 52 times.
But in my scenario I need following, call method1 52 times, than call method2 4 times...
So I do not see how this can be down with competition.
Also sometimes I need to call method2 after method1, but sometimes I need to call method3 after method1...
I was thinking to make deep copy of objects after method1, and then on new object to call method2.
Will that work ?
#Fogmeister 2nd response
I have also come to same conclusion.
But I do not like it duo to following reasons.
I need to put game logic in animation logic, and I would like thing to be loosely coupled.
Animation code should not deal with game logic code.
What I want to do do is to do multiple animation, one after another one same UIView object, without using completion:, because I wont to keep animation code simple and reuse it also.
If you want you can download my code from
http://weborcode.com/dl/Tablic.zip
Animation is done in TBL_CardView.m file method animation*
and it is called from:
TBL_GameViewController.m file method gameStateMachineLoop
all card are shuffled from left to right, and then one card is send to left again.
But problem is that that card is already on the right before shuffle,
If you start the project you will se it.
There isn't really a lot of info to go on but you don't have to animate one things per animation. If you want to animate all the cards then use a single animation and update all the cards in the animations block.
If you want to animate all the cards and then something else then you could do something like this...
- (void)animateCards:(NSArray *)cards
{
// call this once and all of the cards in the array will animate over 5 seconds.
[UIView animateWithDuration:5.0
animations:^{
for (Card *card in cards) {
card.frame = // whatever
}
}
completion:^(BOOL finished) {
if (iNeedToCallMethod2) {
[self doSomethingElse];
} else {
[self somethingCompletelyDifferent];
}
}];
}
- (void)doSomethingElse
{
//this will only run after all the cards have animated
//if the condition requires it to run
[UIView animateWithDuration:5.0
animations:^{
for (Suit *suit in self.suits) {
// not really sure what you want to do here
// but you can do the same as above
}
}
completion:^(BOOL finished) {
}];
}
- (void)somethingCompletelyDifferent
{
//this will run once only if the other method doesn't run
}
All you are doing is controlling the flow. Stop thinking about moving cards around a table and think more about what tools you have available and how you can use them to make things do what you want.
Ever since their introduction in iOS 4, I have been wondering about the internal implementation of the UIView's block-based animation methods. In particular I would like to understand what mystical features of Objective C are used there to capture all the relevant layer state changes before and after execution of the animation block.
Observing the black-box implementation, I gather that it needs to capture the before-state of all layer properties modified in the animation block, to create all the relevant CAAnimations. I guess it does not do a snapshot of whole view hierarchy, as that would be horribly inefficient. The animation block is opaque code blob during runtime, so I don't think it can analyze that directly. Does it replace the implementation of property setters on CALayer with some kind of recoding versions? Or is the support for this property change recoding baked-in somewhere deep inside the CALayers?
To generalize the question a little bit, is it possible do create similar block-based API for recording state changes using some Objective C dark magic, or does this rely on knowing and having the access to the internals of the objects being changed in the block?
It is actually a very elegant solution that is built around the fact that the view is the layers delegate and that stand-alone layers implicitly do animate on property changes.
It just happens to be that I gave a BLITZ talk about this at NSConference just a couple of days ago and I posted my slides on GitHub and tried to write down more or less what I said in the presenter notes.
That said: it is a very interesting question that I don't see being asked very often. It may be a bit to broad but I really like curiosity.
UIView animations existed before iOS 4
Ever since their introduction in iOS 4, I have been wondering about the internal implementation of the UIView's block-based animation methods.
UIView animations existed before iOS 4 but in a different style (that is no longer recommended to use because it is more cumbersome to use). For example, animating position and color of a view with a delay could be done like this. Disclaimer: I did not run this code so it may contains bugs.
// Setup
static void *myAnimationContext = &myAnimationContext;
[UIView beginAnimations:#"My Animation ID" context:myAnimationContext];
// Configure
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelay:0.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
// Make changes
myView.center = newCenter;
myView.backgroundColor = newColor;
// Commit
[UIView commitAnimations];
The view-layer synergy is very elegant
In particular I would like to understand what mystical features of Objective C are used there to capture all the relevant layer state changes before and after execution of the animation block.
It is actually the other way around. The view is built on top of the layer and they work together very closely. When you set a property on the view it sets the corresponding property on the layer. You can for example see that the view doesn't even have it's own variable for the frame, bounds or position.
Observing the black-box implementation, I gather that it needs to capture the before-state of all layer properties modified in the animation block, to create all the relevant CAAnimations.
It does not need to do that and this is where it all gets very elegant. Whenever a layer property changes, the layer looks for the action (a more general term for an animation) to execute. Since setting most properties on a view actually sets the property on the layer, you are implicitly setting a bunch of layer properties.
The first place that the layer goes looking for an action is that it asks the layer delegate (it is documented behaviour that the view is the layers delegate). This means that when the layer property changes, the layers asks the view to provide an animation object for that each property change. So the view doesn't need to keep track of any state since the layer has the state and the layer asks the view to provide an animation when the properties change.
Actually, that's not entirely true. The view needs to keep track of some state such as: if you are inside of the block or not, what duration to use for the animation, etc.
You could imagine that the API looks something like this.
Note: I don't know what the actual implementation does and this is obviously a huge simplification to prove a point
// static variables since this is a class method
static NSTimeInterval _durationToUseWhenAsked;
static BOOL _isInsideAnimationBlock;
// Oversimplified example implementation of how it _could_ be done
+ (void)animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations
{
_durationToUseWhenAsked = duration;
_isInsideAnimationBlock = YES;
animations();
_isInsideAnimationBlock = NO;
}
// Running the animations block is going to change a bunch of properties
// which result in the delegate method being called for each property change
- (id<CAAction>)actionForLayer:(CALayer *)layer
forKey:(NSString *)event
{
// Don't animate outside of an animation block
if (!_isInsideAnimationBlock)
return (id)[NSNull null]; // return NSNull to don't animate
// Only animate certain properties
if (![[[self class] arrayOfPropertiesThatSupportAnimations] containsObject:event])
return (id)[NSNull null]; // return NSNull to don't animate
CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:event];
theAnimation.duration = _durationToUseWhenAsked;
// Get the value that is currently seen on screen
id oldValue = [[layer presentationLayer] valueForKeyPath:event];
theAnimation.fromValue = oldValue;
// Only setting the from value means animating form that value to the model value
return theAnimation;
}
Does it replace the implementation of property setters on CALayer with some kind of recoding versions?
No (see above)
Or is the support for this property change recoding baked-in somewhere deep inside the CALayers?
Yes, sort of (see above)
Creating similar API yourself
To generalize the question a little bit, is it possible do create similar block-based API for recording state changes using some Objective C dark magic, or does this rely on knowing and having the access to the internals of the objects being changed in the block?
You can definitely create a similar block based API if you want to provide your own animations based on property changes. If you look at the techniques I showed in my talk at NSConference for inspecting UIView animations (directly asking the layer for the actionForLayer:forKey: and using layerClass to create a layer that logs all addAnimation:forKey: information) then you should be able to learn enough about how the view is using the layer to create this abstraction.
I'm not sure if recording state changes is you end goal or not. If you only want to do your own animation API then you shouldn't have to. If you really want to do it, You could probably could, but there wouldn't be as much communication infrastructure (delegate methods and callbacks between the view and the layer) available to you as there is for animations.
David's answer is awesome. You should accept it as the definitive answer.
I do have a minor contribution. I created a markdown file in one of my github projects called "Sleuthing UIView Animations." (link) It goes into more detail on how you can watch the CAAnimation objects that the system creates in response to UIView animations. The project is called KeyframeViewAnimations. (link)
It also shows working code that logs the CAAnimations that are created when you submit UIView animations.
And, to give credit where credit is due, it was David who suggested the technique I use.
How might one animate a UIView without using block structure? I just need to force an animation to execute immediately after it is called, and I don't have the luxury of putting the rest of my program in a completion block based on the code structure.
To be specific, right now, here is what is happening
while (actionsRemaining)
{
[self performDesiredAnimation];
....computation ....
[barWidget decreaseValueTo:resultOfComputation];
}
When I run this code, all [performDesiredAnimation] animations happen simultaneously with the 10 or so barWidgets all decreasing simultaneously. What I want is:
performDesiredAnimation1 --> barWidget decreases --> performedDesiredAnimation2 --> barWidget decreases --> performDesiredAnimation3 --> barWidget decreases --> ... however many unknown times.
I don't know how many times because actions will get removed from actionsRemaining if the barWidget decreases past a certain value, i.e. the number of loops will depend on intermediate calculations.
To put it even more simply, what I want is the same result as I am seeing now when I call just one iteration of the loop
[self performDesiredAnimation];
....computation ....
[barWidget decreaseValueTo:resultOfComputation];
, followed by the second iteration, followed by the third, all in isolation. Right now the animation looks fine if I comment out the loop, but they all smash together when I keep the loop there. I just want the animations to execute sequentially from iteration to iteration, not all at once.
Blocks are not what is causing you the problem. Your problem, I think, is your "animations in a for loop" structure.
You need to keep your animating actions in a stack, and when one is finished, pop the next one off the stack and perform that. Alternatively, check in the completion block for the animation if you need to continue, and if so, call the animating method again. Something like:
-(void)performAnimation
{
[UIView animateWithDuration:0.25
animations:^{[self performDesiredAnimations];}
completion:^(BOOL finished){
// It's not clear what your computations are or if they take any significant time
CGFloat result = [self doComputations];
[barWidget decreaseValueTo:resultOfComputation]
if (needToPerformAnotherSet) // Work this out however you need to
{
[self performAnimation];
}
};
}
You can do this:
// Start the work in a background thread.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (actionsRemaining) {
// Do your calculation in the background if your code will allow you to
[self performDesiredAnimation];
....computation ....
// Back to the main thread for a chunk of code
dispatch_sync(dispatch_get_main_queue(), ^{
// Or do it here if not
[self performDesiredAnimation];
....computation ....
// Finally, update your widget.
[barWidget decreaseValueTo:resultOfComputation];
}
}
}
}
Since your loop enters the async'ed background loop on each iteration the application's main run loop will get a chance to update your UI with the value that you set in the sync'd block.
In iOS, you can animate view objects using animation blocks:
[UIView animateWithDuration:1.0 animations:^{
firstView.alpha = 0.0;
secondView.alpha = 1.0;
}];
What we have here is a code block that describes what the view properties will end up looking after the animation is finished.
How does this work?
I could understand (I think) if this was done using some declarative format, but from the looks of it, the animation block is just a regular piece of code that presumably has to be executed, the results inspected and then someone transcoded into the actual lower-level graphics code that performs the animation.
Is the block actually executed (or somehow reverse-engineered) and if so, when?
If this code is executed before the animation starts, then how come the changes to the referenced view properties are not reflected immediately?
What happens if I put code in the block that does not change view properties, but does something else?
Yes, the block is actually invoked -- then it changes the view's properties immediately. The UIView's property setters are responsible to see if the set was used within an animation context -- if so, they calculate the animation frames etc. using CoreAnimation and CoreGraphics.
If you put non-animation code into these blocks, nothing special will happen -- the block will be executed immediately.
It is instructive to look at the equivalent code prior to blocks:
[UIView beginAnimations:#"foo" context:NULL];
[UIView setAnimationDuration:1.0];
firstView.alpha = 0.0;
secondView.alpha = 1.0;
[UIView commitAnimations];
So you see, even before blocks, the properties to change are also set directly; however, they do not take effect immediately (they are animated).
How does it work? Presumably when you set a property on the view, it checks to see if you're run beginAnimations but not commitAnimations, and does not take effect immediately if it is (but rather adds it to the list of things to animate for that animation).
So what the blocks version does is very simple in the context of the pre-blocks version: you can just think of it as running the animation block inside beginAnimations and commitAnimations lines.
Apple doesn't really talk about the nitty-gritty details of how it works, but here's what I think happens:
The system adds KVO observers on all the animatable properties of a view when the view is added to the view hierarchy.
When your animation block executes, the system sets a state that watches for KVO notifications on those properties. The code that gets invoked then creates and adds the appropriate CAAnimation objects to each affected view's layer.