【问题标题】:Second animateWithDuration call disables animation第二个 animateWithDuration 调用禁用动画
【发布时间】:2020-07-30 11:22:05
【问题描述】:

我有一个我认为非常简单的动画案例。有一个视图位于 0 alpha 除非它变为 1 直到具有一种类型事件的未来动画,或者在几秒钟内变为 1 具有另一种类型的事件。我的问题是,当连续调用这两个事件时,实际上会运行以下代码:

    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
    self->bkgView.alpha = 1.0;
} completion:^(BOOL finished){}];
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
    self->bkgView.alpha = 1.0;
} completion:^(BOOL finished){
    NSLog(@"completion finished: %d", finished);
    if (finished)
        [UIView animateWithDuration:0.5 delay:3 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
            self->bkgView.alpha = 0;
        } completion:^(BOOL finished){}];
}];

我认为第二个调用只是取消了第一个调用并从该调用的任何位置接管,因此我将闪烁到 alpha 1 几秒钟。实际发生的根本不是动画,完成块被立即调用,完成为真 - 所以没有迹象表明它被取消了。我想我应该问,而不是在这个看似微不足道的案例上猛烈抨击,我做错了什么?

编辑:当我说代码微不足道时,我的意思是,但如果你想尝试的话,这里是整个视图控制器,你可以放在一个新的单视图项目上:

#import "ViewController.h"

@interface ViewController () {
    UIView *bkgView;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    CGFloat x = 20;;
    for (NSString *str in @[@"show", @"flash", @"both"]) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button addTarget:self action:NSSelectorFromString(str) forControlEvents:UIControlEventTouchUpInside];
        [button setTitle:str forState:UIControlStateNormal];
        button.backgroundColor = [UIColor grayColor];
        button.frame = CGRectMake(x, 100, 80, 40);
        [self.view addSubview:button];
        x += 100;
    }
    bkgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 100)];
    bkgView.backgroundColor = [UIColor blackColor];
    bkgView.alpha = 0;
    [self.view addSubview:bkgView];
}

-(void)both {
    [self show];
    [self flash];
}

-(void)show {
    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
        self->bkgView.alpha = 1.0;
    } completion:^(BOOL finished){}];
}

-(void)flash {
    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
        self->bkgView.alpha = 1.0;
    } completion:^(BOOL finished){
            [UIView animateWithDuration:0.5 delay:3 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
                self->bkgView.alpha = 0;
            } completion:^(BOOL finished){}];
    }];
}

它加载了三个按钮。如果单击“显示”,它会淡出一个矩形。如果单击“闪烁”,它会在矩形中淡出 3 秒钟,然后淡出。我的问题是,如果您单击“两者”,这在这个简单的示例中可能没有多大意义,但会模拟连续调用 show 和 flash 动作,这可能在我的原始代码中发生。我希望它的行为与“flash”相同(因为它应该立即从“show”函数中接管),但它什么也不做,矩形没有显示。

【问题讨论】:

  • 很难用你的描述和你发布的代码说。尝试拼凑一个minimal reproducible example
  • @DonMag MRE 添加。
  • 好的 - 不完全清楚你想要做什么。对于此示例,在点击显示时,您希望黑色视图“淡入”...等待 3 秒...然后淡出?如果我快速点击两次会发生什么?在任何 alpha 处停止“淡入”,然后等待 3 并淡出?如果我点击,让它淡入,然后点击在 3 秒内立即淡出?
  • 您可以定义两个 CABasicAnimation 并将它们与animationKeys 应用到一个CALayer *bkgLayer 或bkgView.layer 由sender 或在init 或viewDidLoad 调用。目前,您定义了一个淡入淡出的块并定义了一个空块,该空块被一个覆盖第一个块的块覆盖,并调用一个将淡出并调用空的第五个块。在 CABasicAnimation 的帮助下,您的代码看起来会更容易。
  • @DonMag,我把这个例子讲得更清楚了。

标签: objective-c core-graphics objective-c-blocks uiviewanimation


【解决方案1】:

好的 - 一些事情正在发生......

试试这个代码 - 从你的例子稍微修改。我已将“淡入”持续时间更改为1.5 秒,将“淡出”持续时间更改为2.5 秒(可以在viewDidLoad 末尾更改),因此我们可以看到更多正在发生的事情.我还添加了一些NSLog() 语句来跟随流程并打印时间信息:

#import "SequentialAnimViewController.h"

@interface SequentialAnimViewController () {
    UIView *bkgView;
    double dur1;
    double dur2;
    NSDate *start;
}
@end

@implementation SequentialAnimViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    CGFloat x = 20;;
    for (NSString *str in @[@"show", @"flash", @"both"]) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button addTarget:self action:NSSelectorFromString(str) forControlEvents:UIControlEventTouchUpInside];
        [button setTitle:str forState:UIControlStateNormal];
        button.backgroundColor = [UIColor grayColor];
        button.frame = CGRectMake(x, 100, 80, 40);
        [self.view addSubview:button];
        x += 100;
    }
    bkgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 100)];
    bkgView.backgroundColor = [UIColor blackColor];
    bkgView.alpha = 0;
    [self.view addSubview:bkgView];
    
    dur1 = 1.5; //0.3;
    dur2 = 2.5; //0.5;
    
    start = [NSDate date];
}

-(void)both {
    [self show];
    [self flash];
}

