第二个 animateWithDuration 调用禁用动画

Second animateWithDuration call disables animation

我有一个我认为非常简单的动画案例。有一个视图位于 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 几秒钟。实际发生的是根本没有动画,完成块被立即调用,完成为真——所以没有迹象表明它被取消了。我想我不会问这个看似微不足道的案例,我做错了什么?

编辑:当我说代码很简单时,我是认真的,但这里是完整的viewcontroller如果你想尝试,你可以放一个新的单视图项目:

#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”功能),但它什么也没做,矩形没有显示。

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

试试这段代码 - 根据您的示例稍作修改。我已将“淡入”持续时间更改为 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

“anim B started”无关紧要,因为它是从 viewDidLoad 到您点击“Flash”

之间经过的时间

“动画 B 完成:1”显示 completion == true,从“B 开始”开始经过的时间约为 1.5 秒

“动画 C 开始”几乎会在 B 完成后立即发生

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

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

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

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

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

同样,“开始”是从 viewDidLoad 到您点击“显示”和“完成:1”(完成 == 真)之间经过的时间,经过时间为 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 的动画不需要任何时间.

这里要注意的一个关键点:动画没有考虑 condition of the 属性 being animated .它评估 属性 本身 .

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

这是我的调试输出:

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 动画 从未完成

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

希望没有太多信息模糊了对正在发生的事情的理解。


编辑

至于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]);
    }];
}

您可以轻按放映,观看它慢慢淡入,在淡入淡出的中间再次轻按放映,然后从它所在的点慢慢观看它逐渐淡出 ].

差异(主要)是由于您的“两者”方法立即调用下一个动画。