我有一个UIView
,其中包含一些使用AutoLayout定位的UILabel
。
在设备旋转时,我切换布局约束以将标签设置为新位置(布局约束来自分配给视图的Layout
对象):
override func viewWillTransition(
to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator
) {
super.viewWillTransition(to: size, with: coordinator)
let isLandscape = size.width > size.height
let layout = isLandscape ? layoutLandscape : layoutPortrait
coordinator.animate(
alongsideTransition: { _ in self.layoutView.setLayout(layout) },
completion: nil
)
}
此效果效果很好,可在针对横向和纵向优化的不同内容布局之间提供平滑过渡。
但是,在转换期间,标签的 size 也会因布局限制而发生变化。值得注意的是,虽然标签位置的过渡是渐进的,但标签的形状会在过渡开始时立即发生变化,然后它们会动画到最终位置。
我希望能够在初始和最终形状之间交叉分解标签,并将其与位置动画一起发生。
理想情况下,我希望以相同的高度分离方式实现这一点,即实现位置动画,布局更新代码不知道更改是否会被动画化。我希望至少可能需要一些折衷方案!
谢谢。
我非常接近这方面的工作解决方案。它还没有回答材料,所以我要在这里写下“我试过的东西” - 如果它成为一个答案,我会把它变成一个。
fantastic article at objc.io显示了如何创建UILabel
子类,告诉其支持CALayer
使用content
为其CATransition
属性设置动画的动画。这是最纯粹的纯粹巫术,但奇妙的是,它实际上都是有道理的,你怀疑也许你也可以戴巫师的帽子。
所以,你最终会得到类似的东西:
class ContentAnimatedLabel: UILabel {
override func action(for layer: CALayer, forKey event: String) -> CAAction? {
guard event == "contents" else {
return super.action(for: layer, forKey: event)
}
let transition = CATransition()
// Oh noes! A hard coded made up value!
// And where do we get the timing curve?! Curses.
transition.duration = 0.3
return transition
}
}
就目前而言,它有效!标签的内容很好地消失,并且它们的位置进行插值。它看起来相当宏伟。
问题是你这个代码不知道它是否是从动画块中调用的。实际上,它应该只在从动画块中调用时返回转换,它应该从动画块给出的动画属性中复制动画属性,例如持续时间和时间。
文章建议解决这个问题......
UIView动画机制实现是私有的,因此没有简单的标志我们可以检查视图当前是否是动画,但我们确实知道一件事:当动画时,UIView的actionForLayer:forKey:将为其动画返回有效的CAActions属性键,当没有动画时,它将为它们返回[NSNull null]。如果我们只是选择一个合适的密钥,我们可以询问UIView以查看它是否正在为该密钥提供操作,并使用它来确定我们对自定义密钥的响应。我们将使用键“backgroundColor”,因为这是通常支持动画的UIView的属性,并且我们知道返回CABasicAnimation作为其动作(从iOS 8开始,一些属性,例如“bounds”返回私有类,所以小心):
...但这似乎不起作用,至少对于我正在测试的iOS 11。
当UIKit
请求content
键的操作时,就像动画块已完成一样。向super
询问其他键使用的操作始终不返回任何内容。因此,您无需从中获取适当的转换参数。