【问题标题】:The proper way of doing chain animations做链式动画的正确方法
【发布时间】:2013-07-30 14:14:58
【问题描述】:
void (^first_animation)();
void (^second_animation)(BOOL finished);


// First animation

first_animation = ^()
{
    g_pin_info_screen.view.alpha = 1.0;
};


// Second animation

second_animation = ^(BOOL finished)
{
    g_shadow_layer.opacity = 0.0;

    void (^set_opacity_to_1)();

    set_opacity_to_1 = ^()
    {
        g_shadow_layer.opacity = 1.0;
    };

    [UIView animateWithDuration : 2.0
            delay               : 0.0
            options             : UIViewAnimationCurveEaseInOut
            animations          : set_opacity_to_1
            completion          : nil
     ];

};



// Begin the animations

{

    float duration;

    duration = 0.35;

    [UIView animateWithDuration : duration
            delay               : 0.00
            options             : UIViewAnimationCurveEaseInOut
            animations          : first_animation
            completion          : second_animation
    ];

}

第一个动画按预期执行。但是第二个动画完成但没有任何动画。

希望有人可以评论上述方案是否是正确的做法。

【问题讨论】:

    标签: ios objective-c ipad


    【解决方案1】:
    __block NSMutableArray* animationBlocks = [NSMutableArray new];
    typedef void(^animationBlock)(BOOL);
    
    // getNextAnimation
    // removes the first block in the queue and returns it
    animationBlock (^getNextAnimation)() = ^{
    
        if ([animationBlocks count] > 0){
            animationBlock block = (animationBlock)[animationBlocks objectAtIndex:0];
            [animationBlocks removeObjectAtIndex:0];
            return block;
        } else {
            return ^(BOOL finished){
                animationBlocks = nil;
            };
        }
    };
    
    [animationBlocks addObject:^(BOOL finished){
        [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
            //my first set of animations
        } completion: getNextAnimation()];
    }];
    
    
    [animationBlocks addObject:^(BOOL finished){
        [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
           //second set of animations
        } completion: getNextAnimation()];
    }];
    
    
    
    [animationBlocks addObject:^(BOOL finished){
        [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
            //third set
        } completion: getNextAnimation()];
    }];
    
    
    [animationBlocks addObject:^(BOOL finished){
        [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
            //last set of animations
        } completion:getNextAnimation()];
    }];
    
    // execute the first block in the queue
    getNextAnimation()(YES);   
    

    【讨论】:

    • 感谢您的回答。这比我想象的要复杂一些。但它非常复杂。你在你的项目中尝试过吗?
    • 它工作得很好,对于很多链接动画来说更干净。如果你尝试在 UIView 动画块的完成块中链接一堆动画,它会很快变得丑陋。但是,如果只有几个动画,这有点过头了。
    • 基本上,这会创建一个动画队列并一个接一个地触发它们。 UIViewAnimationOptionCurveLinear 确保动画流畅。
    • @JeffCompton 使animationBlocks 成为__block 类型变量,然后在最后一个动画之后将其消除(假设序列总是完成)。不幸的是,由于循环问题,在 Obj-C 中使用块进行递归需要一些显式的内存管理(因此容易出错)。块捕获的任何强引用都由块保留。
    • (另外,泄漏是相当保守的。我从未见过它报告误报,但它经常错过周期)
    【解决方案2】:

    在第三方库的帮助下,有一个如下所示的解决方案:

    首先,为方便起见,为UIView 定义一个类别,如下所示:

    +(RXPromise*) rx_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations 
    {
        RXPromise* promise = [RXPromise new];
        [UIView animateWithDuration:duration animations:animations: ^(BOOL finished){
             // ignore param finished here
             [promise fulfillWithValue:@"finished"]; // return just a string indicating success
        }];    
        return promise;
    }
    

    然后,定义任意数量的异步动画依次执行,如下:

    [UIView rx_animateWithDuration:duration animation:^{
            ... //define first animation
        }]
    .then(^id(id result){
        // ignore result, it contains the fulfill value of the promise, which is @"finished"
        return [UIView rx_animateWithDuration:duration animation:^{
            ... // define second animation
        }];
    }, nil)
    .then(^id(id result){
        return [UIView rx_animateWithDuration:duration animation:^{
            ...  // define third animation
        }];
    }, nil)
    .then(^id(id result){
        return [UIView rx_animateWithDuration:duration animation:^{
            ... // and so force
        };
    }, nil);
    

    上面的语句是异步的!

    添加一行代码即可实现取消:

    RXPromise* rootPromise = [UIView rx_animateWithDuration:duration animation:^{
            ... //define first animation
        }];
    
    rootPromise.then(^id(id result){
        return [UIView rx_animateWithDuration:duration animation:^{
            ... // define second animation
        }];
    }, nil)
    .then(^id(id result){
        return [UIView rx_animateWithDuration:duration animation:^{
            ...  // define third animation
        }];
    }, nil)
    ...
    
    // later, in case you need to cancel pending animations:
    [rootPromise cancel];
    

    “RXPromise”库在 GitHub 上可用:RXPromise。它专为这些用例等而设计。由于完全披露:我是作者;)

    【讨论】:

    • 这几乎可以工作,因为我们正在讨论动画,您需要调用 thenOnMain 而不是只是 then。
    • PS 如果能生成一个将在 main 上调用的 promise,不管 then 或 thenOnMain 是否被调用,这将是很好的。
    • @ShayErlichmen 我假设,animateWithDuration:animations: 也可以从非主线程调用。如果不是,那就是 - 我们应该使用 thenOnMain ;)
    • 非常酷的解决方案!我用 AnyPromise 实现了类似的东西:+(AnyPromise *) sm_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { [UIView animateWithDuration:duration animations:animations completion:^(BOOL finished){ resolve(nil); }]; }]; }
    • 我最终使用了 UIView animateWithDuration:delay... 因为这更容易(可以有重叠的动画)。
    【解决方案3】:

    只需检查这里: https://gist.github.com/vadimsmirnovnsk/bce345ab81a1cea25a38

    你可以把它链接成函数式的:

    dispatch_block_t animationsBlock = ^{
        [self.view updateConstraintsIfNeeded];
        [self.view layoutIfNeeded];
    };
    
    [[[[[[[[[BARAnimation construct]
        initially:animationsBlock]
        animationWithDuration:0.425 animationConditions:^{
            [gridView mas_updateConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(imageView).with.offset(32.0);
            }];
        } animations:animationsBlock]
        animationWithDuration:0.425 animationConditions:^{
            [gridView mas_updateConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(imageView).with.offset(0.0);
            }];
        } animations:animationsBlock]
        animationWithDuration:0.425 animationConditions:^{
            [gridView mas_updateConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(imageView).with.offset(-32.0);
            }];
        } animations:animationsBlock]
        animationWithDuration:0.425 animationConditions:^{
            [gridView mas_updateConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(imageView).with.offset(0.0);
            }];
        } animations:animationsBlock]
        animationWithDuration:0.8 animationConditions:nil animations:^{
            foreView.alpha = 1.0;
        }]
        finally:^{
            [self.didEndSubject sendNext:[RACUnit defaultUnit]];
            [self.didEndSubject sendCompleted];
        }]
        run];
    

    【讨论】:

      【解决方案4】:

      您需要使用+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion 将它们链接在一起

      options: 参数中,您需要包含UIViewAnimationOptionBeginFromCurrentState

      祝你好运!

      【讨论】:

      • 已将“show_shadow”选项更改为:UIViewAnimationCurveEaseInOut | UIViewAnimationOptionBeginFromCurrentState。但结果是一样的。还有提示吗?
      • 这个不清楚,这里不解释怎么上链。两个不同的动画。
      • 在“开始动画”块中,我将第二个动画“show_shadow”放在“完成”参数中。
      【解决方案5】:

      在第一个动画的完成处理程序中,启动第二个。

      【讨论】:

      • 我想我已经按照您在标有“开始动画”的块中的建议做了。请评论它是否真的是你所建议的。
      • 是的,但是为什么动画持续时间是0.00,应该是更高的值。此外,Quart2d gudie 也通过完成处理程序链接动画。
      • 猜你看错了编码,它是“延迟”设置为 0.00。持续时间通过变量设置为 0.35。但现在一切似乎都很好。如果我最近的测试是正确的,那么问题出在 CALayer 上。如果使用 UIView,一切都应该没问题。
      • 你是对的,这是动画持续时间为 0.00 的 Jeff 的答案。
      • @AlexWien Ya,我不得不根据之前的动画更新一些不可见的元素。我不希望这些动画花时间,因为它们不可见。我会更新它,这样它就不会那么混乱了。
      猜你喜欢
      • 2015-11-29
      • 2020-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多