1

There is an animation which animated a view from frame CGRect(x: 10, y: 10, width: 100, height: 100) to frame CGRect(x: 10, y: 10, width: 300, height: 300). I use the UIViewPropertyAnimator to implement the animation, and here is the code:

@objc func onTap() {
    animator = UIViewPropertyAnimator(duration: 3, curve: .easeIn)
    
    animator.addAnimations {
      self.testView.frame = CGRect(x: 10, y: 10, width: 300, height: 300)
    }
    
    animator.addCompletion { pos in
      print("test view frame:::", self.testView.frame)
      print("test view safe frame:::", self.testView.safeAreaLayoutGuide.layoutFrame)
      print("test view safe inset:::", self.testView.safeAreaInsets)
      print("test view keyboard frame:::", self.testView.keyboardLayoutGuide.layoutFrame)
    }
    animator.startAnimation()
}

During the animation, there is another button which is used to pause the animation and cancel the animation, here is the code:

@objc func cancelTheAnimation() {

    animator.pauseAnimation()
    animator.isReversed = true
    
    let progress = animator.fractionComplete
    
    animator.continueAnimation(
      withTimingParameters: UICubicTimingParameters(animationCurve: .linear),
      durationFactor: 1 - progress)
  }

Here is the result of the console print

------- animation completion ------
test view frame::: (10.0, 10.0, 100.0, 100.0)
test view safe frame::: (0.0, 37.0, 100.0, 63.0)
test view safe inset::: UIEdgeInsets(top: 37.0, left: 0.0, bottom: 0.0, right: 0.0)
test view keyboard frame::: (-10.0, 100.0, 390.0, 734.0)
------- animation completion ------
test view frame::: (10.0, 10.0, 100.0, 100.0)
test view safe frame::: (0.0, 37.0, 300.0, 263.0)
test view safe inset::: UIEdgeInsets(top: 37.0, left: 0.0, bottom: 0.0, right: 0.0)
test view keyboard frame::: (-10.0, 300.0, 390.0, 534.0)

The result is right for the first time. However, when I cancel the animation for the second time, the result is wrong, which should be 100 instead of 300 ( 263 + 37 ).

After the cancel of the animation, the view's frame is reset back to its initial value, but the safe area layout guide remains at the target value that was set by the completed animation.

Is this the bug of the UIKit?

If this is not the bug of the UIKit, how could I resolve this and make the safe area match with view's frame after the cancel of the animation.

Reyshawn
  • 131
  • 3
  • 5

1 Answers1

0

This behavior is not a bug in UIKit. When you cancel the animation and set the isReversed property to true, the view's frame returns to its original value of

CGRect(x: 10, y: 10, width: 100, height: 100).

However, the safe area of the view is not affected by the animator's fraction complete property, so it still reflects the value from the end of the animation. To reset the safe area, you can set the view's constraints to match its frame. I think this part will solve your issues.

@objc func cancelTheAnimation() {
   animator.pauseAnimation()
   animator.isReversed = true
   let progress = animator.fractionComplete
   animator.continueAnimation(
   withTimingParameters: UICubicTimingParameters(animationCurve: .linear),
                        durationFactor: 1 - progress)
                      
   testView.translatesAutoresizingMaskIntoConstraints = true
   testView.frame = CGRect(x: 10, y: 10, width: 100, height: 100)
   testView.layoutIfNeeded()
}