why is setAnimationDidStopSelector discouraged? - iphone

i saw the following in apples documentation on setAnimationDidStopSelector:
"Use of this method is discouraged in iOS 4.0 and later. If you are using the block-based animation methods, you can include your delegate’s end code directly inside your block."
I tried adding what I was going to put inside the animation stop delegate inside the animation block, but the animation doesn't look the same as when i used setAnimationDidStopSelector.
What is the reason for discouraging the use of setAnimationDidStopSelector?

There are lots of APIs that get deprecated in Cocoa Touch. The framework is fairly young and Apple is still tweaking the underlying code. When it is phrased as in your question it probably means "we are going to put all our effort into making block animations perfect, setAnimationDidStopSelector will at some time in the future be deprecated - do yourself a favor and stop using it now".
It sounds weird that your animation behaves different? Have you done it like this?
[UIView animateWithDuration:1.0
delay: 0.0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
aView.alpha = 0.0;
}
completion:^(BOOL finished){
// Do your setAnimationDidStopSelector stuff here!
}];

Related

iOS UIView block animations stop actually animating

I've got a weird issue with using UIView block-based animations. My app contains an assortment of images in scroll views and, after a while of using the app on the device, all animations in UIView animation blocks (such as the one below) stop animating. The animation is processed (in the example code below, the pageView does move) but this change isn't animated and happens instantly.
[UIView animateWithDuration:0.5 animations:^
{
self.pageView.center = CGPointMake(self.view.frame.size.width/2, 704.0/2);
self.pageView.transform = CGAffineTransformIdentity;
}
completion:^(BOOL finished)
{
NSLog(#"complete");
}];
Has anyone else experienced similar behaviour? I'm using an iPad on 5.1 and am wondering if it could be down to iOS version?
Thanks,
Michael
Check your memory usage. I am seeing the same thing you are -- eventually all of the animations stopped working. The animations didn't even run for me, and completion blocks weren't firing.
I found that I was holding on to a number of UIImageViews that weren't visible anymore because I wanted to re-show them later. Removing them and freeing up the UIImage made the problem go away. I now need to figure out how I'm going to get back the images but at least the animations work as expected.
When you're using transforms, animations can have a really unpredictable behavior if you use them separately. To avoid this, you ought to concatenate all your transforms into a single one via CGAffineTransformConcat and animate that single transform.
Try using some animation options and see if that helps?
For example here I'm letting the user interact and letting views redraw themselves during an animation :
[UIView animateWithDuration:0.5
delay:0
options: UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionAllowAnimatedContent
animations:^ {
self.pageView.center = CGPointMake(self.view.frame.size.width/2, 704.0/2);
self.pageView.transform = CGAffineTransformIdentity;
} completion:^ (BOOL finished) {
} ];

begin/commitAnimations vs block-based animations

I still don't get it why Apple discourages the old method of doing animations and instead says to use blocks.
I mean, how does one realistically stop using the old way? Aren't blocks iOS > 4.0 only? Is one supposed to fill the code with ifs and make two different implementations based on the current device's system version? And why do so since the old method works just fine? Plus the underlying implementation should be the same, right? Is there any reasoning behind this aside from the fact that begin/commit produces ugly code?
The old method works fine, but I think with blocks you have the option to have a completion block. Where as with the old way, the animation begins and the code immediately resumes execution. So something like the following sets the alpha of the view only after the animation to move the frame has completed (in this case nested animation blocks). Once the alpha animation finishes, it then removes the view from the superview.
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationCurveEaseOut animations:^(void) {
CGRect frame = self.actionView.frame;
self.actionView.frame = CGRectMake(frame.origin.x, 370, frame.size.width, frame.size.height);
}
completion:^(BOOL finished) {
[UIView animateWithDuration:0.2 delay:0 options:0 animations:^(void) {
[self.blockingView setAlpha:0];
} completion:^(BOOL finished) {
[self.actionView removeFromSuperview];
[self.blockingView removeFromSuperview];
}];
}];
you would have to use [respondsToSelector] to determine if the UIView supports the block method, if not use the old way, and you'd probably have to be creative with timing to reproduce nested animations.
I still don't get it why Apple
discourages the old method of doing
animations and instead says to use
blocks.
Apple wants you to use the latest and greatest techniques. Apple will optimize all it's new iOS releases for the block based animations and probably will add new possibilities only to the blocks based animations. So thats why Apple is pushing you to the block based animations.
I mean, how does one realistically stop using the old way? Aren't blocks iOS > 4.0 only?
Yes, blocks are iOS > 4.0 only. So if your App is designed for iOS 4.0+ only you can use block based animations. Or you could check for the availability of blocks and add the specific animation only for the iOS 4.0+ devices.
Completely stop animating the old way is only realistic if you drop support for iOS 3.
Is one supposed to fill the code with ifs and make two different implementations based on the current device's system version? And why do so since the old method works just fine? Plus the underlying implementation should be the same, right? Is there any reasoning behind this aside from the fact that begin/commit produces ugly code?
Well, if the old method is okay for you and you want to support older (iOS 3) devices. Just use the old way of animating. There's nothing wrong with it, don't put unnecessary if-statements in your code. It only makes things complicated and you win nothing by doing it.
If your App is iOS 4 go with blocks, it's shorter and somewhat cleaned. It also should be easier to maintain, because Apple will not keep updating the old way of animating.
(You always can add the if-statements somewhere in the future if there will be animations you only can do with blocks, and then fallback to another less complex animation with the old methods. But thats something for the future.)

