Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

gwelmarten

macrumors 6502
Original poster
Jan 17, 2011
476
0
England!
Hi
In my app, I run numerous animations, moving objects and shapes etc.
Many of the animations lead on to the animation of other objects.
A problem with the application is that if an animation is running and you start another that involves one of the objects currently being animated, the screen goes white/we get unexpected results.

I worked up some sample code to show what we are trying to do:

Code:
UIButton *button;
BOOL currentlyShowingQuestion;
- (void)viewDidLoad
{
    [self moveButton:button];
    currentlyShowingQuestion = NO;
    [NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:@selector(hasBoolChanged) userInfo:nil repeats:YES];
- (void)moveButton:(UIButton *)buttonToUse {
    currentlyShowingQuestion = YES;
    NSLog(@"moveButton Called!  Yippee!!!!!!!");
    [UIView animateWithDuration:0.5 delay:0.5 options:nil animations:^{
        buttonToUse.frame = CGRectMake(10, 10, 300, 440);
    } completion:^(BOOL finished){
        currentlyShowingQuestion = NO;
    }];
}

-(void)hasBoolChanged {
    if (currentlyShowingQuestion == YES) {
        NSLog(@"Yes");
    }
    else if (currentlyShowingQuestion == NO) {
        NSLog(@"No");
    }
}

So, when the animation is in progress, we would expect NO to come out of the NSLog in the console. I am checking every 0.1 seconds with the timer used above. Instead, I just get 'NO' printed through the console log.
Any ideas anybody?
Sam
 

jnoxx

macrumors 65816
Dec 29, 2010
1,343
0
Aartselaar // Antwerp // Belgium
thing is, Animations work differently then you'd suspect and are in my opinion not that easy to cancel etc.
They basically wrap up a "script", and handle that in a different thread (is what they told me). I have had something like this:
- in viewWillAppear, I started a UIView animateWithDuration, and when I went through viewWillDisappear I cancelled them, but in the end, it was just triggering 2ce the second time, even though I cancelled animations on all sublayers.

For your problem, have you tried using the UIViewAnimation with options (try setting options = UIViewAnimationOptionBeginFromCurrentContext (or something similair like that, check the Documents, that might give you a head start.
 

gwelmarten

macrumors 6502
Original poster
Jan 17, 2011
476
0
England!
thing is, Animations work differently then you'd suspect and are in my opinion not that easy to cancel etc.
They basically wrap up a "script", and handle that in a different thread (is what they told me). I have had something like this:
- in viewWillAppear, I started a UIView animateWithDuration, and when I went through viewWillDisappear I cancelled them, but in the end, it was just triggering 2ce the second time, even though I cancelled animations on all sublayers.

For your problem, have you tried using the UIViewAnimation with options (try setting options = UIViewAnimationOptionBeginFromCurrentContext (or something similair like that, check the Documents, that might give you a head start.

Hi
Yes, I did try but the problem is that, what's not shown in that code, is that directly after the animation, we move the CGRect that the object is in to the other side of the screen.
The animation takes the net position and animates it going to the place it should be moved to instantly after the first animation. If that makes sense.
I have reffered to the documentation, but can't seem to find a method for this. Maybe I will have to re-write the animation sections using a different method, but I'd very much prefer not to.

Sam
 

jnoxx

macrumors 65816
Dec 29, 2010
1,343
0
Aartselaar // Antwerp // Belgium
Hi
Yes, I did try but the problem is that, what's not shown in that code, is that directly after the animation, we move the CGRect that the object is in to the other side of the screen.
The animation takes the net position and animates it going to the place it should be moved to instantly after the first animation. If that makes sense.
I have reffered to the documentation, but can't seem to find a method for this. Maybe I will have to re-write the animation sections using a different method, but I'd very much prefer not to.

Sam

Don't really understand, have you tried doing the second thing in a seperate method and calling it after a delay?
[self performSelector:mad:selector(secondanimation) ];
There should be a delay thingy after the performSelector, so maybe that's what you need.
I think there is a small fail in your code because I do changing of aniamtions all the time. (as you can see in the app from my signature)
 

idelovski

macrumors regular
Sep 11, 2008
235
0
The first problem I see is that animation starts after half a second and lasts that long and that gives a whole second. On the other hand, the timer will trigger in .1 seconds.

EDIT: saw repeats:YES round the corner. Oops!

So I can move to the other problem.

You set the flag currentlyShowingQuestion to YES for a brief moment in -moveButton: and set it back to NO in the very next line. It's either that or I'm failing to see something important again.

;)
 
Last edited:

gwelmarten

macrumors 6502
Original poster
Jan 17, 2011
476
0
England!
Don't really understand, have you tried doing the second thing in a seperate method and calling it after a delay?
[self performSelector:mad:selector(secondanimation) ];
There should be a delay thingy after the performSelector, so maybe that's what you need.
I think there is a small fail in your code because I do changing of aniamtions all the time. (as you can see in the app from my signature)

Thanks for your help so far.
Can I just confirm what you are saying I should start doing - the first thing is that I should re-write my animation code for this section from being like:
Code:
[UIView animateWithDuration:0.5 delay:0.5 options:nil animations:^{
        buttonToUse.frame = CGRectMake(10, 10, 300, 440);
    } completion:^(BOOL finished){
        currentlyShowingQuestion = NO;
    }];

to be something more like this:

Code:
[UIView beginAnimations:@"shadowMove" context:nil]; // Begin animation
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

[imageView setFrame:CGRectOffset([imageView frame], 300, 0)]; 

[UIView commitAnimations]; // End animations
With this method, would I be able to set my boolean values before and after this section?

Sam
 

gwelmarten

macrumors 6502
Original poster
Jan 17, 2011
476
0
England!
Hi Again
So, I tried that (rewriting my animation code) and I changed how I call setting the boolean value to YES, as follows:

Code:
- (void)moveButton:(UIButton *)buttonToUse {
    [self setToYes];
    NSLog(@"moveButton Called!  Yippee!!!!!!!");
[UIView beginAnimations:@"shadowMove" context:nil]; // Begin animation
    [UIView setAnimationDuration:0.5];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    
    [buttonToUse setFrame:CGRectMake(10, 10, 300, 340)]; 
    
    [UIView commitAnimations]; // End animations
    currentlyShowingQuestion = NO;
}

-(void)setToYes {
    currentlyShowingQuestion = YES;
}

Still no luck! Loads of NO's being printed. I wondered if it was because of the argument after the method name, but no.

Sam

----------

The first problem I see is that animation starts after half a second and lasts that long and that gives a whole second. On the other hand, the timer will trigger in .1 seconds.

EDIT: saw repeats:YES round the corner. Oops!

So I can move to the other problem.

You set the flag currentlyShowingQuestion to YES for a brief moment in -moveButton: and set it back to NO in the very next line. It's either that or I'm failing to see something important again.

;)

Hi. I don't follow - I set it to yes before the animation, and then back to NO in the stuff to be done when the animation has being completed.
Sam
 

chown33

Moderator
Staff member
Aug 9, 2009
10,740
8,416
A sea of green
Code:
UIButton *button;
BOOL currentlyShowingQuestion;
- (void)viewDidLoad
{
[COLOR="Red"]    [self moveButton:button];
    currentlyShowingQuestion = NO;
[/COLOR]    [NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:@selector(hasBoolChanged) userInfo:nil repeats:YES];
[COLOR="blue"]- (void)moveButton:(UIButton *)buttonToUse {
[/COLOR]    currentlyShowingQuestion = YES;
    NSLog(@"moveButton Called!  Yippee!!!!!!!");
    [UIView animateWithDuration:0.5 delay:0.5 options:nil animations:^{
        buttonToUse.frame = CGRectMake(10, 10, 300, 440);
    } completion:^(BOOL finished){
        currentlyShowingQuestion = NO;
    }];
}

-(void)hasBoolChanged {
    if (currentlyShowingQuestion == YES) {
        NSLog(@"Yes");
    }
    else if (currentlyShowingQuestion == NO) {
        NSLog(@"No");
    }
}

The first red code is setting currentlyShowingQuestion to NO.

The blue code is missing a closing } for viewDidLoad, at the very least. Something didn't paste completely, or it got garbled in transmission. It certainly won't compile as posted.
 

gwelmarten

macrumors 6502
Original poster
Jan 17, 2011
476
0
England!
The first red code is setting currentlyShowingQuestion to NO.

The blue code is missing a closing } for viewDidLoad, at the very least. Something didn't paste completely, or it got garbled in transmission. It certainly won't compile as posted.

Hi, ah ok Sorry - since I was selectively copying, I missed the }. Just ignore that.
So, I am setting it to NO when the application first loads
Then, when ever the animation runs (on viewDidLoad in this example case, but not in the real thing) I immediately change it to yes.
Once the animation is complete, I change it back to NO, to indicate that the animation has finished.
I'm basically doing this so I can ensure that two animations never happen at once. I was going to put an IF in front of all the code for animations that prevented them happening if the BOOL was YES.
Hope that explains it...
Sam

----------

You set the flag currentlyShowingQuestion to YES for a brief moment in -moveButton: and set it back to NO in the very next line. It's either that or I'm failing to see something important again.
;)

