How do I animate constraint changes?

后端 未结 12 1812
野的像风
野的像风 2020-11-21 23:22

I\'m updating an old app with an AdBannerView and when there is no ad, it slides off screen. When there is an ad it slides on the screen. Basic stuff.

O

12条回答
  •  萌比男神i
    2020-11-22 00:00

    Swift 4 solution

    UIView.animate

    Three simple steps:

    1. Change the constraints, e.g.:

      heightAnchor.constant = 50
      
    2. Tell the containing view that its layout is dirty and that the autolayout should recalculate the layout:

      self.view.setNeedsLayout()
      
    3. In animation block tell the layout to recalculate the layout, which is equivalent of setting the frames directly (in this case the autolayout will set the frames):

      UIView.animate(withDuration: 0.5) {
          self.view.layoutIfNeeded()
      }
      

    Complete simplest example:

    heightAnchor.constant = 50
    self.view.setNeedsLayout()
    UIView.animate(withDuration: 0.5) {
        self.view.layoutIfNeeded()
    }
    

    Sidenote

    There is an optional 0th step - before changing the constraints you might want to call self.view.layoutIfNeeded() to make sure that the starting point for the animation is from the state with old constraints applied (in case there were some other constraints changes that should not be included in animation):

    otherConstraint.constant = 30
    // this will make sure that otherConstraint won't be animated but will take effect immediately
    self.view.layoutIfNeeded()
    
    heightAnchor.constant = 50
    self.view.setNeedsLayout()
    UIView.animate(withDuration: 0.5) {
        self.view.layoutIfNeeded()
    }
    

    UIViewPropertyAnimator

    Since with iOS 10 we got a new animating mechanism - UIViewPropertyAnimator, we should know that basically the same mechanism applies to it. The steps are basically the same:

    heightAnchor.constant = 50
    self.view.setNeedsLayout()
    let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
    animator.addAnimations {
        self.view.layoutIfNeeded()
    }
    animator.startAnimation()
    

    Since animator is an encapsulation of the animation, we can keep reference to it and call it later. However, since in the animation block we just tell the autolayout to recalculate the frames, we have to change the constraints before calling startAnimation. Therefore something like this is possible:

    // prepare the animator first and keep a reference to it
    let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
    animator.addAnimations {
        self.view.layoutIfNeeded()
    }
    
    // at some other point in time we change the constraints and call the animator
    heightAnchor.constant = 50
    self.view.setNeedsLayout()
    animator.startAnimation()
    

    The order of changing constraints and starting an animator is important - if we just change the constraints and leave our animator for some later point, the next redraw cycle can invoke autolayout recalculation and the change will not be animated.

    Also, remember that a single animator is non-reusable - once you run it, you cannot "rerun" it. So I guess there is not really a good reason to keep the animator around, unless we use it for controlling an interactive animation.

提交回复
热议问题