The following code is sheer simple. _facebookF is a UIImageView.
if (animationOn == YES) {
[UIView animateWithDuration:0.4 animations:^{
[_faceBookF setAlpha:0];
}];
[_faceBookF setImage:[UIImage imageNamed:#"facebookFBlue.png"]];
[UIView animateWithDuration:0.4 animations:^{
[_faceBookF setAlpha:1];
}];
} else {
[UIView animateWithDuration:0.4 animations:^{
[_faceBookF setAlpha:0];
}];
[_faceBookF setImage:[UIImage imageNamed:#"facebookF.png"]];
[UIView animateWithDuration:0.4 animations:^{
[_faceBookF setAlpha:1];
}];
}
The app gets to the method which contains this code fine, but no changes to the UI occur throughout the entire block of code. To my knowledge, the app is running on the main thread.
Any guesses?
UPDATE--In fact, the issue is that the image is not being changed at all--with or without the animation. Both images are accessed fine in other parts of the app.
It looks like you are trying to run sequential animations, but the way you have coded them, they are likely to run in parallel. What you need to do is this:
if (animationOn == YES) {
[UIView animateWithDuration:0.4 animations:^{
[_faceBookF setAlpha:0];
} completion:^(BOOL finished) {
[_faceBookF setImage:[UIImage imageNamed:#"facebookFBlue.png"]];
[UIView animateWithDuration:0.4 animations:^{
[_faceBookF setAlpha:1];
}];
}];
}
else {
[UIView animateWithDuration:0.4 animations:^{
[_faceBookF setAlpha:0];
}completion:^(BOOL finished) {
[_faceBookF setImage:[UIImage imageNamed:#"facebookF.png"]];
[UIView animateWithDuration:0.4 animations:^{
[_faceBookF setAlpha:1];
}];
}];
}
Try this. This starts the second animation after the first has completed. So it will fade out the UIImageView and then fade it up with different version of the image.
[UIView animateWithDuration: 0.4
animations:^
{
[_faceBookF setAlpha:0];
}
completion:^(BOOL finished)
{
[_faceBookF setImage:[UIImage imageNamed:#"facebookFBlue.png"]];
[UIView animateWithDuration:0.4
animations:^
{
[_faceBookF setAlpha:1];
}];
}];
Thanks for your responses. I've implemented the completion block you both mentioned to avoid both animations firing at once. However, the main issue had to do with the method not being accessed properly. I can't see exactly why, as the facebookF variable was not nil, and the if and log statements in the method worked fine. Anyway, I have switched to a NSNotification solution, and now all parts of the method are being accessed.
Related
I want to schedule a series of animations which animate intro text across the screen. The very last animation, on completion, should fire off game tick logic that should run the game.
For some reason everything is happening immediately. Can anyone shed any light on why this is happening or a better alternative?
[self.view bringSubviewToFront:_ViewIntroSeconds];
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut
animations:^(void) {
[_IntroLabel1 setAlpha:1];
}
completion:^(BOOL finished){
}];
[UIView animateWithDuration:0.4 delay:3.0 options:UIViewAnimationOptionCurveEaseInOut
animations:^(void) {
[_IntroLabel2 setAlpha:1];
[_IntroLabel1 setAlpha:0];
[_IntroLabel1 setCenter:CGPointMake(0, _IntroLabel1.center.y)];
}
completion:^(BOOL finished){
}];
[UIView animateWithDuration:0.4 delay:5.0 options:UIViewAnimationOptionCurveEaseInOut
animations:^(void) {
[_IntroLabel3 setAlpha:1];
[_IntroLabel2 setAlpha:0];
[_IntroLabel2 setCenter:CGPointMake(0, _IntroLabel2.center.y)];
}
completion:^(BOOL finished){
[self updateGame];
[self updateClock];
[_ViewIntroSeconds setAlpha:0];
}];
My suggestion would be to set the delay to 0, and place the subsequent UIView animation blocks in the completion block of the previous animateWithDuration statements:
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut
animations:^(void) {
[_IntroLabel1 setAlpha:1];
}
completion:^(BOOL finished){
[UIView animateWithDuration:0.4 delay:2.6 options:UIViewAnimationOptionCurveEaseInOut
animations:^(void) {
[_IntroLabel2 setAlpha:1];
[_IntroLabel1 setAlpha:0];
[_IntroLabel1 setCenter:CGPointMake(0, _IntroLabel1.center.y)];
}
completion:^(BOOL finished){
[UIView animateWithDuration:0.4 delay:1.6 options:UIViewAnimationOptionCurveEaseInOut
animations:^(void) {
[_IntroLabel3 setAlpha:1];
[_IntroLabel2 setAlpha:0];
[_IntroLabel2 setCenter:CGPointMake(0, _IntroLabel2.center.y)];
}
completion:^(BOOL finished){
[self updateGame];
[self updateClock];
[_ViewIntroSeconds setAlpha:0];
}];
}];
}];
This will give you the desired effect.
EDIT : The reason this is happening is, UIView animateWithDuration block begins animation, and moves on to execute the following code, while the animation is still in effect. Hence it is advised to perform the 'next' animation effects in the completion block. Otherwise, all animateWithDuration requests will be performed almost instantly.
EDIT 2: I have edited the delay in the blocks to give you the 'illusion' of 0, 3, and 5 seconds respectively.
I have a set of UIButtons and UILabels that I want to fade out when one button is selected and it is the correct button.
I've tried a number of things (commented them in the code block) and only the UILabels fade out. What am I missing here?
-(IBAction)answerButtonPressed:(UIButton *)sender {
NSLog(#"Game Question Answer Pressed: %i", sender.tag);
NSLog(#"%#", sender.titleLabel.text);
int selectedAnswer =0;
selectedAnswer = [question.answer intValue];
if (selectedAnswer == sender.tag) {
NSLog(#"GameQ %# is the correct answer", sender.titleLabel.text);
//self.toggleView;
[labelA setAlpha:0];
[labelB setAlpha:0];
[labelC setAlpha:0];
[labelD setAlpha:0];
/*
[buttonA setAlpha:0];
[buttonB setAlpha:0];
[buttonC setAlpha:0];
[buttonD setAlpha:0];
[buttonA setHidden:YES];
[buttonB setHidden:YES];
[buttonC setHidden:YES];
[buttonD setHidden:YES];
*/
[sender setAlpha:1];
[sender setHidden:NO];
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.5];
[UIView animateWithDuration:2.0
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{buttonA.alpha = 0;}
completion:nil];
[UIView setAnimationDelegate:[UIApplication sharedApplication]];
[UIView setAnimationDidStopSelector:#selector(endIgnoringInteractionEvents)];
[UIView commitAnimations];
I have since cleaned up the method and utilize only one type of animation block. The UIButton still will not fade out, but the label does. Here is what I have as the animation block:
[UIView animateWithDuration:2.0
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{buttonA.alpha = 0;}
completion:nil];
[UIView animateWithDuration:2.0
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{labelA.alpha = 0;}
completion:nil];
I couple things I notice off the bat. You are combining two types of animation techniques. Old and new:
Either do:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.5];
buttonA.alpha = 0;
[UIView commitAnimations];
OR (and preferred as it is the "modern" way)
[UIView animateWithDuration:2.0
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{buttonA.alpha = 0;}
completion:nil];
Combining the two, as you have, would probably cause the block to be called many times as the first animation technique interpolated the second. Basically, putting many many animations in the queue, fading the button to 0 VERY fast.
Additionally, by default (using the block at least) user interaction is disabled by default. No need to do it explicitly.
I have the following animation block:
[UIView animateWithDuration:2
animations:^{
[childViewController_.view setAlpha:0];
}
completion:^(BOOL finished) {
[childViewController_.view removeFromSuperview];
}];
When performed as above, the completion block is called immediately.
If I don't have the completion block however, then the animation is performed as expected.
What am I doing wrong here?
Update
The finished flag in the completion block is NO.
You just forgot to check one condition
[UIView animateWithDuration:2
animations:^{
[childViewController_.view setAlpha:0];
}
completion:^(BOOL finished) {
if (finished)
{
[childViewController_.view removeFromSuperview];
}
}];
I'm trying a flip or curl between to views in a container which works pretty well using the following code:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.8];
[UIView setAnimationTransition:animationOption
forView:container cache:YES];
[container addSubview:nuView];
[oldView removeFromSuperview];
[UIView commitAnimations];
but does not with
[UIView transitionWithView:container
duration:.8
options:animationOption
animations:^
{
[container addSubview:nuView];
[oldView removeFromSuperview];
}
completion:^(BOOL finished)
{
}];
Any ideas?
What is animationOption? This code works perfectly well in one of my apps:
[UIView transitionWithView:currentContainerView
duration:0.75
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[currentContainerView.frontView removeFromSuperview];
[currentContainerView addSubview:currentContainerView.flippedSideView];
}
completion:^(BOOL finished) {
// stuff
}];
Check if you are using UIViewAnimationOptionTransition or UIViewAnimationTransition !
I would like to have a nested animation for my view.
I have one animation stop selector which gets called just fine:
[UIView setAnimationDidStopSelector:#selector(growAnimationDidStop1:finished:context:)];
However inside of this selector I want to do more animation and when done yet another selector to be called:
- (void)growAnimationDidStop1:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
...
[UIView setAnimationDidStopSelector:#selector(growAnimationDidStop2:finished:context:)];
...
[UIView commitAnimations];
}
The problem is that growAnimationDidStop2 is never called. Why is this?
You can also do this with blocks, and Apple now recommends this. Consider the following as pseudo-code:
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationCurveEaseInOut
animations:^{
[self doAnimationOne];
} completion:^(BOOL finished){
[UIView animateWithDuration:0.4
delay:0.0
options:UIViewAnimationCurveEaseInOut
animations:^{
[self doAnimationNine];
}
completion:^(BOOL finished) {
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationCurveEaseInOut
animations:^{
[self doAnimationFive];
}
completion:^(BOOL finished) {}];
}];
}];
It's also good practice for your animations to be "social", for instance do:
BOOL userInteractionEnabled = [self isUserInteractionEnabled];
[self setUserInteractionEnabled:NO];
BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:YES];
before starting your animation, and then in the final completion block do:
[UIView setAnimationsEnabled:animationsEnabled];
[self setUserInteractionEnabled:userInteractionEnabled];
Oops, answered it myself. I had to start a brand new animation context in the first stop method
The best way to do multiple animations in sequence is with a queue of blocks.
See how clean the result is:
NSMutableArray* animationBlocks = [NSMutableArray new];
typedef void(^animationBlock)(BOOL);
// getNextAnimation
// removes the first block in the queue and returns it
animationBlock (^getNextAnimation)() = ^{
animationBlock block = (animationBlock)[animationBlocks firstObject];
if (block){
[animationBlocks removeObjectAtIndex:0];
return block;
}else{
return ^(BOOL finished){};
}
};
//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
[UIView animateWithDuration:1.0 animations:^{
//...animation code...
} completion: getNextAnimation()];
}];
//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
[UIView animateWithDuration:1.0 animations:^{
//...animation code...
} completion: getNextAnimation()];
}];
//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
NSLog(#"Multi-step Animation Complete!");
}];
// execute the first block in the queue
getNextAnimation()(YES);
Taken from: http://xibxor.com/objective-c/uiview-animation-without-nested-hell/