I set NO when the application first loads/opens. In this example, I call the moveButton method, and at the start of this method I immediately set it to YES. after the animation has taken place, I set it back to NO to indicate the animation took place. In reality, it stays on NO for a period of time from first opening the app, as the moveButton method is only called occasionally (and certainly not on viewDidLoad!). I hope that helps - selective copying and pasting can be a little confusing.

Any other ideas on a method to do this then?
 

chown33

Moderator
Staff member
Aug 9, 2009
10,740
8,416
A sea of green
Your completion block has a BOOL parameter:
Code:
    } completion:^(BOOL finished){
        currentlyShowingQuestion = NO;
    }];
Try looking at that when the block is called, either in the debugger or using NSLog.

You could try a test where the animation duration is longer, like 5 secs, and see what happens over the longer time interval. In general, make things more observable, then observe them.

The time interval should be longer, so more things happen. Then put observation points (NSLogs or breakpoints) at everything that happens during the time interval (both the animation block and the completion block).

Make predictions about what should happen when. If an observation doesn't match a prediction, figure out why. Example, "I predict completion block executes exactly once, after 5 secs", or "Animation block will be called at time ___" and fill in the blank.


Also, if the button being animated is already at its final state, with a frame of 10, 10, 300, 440, then how would an animation occur?

The first thing you do is call moveButton:, so maybe the problem is that the button is already at its final state. We can't tell, because you haven't posted enough code. Posting the viewDidLoad code just shows the starting point. It doesn't show what actually triggers an animation.

I'm also wondering if the button being animated might be nil. It would be useful to print the button parameter in moveButton, instead of a constant "Yippee" string.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.