为实现“drawRect”的“UIView”的“backgroundColor”设置动画

Animate `backgroundColor` of a `UIView` that implements `drawRect`

我有一个自定义 UIView,我想为其设置动画 backgroundColor 属性。这是 UIViewanimatable property

这是代码:

class ETTimerUIView: UIView {
  required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }
  // other methods
  func flashBg() {
    UIView.animateWithDuration( 1.0, animations: {
      self.backgroundColor = UIColor.colorYellow()
    })
  }
  override func drawRect() {
    // Something related to a timer I'm rendering
  }

这段代码导致动画跳过并且颜色立即改变:

self.backgroundColor = UIColor.colorYellow() // Changes immediately to yellow

如果我对 alpha 进行动画处理,这会按预期在一秒内从 1 到 0 进行动画处理:

self.alpha = 0 // animates

在这种情况下如何为背景颜色变化设置动画?

我想我需要创建一个单独的视图——它应该放在同一个视图控制器的故事板中吗?以编程方式创建?

抱歉,我是 iOS 和 Swift 的新手。

当我尝试它时它确实不起作用,我有一个相关的问题,将 layoutIfNeeded() 方法放入动画中工作并使视图平滑动画(move button towards target using constraints, no reaction?)。但是在这种情况下,使用 backgroundColor 是行不通的。如果有人知道答案,我会很想知道。

但如果您现在需要一个解决方案,您可以创建一个仅用作容器的 UIView(以编程方式或通过故事板)。然后在里面添加 2 个视图:一个在上面,一个在下面,与容器具有相同的框架。你只改变顶视图的alpha,让用户看到后面的视图:

class MyView : UIView {
    var top : UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.blueColor()

        top = UIView(frame: CGRectMake(0,0, self.frame.width, self.frame.height))
        top.backgroundColor = UIColor.yellowColor()
        self.addSubview(top)
    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        let sub = UIView(frame: CGRectMake(0,0, self.frame.width, self.frame.height))
        sub.backgroundColor = UIColor.purpleColor()
        self.sendSubviewToBack(sub)
        UIView.animateWithDuration(1, animations: { () -> Void in
            self.top.alpha = 0
            }) { (success) -> Void in
                println("anim finished")
        }
    }
}

答案是您无法为实现 drawRect 的视图设置动画 backgroundColor。我在任何地方都没有看到这方面的文档(如果您知道请发表评论)。

您无法使用 animateWithDuration 或 Core Animation 对其进行动画处理。

This thread 是我找到的最好的解释:

When you implement -drawRect:, the background color of your view is then drawn into the associated CALayer, rather than just being set on the CALayer as a style property... thus prevents you from getting a contents crossfade

正如@Paul 指出的那样,解决方案是在上方、后方或任何地方添加另一个视图,并为其设置动画。这动画效果很好。

希望能很好地理解为什么会这样,以及为什么它会默默地吞下动画而不是大喊大叫。

不确定这是否适合您,但为了使 UIView 的背景颜色动起来,我将其添加到 UIView 扩展中:

extension UIView {

    /// Pulsates the color of the view background  to white.
    ///
    /// Set the number of times the animation should repeat, or pass
    /// in `Float.greatestFiniteMagnitude` to pulsate endlessly.
    /// For endless animations, you need to manually remove the animation.
    ///
    /// - Parameter count: The number of times to repeat the animation.
    ///
    func pulsate(withRepeatCount count: Float = 1) {

        let pulseAnimation = CABasicAnimation(keyPath: "backgroundColor")

        pulseAnimation.fromValue = <#source UIColor#>.cgColor
        pulseAnimation.toValue = <#target UIColor#>.cgcolor

        pulseAnimation.duration = 0.4
        pulseAnimation.autoreverses = true
        pulseAnimation.repeatCount = count

        pulseAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        self.layer.add(pulseAnimation, forKey: "Pulsate")
        CATransaction.commit()
    }
}

将其粘贴到 Xcode 中的源文件时,请将占位符替换为您想要的两种颜色。或者您可以将整行替换为以下值:

pulseAnimation.fromValue = backgroundColor?.cgColor
pulseAnimation.toValue = UIColor.white.cgColor