Duration when using CGAffineTransformTranslate

I am moving a view using CGAffineTransformTranslate and want to slow the move down. I tried using [UIView setAnimationDuration] but it does not do anything and the docs discourages it's use in iOS 4.0 and later.
whatIfToolBar.transform = CGAffineTransformTranslate(whatIfToolBar.transform,0.0, -whatIfToolBar.frame.size.height);
What is the proper way to set the duration?
Thanks,
John
I should have read further before asking my question...
[UIView setAnimationDuration] only works when using Begin/Commit methods and must be called between calls to begin and commit animations and before changing any animatable properties of the view.
For iOS 4 or later applications you should use block-based methods for animation. Duration is set when calling a block method. See the Animations section of the View Programming Guide for iOS".
If you application will be run in iOS 3.2 and earlier you must use Begin/Commit methods.
In my case I used Begin/Commit methods...
[UIView beginAnimations:#"whatIfToolBar" context:whatIfToolBar];
[UIView setAnimationDuration:0.5];
whatIfToolBar.transform = CGAffineTransformTranslate(CGAffineTransformIdentity,0.0, - whatIfToolBar.frame.size.height);
[UIView commitAnimations];
If I were to use block-based methods it would look like this...
[UIView animateWithDuration:0.5
animations:^{
whatIfToolBar.transform = CGAffineTransformTranslate(CGAffineTransformIdentity,0.0, -whatIfToolBar.frame.size.height);
}
];
John

Why do I need to do this and is there a better way? Animation blocks on iPhone app OS4

So I have some animation blocks to create some very simple animations on my app (eg. a wheel spinning continuously). This is the code for the animation (I have changed if from the old commitanimations style block but was getting the same problem with that).
[UIView animateWithDuration:30 delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionCurveLinear
| UIViewAnimationOptionRepeat)
animations:^(void){
wheel.transform = CGAffineTransformMakeRotation(M_PI*-0.5);
}
completion:^(BOOL finished){
if(finished){NSLog(#"^^^^^^^^wheel^^^^FINSIHED");
]}
}];
The problem I have is that on OS4 when the app has been dismissed to the multitasking bar and resumed the animation stops. If on resume I reset the position when the app becomes active again, like this....
wheel.transform = CGAffineTransformMakeRotation(0);
then it continues.
This isn't ideal because my animation skips when it restarts. I don't understand why this happens or why I should need to do this.
The "on finished" method gets called almost immediately as the animation starts which is odd as this animation should never finish. Also it DOES NOT get called when the app becomes active and the animation actually stops.
Anyone any ideas or suggestions? I'm been struggling with this for some time now...
You should either setup a timer using NSTimer or use CADisplaylink like so:
displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(animateImages:)]; and change the rotation CGAffineTransformMakeRotation(degrees) of your view in the delegate method.
I'm having a similar problem. I was able to overcome this problem by
turning off the ability for the app to run in the background by adding
the key: "Application does not run in background" and setting it to YES
in the info.plist. If your app does not need to run in the background and
is OK if it is brought back to its initial state when resuming then this
will work.
The syntax of your code is invalid; your code will not compile as shown. A correct version would be:
[UIView animateWithDuration:2 delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionCurveLinear
| UIViewAnimationOptionRepeat)
animations:^(void){
wheel.transform = CGAffineTransformMakeRotation(M_PI*-0.5);
}
completion:^(BOOL finished){
if(finished) NSLog(#"^^^^^^^^wheel^^^^FINSIHED");
}
];
The reason why the "on finished" block is called when the app resumes after suspension and you set the animation going again is that the wheel's transform is already at its final position (this is where you set it in your animations block). So, when the app resumes, you must first reset the wheel's transform (I would do this by setting it to CGAffineTransformIdentity) and then set the animation going.
A smarter approach would be to use CGAffineTransformRotate (and not CGAffineTransformMakeRotation). That way, you can set the transform based on what the transform is now, rather than setting it absolutely.
[UIView animateWithDuration:2 delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionCurveLinear
| UIViewAnimationOptionRepeat)
animations:^(void){
wheel.transform = CGAffineTransformRotate(view.transform, (M_PI*-0.5));
}
completion:^(BOOL finished){
// whatever
}
];
I presume that your wheel is symmetric with respect to your chosen angle, so the user will never see the difference, and the slip you're complaining about (when you reset the transform) will be eliminated.

