实现`drawRect`的`UIView`如何动画化`backgroundColor`?

7

我有一个自定义的UIView,我想要动画地改变它的backgroundColor属性。这是UIView的一个可动画属性

下面是代码:

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

如果我对透明度进行动画处理,这将在一秒钟内从1渐变到0,如预期所示:

self.alpha = 0 // animates

我该如何在这种情况下实现背景颜色的动画变化? 我猜我需要制作一个单独的视图 - 这应该放在故事板中的同一视图控制器中吗?还是通过编程创建?
抱歉,我真的很新iOS和Swift。

不确定,但尝试在更改颜色后调用setNeedsDisplay。 - kkarayannis
没有运气。颜色确实显示了,但是completion块立即触发并将其切换回来(在具有完成块的版本中)。我认为这是一个动画问题。 - SimplGy
你在哪里调用 flashBg?你能把它也发出来吗? - Zell B.
当计时器完成时,我调用flashBg函数,但是不想在示例中混淆这个,因为它并不重要--可以在应用启动时调用,也可以在你点击屏幕时调用。它始终不会执行动画。 - SimplGy
刚刚尝试了 let animation = CABasicAnimation(keyPath: "backgroundColor"); self.layer.addAnimation(animation, forKey: "flashBg"),但还是不起作用。我可以动画化 position.x。真希望知道出了什么问题。我不知道如何获取任何有用的调试输出,甚至。 - SimplGy
3个回答

4
当我尝试它时,确实没有起作用。我有一个相关的问题,在动画中放置layoutIfNeeded()方法可以使视图平滑地动画化(move button towards target using constraints, no reaction?)。但是,在这种情况下,使用backgroundColor却不起作用。如果有人知道答案,我会很感兴趣。
但是,如果您现在需要解决方案,您可以创建一个UIView(通过编程或通过storyboard),仅用作容器。然后,您在其中添加两个视图:一个在顶部,一个在底部,与容器具有相同的框架。并且您只更改顶部视图的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")
        }
    }
}

2
答案是你不能对实现drawRect的视图的backgroundColor进行动画。我没有在任何地方看到这方面的文档(如果您知道,请评论)。
您无法使用animateWithDuration或Core Animation来进行动画。 这个线程提供了我找到的最好的解释:
“当您实现-drawRect:时,您的视图的背景颜色会被绘制到关联的CALayer中,而不仅仅是设置为样式属性...这样就防止您获得内容交叉淡化。”
正如@Paul所指出的那样,解决方法是添加另一个视图,放在上面、后面或其他位置,并对其进行动画处理。这样可以很好地进行动画处理。
希望能够深入了解为什么会这样以及为什么它会默默地吞噬动画而不是大声呼喊。

链接已经损坏。 - Blind Ninja

1

我不确定这对你是否有用,但是为了使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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接