为什么不早点调用自定义CALayer的draw方法呢?
Why isn't the draw method of the custom CALayer called earlier?
问题
我使用计时器在选择的 UIView 上触发随机动画。所有动画都按预期工作,但在定时器选择器退出后调用 CALayer 的绘制方法。
详细说明
为了清楚和简化,让我对执行的操作进行图解。
到目前为止我创建的所有动画都按预期工作:它们是现有 subviews/sublayers 上的 CABasicAnimations 的组合或添加到所选视图层次结构的新动画。它们被编码为 UIView 扩展,因此可以在任何视图上调用它们,而不管视图所在的视图或视图控制器如何。好吧,除了一个之外,所有工作都有效。
我确实创建了一个自定义 CALayer class,它包含在 CALayer 上绘制图案。在这个习惯的扩展 class 中,有一种方法可以使这些模式动画化(请参阅后面的代码)。所以总而言之,当我到达 step/method animate selected view
和 运行 这个特定的动画时,这是 应该发生的 :
- 调用了一个名为 animatePattern 的方法
- 该方法将自定义的CALayerclass添加到选中的视图中,然后调用该层的动画扩展
问题:如果加上其他所有的动画,所有的绘制都是在animate selected view
step/method退出之前执行的,特别是case,自定义CALayer class draw方法在performAnimation
方法退出后被调用,进而导致动画崩溃
我应该补充一点,我已经在一个单独的和简化的操场上尝试了自定义 CALayer class 动画并且效果很好(我将自定义层添加到 UIViewController 的 UIView
viewDidLoad
方法,然后我在 UIViewController 的 viewDidAppear
方法中调用图层动画。)
代码
animate selected view
调用的方法step/method:
func animatePattern(for duration: TimeInterval = 1) {
let patternLayer = PatternLayer(effectType: .dark)
patternLayer.frame = self.bounds
self.layer.addSublayer(patternLayer)
patternLayer.play(for: duration)
}
(注意这个方法在UIView扩展中,所以这里的self
代表调用了动画的UIView)
简化的自定义 CALayer Class:
override var bounds: CGRect {
didSet {
self.setNeedsDisplay()
}
// Initializers
override init() {
super.init()
self.setNeedsDisplay()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(effectType: EffectType){
super.init()
// sets various properties
self.setNeedsDisplay()
}
// Drawing
override func draw(in ctx: CGContext) {
super.draw(in: ctx)
guard self.frame != CGRect.zero else { return }
self.masksToBounds = true
// do the drawings
}
自定义CALayer的动画扩展class:
func play(for duration: Double, removeAfterCompletion: RemoveAfterCompletion = .no) {
guard self.bounds != CGRect.zero else {
print("Cannot animate nil layer")
return }
CATransaction.begin()
CATransaction.setCompletionBlock {
if removeAfterCompletion.removeStatus {
if case RemoveAfterCompletion.yes = removeAfterCompletion { self.fadeOutDuration = 0.0 }
self.fadeToDisappear(duration: &self.fadeOutDuration)
}
}
// perform animations
CATransaction.commit()
}
目前尝试次数
我试图通过在代码的不同位置插入 setNeedsDisplay
/ setNeedsLayout
来强制绘制图层,但它不起作用:调试自定义 CALayer class 的 draw
方法在performAnimation
方法退出后不断到达,而在animatePattern
方法中修改图层的框架时应该调用它。我一定错过了一些非常明显的东西,但我目前正在 运行 转圈子,我希望能有新的目光关注它。
提前感谢您花时间考虑这个问题!
最好的,
通常情况下,当您花时间写下您的问题时,新的想法就会开始涌现。事实上,我记得 self.setNeedsDisplay()
通知系统需要(重新)绘制图层但它不会立即(重新)绘制它:它将在下次刷新时完成。似乎刷新发生在该特定动画的循环结束之后。为了克服这个问题,我首先在设置 patternLayer 边界后立即添加了对 display
方法的调用,并且它起作用了,但是鉴于@Matt 评论,我通过调用 displayIfNeeded
更改了解决方案方法代替。它也有效。
func animatePattern(for duration: TimeInterval = 1) {
let patternLayer = PatternLayer(effectType: .dark)
patternLayer.frame = self.bounds
self.layer.addSublayer(patternLayer)
// asks the system to draw the layer now
patternLayer.displayIfNeeded()
patternLayer.play(for: duration)
}
再一次,如果有人想出另一个更优雅的solution/explanation,请不要拒绝分享!
您重写 draw(_ context),因为 UIView 可以是 CALayer 的委托。
UIView: {
var patternLayer : PatternLayer
func animatePattern(for duration: TimeInterval = 1) {
patternLayer = PatternLayer(effectType: .dark)
patternLayer.frame = self.bounds
self.layer.addSublayer(patternLayer)
}
func draw(_ layer: CALayer, in ctx: CGContext){
layer.draw(ctx)
if NECESSARY { patternLayer.play(for: duration)}
}
}
问题
我使用计时器在选择的 UIView 上触发随机动画。所有动画都按预期工作,但在定时器选择器退出后调用 CALayer 的绘制方法。
详细说明
为了清楚和简化,让我对执行的操作进行图解。
到目前为止我创建的所有动画都按预期工作:它们是现有 subviews/sublayers 上的 CABasicAnimations 的组合或添加到所选视图层次结构的新动画。它们被编码为 UIView 扩展,因此可以在任何视图上调用它们,而不管视图所在的视图或视图控制器如何。好吧,除了一个之外,所有工作都有效。
我确实创建了一个自定义 CALayer class,它包含在 CALayer 上绘制图案。在这个习惯的扩展 class 中,有一种方法可以使这些模式动画化(请参阅后面的代码)。所以总而言之,当我到达 step/method animate selected view
和 运行 这个特定的动画时,这是 应该发生的 :
- 调用了一个名为 animatePattern 的方法
- 该方法将自定义的CALayerclass添加到选中的视图中,然后调用该层的动画扩展
问题:如果加上其他所有的动画,所有的绘制都是在animate selected view
step/method退出之前执行的,特别是case,自定义CALayer class draw方法在performAnimation
方法退出后被调用,进而导致动画崩溃
我应该补充一点,我已经在一个单独的和简化的操场上尝试了自定义 CALayer class 动画并且效果很好(我将自定义层添加到 UIViewController 的 UIView
viewDidLoad
方法,然后我在 UIViewController 的 viewDidAppear
方法中调用图层动画。)
代码
animate selected view
调用的方法step/method:func animatePattern(for duration: TimeInterval = 1) { let patternLayer = PatternLayer(effectType: .dark) patternLayer.frame = self.bounds self.layer.addSublayer(patternLayer) patternLayer.play(for: duration) }
(注意这个方法在UIView扩展中,所以这里的self
代表调用了动画的UIView)
简化的自定义 CALayer Class:
override var bounds: CGRect { didSet { self.setNeedsDisplay() } // Initializers override init() { super.init() self.setNeedsDisplay() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } init(effectType: EffectType){ super.init() // sets various properties self.setNeedsDisplay() } // Drawing override func draw(in ctx: CGContext) { super.draw(in: ctx) guard self.frame != CGRect.zero else { return } self.masksToBounds = true // do the drawings }
自定义CALayer的动画扩展class:
func play(for duration: Double, removeAfterCompletion: RemoveAfterCompletion = .no) { guard self.bounds != CGRect.zero else { print("Cannot animate nil layer") return } CATransaction.begin() CATransaction.setCompletionBlock { if removeAfterCompletion.removeStatus { if case RemoveAfterCompletion.yes = removeAfterCompletion { self.fadeOutDuration = 0.0 } self.fadeToDisappear(duration: &self.fadeOutDuration) } } // perform animations CATransaction.commit() }
目前尝试次数
我试图通过在代码的不同位置插入 setNeedsDisplay
/ setNeedsLayout
来强制绘制图层,但它不起作用:调试自定义 CALayer class 的 draw
方法在performAnimation
方法退出后不断到达,而在animatePattern
方法中修改图层的框架时应该调用它。我一定错过了一些非常明显的东西,但我目前正在 运行 转圈子,我希望能有新的目光关注它。
提前感谢您花时间考虑这个问题! 最好的,
通常情况下,当您花时间写下您的问题时,新的想法就会开始涌现。事实上,我记得 self.setNeedsDisplay()
通知系统需要(重新)绘制图层但它不会立即(重新)绘制它:它将在下次刷新时完成。似乎刷新发生在该特定动画的循环结束之后。为了克服这个问题,我首先在设置 patternLayer 边界后立即添加了对 display
方法的调用,并且它起作用了,但是鉴于@Matt 评论,我通过调用 displayIfNeeded
更改了解决方案方法代替。它也有效。
func animatePattern(for duration: TimeInterval = 1) {
let patternLayer = PatternLayer(effectType: .dark)
patternLayer.frame = self.bounds
self.layer.addSublayer(patternLayer)
// asks the system to draw the layer now
patternLayer.displayIfNeeded()
patternLayer.play(for: duration)
}
再一次,如果有人想出另一个更优雅的solution/explanation,请不要拒绝分享!
您重写 draw(_ context),因为 UIView 可以是 CALayer 的委托。
UIView: {
var patternLayer : PatternLayer
func animatePattern(for duration: TimeInterval = 1) {
patternLayer = PatternLayer(effectType: .dark)
patternLayer.frame = self.bounds
self.layer.addSublayer(patternLayer)
}
func draw(_ layer: CALayer, in ctx: CGContext){
layer.draw(ctx)
if NECESSARY { patternLayer.play(for: duration)}
}
}