3
votes

I am trying to create animation behavior wherein a view rotates 45 degrees clockwise and then adds a red circle when the user drags its center above a certain line. Dragging this same view back below the same line would revert it back to its original orientation and then remove the red circle. The view should take 3 seconds to rotate 45 degrees with animation curve UIViewAnimationCurveEaseInOut:

Visualization of desired behavior

I have two functions that embody this behavior, - (void)viewHasMovedAboveLine:(UIView *)view and - (void)viewHasMovedBelowLine:(UIView *)view, that are called when the view is dragged above or below the line by the user. Both of these functions contain animations with completion handlers, i.e., + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion. The completion handler adds or removes the red circle as appropriate.

I can get the animation to rotate correctly if the user happens to drag the view back across the line while it is in the middle of an animation by setting options: to UIViewAnimationOptionBeginFromCurrentState and by checking the value of view.layer.animationKeys.count prior to executing the animation and removing all animations if there are any currently executing via [view.layer removeAllAnimations].

However, the completion handler of the prior animation still seems to execute even when using [view.layer removeAllAnimations]. Is there a way to stop both the animation and its completion handler if that animation is currently executing?

I would prefer something more elegant than having to create private properties for each animation, e.g., @property (nonatomic) BOOL animation01IsCurrentlyExecuting and @property (nonatomic) BOOL animation02IsCurrentlyExecuting. The ideal solution would encompass a wide variety of animation scenarios containing both animation code and a completion handler.

ALSO: Is there a way to see how far the animation has progressed when it has been interrupted? I myself am more interested in timing (e.g., the animation was interrupted after 2.1 seconds) so that I can make sure that any further animations are properly timed.

1
Couldn't you just check the finished parameter in your completion handler?omz
Wow that was easy. I fundamentally misunderstood how (BOOL finished) was being used. Please add as answer and I will approve?Ken M. Haggerty
Also, when an animation is interrupted, is there a way to figure out how far the animation has progressed? (adding above as edit)Ken M. Haggerty

1 Answers

1
votes

The 'finished' parameter of a UIView animation block is very useful for this case.

[UIView animateWithDuration:0.5 animations:^{
    //set your UIView's animatable property
} completion:^(BOOL finished) {
    if(finished){
       //the animation actually completed
    }
    else{
       //the animation was interrupted and did not fully complete
    }
}];

For finding out how long an animation progressed before being interrupted, some methods on NSDate could come in handy.

__block NSDate *beginDate = [NSDate new];
__block NSTimeInterval timeElapsed;

[UIView animateWithDuration:0.5 animations:^{
    //your animations
    beginDate = [NSDate date];
} completion:^(BOOL finished) {
    if(finished){
        //the animation actually completed
    }
    else{
        //the animation was interrupted and did not fully complete
        timeElapsed = [[NSDate date] timeIntervalSinceDate:beginDate];
        NSLog(@"%f", timeElapsed);
    }
}];