iphone - 如何为一组嵌套的 UIView animateWithDuration 调用调用单个完成 block ?

标签 iphone ios objective-c-blocks grand-central-dispatch uiviewanimation

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

-(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 count 变量在 nestedCompletionBlock 中递减,作为确定哪个是最后一个的方法,但在我的代码中,这是非常困惑的(上面是一个简化的例子)。
有什么好的方法吗?也许某种同步执行 animateWithDuration 的方式,以便它可以与 dispatch_group_async 一起工作?

在 iOS 5.0 上工作。

更新:

@Rob-
谢谢你的回答!这几乎解决了我的问题——之前没有研究过 CATransaction,我想我应该在提问之前更深入地研究一下。 :)
但是还有一个问题。正如我之前提到的,我的例子被简化了,剩下的问题是动画有嵌套的完成 block (即,对于链式动画),我需要将其包含在封闭的事务中。
例如,如果我运行以下代码:

-(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] 动画第一阶段
2011-12-08 15:11:35.831 testProj[51550:f803] 动画第一阶段
2011-12-08 15:11:37.832 testProj[51550:f803] 动画第二阶段
2011-12-08 15:11:37.834 testProj[51550:f803] 动画第二阶段
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] 第二阶段完成

而我需要的是“所有动画完成!”只有在所有内部操作结束后才会触发事件。
也许是因为我只在类级别设置完成 block ,因此无法将每个 block 绑定(bind)到特定操作?
无论如何我还是不太明白,使用 UIView animateWithDuration 重载和它自己的完成 block 参数只会引发同样的问题。
有什么想法吗?

最佳答案

将整个事情包装在 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 框架,则需要添加。

关于您更新后的问题:由于您是从完成 block 创建新动画,这些新动画不会成为原始交易的一部分,因此原始交易不会等待它们完成。

通过使用 +[UIView animateWithDuration:delay:options:animations:completion:] 以及启动稍后在适当的时间播放动画。

关于iphone - 如何为一组嵌套的 UIView animateWithDuration 调用调用单个完成 block ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8421441/

相关文章:

iphone - 如何在 iOS 6 中登录 facebook?

ios - 如何在 iOS 中不使用 UIImageView 的情况下在 View 上绘制/绘制 UIIMage

ios - self.fetchedResultsController 抛出 EXC_BAD_ACCESS 而 _fetchedResultsController 工作正常

ios - 我怎样才能回到我的主处理线程?

ios - 核心数据错误 : 'Objects must be sorted by section name' for specific languages such as Thai

iphone - App Delegate 中的 Mac OSX/iPhone 错误处理?

iphone - UITableViewCell 的 subview 偶尔会消失

ios - 如何在 iOS 应用程序中发送带附件(文件 PDF)的电子邮件?

objective-c - Xcode12 针对 Cocos3D 编译错误 : Incompatible block pointer types

objective-c - 当我在 OC 中使用强指针引用 block 中的弱指针时,它会变成悬空指针吗?