Cancel a Uiview Animation

Cancel a UIView animation?

The way I do it is to create a new animation to your end point. Set a very short duration and make sure you use the +setAnimationBeginsFromCurrentState: method to start from the current state. When you set it to YES, the current animation is cut short. Looks something like this:

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

How to cancel UIView block-based animation?

You can stop all animations on a view by calling:

[view.layer removeAllAnimations];

(You'll need to import the QuartzCore framework to call methods on view.layer).

If you want to stop a specific animation, not all animations, your best best bet is to use CAAnimations explicitly rather than the UIView animation helper methods, then you will have more granular control and can stop animations explicitly by name.

The Apple Core Animation documentation can be found here:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html

cancel a UIView animateWithDuration before completion

Update: prefer this answer https://stackoverflow.com/a/21527129/194309 from Borut Tomazin

How to cancel UIViews block-based animation?

If you start a new animation that takes 0.0 seconds and goes to the state you want to go to, it will cancel the old one and start the new (instant) 'animation'.

Example for when you want to stop a moving view by going to the place it already is at:

[UIView animateWithDuration:0.0
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{movingView.frame = ((CALayer *)movingView.layer.presentationLayer).frame;}
completion:^(BOOL finished){}
];

options:UIViewAnimationOptionBeginFromCurrentState is important. Not calling it will let your animation start at the end state of the previous animation. In movement, it would warp to the end location before warping to the place you want it to stop at. Even though your cancel-'animation' is instant, the jumping back and forth may be visible.

Note: The animation time doesn't have to be 0.0 seconds, any animation will cancel the old one. Not entirely sure about different types of animations though. For example, I don't know if changing a frame would stop a fade.

How to stop and reverse a UIView animation?

In addition to the below (in which we grab the current state from the presentation layer, stop the animation, reset the current state from the saved presentation layer, and initiate the new animation), there is a much easier solution.

If doing block-based animations, if you want to stop an animation and launch a new animation in iOS versions prior to 8.0, you can simply use the UIViewAnimationOptionBeginFromCurrentState option. (Effective in iOS 8, the default behavior is to not only start from the current state, but to do so in a manner that reflects both the current location as well as the current velocity, rendering it largely unnecessary to worry about this issue at all. See WWDC 2014 video Building Interruptible and Responsive Interactions for more information.)

[UIView animateWithDuration:3.0
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction
animations:^{
// specify the new `frame`, `transform`, etc. here
}
completion:NULL];

You can achieve this by stopping the current animation and starting the new animation from where the current one left off. You can do this with Quartz 2D:

  1. Add QuartzCore.framework to your project if you haven't already. (In contemporary versions of Xcode, it is often unnecessary to explicitly do this as it is automatically linked to the project.)

  2. Import the necessary header if you haven't already (again, not needed in contemporary versions of Xcode):

    #import <QuartzCore/QuartzCore.h>
  3. Have your code stop the existing animation:

    [self.subview.layer removeAllAnimations];
  4. Get a reference to the current presentation layer (i.e. the state of the view as it is precisely at this moment):

    CALayer *currentLayer = self.subview.layer.presentationLayer;
  5. Reset the transform (or frame or whatever) according to the current value in the presentationLayer:

    self.subview.layer.transform = currentLayer.transform;
  6. Now animate from that transform (or frame or whatever) to the new value:

    [UIView animateWithDuration:1.0
    delay:0.0
    options:UIViewAnimationOptionAllowUserInteraction
    animations:^{
    self.subview.layer.transform = newTransform;
    }
    completion:NULL];

Putting that all together, here is a routine that toggles my transform scale from 2.0x to identify and back:

- (IBAction)didTouchUpInsideAnimateButton:(id)sender
{
CALayer *currentLayer = self.subview.layer.presentationLayer;

[self.subview.layer removeAllAnimations];

self.subview.layer.transform = currentLayer.transform;

CATransform3D newTransform;

self.large = !self.large;

if (self.large)
newTransform = CATransform3DMakeScale(2.0, 2.0, 1.0);
else
newTransform = CATransform3DIdentity;

[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.subview.layer.transform = newTransform;
}
completion:NULL];
}

Or if you wanted to toggle frame sizes from 100x100 to 200x200 and back:

- (IBAction)didTouchUpInsideAnimateButton:(id)sender
{
CALayer *currentLayer = self.subview.layer.presentationLayer;

[self.subview.layer removeAllAnimations];

CGRect newFrame = currentLayer.frame;

self.subview.frame = currentLayer.frame;

self.large = !self.large;

if (self.large)
newFrame.size = CGSizeMake(200.0, 200.0);
else
newFrame.size = CGSizeMake(100.0, 100.0);

[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.subview.frame = newFrame;
}
completion:NULL];
}

By the way, while it generally doesn't really matter for really quick animations, for slow animations like yours, you might want to set the duration of the reversing animation to be the same as how far you've progressed in your current animation (e.g., if you're 0.5 seconds into a 3.0 second animation, when you reverse, you probably don't want to take 3.0 seconds to reverse that small portion of the animation that you have done so far, but rather just 0.5 seconds). Thus, that might look like:

- (IBAction)didTouchUpInsideAnimateButton:(id)sender
{
CFTimeInterval duration = kAnimationDuration; // default the duration to some constant
CFTimeInterval currentMediaTime = CACurrentMediaTime(); // get the current media time
static CFTimeInterval lastAnimationStart = 0.0; // media time of last animation (zero the first time)

// if we previously animated, then calculate how far along in the previous animation we were
// and we'll use that for the duration of the reversing animation; if larger than
// kAnimationDuration that means the prior animation was done, so we'll just use
// kAnimationDuration for the length of this animation

if (lastAnimationStart)
duration = MIN(kAnimationDuration, (currentMediaTime - lastAnimationStart));

// save our media time for future reference (i.e. future invocations of this routine)

lastAnimationStart = currentMediaTime;

// if you want the animations to stay relative the same speed if reversing an ongoing
// reversal, you can backdate the lastAnimationStart to what the lastAnimationStart
// would have been if it was a full animation; if you don't do this, if you repeatedly
// reverse a reversal that is still in progress, they'll incrementally speed up.

if (duration < kAnimationDuration)
lastAnimationStart -= (kAnimationDuration - duration);

// grab the state of the layer as it is right now

CALayer *currentLayer = self.subview.layer.presentationLayer;

// cancel any animations in progress

[self.subview.layer removeAllAnimations];

// set the transform to be as it is now, possibly in the middle of an animation

self.subview.layer.transform = currentLayer.transform;

// toggle our flag as to whether we're looking at large view or not

self.large = !self.large;

// set the transform based upon the state of the `large` boolean

CATransform3D newTransform;

if (self.large)
newTransform = CATransform3DMakeScale(2.0, 2.0, 1.0);
else
newTransform = CATransform3DIdentity;

// now animate to our new setting

[UIView animateWithDuration:duration
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.subview.layer.transform = newTransform;
}
completion:NULL];
}

UIView Animation Cancel

Set a flag in touchesBegan and clear it again in touchesEnded. In your animation block for touchesEnded, check the flag and do not rescale to (1,1) if the flag is set. That way if you get a second touch after the first has ended but before the animation completes, it will not come back.



Related Topics



Leave a reply



Submit