Search code examples
iosswiftuiviewuikituiviewanimation

UIKit: What happens when I have a UIView.animate inside an animation block?


Often in swift, I may want to have a property that, when set generally animates. In other words

class SelectableView: UIView {
   var isSelected: Bool = false {
      willSet {
        UIView.animate(withDuration: 0.200) {
           self.backgroundColor = .red
        }
      }
   }
}

However, I am realizing that it may not be the best practice. For example, when I use my class elsewhere, I may accidentally do something like:

let k = SelectableView()
UIView.animate(withDuration: 0.500) {
   k.isSelected = true
   k.alpha = 1.0
   k.backgroundColor = .red // notice this!
}

In that event, what even occurs? What exactly is the behavior there? Does the color go to red in 500 milliseconds or 200 milliseconds? Does willSet matter versus didSet? What happens if I straight up nested the UIView.animate inline rather in the setter?

In general, is it perhaps a bad practice to have an animation block in the setter?


Solution

  • In the setter you can check the value of UIView.inheritedAnimationDuration. If it’s 0, use default duration (i.e. 0.2). If it’s >0, use that value. Also, alternatively, if inherited duration is >0, you can skip animation in the setter, and just directly assign the value.

    class SelectableView: UIView {
       var isSelected: Bool = false {
          didSet {
            let duration = UIView.inheritedAnimationDuration
            UIView.animate(withDuration: duration > 0 ? duration : 0.200) {
               self.backgroundColor = .red
            }
          }
       }
    }
    

    In terms of good practices, Apple uses two setters in such cases:

    • isSelected - no implicit animation
    • setSelected(_ selected: Bool, animated: Bool) - opted-in animation

    The first one can be dynamic and call the second one with animated: false argument.