【问题标题】:Why the pause between animateWithDuration animation and completion blocks?为什么 animateWithDuration 动画和完成块之间的暂停?
【发布时间】:2012-05-15 03:35:06
【问题描述】:

按照 Apple 的建议,我通过将后续对 -animationWithDuration:animation: 的调用放在对 aanimateWithDuration:animation:completion: 的另一个调用的 completion: 块中来链接 UIView 动画,如下所示:

[UIView animateWithDuration:scaleDuration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
    // Scale the controllers' views down.
    self.view.transform = CGAffineTransformScale(self.view.transform, 0.8, 0.8);
} completion:^(BOOL finished) {
    // Transition to the new view and push on the new view controller.
    [UIView transitionWithView:self.view duration:1 options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionTransitionFlipFromLeft animations:^{
        [self pushViewController:viewController animated:NO];
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:scaleDuration delay:0 options:UIViewAnimationOptionCurveLinear animations:
^{
            // Scale back to the original size.
            self.view.transform = CGAffineTransformScale(self.view.transform, 1.25, 1.25);
        } completion:nil];
    }];
}];

动画都执行正确的顺序,但它们之间有一个微小的延迟,尤其是在-transitionWithView:duration:options:animations:completion: 调用之前。如何平滑动画步骤之间的过渡?

【问题讨论】:

  • 在这种情况下self 是一个导航控制器,因为您将pushViewController: 发送给它?什么是预期的动画,在视图之间“翻转”而不是像普通导航控制器那样向左/向右滑动?
  • 好的。被推送的视图控制器是否实现loadViewviewDidLoadviewWillAppearviewWillLayoutSubviews?如果是这样,请检查它们是否在动画期间被调用,并确保它们不做任何需要时间的事情。
  • 抱歉,@MattiasWadman 的回复延迟了,我上周都在参加会议。是的,它在viewWillAppear 中加载了一个AQGridView,它又加载了一个NSFetchedResultsController 和一堆带有从磁盘加载的图像的视图。这显然可能会产生一些开销。我想知道我是否可以在动画开始之前让它做到这一点……
  • 好的。是的,这些图像听起来是一个很好的起点。首先尝试跳过加载它们检查您是否看到和差异。也许您可以以某种方式预加载图像?有点丑陋的解决方案是让视图控制器在其init 中加载它,然后在使用它等创建视图时将其作为参数传递。
  • 感谢您回来查看,@MattiasWadman。如果我减少在gridView:cellForItemAtIndex: 中完成的工作,它肯定会减少时间,尤其是在我删除图像加载的情况下。仍然有轻微的停顿,但大大减少了。我只需要弄清楚如何在动画开始之前加载这些东西。

标签: cocoa-touch uiview uinavigationcontroller core-animation uiviewanimationtransition


【解决方案1】:

旁白:
为什么你以这种方式滥用导航控制器有什么特别的原因吗?难道你不能直接使用presentViewController:animated:completion: 将其转换样式设置为UIModalTransitionStyleFlipHorizontal

回到你的问题:

我很确定卡顿来自一个简单的事实,即pushViewController:animated: 隐式地必须加载要推送的视图控制器的视图,这需要一些时间。

所以如果你不能使用presentViewController:animated:completion:(或presentModalViewController:animated: 如果你必须支持iOS 4)方法,我鼓励你试试这个:

// ensure the view is already loaded when you invoke `pushViewController:animated:`
[viewController view];

// there is no strict need to calculate those in situ, so we do it up front
CGAffineTransform originalTransform = self.view.transform;
CGAffineTransform downscalingTransform = CGAffineTransformScale(originalTransform, 0.8, 0.8);

// I found it hard to read the block-in-a-block-in-a... while editing here, so I've taken them apart:
void (^scaleDownAnimation)() = ^{
    self.view.transform = downscalingTransform;
};

void (^restoreScaleAnimation)() = ^{
    self.view.transform = originalTransform;
};

void (^pushControllerAnimation)() = ^{
    [self pushViewController:viewController animated:NO];
};

void (^pushAnimationCompletion)(BOOL) = ^(BOOL unused) {
    [UIView animateWithDuration:scaleDuration
                          delay:0
                        options:UIViewAnimationOptionCurveLinear
                     animations:restoreScaleAnimation
                     completion:nil];
};

void (^downscaleCompletion)(BOOL) = ^(BOOL unused){
    UIViewAnimationOptions linearFlipFromLeft = UIViewAnimationOptionCurveLinear | UIViewAnimationOptionTransitionFlipFromLeft;
    [UIView transitionWithView:self.view
                      duration:1
                       options:linearFlipFromLeft
                    animations:pushControllerAnimation
                    completion:pushAnimationCompletion];
};

