Browse Questions
  • XCTAssert functions are how you verify expected behaviour in tests.
  • UIViewPropertyAnimator is an object that owns an animation, which makes that animation interruptible, reversible, and scrubbable.
  • setNeedsLayout schedules a layout pass for later, layoutIfNeeded forces any pending layout to happen immediately, and setNeedsDisplay schedules a redraw of the view's content.
  • Secure Comparator is a zero-knowledge protocol that lets two parties confirm they hold the same secret without either side transmitting it, or leaking anything usable when the secrets differ.

Answer: UIViewPropertyAnimator is an object that owns an animation, which makes that animation interruptible, reversible, and scrubbable. You can pause it mid-flight, read and set fractionComplete, reverse its direction, and resume it — UIView.animate is fire-and-forget: once started you cannot inspect it, retarget it, or drive it from a gesture.

Code Example:

// Fire-and-forget — still the right call for simple, uninterruptible animations
UIView.animate(withDuration: 0.3) {
    self.card.alpha = 1
}

// Interactive, gesture-driven sheet
final class SheetController: UIViewController {

    private var animator: UIViewPropertyAnimator?

    @objc func handlePan(_ gesture: UIPanGestureRecognizer) {
        let translation = gesture.translation(in: view).y

        switch gesture.state {
        case .began:
            animator = UIViewPropertyAnimator(duration: 0.4, dampingRatio: 0.8) {
                self.sheet.transform = CGAffineTransform(translationX: 0, y: -400)
            }
            animator?.pauseAnimation()          // pause so we can scrub it manually

        case .changed:
            animator?.fractionComplete = -translation / 400   // drive it with the finger

        case .ended:
            let velocity = gesture.velocity(in: view).y
            animator?.isReversed = velocity > 0               // flick down → play backwards
            animator?.continueAnimation(
                withTimingParameters: UISpringTimingParameters(dampingRatio: 0.8),
                durationFactor: 0
            )

        default: break
        }
    }
}

// Custom timing curves and staged work
let animator = UIViewPropertyAnimator(duration: 0.5, curve: .easeOut) {
    self.header.transform = .init(scaleX: 1.2, y: 1.2)
}
animator.addAnimations({ self.header.alpha = 0 }, delayFactor: 0.5)  // second half only
animator.addCompletion { position in
    print(position == .end ? "finished" : "reversed or stopped")
}
animator.startAnimation()

Key Points:

  • Only UIViewPropertyAnimator gives you fractionComplete, pauseAnimation(), isReversed, and stopAnimation(_:) — the building blocks of any interactive transition
  • Hold a strong reference to the animator; a local one deallocates and the animation stops
  • stopAnimation(false) freezes the presentation values, then finishAnimation(at:) commits them — stopAnimation(true) leaves the model layer untouched
  • Reach for UIView.animate when the animation just needs to run to completion; the extra API isn't free in complexity

References: