iDevGames Forums

Full Version: Slightly more complex animations through UIView Animations
You're currently viewing a stripped down version of our content. View the full version with proper formatting.

While the [UIView animateWithDuration: delay: option: animations^{} completion^{}] is a great way for doing simple animations such as moving views in the same direction, or transforming the scale and rotation one way, however for more complex situations such as scaling up and scaling down in the midst of the same animation block is really difficult. The compiler does not interpret it as 2 separate animations and does the first one super quick and then goes on to the other one with respect to the duration defined in the method.

What I am trying to do, is to create a label view out of thin air in the middle of the screen and move it upwards towards the top of the screen margin and then fade out. So I am first changing alpha of the view from 0 to .8 and moving the view 75 pixels up. Then in the completion block am moving it another 75 pixels up but changing the alpha from .8 to 0. both have 0 delays. But the labelview stops for one fraction of a second, before moving onto the completion block.

Both have linear curves.

Does anyone have a good way to indent these kind of animations so that several actions can be done in a sequential form. Such as zooming and zooming out while moving in one direction, an earthquakey wiggle, becoming visible and then becoming invisible while continuing moving?

Any info/insights would help.

Quick things:
a) The compiler is not involved in running your code at all.
b) Code always helps

When you say the first one happens "super quick" and because of something I think you said in the last thread, it makes me think you have some misconception of how the animation works in general. When setting animatable properties inside of the animations block, they don't happen synchronously to each other. That entire block is executed within a microsecond and the animation happens asynchronously. So if you're doing…

animations:^{ a.frame = x; a.frame = y; }

…that's not two animations that are going to run back to back. What you did was tell A that it should animate its frame from its current value to x. Then you immediately told it to instead animate to y, not x. You've reset the target value of the animation from x to y. Then when the animation actually begins (after the entire block executes) it's simply going to animate from the current value to y.

To chain animations you could use the completion block, wrapping another call to animateWithDuration:… or you could make the calls to animateWithDuration: at the same time, but make the second animation have a delay exactly equal to the duration of the first etc.
Thanks a ton Seth!

I did some more research and decided basically animate the labels using a NSTimer.

This allows for more freedom and flexibility, and I just stop the timer once the animation is completed.
Hmm, for what you described I don't see how it's a better solution for you. Having timers around inherently means dealing with everything yourself including the memory management, while leveraging Core Animation will yield better performance and—more often than not—less code.
I would recommend avoiding the NSTimer, and instead using

Check the "finished" boolean that is passed to the completion block to determine if it's time to fire the next animation after the first one completes. It will be set to YES when the first animation is complete.
I tried using the animateWithDuration method, however the problem I faced was because of nested animations.

For example, I wanted a UILabel to move from one edge of the screen to the other, and while it was doing so, scale up till it reached the middle of the screen and start scaling down while it is moving towards the other edge.

This was proving to be overly difficult. So i just shifted to using NSTimers. You can do it using perform selector afterdelay, or just using a NSDelay. But it is working with the timers. I agree with you guys though, that this is not the best way to perform OS animations.

Well, a simple way:

[UIView animateWithDuration:1.0
        delay: 0.0
            view.frame = right;
            [UIView animateWithDuration:0.5
                 options: UIViewAnimationOptionOverrideInheritedDuration |
                     view.layer.transform = CATransform3DMakeScale(2.0, 2.0, 1);
                 completion:^(BOOL finished){
                     view.layer.transform = CATransform3DMakeScale(1.0, 1.0, 1);

Seth, I have noticed that I need to check the "finished" boolean to really make sure that block gets called properly. Though I swear I did not need to always do that check.

Do you know if something changed in a version of iOS that requires checking the finished variable? I see you're not checking it, and I just wanted to confirm that this code would truly work as intended (meaning the completion: block would not run until the animations: block has finished all the way).
I'm not the one to ask about changes between OS versions. I don't do any iOS programming. Smile

In this case, if the animation is cancelled you probably don't want it hanging around at 2x scale, and always want it to be reset. It seems like there'd be no universal rule that you should only ever act on finished being true. It depends on the circumstances.
Reference URL's