【问题标题】:UIView replicate CAAnimation of another view, in real time?UIView 实时复制另一个视图的 CAAnimation?
【发布时间】:2020-03-22 21:14:06
【问题描述】:

所以我有一个带有渐变子图层的背景视图,它会不断地动画以缓慢地改变颜色。我用CATransaction 来做这件事,因为我还需要为其他属性设置动画:

CATransaction.begin()

gradientLayer.add(colorAnimation, forKey: "colors")
// other animations

CATransaction.setCompletionBlock({
    // start animation again, loop forever
}

CATransaction.commit()

现在我想复制这个渐变动画,比如说,一个按钮的标题。

注意 1:如果可能的话,我不能只是在按钮上“打个洞”,因为我可能在按钮和背景之间有其他不透明的视图。

注意2:按钮上的渐变位置并不重要。我不希望文本渐变复制下面的确切颜色,而是模仿背景的“情绪”。

所以当按钮被创建时,我将它的渐变子层添加到一个注册层列表中,后台管理器也会更新:

func register(layer: CAGradientLayer) {
    let pointer = Unmanaged.passUnretained(layer).toOpaque()
    registeredLayers.addPointer(pointer)
}

因此,虽然在动画的下一次迭代中为文本渐变设置动画很容易,但我更希望按钮在添加后立即开始动画,因为动画通常需要几秒钟。如何复制背景动画,即将文本渐变设置为背景动画的当前状态,并使用右时长左和定时功能对其进行动画处理?

【问题讨论】:

  • 我不明白为什么你不能在包含文本“HELLO”的图层和后面的背景图层中添加一个类似的动画。你有更多关于你的动画是什么样子的细节吗?
  • 我可以,如果动画同时开始的话。但是,虽然背景动画在应用程序启动时开始,但可以随时添加按钮。因此我的问题是如何获得背景动画的当前状态,以便我可以向按钮添加与背景同步且不延迟的动画。
  • CABasicAnimation 有一个 beginTime 属性,您可以使用它与其他人同步您的动画
  • @Coconuts 上面的示例是否托管在 github 中?如果有,可以分享一下链接吗?

标签: swift animation cabasicanimation cagradientlayer catransaction


【解决方案1】:

正如@Shivam Gaur 的评论所建议的那样,解决方案确实是使用beginTime 属性。我实现如下:

// The background layer, with the original animation
var backgroundLayer: CAGradientLayer!

// The animation
var colorAnimation: CABasicAnimation!

// Variable to store animation begin time
var animationBeginTime: CFTimeInterval!

// Registered layers replicating the animation
private var registeredLayers: NSPointerArray = NSPointerArray.weakObjects()

...

// Somewhere in our code, the setup function
func setup() {
    colorAnimation = CABasicAnimation(keyPath: "colors")
    // do the animation setup here
    ...
}
...

// Called by an external class when we add a view that should replicate the background animation
func register(layer: CAGradientLayer) {

    // Store a pointer to the layer in our array
    let pointer = Unmanaged.passUnretained(layer).toOpaque()
    registeredLayers.addPointer(pointer)

    layer.colors = colorAnimation.toValue as! [Any]?

    // HERE'S THE KEY: We compute time elapsed since the beginning of the animation, and start the animation at that time, using 'beginTime'
    let timeElapsed = CACurrentMediaTime() - animationBeginTime
    colorAnimation.beginTime = -timeElapsed

    layer.add(colorAnimation, forKey: "colors")
    colorAnimation.beginTime = 0
}

// The function called recursively for an endless animation
func animate() {

    // Destination layer
    let toLayer = newGradient() // some function to create a new color gradient
    toLayer.frame = UIScreen.main.bounds

    // Setup animation
    colorAnimation.fromValue = backgroundLayer.colors;
    colorAnimation.toValue = toLayer.colors;

    // Update background layer
    backgroundLayer.colors = toLayer.colors

    // Update registered layers (iterate is a custom function I declared as an extension of NSPointerArray)
    registeredLayers.iterate() { obj in
        guard let layer = obj as? CAGradientLayer else { return }
        layer.colors = toLayer.colors
    }

    CATransaction.begin()

    CATransaction.setCompletionBlock({
        animate()
    })

    // Add animation to background
    backgroundLayer.add(colorAnimation, forKey: "colors")

    // Store starting time
    animationBeginTime = CACurrentMediaTime();

    // Add animation to registered layers
    registeredLayers.iterate() { obj in
        guard let layer = obj as? CAGradientLayer else { return }
        layer.add(colorAnimation, forKey: "colors")
    }

    CATransaction.commit()

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多