preface

As we all know, animation is essential in daily development, and we can also see from those top apps that a high-quality APP is not without the blessing of animation. Early iOS is brilliant because iOS uses animation in every corner of the system. Developers working on iOS also know and use animations. Uiview.animate (withDuration:animations:) is the most popular, followed by CABasicAnimation, CAKeyFrameAnimation, and transitions. We all know that, but the one we use less often is UIViewPropertyAnimator.

The UIViewPropertyAnimator object allows you to animate views and dynamically modify them before the animation is complete. With the property animator, you can run animations normally from start to finish, or you can turn them into interactive animations and time them yourself. The animator operates on the view’s animatable properties, such as frame, center, alpha, and transform properties, to create the required animation from the blocks you provide.

… .

If you create an animator using one of the standard initialization methods, you must explicitly start the animation by calling startAnimation(). If you want to start the animation immediately after it is created, use the runningPropertyAnimator method instead of the standard initializer.

Such USES UIViewAnimating and UIViewImplicitlyAnimating agreement, these protocols define the start, stop, and modify the animation method. More information about the method of the agreements, please refer to the UIViewAnimating and UIViewImplicitlyAnimating.

More details can be found in the official documentation

A preliminary UIViewPropertyAnimator

UIViewPropertyAnimator was introduced in iOS 10 to create animations using this class. From the above we know that we must manually call the start animation method:

Here’s how to use UIView:

UIView.animate(withDuration: 0.25) {
    view.frame = view.frame.offsetBy(dx: 150, dy: 0)
}
Copy the code

This is done via UIViewPropertyAnimator

letAnimator = UIViewPropertyAnimator (duration: 0.25, the curve: .linear) { view.frame = view.frame.offsetBy(dx:150, dy:0) } animator.startAnimation()Copy the code

It doesn’t look very different from the top. Any other features? Definitely, especially in interactive animation and interruptible animation. Remember the “slide to power off” interactive animation that appeared when we shut down; And sliding out of the control center from the bottom and so on are interactive animations and interruptible animations are the best examples.

The animation state

By complying with the UIViewAnimating and UIViewImplicitlyAnimating protocol, using this way has the complete animation logic, For example, calling startAnimation, pauseAnimation, and stopAnimation will change between inactive, Active, and Stopped states.

The animation is active when it is started or paused, and inactive when the control is created before it is started or executed. The difference between inactive and stopped is: When pauseAnimation is called, the stopped function is called, and the finishAnimation(at:) function is called inside the animator to indicate that the animation is finished, then it enters inactive state, and finally the closure is called back.

The animation options

Animation duration, animation time curve (UIViewAnimationCurve already contains most of the available curves) are common when creating animations and can also be customized.

// Set the animation time and animation time curveletAnimator = UIViewPropertyAnimator(Duration: 1.0, curve:.easeout){aview.center = finalPoint} // Use two control points to define a Bessel time curveletAnimator = UIViewPropertyAnimator(Duration: 1.0, point1: CGPoint(0.1,0.5), point2: CGPoint(0.5, 0.2){aview. alpha = 0.0letanimator = UIViewPropertyAnimator( duration: {aview. center = CGPoint(x:0, y:0)}Copy the code

Animation callback

UIViewPropertyAnimator kept UIViewImplicitlyAnimating agreement, this agreement gives UIViewPropertyAnimator many interesting features. For example, you can specify multiple animation blocks in addition to the first animation block specified during initialization. It is worth noting that although we can add animations to already started animations, this will result in the remaining animation time to execute the newly added animations.

// Initialization
letAnimator = UIViewPropertyAnimator(Duration: 2.5, curve: .easeOut){aview. alpha = 1.0} // Another Animation Block animator.addAnimation{aview.center = aNewPosition} // Add a animation block animator.addAnimation{ aView.center.y = aNewPosition } animator.startAnimation()Copy the code

Animation, interactive

We know that we can change between the three states, and we can loop the animation. There is also a property that controls the percentage of completion of the animation: fractionComplete, which ranges from 0 to 1.0. This value can be used to get the percentage of the animation, or set to control the percentage of the animation. It is common to change and control animations in real time through gestures or some sliders, etc.

animator.fractionComplete = progress
Copy the code

There are also scenes that need to perform tasks at the end of the animation, and UIViewPropertyAnimator allows you to add execution completion closures

// Position is a UIViewAnimatingPosition enumeration: starting, current, end. Normally, the value of position is end animator.addcompletion {(position)in
    print("Animation completed")}Copy the code

Advanced animation implementation

This is a very common application scenario, and some page sharing, preview, and other pages will present their content in this way. When users use this scene, they will feel that the APP is not bad. Since Apple launched slide shutdown, animation has been widely applied in various apps and systems, because people like beautiful and beautiful things and have a pursuit of beauty. Back to this figure, we know there are a lot of practice can, generally USES the custom transitions, if there are transitions of encapsulation complete interface implementation is relatively simple, but in other ways, control the progress of the animation is a problem, we know from the above introduction UIView animation is unable to control the progress of the animation, Using other animations to do this is even less common.

Animate the view

let transitionAnimator = UIViewPropertyAnimator.init(duration: duration, dampingRatio: 1) {
  switch state{
       case .close:
            self.popupView.layer.cornerRadius = 0
            self.popupView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
            self.bottomConstraint.constant = self.popupViewOffset
            self.overlayView.alpha = 0.0
      case .open:
            self.popupView.layer.cornerRadius = 20
            self.popupView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
            self.bottomConstraint.constant = 0
            self.overlayView.alpha = 0.5
  }
  self.view.layoutIfNeeded()
}

transitionAnimator.addCompletion { (position) in
            switch position{
            case .start:
                self.currentState = state.change
            case .end:
                self.currentState = state
            case .current:
                ()
            @unknown default:
                fatalError()
            }
}

transitionAnimator.startAnimation()
Copy the code

View adds gesture control

@objc private func handlePanGesture(_ recognzier: UIPanGestureRecognizer){
        switch recognzier.state {
        case .began:
            self.animateToState(self.currentState.change, duration: 0.5)
            self.runningAnimators.forEach{ $0.pauseAnimation() }
            self.runningAnimatorProgress = self.runningAnimators.map{$0.fractionComplete }
        case .changed:
            let translation = recognzier.translation(in: popupView)
            var fraction = -translation.y / popupViewOffset
            if self.currentState == .open {
                fraction *= -1.0
            }
            if self.runningAnimators.first?.isReversed ?? false {
                fraction *= -1.0
            }
            for (index, animator) in runningAnimators.enumerated(){
                animator.fractionComplete = fraction + self.runningAnimatorProgress[index]
            }
        case .ended:
            self.runningAnimators.forEach{ $0.continueAnimation(withTimingParameters: nil, durationFactor: 0)}default: ()}}Copy the code

conclusion

After we understand UIViewPropertyAnimator, development animations can be used in some appropriate scenarios, because you can manually control the start, pause, resume and progress, which largely meets the functional requirements of developers.

Animation and UI this aspect of many foreign design are great, is a visual feast and extraordinary experience, and see the beauty is the same feeling, only a few in China.