禁用 CALayer 蒙版帧更改动画
Posted
技术标签:
【中文标题】禁用 CALayer 蒙版帧更改动画【英文标题】:Disable CALayer mask frame change animations 【发布时间】:2017-01-05 23:22:05 【问题描述】:我有一个 CAShapeLayer
实例,其 CALayer mask
非零。我正在尝试使用该蒙版的框架来剪辑形状。哪个工作正常。但是当我更改框架时,我不希望它为框架更改设置动画。我更新了我视图的 layoutSubviews
中的框架,我注意到了一些有趣的事情:
override func layoutSubviews()
super.layoutSubviews()
...
if let futureMask = self.futureShape.mask
"0.future position animation \(futureMask.animation(forKey: "position"))".print()
futureMask.removeAllAnimations()
futureMask.add(CAAnimation(), forKey: "position")
futureMask.add(CAAnimation(), forKey: "bounds")
"1.future position animation \(futureMask.animation(forKey: "position"))".print()
let nowX = computeNowX()
futureMask.frame = CGRect(x: box.left + nowX, y: box.top, width: box.width - nowX, height: box.height)
"2.future position animation \(futureMask.animation(forKey: "position"))".print()
这产生的输出看起来像:
0.future position animations Optional(<CABasicAnimation:0x174624ac0; duration = 0.25; fillMode = backwards; timingFunction = default; keyPath = position; fromValue = NSPoint: 418.94695306710298, 14>)
1.future position animation Optional(<CAAnimation:0x170625780; duration = 0.25>)
2.future position animation Optional(<CABasicAnimation:0x170625820; duration = 0.25; fillMode = backwards; timingFunction = default; keyPath = position; fromValue = NSPoint: 418.94695306710298, 14>)
一开始是CABasicAnimation
,指向position
keyPath,持续时间为250毫秒。我把它吹走(通过removeAllAnimations()
)并添加一个存根CAAnimation,希望它不会做任何事情。而1
打印表明这确实发生了。但是在我设置了frame
属性之后,它又变回了原来的样子。似乎只是设置框架会重置这些动画。
没有这些动画就没有办法设置框架吗?我希望系统中的其他动画继续工作。我只是想让这个特殊的遮罩层不设置动画。
【问题讨论】:
为什么投反对票? 【参考方案1】:只需调用层removeAllAnimations()
之后设置框架。
class GradientView: UIView
private(set) var gradientLayer: CAGradientLayer
override init(frame: CGRect)
gradientLayer = CAGradientLayer()
super.init(frame: frame)
layer.insertSublayer(gradientLayer, at: 0)
required init?(coder aDecoder: NSCoder)
fatalError("not implemented")
override func layoutSubviews()
super.layoutSubviews()
gradientLayer.frame = bounds
gradientLayer.removeAllAnimations() // remove implicit animation from frame change
【讨论】:
这是一个更好的答案。【参考方案2】:最后,我发现唯一有效的方法是执行以下操作:
override func layoutSubviews()
super.layoutSubviews()
...
if let futureMask = self.futureShape.mask
let nowX = computeNowX()
CATransaction.setDisableActions(true)
futureMask.frame = CGRect(x: box.left + nowX, y: box.top, width: box.width - nowX, height: box.height)
CATransaction.commit()
基本上,我只是用 CATransaction
调用来包装框架的设置以禁用操作并强制提交。
更新
@Hlung 的回答更正确。设置remove
后,添加了两个动画(此时打印animationKeys()
即可)。我添加了以下扩展方法来捕获模式:
extension CALayer
func dontAnimate(_ closure:(CALayer) -> ())
closure(self)
self.removeAllAnimations()
这使我可以编写如下代码:
self.someSubLayer.dontAnimate layer in layer.frame = whatever
dontAnimate
的一个更强大的变体可能首先对当前动画进行快照,然后运行 closure
,然后然后 removeAllAnimations()
然后恢复快照的动画。
【讨论】:
我发现这不是一个好的解决方案。CATransaction.setDisableActions(true)
可以影响全球每一层。使用它,我会在整个应用程序中遇到奇怪的动画和布局,例如视图控制器推送没有动画,系统标签栏不知何故不再尊重底部安全区域。即使我将 setDisableActions 恢复到以前的值,它仍然没有帮助。对我有用的只是在设置layer.frame
后调用layer.removeAllAnimations()
。我将添加一个新答案。以上是关于禁用 CALayer 蒙版帧更改动画的主要内容,如果未能解决你的问题,请参考以下文章