开发者

Preventing user input during UIView animations

I have a UIView animation (called by a touchesEnded: event) that I want to finish executing before more user input is allowed. Before the animation is called, I inspect a flag "isMoving" to see if the animation is in progress. I then set isMoving to YES and execute the animation as follows (yes, I know it's not in block format)...

- (void) methodCalledFromViewControllerTouchesEnded {
    if (isMoving == YES) {
        NSLog (@"Don't start another animation!");
        return;
    }   
    isMoving = YES;

    ......

    [UIView beginAnimations:@"move" context:NULL];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
    [UIView setAnimationDuration:0.开发者_StackOverflow中文版25f];
    ...
    // DO MOVE STUFF
    ...
    [UIView commitAnimations];

    isMoving = NO;
}

I then set isMoving to NO so subsequent calls from touchesEnded: don't start another animation while the current one is in progress. However, this FAILS and the animations are done repeated times if the user keeps touching while the initial animation is in progress.

I wonder if this may have to with the possibility that the touch events are handled on a different thread than the animations.

I also ran in debug mode and noticed that the animations aren't actually "committed" until control has left methodCalledFromViewControllerTouchesEnded and returned to touchesEnded:, thus rendering the isMoving = NO; flag useless.

Any help in preventing the repeated animations would be much appreciated.


If you use UIView's class method

+ (void)animateWithDuration:(NSTimeInterval)duration 
                 animations:(void (^)(void))animations

user interaction is disabled by default until the animation finishes. It would save you a lot of work. For you, you'd want to use the full method so you could use your EaseIn option. It would look like this

- (void) methodCalledFromViewControllerTouchesEnded {

        [UIView animateWithDuration:0.25f 
                              delay:0.0 
                            options:UIViewAnimationOptionCurveEaseIn 
                         animations:^(void){
                                     // You animations here
                         } 
                         completion: nil];

}

If you did it the way you currently are, you need to change a few things. Keep in mind that the animation is not blocking, so when you set isMoving to NO at the end, that happens IMMEDIATELY after the animation begins, not after it ends. Now, let me reiterate that the above code is much simpler than your method, but if you did want to continue down your path, you will want to set the animationStopped completion method for your animation, and in that method, set isMoving to NO. It would look like this:

isMoving = YES;
[UIView beginAnimations:@"move" context:NULL];
//...
[UIView setAnimationDidStopSelection:@selector(animationDidStop:finished:context:)];
// ... DO MOVE STUFF
//....
[UIView commitAnimations];

And then you would implement the selector like this:

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
     isMoving = NO;
}


I had a similar problem, solved it with:

[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];


You're stuff is correct, except you misunderstand how the animation works. It's working just as you told it to :P.

The problem is that you set isMoving to NO too soon. The animation block doesn't pause execution of the code. Instead it queues the animations and continues executing.

- (void) methodCalledFromViewControllerTouchesEnded {
    if (isMoving == YES) {
        NSLog (@"Don't start another animation!");
        return;
    }   
    isMoving = YES;

    [UIView animateWithDuration:.25 
                         animations:^(void){
                                     //DO STUFF HERE
                         } 
                         completion: ^(BOOL finished) {
                                     isMoving = NO;
                         }];
}

Is more of what you want.

If you really want to not do a block structure. Then go with something like

- (void) methodCalledFromViewControllerTouchesEnded {
    if (isMoving == YES) {
        NSLog (@"Don't start another animation!");
        return;
    }   
    isMoving = YES;

    ......

    [UIView beginAnimations:@"move" context:NULL];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
    [UIView setAnimationDuration:0.25f];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animate:finished:context:)];
    ...
    // DO MOVE STUFF
    ...
    [UIView commitAnimations];

}

-(void)animate:(NSString *)str_animationID finished:(BOOL)b_isFinished context:(void *)v_context {

    if(str_animationID == @"move") {
        isMoving = NO;
    } 
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