当我设计需要同时使用特殊布局和动画的复杂UIView时,我经常会遇到一个常见问题。
我目前正在构建的内容与邮件应用在iOS上用于在撰写电子邮件时显示收件人列表(蓝色徽章容器)的视图相同:
所以我理解并知道如何构建这样的视图,但在面对这样的情况时我总是有这个问题:如何处理复杂的布局(布局所有蓝色圆角矩形)和它们的动画同时(添加或删除收件人时)?
1。通常我会重新实现:
- (void)layoutSubviews
根据我当前的界限反映布局时刻的视图状态(即并排布局每个蓝色圆形UIButton),并在有人向列表添加新人时添加动画UIButton。
但是如果布局传递已经在运行,动画会发生什么? (这可能是一个愚蠢的问题,因为一切都应该发生在主线程上,因此这里不涉及并发性,但我不确定如何处理这个问题)
我认为这两件事(布局和动画)不应该同时发生,因为主要的runloop"排队工作和队列工作"一个"块"一次(但也许一个动画只有几百个排队加入不同的东西超时,这可能很容易被一个布局块在其中妥协?)
2。此外,这个解决方案是否可以接受?
当有人添加或删除某个人时,只需将其调用以重新定位所有动画
[UIView animateWithDuration:0.25f animations:^{
[self setNeedsLayout];
[self layoutIfNeeded];
}];
正如你所看到的,这里有很多问题,我不知道如何妥善处理这个问题。
这两种解决方案都经过测试和批准,但问题是哪一种最好?
你有更好的解决方案吗?
感谢您的帮助!
答案 0 :(得分:1)
据我了解,您的意思是应用于实施layoutSubviews
方法的视图的动画,例如:其框架的动画变化。如果视图中的某些内容在动画正在进行时会导致视图重新布局,则会调用layoutSubviews
方法。
基本上,这种动画只不过是:
layoutSubviews
layoutSubviews
所有这些步骤都在主线程上执行,因此上述步骤是连续的,不是并发的。
一般来说,layoutSubviews
方法应根据自己的边界定义子视图的坐标和大小。动画会更改视图的帧以及相应的边界,因此layoutSubviews
方法的实现应该能够正确处理这些更改。
也就是说,您的问题的答案是正确实施layoutSubviews
方法。
P.S。 This answer对动画的实施方式有很好的解释。
以前我似乎错误地理解了这个问题。我的理解是:
如果为动画视图调用
layoutSubviews
方法,会发生什么? 也就是说,我认为有一些观点,例如通过动画更改其y
坐标,在动画期间为此视图调用layoutSubviews
方法,并且视图的某个子视图会更改其在layoutSubviews
中的位置。
在@Nicolas澄清之后,我的理解如下:
让我们看一下带有子视图(
'Parent'
)的视图('Child'
);让我们为这个子视图设置动画('Child'
);在动画正在进行时,让我们调用layoutSubviews
视图的'Parent'
方法,并在此'Child'
方法中更改layoutSubviews
子视图的帧。会发生什么?
UIView
本身不会呈现任何内容;它是由Core Animation层完成的。每个视图都有一个关联的CALayer
,它包含视图的位图快照。实际上,UIView
只是核心动画层之上的一个薄组件,它使用视图的位图快照执行实际绘制。这种机制优化了绘图:光栅化的位图可以通过图形硬件快速渲染;如果视图结构没有改变,那么位图快照就不会改变,也就是说它们被加入了#39;
核心动画图层次结构与UIView
的层次结构相匹配;也就是说,如果某些UIView
具有子视图,则与容器UIView
对应的核心动画层具有与子视图对应的子图层。
嗯......实际上,每个UIView
甚至超过1个对应的CALayer
。 UIView
层次结构生成3个匹配的Core Animation Layers树:
UIView
' s layer
属性使用的图层更改UIView的属性(例如框架)实际上是更改了CALayer的属性。也就是说,UIView的属性是相应CALayer属性的包装器。
UIView框架的动画实际上是CALayer框架的变化;层树的层的帧被立即设置为目标值,而层的帧值从表示树的变化被及时拉伸。以下电话:
[UIView animateWithDuration:5个动画:^ {
CGRect frame = self.label.frame;
frame.origin.y = 527;
self.label.frame = frame;
}];
并不意味着self.label
drawRect:
方法将在接下来的5秒内被多次调用;这意味着y
- 与CALayer
对应的演示树self.label
的坐标将在这5秒内从初始值逐渐变为目标值,self.label
存储在此CALayer
中的位图快照将根据其y
坐标的更改重新绘制多次。
鉴于此背景,现在我们可以回答原始问题。
因此,我们为子视图提供动画,并为父视图调用layoutSubviews
方法;在这种方法中,子视图的框架会发生变化。这意味着与子视图关联的图层框架将立即设置为新值。同时,来自表示树的图层具有一些中间值(根据正在进行的动画);设置新帧只会更改演示树图层的目标值,以便动画将继续到新目标。
也就是说,原始问题中描述的情况的结果是“跳跃”'动画。请参阅GitHub sample project中的演示。
在layoutSubviews
方法中,如果动画正在进行,则需要使用飞行中的动画坐标。您可以使用与视图相关联的presentationLayer
CALayer
方法获取它们。也就是说,如果正在设置动画的视图aView
,则可以使用[aView.layer presentationLayer]
访问此视图的表示层。