【问题标题】:Animating UIView based on as-yet undetermined layout基于尚未确定的布局动画 UIView
【发布时间】:2025-12-24 04:00:07
【问题描述】:

我有一个自定义视图 CustomView,它可以为自己制作动画以响应 animate 消息:

// in a view controller
[self.customView animate];

-[CustomView animate]内部,逻辑如下:

- (void)animate {
    for each subview {
        startFrame = subview.frame;
        endFrame = EndFrameFromStartFrame(startFrame);
        subview.animation = AnimationLoopBetween(startFrame, endFrame);
    }
}

* 这个块是伪代码。

也就是说,animate 方法检查每个子视图的当前帧,并为每个子视图生成一个动画结束帧,该帧本质上是一个比当前帧大固定比例的帧。然后,它为每个子视图创建一个动画,该动画改变其子视图的帧,在当前(或开始)帧和结束帧之间交替,之前计算。

一般来说,这种方法效果很好。它允许CustomView 正确地制作动画,而不管其超级视图的位置和大小如何。

然而,在少数情况下,CustomView(即视图控制器)的“用户”在其生命周期的早期就调用了animate——事实上,太早了,以至于 Auto Layout 还没有做出任何约束或布局通过。这意味着当CustomView 的所有子视图帧都是CGRectZero 时调用animate。正如您可能猜到的那样,这会导致动画的开始值和结束值计算不正确。

即使布局传递完成,并且视图的模型层具有适当的框架,CustomView 实例仍然不可见,因为它的子视图正在愉快地从一个零矩形动画到另一个。不好。

我应该如何重构这个动画行为,让这个类的用户可以随时调用animate而不产生负面影响?


以下是我考虑过的一些可能的答案,以及为什么它们对我来说似乎不满意:

  1. 在任何使用CustomView 的视图控制器中,推迟对animate 的调用,直到viewDidLayoutSubviews
    这对使用该视图的视图控制器施加了一个笨拙的限制。

  2. CustomViewlayoutSubviews方法中,先调用[super layoutSubviews],如果动画已经运行则重启。
    一个有趣的想法,但CustomView 有不止一个级别的子视图。因此,即使在[super layoutSubviews] 之后,视图层次结构中更靠后的许多视图都没有有效的帧,这意味着animate 仍然会出现异常。

  3. CustomViewlayoutSubviews 方法中,首先调用[super layoutSubviews],然后使用[<view> layoutIfNeeded] 手动布局任何需要的视图帧,最后重新启动动画(如果它已经在运行)。
    这实际上可能有效,但似乎不是一个好主意。我想尽可能将布局工作留给 Auto Layout。

【问题讨论】:

  • “我想尽可能将布局工作留给自动布局。”但是调用super layoutSubviews确实执行自动布局。这就是自动布局。在调用 super 之后将代码放入 layoutSubviews 是完全标准且正确的 - 包括在必要时更改约束并再次调用 super

标签: ios animation uiview autolayout


【解决方案1】:

为什么不跳过无趣的案例?

- (void)animate {
  if (CGRectEqualToRect(self.frame, CGRectZero) return;
  // ...animate.
}

【讨论】:

  • 我希望视图控制器能够在它选择时调用animate,包括在布局发生之前(例如,在viewDidLoad中)。如果我跳过这样的调用,像这样,我的视图会出现,但不会动画。
  • 如果没有布局,您对动画的期望究竟是什么?或者您是否打算将 animate 用作一个大的“开启”按钮,在您的视图布局后开始动画?
  • 后半部分,是的。我想animate 只启用动画,不需要考虑。也就是说,在调用该方法之后,视图将在屏幕上的任何时间进行动画处理,即使调用是在布局之前进行的。
  • 视图的框架对子视图动画有影响吗?根据您之前的陈述,我猜“是”。当您在制作动画时将帧从一个非零矩形更改为另一个时,您希望动画会发生什么?
  • 好问题(是的,确实如此)。 Auto Layout 将重新计算视图的模型框架,但无需干预,动画将继续进行而不会发生变化。我想我希望视图记录下来,结束其当前动画,然后重新启动,animate 使用新的当前帧作为起点。