[UIView animateWithDuration:scaleDuration
                      delay:0
                    options:UIViewAnimationOptionCurveEaseIn
                 animations:scaleDown
                 completion:downscaleCompletion];

注意牛肉在前六行之内,其余的只是为了完整。

【讨论】:

  • 谢谢@danyowdee。主要是在执行动画之前加载视图,以便我们在动画期间消除开销?或者您是否也认为尝试从动画块中删除所有计算很重要(例如,downscalingTransform)?我刚刚加载了视图,并没有看到太大的不同。会继续戳它……
  • 我将viewWillAppear:animated中的代码注释掉了,停顿也大大减少了,虽然没有消除。所以很明显,这是由于过渡期间发生的事情造成的。我只需要在动画开始之前弄清楚如何让这一切发生。
  • 人力资源部。如果我在推送之前调用viewWillAppear:,或者将AQGridView 的加载移动到viewDidLoad,我仍然会暂停。但如果我根本不加载 AQGridView,只会有一个非常轻微的停顿。我想知道加载 AQGridView 会延迟什么?
  • 哦,回答你的问题,我的设计师伙伴想出了一个非常漂亮的过渡动画,包括将工具栏移出屏幕,将可见视图缩小到 80%,然后执行翻转动画,然后放大并返回工具栏(带有不同的项目)。它非常好,并且非常适合我们应用程序的两个方面,所以我自然而然地试图让它尽可能流畅。
  • 所以问题是简单地调用[viewController view] 不会导致视图加载:layoutSubviews 不会被调用,所有加载都发生在此处。这似乎确实是问题的根源。我只需要在开始动画之前弄清楚如何让它完成所有这些工作。还有其他建议吗?
【解决方案2】:

我已经按照您的描述链接了动画,没有任何延迟。

您正在使用 transitionWithView:duration:options:animations:completion: 动画块是对 pushViewController:animated: 的调用。这对我来说没有任何意义。

transitionWithView:duration:options:animations:completion 从一个子视图过渡到下一个。

pushViewController:animated 是一种将视图控制器推送到导航控制器堆栈的导航控制器方法。将这两个一起使用对我来说毫无意义。

如果您希望缩放动画以推送结束,则只需直接从完成块调用推送动画。

【讨论】:

  • 我不希望它以推动结束。我希望它翻转视图,而不是推动它们。所以transitionWithView:duration:options:animations:completion: 调用进行翻转,pushViewController:animated: 进行切换视图的实际工作。我设置了 animated:NO 使其不动画。它看起来很不错,除了非常轻微的停顿。
  • BUt pushViewController:animated: 不应该用于切换视图。仅查看控制器。您可以在控制器内使用动画切换视图。那是另一回事。
  • pushViewController:animated 是将视图控制器推送到 UINavigationController AFAIK 的唯一方法。这就是我正在做的事情。当我的意思是“视图控制器”时,我很抱歉说“视图”。
【解决方案3】:

根据我的经验,“使用完成块链接动画”是有缺陷的(在子动画之间产生小的停顿)。我确保没有 loadView/viewDidLoad/viewWillAppear/viewWillLayoutSubviews 滥用发生。

完全相同的代码会因“completion-block_chainig”而结结巴巴,但如果我将其重新设计为使用关键帧,则效果会很好。

所以,而不是(例如)...

        UIView.animate(withDuration: 1, animations: {[weak self] in
            ...
            self?.view.layoutIfNeeded()
        }, completion: { [weak self] _ in
            UIView.animate(withDuration: 1, animations: {
                ...
                self?.view.layoutIfNeeded()
            }, completion: { [weak self] _ in
                UIView.animate(withDuration: 1, animations: {
                    ...
                    self?.view.layoutIfNeeded()
                }, completion: { [weak self] _ in
                    ...
                })
            })
        })

...我更喜欢...

        UIView.animateKeyframes(withDuration: 3, delay: 0, options: .calculationModeLinear, animations: {[weak self] in

            UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.33, animations: {[weak self] in
                ...
                self?.view.layoutIfNeeded()
            })

            UIView.addKeyframe(withRelativeStartTime: 0.33, relativeDuration: 0.33, animations: {[weak self] in
                ...
                self?.view.layoutIfNeeded()
            })

            UIView.addKeyframe(withRelativeStartTime: 0.66, relativeDuration: 0.33, animations: {[weak self] in
                ...
                self?.view.layoutIfNeeded()
            })

        }, completion: { [weak self] _ in
            ...
        })

【讨论】:

    猜你喜欢
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    • 2019-05-06
    • 1970-01-01
    • 2013-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多