是否有可能取消一个UIView动画,而它正在进行中?或者我必须降到CA级别?

例如,我做过这样的事情(可能设置了一个结束动画动作):

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

// set view properties

[UIView commitAnimations];

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


当前回答

在ios4及更高版本上,在第二个动画上使用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;
                     }
                 }];
}

其他回答

Even if you cancel the animation in the ways above animation didStopSelector still runs. So if you have logic states in your application driven by animations you will have problems. For this reason with the ways described above I use the context variable of UIView animations. If you pass the current state of your program by the context param to the animation, when the animation stops your didStopSelector function may decide if it should do something or just return based on the current state and the state value passed as context.

我也有同样的问题;api没有任何东西来取消某些特定的动画。的

+ (void) setAnimationsEnabled:(BOOL)启用

禁用所有动画,因此不为我工作。有两种解决方案:

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

2)只重复一个anim,并在需要时创建一个委托选择器重新启动anim,如下所示:

-(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)总是延迟停止动画,因为它完成当前的动画周期。

希望这能有所帮助。

我的方法是创建一个新的动画到你的终点。设置一个非常短的持续时间,并确保使用+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];

当我使用UIStackView动画时,除了removeAllAnimations()之外,我需要将一些值设置为初始值,因为removeAllAnimations()可以将它们设置为不可预知的状态。我有stackView与view1和view2在里面,一个视图应该是可见的和一个隐藏:

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)
}

如果你正在寻找灵活的动画,可以根据你的需要开始和停止,使用UIViewPropertyAnimator。

示例代码

let sampleView = UIView()

lazy var animator: UIViewPropertyAnimator = {
    let animator = UIViewPropertyAnimator(duration: 1.2, curve: .easeOut)
    animator.isInterruptible = true
    return animator
}()

// add and your animatable property
animator.addAnimations {
   // what you need to animate add here
   sampleView.alpha = 0
}

// start animation
animator.startAnimation(afterDelay: 0.5)

如果你需要在动画中间停下来。它将在当前状态下停止动画。

// stop animation
if animator.isRunning {
    animator.stopAnimation(true)
}