Animate `backgroundColor` of a `UIView` that imple

2019-02-22 02:47发布

I have a custom UIView and I would like to animate its backgroundColor property. This is an animatable property of a UIView.

This is the code:

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
  }

This code causes causes the animation to skip and the color to change immediately:

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

If I animate alpha, this animates from 1 to 0 over one second as expected:

self.alpha = 0 // animates

How do I animate a background color change in this situation?

I guess I need to make a separate view--should this go in the storyboard in the same view controller? programmatically created?

Sorry, I'm really new to iOS and Swift.

2条回答
劫难
2楼-- · 2019-02-22 03:28

It is indeed not working when I try it, I had a related question where putting the layoutIfNeeded() method inside the animation worked and made the view smoothly animating (move button towards target using constraints, no reaction?). But in this case, with the backgroundColor, it does not work. If someone knows the answer I will be interested to know.

But if you need a solution right now, you could create a UIView (programmatically or via the storyboard) that is used only as a container. Then you add 2 views inside : one on top, and one below, with the same frame as the container. And you only change the alpha of the top view, which let the user see the view behind :

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")
        }
    }
}
查看更多
神经病院院长
3楼-- · 2019-02-22 03:45

The answer is that you cannot animate backgroundColor of a view that implements drawRect. I do not see docs for this anywhere (please comment if you know of one).

You can't animate it with animateWithDuration, nor with Core Animation.

This thread has the best explanation I've found yet:

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

The solution, as @Paul points out, is to add another view above, behind, or wherever, and animate that. This animates just fine.

Would love a good understanding of why it is this way and why it silently swallows the animation instead of hollering.

查看更多
登录 后发表回答