取消UIView动画?


244

是否可以UIView在进行动画时取消动画?还是我必须降至CA级别?

即我做了这样的事情(也许也设置了结束动画动作):

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

// set view properties

[UIView commitAnimations];

但是在动画完成之前,我得到了动画结束事件,我想取消它(简称它)。这可能吗?谷歌搜索发现有几个人问同样的问题而没有答案-一两个人猜测这是不可能完成的。


您是说要在中间暂停动画,然后将其保留在中间吗?还是回到开始时的位置?
克里斯·伦迪

2
要么将其精确地保留在动画中(实际上,无论如何我都会直接开始另一个动画),或者直接跳到终点。我以为第一个比较自然,但是在这种情况下,两者都对我有用。
philsquared

3
@克里斯·汉森(Chris Hanson):非常感谢您的编辑,但您想知道删除iPhone标签的理由是什么。可可触摸可能暗示了这一点,但是我一个人在iPhone上安装了标签过滤器,在这种情况下会错过它。
philsquared

@Boot To The Head(再次):您的问题以及我对它的自己的回答,促使我单独进行了一些测试,我发现如果在第一个动画完成之前开始新动画,它会跳到终点在开始新的之前。
philsquared

-这满足了我的迫切需求(并暗示我的真实代码中还有其他错误),但是我将这个问题悬而未决,因为我知道其他人也一直在问同样的问题。
philsquared

Answers:


114

我这样做的方法是在端点创建一个新动画。设置很短的持续时间,并确保您使用该+setAnimationBeginsFromCurrentState:方法从当前状态开始。当将其设置为YES时,当前动画将被剪切。看起来像这样:

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

// set view properties

[UIView commitAnimations];

谢谢斯蒂芬。实际上,如果您阅读了我的问题下的评论,这恰恰是我最终所做的事情-但由于您在此已经对其进行了很好,清晰的说明,因此我将其标记为接受
-philsquare

1
@Vojto怎么样?文档说,setAnimationBeginsFromCurrentState:iOS 4不鼓励使用,但是它仍然存在并且应该仍然起作用。
斯蒂芬·达灵顿,2010年

55
在iOS4 +中,在animateWithDuration:delay:options:animations:completion:上使用UIViewAnimationOptionBeginFromCurrentState选项:
Adam Eberbach

UIViewAnimationOptionBeginFromCurrentState会取消正在进行的任何动画还是仅针对相同属性的动画?如果是任何动画,如何在不停止进行中相同动画的情况下使其在同一点继续一个新动画?
zakdances 2012年

1
@yourfriendzak,基于简短的测试,看起来它只是取消了针对同一属性的动画。我尝试更改scrollView的alpha值,其contentOffset已设置动画,并且contentOffset动画继续。
arlomedia

360

用:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];

22
我发现我必须添加#import <QuartzCore / QuartzCore.h>来删除编译器警告;)
deanWombourne 2010年

4
如此简单,却很难找到。谢谢你,我花了一个小时试图解决这个问题。
MikeyWard 2010年

4
此解决方案可能的问题是(至少在iOS 5中)有一个延迟才能生效-如果您的意思是“立即停止动画,然后再继续下一行,则无法立即生效”码”。
马特2012年

14
我发现如果使用+(void)animateWithDuration:(NSTimeInterval)持续时间动画:(void(^)(void))动画完成:(void(^)(BOOL finish) )完成
JWGS,2012年

1
@matt您知道立即停止动画的解决方案吗?在动画真正停止之前,我也观察到一个小的延迟。
tiguero 2013年

62

最简单的方法停止所有对特定视图的动画,立即,是这样的:

将项目链接到QuartzCore.framework。在代码的开头:

#import <QuartzCore/QuartzCore.h>

现在,当您想要停止视图中所有动画的轨迹消失时,请说出以下内容:

[CATransaction begin];
[theView.layer removeAllAnimations];
[CATransaction commit];

中间的一行可以单独运行,但是要等到运行循环结束(“重绘时刻”)后再进行延迟。为避免这种延迟,请如图所示将命令包装在显式事务块中。前提是当前运行循环中未对该层进行任何其他更改,则此项工作有效。


2
您的代码运行良好后,我一直在使用[CATransaction flush],有时会导致它不能立即写入。但是,这里存在一个0.1秒的性能问题,例如,在特定时间段内滞后。任何解决方法?
席德温岛

5
removeAllAnimations将具有将动画带到最终帧的副作用,而不是将动画停在当前位置。
伊利亚2015年

3
通常,当希望动画停止播放时,他们希望动画停止在当前帧上,而不是第一帧或最后一帧。跳到第一帧或最后一帧看起来像是抖动的动作。
伊利亚2015年

