【问题标题】:UIView animateWithDuration completion has finished = YES despite being cancelled?UIView animateWithDuration 完成已完成 = YES 尽管被取消?
【发布时间】:2015-02-23 05:02:50
【问题描述】:

我在用户交互时运行此动画,有时动画可能会在正在进行的动画完成之前再次运行。我想做的是取消之前的动画并继续新的动画。

[UIView animateWithDuration:duration
                      delay:0
                    options:UIViewAnimationOptionBeginFromCurrentState
                 animations:^{
                     self.bounds = bounds;
                 }
                 completion:^(BOOL finished) {
                     if (finished) {
                         // Do some cleanup after animating.
                     }
                 }];

从视觉上看,这似乎可行,但在我的完成块中,我被告知它在两种情况下都完成了,这导致清理代码过早运行。所以第一个动画的完成块在第二个动画开始后立即运行,finished = YES。我预计它的最终值为NO,第二个(一旦完成)为YES

有没有办法知道动画是完成了还是被其他人取消了?

旁注:我尝试使用 CABasicAnimation 制作相同的动画,然后我第一次完成 = NO 和第二次完成 YES,所以我得到的行为似乎特定于 animateWithDuration .

这是一个 GIF,显示了上面的代码,持续时间为 10,完成块更新标签。如您所见,每次使用animateWithDuration 调用重新启动动画时,finished 都是YES

【问题讨论】:

    标签: ios objective-c animation uiview


    【解决方案1】:

    所以我对此进行了进一步调查,我认为这是 iOS 8 特有的事情,因为动画现在默认情况下是相加的,这意味着动画将继续并相互叠加,而不是被取消。

    在我的搜索中,我发现a WWDC session called "Building Interruptible and Responsive Interactions" 谈到了这一点。在会话中,他们声称动画将同时完成,但在我的测试中并非如此(请参阅问题中的 GIF)。会议中建议的解决方法是保持计数器运行动画开始与完成的次数,并在计数器达到零时执行动画后操作。这有点hacky,但有效。这是为此调整的问题代码:

    // Add this property to your class.
    self.runningAnimations++;
    [UIView animateWithDuration:duration
                          delay:0
                        options:UIViewAnimationOptionBeginFromCurrentState
                     animations:^{
                         self.bounds = bounds;
                     }
                     completion:^(BOOL finished) {
                         if (--self.runningAnimations == 0) {
                             // Do some cleanup after animating.
                         }
                     }];
    

    显然,您需要为每种类型的动画设置一个单独的计数器,这样最后可能会有些混乱,但我认为没有更好的方法,除非您直接开始使用 CAAnimation

    【讨论】:

    • 感谢这帮助我修复了由于在 iOS 9 上完成始终为真而导致的动画故障。
    • 其实更好的方法是检查表示层的边界然后你就知道它是否完成了。
    • 这也是我最终做的事情(iOS 9.3,Swift)!感谢您的帮助。
    【解决方案2】:

    你试过什么方法取消动画?

    theView_ = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    [theView_ setBackgroundColor:[UIColor magentaColor]];
    [self.view addSubview:theView_];
    
    [UIView animateWithDuration:10
                     animations:^{
                         theView_.frame = CGRectMake(200, 200, 50, 50);
                     } completion:^(BOOL finished) {
                         NSLog(@"done : %@", @(finished));
                         if(finished) {
    
                         }
                         else {
    
                         }
                     }];
    
    [self performSelector:@selector(cancelAnimation) withObject:nil afterDelay:2];
    

    cancelAnimation方法如下:

    - (void)cancelAnimation {
        NSLog(@"cancel");
        [theView_.layer removeAllAnimations];
    

    }

    当动画取消时finished 将返回false

    日志消息符合预期:

    2015-02-23 14:13:58.625 TestPrj[47338:6070317] cancel
    2015-02-23 14:13:58.626 TestPrj[47338:6070317] done : 0
    

    【讨论】:

    • 是的,但是调用removeAllAnimations 会立即将视图置于其最终状态。我想通过第二次调用animateWithDuration 来取消第一个动画。第二次调用确实取消了它(completion 块被直接调用),但finished 的值是YES 而不是NO 所以我无法区分取消的动画和实际的动画完成。
    • @Blixt 你想要实现的是使用块动画方法的暂停/恢复动画。你已经看过这个stackoverflow.com/questions/15119839/… 了吗?
    • 这很好,但我实际上需要替换动画,因为bounds 的动画会根据用户的输入移动到任意位置。有没有办法替换动画的toValue而不是暂停它?
    • @Blixt 如果您必须使用UIView animationWith...,我认为没有可能的方法。但是可以使用CABasicAnimation,您可以在动画开始时将fromValue 设置为`toValue`。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-28
    相关资源
    最近更新 更多