I wan't to perform an animation before quitting a view. The problem is that if I write this code:
[self animationMethod];
[self removeFromSuperview];
the animation is not presented because the removeFromSuperview instruction is immediatly executed, i suppose.
There's a way to specify thath the removeFromSuperview method must be executed after a a specified time? thanks.
Does animationMethod use a [UIView beginAnimations: context:] section? If that's the case, you should use the animation delegate. Specifically:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(removeFromSuperview)];
//Your animation stuff
[UIView commitAnimations];
Otherwise, if you're doing something else that doesn't have a callback, you can call the method after a delay using:
[self performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:0.25f];
where 0.25f is the delay you want to use. I chose 0.25 because that is the default animation length for animation blocks (as shown above).
Related
I'm trying to animate a label embedded in a UIView.
this is the code :
-(void)displayText:(NSString*)text {
[label setText:text];
[UIView animateWithDuration:5.0
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[labelView setAlpha:1.0];
}
completion:nil
];
[UIView animateWithDuration:5.8
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[labelView setAlpha:0.0];
}
completion:nil
];
}
To verify, the method is called, i set a breakpoint.
The calls return immediatly but only the end of animations is displayed.
I wired the UIView to the controller.
Pls help, I'm stuck.
Thanks in advance !
Patrick
Correct,
When you animate views like this the animation doesn't actually happen on screen until the next pass of the runloop (i.e. once your method returns).
UIView will coalesce animations that are programmed sequentially.
Use the completion block to fade back out. The code looks a bit odd but it works great!
[UIView animateWithDuration:5.0
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[labelView setAlpha:1.0];
}
completion:^(BOOL completed){
[UIView animateWithDuration:5.8
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{[labelView setAlpha:0.0];}
completion:nil];
}];
In response to your comments:
The animations won't start until the next run of the runloop. They won't start until your app finishes what its doing. If you wait in the loop you will have the same problem and also freeze up your interface. Consider using individual labels for each letter, and add a progressively bigger delay for each animation. All these animation instructions will be queued up at once and then played out over the course of the next however many seconds. Imagine you are like a movie director, you tell each actor what to do in the next scene. Then, once everyone knows what to do you sit back and yell "action" and watch it all play out.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.30f];
[UIView setAnimationTransition:UIViewAnimationTransitionNone forView:viewSettings cache:YES];
viewSettings.alpha = 0;
[viewSettings removeFromSuperview];
[UIView commitAnimations];
I ve written the code above that works well when I add the view via animation, but it doesn't work when i remove the view from superview. Animation works if I remove [viewSettings removeFromSuperview] line. I don't know where I'm doing wrong.
You need to remove it from the superview after the animation has completed. This is very easy to accomplish if you use the blocks based API, which Apple is encouraging you to do:
[UIView transitionWithView:viewSettings
duration:0.30f
options:UIViewAnimationOptionTransitionNone
animations:^{
[viewSettings setAlpha:0];
} completion:^(BOOL finished) {
[viewSettings removeFromSuperview];
}];
You can read about all the options in Apple's documentation.
removeFromSuperview is not an animatable action, so it is getting performed immediately. Once you commitAnimations, your view is no longer part of it's superview, so you can't see the animation, if it is still even happening.
If you want your animation to happen, then the view to get removed, call removeFromSuperview when the animation ends, such as in a selector specified with setAnimationDidStopSelector:.
Try removing view after the animation is completed. Initially alpha value of the view is 1 then, you apply the animation and make it 0. Now the view is still there but it is not visible. Once the animation is over then remove the view. I think it should work.
I think viewSettings is removed before you commit the animation.
Try inverting the two last lines.
I have connected the two methods below to separate buttons in my UI but have noticed that after pressing the "VERSION 1" button that I could not press the button again until the animation duration within the method had ended. My understanding was that the animation uses its own thread so as not to block the main application.
// VERSION 1
-(IBAction)fadeUsingBlock {
NSLog(#"V1: Clicked ...");
[myLabel setAlpha:1.0];
[UIView animateWithDuration:1.5 animations:^{
[myLabel setAlpha:0.0];
}];
}
The older style version (below) does allow the button to be repressed before the animation timer ends, simply resetting the timer to start again. Should these both work the same, am I missing something or has there been a change in operation between 3.2 and 4?
// VERSION 2
-(IBAction)fadeUsingOld {
NSLog(#"V2: Clicked ...");
[myLabel setAlpha:1.0];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.5];
[myLabel setAlpha:0.0];
[UIView commitAnimations];
}
Cheers gary
Animating with blocks doesn't block the main thread. I think the behavior you're seeing is because, by default, user interaction is disabled duration animation with the new block calls. You can override this by passing UIViewAnimationOptionAllowUserInteraction (calling animationWithDuration:delay:options:animations:completion), like this:
-(IBAction) fadeUsingBlock {
NSLog(#"V1: Clicked ...");
[myLabel setAlpha:1.0];
[UIView animateWithDuration:1.5
delay:0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
[myLabel setAlpha:0.0];
}
completion:nil];
}
For animateWithDuration:, the class reference doesn't say anything about threading, so I am not sure.
For beginAnimations:context: and commitAnimation:, yeah, they run in a separate thread
UIView class Reference.
Some of the property changes to view objects can be animated—for example, setting the frame, bounds, center, and transform properties. If you change these properties in an animation block, the changes from the current state to the new state are animated. Invoke the beginAnimations:context: class method to begin an animation block, set the properties you want animated, and then invoke the commitAnimations class method to end an animation block. The animations are run in a separate thread and begin when the application returns to the run loop. Other animation class methods allow you to control the start time, duration, delay, and curve of the animations within the block.
In an iPhone application, I try to catch animation endings using setAnimationDidStopSelector. I try to suspend code execution until animation ends. I have tried this; set a global BOOL variable, set it to TRUE before commiting animation and after commiting animations waited using a while loop. In the setAnimationDidStopSelector, set the BOOL variable to FALSE and hope while loop to break. But unluckily this did not work, the code did not even fall into setAnimationDidStopSelector (I check that with some trace outputs). EDIT: If that BOOL variable handling is not added, code runs into the handler method.
The code where animation takes place is below:
self.AnimationEnded=FALSE;
[UIView beginAnimations:NULL context:NULL];
[UIView setAnimationDuration:2];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
// do sth.
[UIView commitAnimations];
while(![self AnimationEnded]);
Also this is the code of handler:
- (void)animationDidStop:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context {
printf("abc\n"); fflush(stdout);
self.AnimationEnded=true;
}
What do you suggest?
In iOS 4, you can set a completion block instead of using an animation delegate and handlers. This is a simpler way of taking action when your animation has ended. I recommend using it if you aren't supporting pre-iOS 4 devices.
Your example changes to:
self.animationEnded = NO;
[UIView animateWithDuration:2
animations:^{ /* Do something here */ }
completion:^(BOOL finished){
printf("abc\n");
fflush(stdout);
self.animationEnded = YES;
}];
See +UIView animateWithDuration:animations:completion: at the iOS developer site for more.
You have to call setAnimationDelegate:to designate the object that you want the selector called upon when the animation stops. Assuming that the method that sets your flag to FALSE is in the same class as the one where you are creating the animation this will be self. For details see the UIView class reference.
Try this:
__block BOOL done = NO;
[UIView animateWithDuration:0.3 animations:^{
// do something
} completion:^(BOOL finished) {
done = YES;
}];
// wait for animation to finish
while (done == NO)
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
// animation is finished, ok to proceed
The animation will not start until this loop finishes. This loop will not finish until the animation starts.
while(![self AnimationEnded]);
Whatever you want to do after the animation needs to go in the animationDidStop method.
I tried to add a animation to viewDidLoad and viewDidAppear, but it doesn't work:
- (void)viewDidAppear:(BOOL)animated{
[UIView beginAnimations:#"transition" context:NULL];
[UIView setAnimationTransition:110 forView:self.view cache:YES];
[UIView commitAnimations];
}
Why?
I had the same problem and I think I found the solution on this SO question.
When viewDidAppear gets called you still don't see anything on the screen (despite the name), but you are about to. You can then use a performSelector:withDelay or an NSTimer to launch your animation. The delay can just be 0.1 and your animation will play just when the screen appears.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"View did appear!");
[self performSelector:#selector(animationCode) withObject:nil afterDelay:0.1f];
}
- (void)animationCode {
// you animation code
}
You are not telling the view which state it should animate to so it won't do anything. You need to place code between beginAnimations:context: and commitAnimations that changes the appearance of the view (e.g. by removing one subview and adding another).
You're not using beginAnimations: and commitAnimations correctly. You're supposed to put something in between them that normally wouldn't be animated: e.g. with self.view.alpha = 0.5 you get a fading effect. They have no effect on anything that isn't between them.
By the time viewDidAppear: is called, your view, well... has appeared. It's too late to animate anything. What you actually want to do is something like this:
- (void)showMyViewWithAnimation {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationTransition:110 forView:childView cache:YES];
[parentView addSubview:childView];
[UIView commitAnimations];
}
In the example above, childView is what in your example is called self.view.
Please write out the name of the transition; no one knows what 110 is by looking at it. It's bad style. </pedantry>