首页 文章

如何为嵌套的UIView animateWithDuration调用组调用单个完成块?

提问于
浏览
4

我有一批动画调用,通过遍历数组调用 . 所有这些调用都嵌套在封装动画块中,以便它们并行有效地执行 . 我也有一个完成块,我只想在所有嵌套动画完成后触发 .
问题是嵌套动画的持续时间是未知的,所以我不能简单地计算出最后一个完成的调用,并在此调用中设置完成块 . 类似地,我无法计算持续时间并在完成块上使用延迟调用 .
希望一个例子可以使这更清楚 . 这是我正在尝试做的(非常简化)版本:

-(void) animateStuff:(CGFloat)animationDuration withCompletionBlock:(void) (^)(BOOL)completionBlock {

// encapsulating animation block so that all nested animations start at the same time
 [UIView animateWithDuration:animationDuration animations:^{

    for(MyObject* object in self.array) {
        // this method contains other [UIView animateWithDuration calls...
        [self animationOfUnknownDurationWithObject:object nestedCompletionBlock:^(BOOL finished) {
            // what I effectively what here is:
            if(last animation to finish) {
                completionBlock(YES);
            }
        }];
    }

 }]; // cannot use the completion block for the encapsulating animation block, as it is called straight away
}

通过使用dispatch_groups和异步调用提供的功能,如下所述:
http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

显然是理想的,但是UIView animateWithDuration调用本身就是一个异步调用,因此在dispatch_group_async中调用它将无法正常工作 .
我知道我可以做一些事情,比如让一个__block计数变量在nestedCompletionBlock中递减,作为确定哪一个是最后一个的方法,但在我所拥有的代码中这是非常混乱的(上面是一个简化的例子) .
有这样做的好方法吗?也许某种方式同步进行animateWithDuration,以便它可以与dispatch_group_async一起使用?

在iOS 5.0上工作 .

UPDATE:

@抢-
谢谢您的回答!这几乎解决了我的问题 - 以前没有考虑过CATransaction,猜测我应该先问一下然后再问一下 . :)
但是仍有一个问题 . 正如我之前提到的,我的例子被简化了,剩下的问题来自于动画具有嵌套的完成块(即链接动画),我需要将其包含在封闭的事务中 .
例如,如果我运行以下代码:

-(void) testAnim {
NSArray* testArray = [NSArray arrayWithObjects:self.redView, self.blueView, nil];

[CATransaction begin]; {
    [CATransaction setCompletionBlock:^{
        NSLog(@"All animations complete!");
    }];

    for(UIView* view in testArray) {
        [CATransaction begin]; {

            [CATransaction setCompletionBlock:^{

                [CATransaction begin]; {
                    [CATransaction setCompletionBlock:^{
                        NSLog(@"2nd stage complete");
                    }];

                    NSLog(@"Animation 2nd stage");
                    [UIView animateWithDuration:2 animations:^{
                        setX(view, 100);
                    }];
                } [CATransaction commit];
            }];

            NSLog(@"animation 1st stage");
            [UIView animateWithDuration:2 animations:^{
                setX(view, 150);
            }];
        }[CATransaction commit];
    }

} [CATransaction commit];
}

我得到以下输出:

2011-12-08 15:11:35.828 testProj [51550:f803]动画第1阶段
2011-12-08 15:11:35.831 testProj [51550:f803]动画第1阶段
2011-12-08 15:11:37.832 testProj [51550:f803]动画第2阶段
2011-12-08 15:11:37.834 testProj [51550:f803]动画第2阶段
2011-12-08 15:11:37.848 testProj [51550:f803]所有动画完成!
2011-12-08 15:11:39.834 testProj [51550:f803]第二阶段完成
2011-12-08 15:11:39.851 testProj [51550:f803]第二阶段完成

我需要的是只有在所有内部操作结束后才会触发"All animations complete!"事件 .
也许与我将每个块绑定到特定操作的事实有关?
无论如何,我自己的完成块参数只会引发同样的问题 .
有任何想法吗?

2 回答

  • 1

    将整个事物包裹在 CATransaction 中,并设置事务的 completionBlock . 这是我的测试:

    static void setX(UIView *view, CGFloat x)
    {
        CGRect frame = view.frame;
        frame.origin.x = x;
        view.frame = frame;
    }
    
    - (IBAction)startAnimation:(id)sender {
        label.text = @"Animation starting!";
        setX(redView, 0);
        setX(blueView, 0);
        [CATransaction begin]; {
            [CATransaction setCompletionBlock:^{
                label.text = @"Animation complete!";
            }];
            [UIView animateWithDuration:1 animations:^{
                setX(redView, 300);
            }];
            [UIView animateWithDuration:2 animations:^{
                setX(blueView, 300);
            }];
        } [CATransaction commit];
    }
    

    如果你还没有,你需要添加QuartzCore框架 .

    关于您更新的问题:由于您是从完成块创建新动画,因此这些新动画将不会成为原始事务的一部分,因此原始事务不会等待它们完成 .

    通过使用带有延迟值的 +[UIView animateWithDuration:delay:options:animations:completion:] 在正确的时间启动后一个动画,将每个以后的动画作为原始事务的一部分启动可能是最简单的 .

  • 3

    它可能会帮助你 .

    -(void) testAnim2 {
       // NSArray* testArray = [NSArray arrayWithObjects:self.redView, self.blueView, nil];
    
    
    
        [CATransaction begin]; {
            [CATransaction setCompletionBlock:^{
    
                [CATransaction begin]; {
    
                    [CATransaction setCompletionBlock:^{
                        NSLog(@"All completion block");
    
                    }];
    
    
                }[CATransaction commit];
    
                NSLog(@"animation 2nd stage");
                [UIView animateWithDuration:2 animations:^{
                    setX(blueView, 150);
                }];
    
    
            }];
            NSLog(@"animation 1st stage");
            [UIView animateWithDuration:2 animations:^{
                setX(redView, 150);
    
    
            }];
    
    
        } [CATransaction commit];
    }
    

相关问题