Source of UIView Implicit Animation delay?

I have a block of UIView animation code that looks like this:
[UIView beginAnimations:#"pushView" context:nil];
[UIView setAnimationDelay:0];
[UIView setAnimationDuration:.5];
[UIView setAnimationDelegate:self];
[UIView setAnimationWillStartSelector:#selector(animationWillStart)];
view.frame = CGRectMake(0, 0, 320, 416);
[UIView commitAnimations];
The code basically mimics the animation of a ModalView presentation and is tied to a button on my interface. When the button is pressed, I get a long (.5 sec) delay (on iPod Touch...twice as fast on iPhone 3GS) before the animationWillStart: actually gets called. My app has lots going on besides this, but I've timed various points of my code and the delay definitely occurs at this block. In other words, a timestamp immediately before this code block and a timestamp when animationWillStart: gets called shows a .5 sec difference.
I'm not too experienced with Core Animation and I'm just trying to figure out what the cause of the delay is...Memory use is stable when the animation starts and CoreAnimation FPS seems to be fine in Instruments. The view that gets animated does have upwards of 20 total subviews, but if that were the issue wouldn't it cause choppiness after the animation starts, rather than before? Any ideas?
Try it with a single subview or with no subviews at all to make sure the delay is not caused by so many children.
Profile the code in Instruments to see where exactly the code lags. You might get down to some internal Core Animation function call that will hint you what’s going on.
Try the code without the “lot that’s going on” to make sure you’re not stepping on Core Animation’s toes with your other code.
Or, in short: experiment and measure, because conjectures seldom work when optimizing.
In your pasted block, you specify the selector animationWillStart (no colon), but later in your question, you refer to animationWillStart: (with colon). These selectors are not equivalent, so is it possible that your intended selector is never being called on account of this animation, and is being called 0.5 seconds later on account of some other animation?