如何在复杂的层次结构中上下同步 CALayer 和 UIView 动画

2024-06-21

See 如何在整个层次结构中管理 CALayer 动画 https://stackoverflow.com/questions/26917908/how-to-manage-calayer-animations-throughout-a-hierarchy/26917967#26917967后续问题仅涉及如何同步父子层动画。

这是一个设计问题,涉及在视图层次结构的不同级别运行相关动画以响应层次结构上的动画。

我有一个容器控制器,它有任意数量的子控制器。父控制器在屏幕上组织这些内容,并且在不同的点需要更改其子视图的大小和位置。

我正在尝试为每个子视图设置动画,以将其大小/形状/位置从原始起点转换到目的地。基本的第一遍和一些基本的动画开始完成工作。

事情变得更加复杂,因为一些正在调整大小的视图也应该对视图的内容执行动画。想象一个内容居中的子视图。当子项缩小或展开时,居中的内容应与外部动画一起进行动画处理,以补偿边界变化,使内容保持居中。

更复杂的是,我还为每个子视图设置了一个遮罩层,需要与子视图一起进行动画处理。我还有一些不需要使用动画进行过渡的手势 - 所以我需要一种方法来有时为整个视图/图层树设置动画,有时则不需要。

所以所有这些给了我一个架构,其中我有如下所示的东西

ContainerViewController.view
 -> auxiliary and decorative views
 -> WrapperView (multiple)
    ----> mask layer
    -> Child controller view
       -> subviews & layers

现在我的问题实际上是可维护性之一。我可以使用显式或隐式动画为每个部分设置动画。我需要弄清楚的是,确保所有正在完成的动画都是使用相同的持续时间和计时函数完成的最佳方法是什么。目前,我触发了很多这样的属性更改。在某些情况下,属性更改来自layoutSubviews(由setNeedsLayout触发)。

那么,设置这些动画(尤其是显式动画)的最佳策略是什么?我能做的最好的事情就是从 CATransaction 中获取值吗?我担心的是,并不是每个属性都需要在每种情况下都进行动画处理(例如在辅助视图中) - 我已经在打开/关闭 setDisableActions 来强制/拒绝某些属性动画。

是否应该使用 CATransaction 来触发显式视图动画的设置?如何将为 UIView 动画指定的参数绑定到将用于底层的参数?下面的代码似乎可以完成工作,但看起来真的很难看。

-(void) animateForReason:(enum AnimationReason) animationReason
              animations:(void(^)()) animationBlock completion:(void(^)(BOOL)) completionBlock {
    const auto animationDuration = 3.0; // make this long to be noticeable!
    [UIView animateWithDuration:animationDuration delay:0 options:UIViewAnimationOptionLayoutSubviews
                     animations:^{
                         [CATransaction begin];
                         [CATransaction setAnimationDuration:animationDuration];
                         animationBlock();
                         [CATransaction commit];
                     }completion:completionBlock];
}

我认为 UIViewControllerTransitionCoordinator 已经过时了,因为我需要执行所有这些动画来响应用户操作,而不仅仅是外部事物,例如旋转或帧更改。


有几个选项需要考虑:

  1. 对于 UIView 转换,您可以传递UIViewAnimationOptionLayoutSubviews选项。这将在调用之前和之后对子视图之间的变化进行动画处理layoutSubviews在您刚刚更改其框架的视图上。
  2. 而不是使用layoutSubviews无论如何,你可以覆盖setBounds: or setFrame:在你的UIView and CALayer子类。这样,如果在动画块中调用它们,子视图将与超级视图一起进行动画处理。如果它们没有在动画块中被调用,它们将立即更新。

我担心的是,并不是每个属性都需要在每种情况下都进行动画处理(例如在辅助视图中) - 我已经在打开/关闭 setDisableActions 来强制/拒绝某些属性动画。

一般来说,如果您希望它具有动画效果,请将其放入动画块中,如果不需要,则不要。显然,它可以变得比这更复杂,这就是为什么苹果有时有一个带有animated论证(比如setSelected:animated:).

如果您有有时打开、有时关闭的属性,请自行遵循此模式。一种可能的实现:

- (void) setNumberOfWidgets:(int)widgetCount animated:(BOOL)animated {
    BOOL oldAnimationValue = [UIView areAnimationsEnabled];

    if (!animated) {
        [UIView setAnimationsEnabled:NO];
    }

    // a related property which may or may not cause an animation
    self.someOtherProperty = someValue;

    if (!animated) {
        [UIView setAnimationsEnabled:oldAnimationValue];
    }
}

我的问题实际上是可维护性之一。

是的,听起来您正在考虑某种巨型对象来为您管理所有动画可能性。抵制这种诱惑。让每个视图负责为其自己的子视图设置动画,并且tell一个视图(不要让它询问)是否应该对这些更改进行动画处理。

我认为 UIViewControllerTransitionCoordinator 已经过时了,因为我需要执行所有这些动画来响应用户操作,而不仅仅是外部事物,例如旋转或帧更改。

对,那是正确的。不要使用UIViewControllerTransitionCoordinator为了这。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在复杂的层次结构中上下同步 CALayer 和 UIView 动画 的相关文章

随机推荐