1
然后,我将详细说明答案,以指定默认行为以及如何更改默认行为。显然,正在制作动画的人对他的方法调用后在屏幕上发生的事情非常感兴趣:)
Ilya 2015年

1
我已经在其他地方详细说明了。那不是要问的,所以不是我在这里回答的。如果您还有其他答案,请绝对将其作为答案!
马特2015年

48

在iOS 4及更高版本上,使用UIViewAnimationOptionBeginFromCurrentState第二个动画上选项将第一个动画缩短。

例如,假设您有一个带有活动指示器的视图。您希望在一些可能耗时的活动开始时淡入活动指示器,并在活动结束时淡出它。在下面的代码中,带有活动指示器的视图称为activityView

- (void)showActivityIndicator {
    activityView.alpha = 0.0;
    activityView.hidden = NO;
    [UIView animateWithDuration:0.5
                 animations:^(void) {
                     activityView.alpha = 1.0;
                 }];

- (void)hideActivityIndicator {
    [UIView animateWithDuration:0.5
                 delay:0 options:UIViewAnimationOptionBeginFromCurrentState
                 animations:^(void) {
                     activityView.alpha = 0.0;
                 }
                 completion:^(BOOL completed) {
                     if (completed) {
                         activityView.hidden = YES;
                     }
                 }];
}

41

要取消动画,您只需要在UIView动画之外设置当前正在动画的属性。它将停止动画,无论它在哪里,UIView都将跳转到您刚刚定义的设置。


9
这只是部分正确。它确实停止了动画,但是并没有阻止它触发委托方法,最著名的是animationComplete处理程序,因此,如果那里有逻辑,就会发现问题。
M. Ryan

33
这就是“完成”论证的-animationDidStop:finished:context:目的。如果为[finished boolValue] == NO,则您知道动画以某种方式被取消。
尼克·福奇

这是7年前的一个很好的提示!请注意,这有一种怪异的行为:假设您正在前后移动Alpha动画,并且将其设置为“转到0”。要停止动画,您不能将alpha设置为零;你把它说1
Fattie

26

很抱歉,无法重新回答此问题,但是在iOS 10中,情况有所更改,现在可以取消,甚至可以优雅地取消!

在iOS 10之后,您可以使用UIViewPropertyAnimator取消动画!

UIViewPropertyAnimator(duration: 2, dampingRatio: 0.4, animations: {
    view.backgroundColor = .blue
})
animator.stopAnimation(true)

如果通过,则取消动画,并在取消动画的位置停止。完成方法将不会被调用。但是,如果传递false,则您有责任完成动画:

animator.finishAnimation(.start)

您可以完成动画并停留在当前状态(.current)或进入初始状态(.start)或结束状态(.end)

顺便说一句,您甚至可以暂停然后稍后重新启动...

animator.pauseAnimation()
animator.startAnimation()

注意:如果您不希望突然取消,可以反转动画,甚至在暂停后更改动画!


2
这绝对是与现代动画最相关的。
道格

10

如果要通过更改常量而不是视图属性来为约束设置动画,则其他方法都无法在iOS 8上运行。

动画示例:

self.constraint.constant = 0;
[self.view updateConstraintsIfNeeded];
[self.view layoutIfNeeded];
[UIView animateWithDuration:1.0f
                      delay:0.0f
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                     self.constraint.constant = 1.0f;
                     [self.view layoutIfNeeded];
                 } completion:^(BOOL finished) {

                 }];

解:

您需要从受约束更改影响的任何视图的图层及其子图层中删除动画。

[self.constraintView.layer removeAllAnimations];
for (CALayer *l in self.constraintView.layer.sublayers)
{
    [l removeAllAnimations];
}

1
也仅self.constraintView.layer.removeAllAnimations()适用于(Swift)
Lachtan's

其他解决方案均无效,谢谢,对我有用。
swapnilagarwal's


5

上面的方法都没有为我解决它,但这有所帮助:UIView动画立即设置属性,然后对其进行动画处理。当表示层与模型(set属性)匹配时,它将停止动画。

我解决了我的问题,即“我想从看起来像的位置开始制作动画”(“您”表示视图)。如果您想要那,那么:

  1. 添加QuartzCore。
  2. CALayer * pLayer = theView.layer.presentationLayer;

将位置设置为表示层

我使用一些选项,包括 UIViewAnimationOptionOverrideInheritedDuration

但是由于Apple的文档含糊不清,所以我不知道它在使用时是否真的会覆盖其他动画,或者只是重置计时器。

[UIView animateWithDuration:blah... 
                    options: UIViewAnimationOptionBeginFromCurrentState ... 
                    animations: ^ {
                                   theView.center = CGPointMake( pLayer.position.x + YOUR_ANIMATION_OFFSET, pLayer.position.y + ANOTHER_ANIMATION_OFFSET);
                   //this only works for translating, but you get the idea if you wanna flip and scale it. 
                   } completion: ^(BOOL complete) {}];