-(void)show {
    [UIView animateWithDuration:dur1 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
        NSLog(@"anim A started.... \t %f", [[NSDate date] timeIntervalSinceDate:self->start]);
        self->bkgView.alpha = 1.0;
    } completion:^(BOOL finished){
        NSLog(@"anim A finished: %d \t %f", finished, [[NSDate date] timeIntervalSinceDate:self->start]);
    }];
}

-(void)flash {
    [UIView animateWithDuration:dur1 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
        NSLog(@"anim B started.... \t %f", [[NSDate date] timeIntervalSinceDate:self->start]);
        self->bkgView.alpha = 1.0;
    } completion:^(BOOL finished){
        NSLog(@"anim B finished: %d \t %f", finished, [[NSDate date] timeIntervalSinceDate:self->start]);
        [UIView animateWithDuration:self->dur2 delay:3 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
            NSLog(@"anim C started.... \t %f", [[NSDate date] timeIntervalSinceDate:self->start]);
            self->bkgView.alpha = 0;
        } completion:^(BOOL finished){
            NSLog(@"anim C finished: %d \t %f", finished, [[NSDate date] timeIntervalSinceDate:self->start]);
        }];
    }];
}

@end

首先要尝试的是点击“Flash”。您应该看到您期望看到的内容,调试控制台输出如下:

anim B started....   2.780529
anim B finished: 1   4.282427
anim C started....   4.282540
anim C finished: 1   9.784028

“动画 B 开始”无关紧要,因为它是 viewDidLoad 和您点击“Flash”之间的经过时间

“anim B finished: 1”显示completion == true,从“B started”开始经过的时间约为 1.5 秒

“动画 C 开始”将在 B 完成后几乎瞬间发生

现在,“动画 C 完成:1”(completion == true)的经过时间可能会让您感到惊讶,因为 2.5 秒的动画需要 5.5 秒。那是因为您在delay 发生之前进入了动画块。所以,

3-second delay + 2.5-second anim == 5.5

这是我们的第一个线索,为什么您的“Both”没有按照您认为的那样做。

接下来要尝试的是 - 为了清晰起见重新启动应用程序 - 并点击“显示”...调试输出将是:

anim A started....   2.039016
anim A finished: 1   3.541033

同样,“开始”是从viewDidLoad 到您点按“显示”和“完成:1”(完成 == true)之间的经过时间,经过时间为 1.5 秒。

现在,当黑色视图仍在显示时,再次点击“显示”:

anim A started....   2.039016
anim A finished: 1   3.541033
anim A started....   7.357023
anim A finished: 1   7.357585

等等……1.5秒的动画只用了0.000562秒???

那是因为黑色视图的 alpha已经 == 1 ...所以从 1.0 到 1.0 的动画不需要任何时间。

这里要注意的一个关键点:动画没有考虑动画属性的条件。它评估属性本身

那么,当我们点击“两者”时会发生什么?重启应用并点击按钮。

这是我的调试输出:

anim A started....   1.744193
anim B started....   1.744692
anim B finished: 1   1.745148
anim C started....   1.745233
anim A finished: 0   1.745463
anim C finished: 1   7.246692

同样,“开始”是在viewDidLoad 和您点击“两者”之间经过的时间...

那么我们已经过去了:

0.0004989
0.000456
0.000085
0.000229
5.501229

事情是这样的:

  • 点击,开始从 alpha == 0 到 alpha == 1 的动画
  • 立即开始从 alpha == 1 到 alpha == 1 ... 不需要时间,也不会发生实际动画
  • 完成该动画
  • 立即开始从 alpha == 1 到 alpha == 0(延迟 3 秒)
  • 第一个动画未完成
  • 最终动画运行,但我们看不到动画,因为 0 到 1 动画从未完成

您需要记住的是 属性 会立即设置...它不会等待 visual 动画完成.

希望这不是太多的信息来模糊对正在发生的事情的理解。


编辑

至于UIViewAnimationOptionBeginFromCurrentState,试试这个...

将持续时间更改为 5 秒:

dur1 = 5.0; //0.3;

然后点按“显示”...这将是一个非常缓慢的“淡入”... 2 秒后,再次点按“显示”。它将继续动画。

将您的 show 方法更改为:

-(void)show {
    double f = bkgView.alpha == 0.0 ? 1.0 : 0.0;
    
    [UIView animateWithDuration:dur1 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
        NSLog(@"anim A started.... \t %f", [[NSDate date] timeIntervalSinceDate:self->start]);
        self->bkgView.alpha = f;
    } completion:^(BOOL finished){
        NSLog(@"anim A finished: %d \t %f", finished, [[NSDate date] timeIntervalSinceDate:self->start]);
    }];
}

您可以点击 show,观看它慢慢淡入,在淡入淡出的中间再次点击 show,然后观看它从原来的位置慢慢淡出

不同之处(主要)在于您立即调用下一个动画的“Both”方法。

【讨论】:

  • 我想这可以解释。我的问题是UIViewAnimationOptionBeginFromCurrentState 基本上,当我阅读文档Start the animation from the current setting associated with an already in-flight animation. 时,我理解它是从属性所在的位置继续,在动画中间,当它只取最终值时,这不是很有用.. . 我可能会从完成动画中删除UIViewAnimationOptionBeginFromCurrentState,除非我看到另一个故障,在这种情况下我会重新考虑我的动画策略...
  • @Ecuador - 请参阅我的答案底部的 编辑 了解更多信息。
猜你喜欢
  • 2014-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-13
相关资源
最近更新 更多