目前,这应该是一个不错的解决方案。


5
[UIView setAnimationsEnabled:NO];
// your code here
[UIView setAnimationsEnabled:YES];

2

史蒂芬·达林顿解决方案的Swift版本

UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(0.1)
// other animation properties

// set view properties
UIView.commitAnimations()

1

我也有同样的问题; API没有取消某些特定动画的任何方法。的

+ (void)setAnimationsEnabled:(BOOL)enabled

禁用所有动画,因此不适用于我。有两种解决方案:

1)使您的动画对象成为子视图。然后,当您想取消该视图的动画时,请删除该视图或将其隐藏。非常简单,但是如果需要将子视图保持在视图中,则需要重新创建没有动画的子视图。

2)仅重复执行动画,并在需要时创建一个委托选择器以重新启动动画,如下所示:

-(void) startAnimation {
NSLog(@"startAnim alpha:%f", self.alpha);
[self setAlpha:1.0];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(pulseAnimationDidStop:finished:context:)];
[self setAlpha:0.1];
[UIView commitAnimations];
}

- (void)pulseAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
if(hasFocus) {
    [self startAnimation];
} else {
    self.alpha = 1.0;
}
}

-(void) setHasFocus:(BOOL)_hasFocus {
hasFocus = _hasFocus;
if(hasFocus) {
    [self startAnimation];
}
}

2)的问题是,在动画结束当前动画周期时,总是会延迟停止动画。

希望这可以帮助。


1

即使以上述方式取消动画,动画didStopSelector仍会运行。因此,如果您的应用程序中有由动画驱动的逻辑状态,那么您将遇到问题。基于上述原因,我使用UIView动画的上下文变量。如果通过上下文参数将程序的当前状态传递给动画,则动画停止播放时,您的didStopSelector函数可以根据当前状态和作为上下文传递的状态值来决定是应该执行某些操作还是应返回。


NickForge解释了这个完美的在下面TomTaylor的上述正确答案的评论...
Fattie

非常感谢您的来信!我确实有这种情况,当animationDidStop被调用时,下一个动画被触发。如果您只是删除一个动画并立即为同一关键点重新添加一个新动画,我发现它将不起作用。您需要等待,例如,直到animationDidStop触发另一个动画或其他事件为止。刚刚删除的动画将不再触发animationDidStop。
RickJansen

0
CALayer * pLayer = self.layer.presentationLayer;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView animateWithDuration:0.001 animations:^{
    self.frame = pLayer.frame;
}];

0

要暂停动画而不将状态恢复为原始或最终状态:

CFTimeInterval paused_time = [myView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
myView.layer.speed = 0.0;
myView.layer.timeOffset = paused_time;

0

当我处理UIStackView动画时,除了removeAllAnimations()需要将一些值设置为初始值之外,因为removeAllAnimations()可以将它们设置为不可预测的状态。我有stackViewview1view2里面,一个视图应该是可见的,一个隐藏的:

public func configureStackView(hideView1: Bool, hideView2: Bool) {
    let oldHideView1 = view1.isHidden
    let oldHideView2 = view2.isHidden
    view1.layer.removeAllAnimations()
    view2.layer.removeAllAnimations()
    view.layer.removeAllAnimations()
    stackView.layer.removeAllAnimations()
    // after stopping animation the values are unpredictable, so set values to old
    view1.isHidden = oldHideView1 //    <- Solution is here
    view2.isHidden = oldHideView2 //    <- Solution is here

    UIView.animate(withDuration: 0.3,
                   delay: 0.0,
                   usingSpringWithDamping: 0.9,
                   initialSpringVelocity: 1,
                   options: [],
                   animations: {
                    view1.isHidden = hideView1
                    view2.isHidden = hideView2
                    stackView.layoutIfNeeded()
    },
                   completion: nil)
}

0

没有一个答案对我有用。我以这种方式解决了我的问题(我不知道这是否是正确的方法吗?),因为调用此方法的速度太快(以前的动画尚未完成时)时出现了问题。我通过customAnim块传递了想要的动画。

extension UIView
{

    func niceCustomTranstion(
        duration: CGFloat = 0.3,
        options: UIView.AnimationOptions = .transitionCrossDissolve,
        customAnim: @escaping () -> Void
        )
    {
        UIView.transition(
            with: self,
            duration: TimeInterval(duration),
            options: options,
            animations: {
                customAnim()
        },
            completion: { (finished) in
                if !finished
                {
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    self.layer.removeAllAnimations()
                    customAnim()
                }
        })

    }

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.