This is one of my notes on learning iOS Animations by Tutorials. The code in detail on andyRon/LearniOSAnimations my lot.

This section introduces UIKit Animations apis that are designed to make it easy to create View Animations while avoiding the complexities of Core Animation (see System Learning iOS Animations 3: Layer Animations).

The UIKit animation API is not only easy to use, but also provides a great deal of flexibility and power to handle most (but certainly not all) animation requirements.

The UIKit animation API can animate any object on screen that eventually inherits from UIView, such as UILabel, UIImageView, UIButton, etc., or any custom object that you create that eventually inherits from UIView.

This article consists of five chapters, completing two projects, BahamaAirLoginScreen and Flight Info.

BahamaAirLoginScreen is a login page project, and chapters 1, 2, and 3 add various animations to some of the UI of this project.

1- View animation primer – Learn how to move, zoom, and desalinate views and other basic UIKit apis. 2- Spring animation – Based on the concept of linear animation, use spring animation to create a more compelling effect. 😊 3- Transition animation – Views appear and disappear.

Flight Info is a Flight status change project, and chapters 4 and 5 use some advanced animations to complete the project.

4- Practice view animation – Practice the animation techniques learned earlier. 5- Keyframe animation – Use keyframe animation to create complex animations made up of many different stages.

1- View animation primer

The first animation

The start project BahamaAirLoginScreen is a simple login page with two Textfields, a Label, a Button, 4 cloud images, and a background image that looks like this:

Move the Label and two Textfields out of the screen before the view is displayed. In viewWillAppear() add:

heading.center.x    -=  view.bounds.width
username.center.x   -=  view.bounds.width
password.center.x   -=  view.bounds.width
Copy the code

Add the Label and two textfields to the screen animation, and in viewDidAppear() add:

UIView.animate(withDuration: 0.5) {
  self.heading.center.x += self.view.bounds.width
}

UIView.animate(withDuration: 0.5, delay: 0.3, options: [],
  animations: {
    self.username.center.x += self.view.bounds.width
  }, 
  completion: nil
)

UIView.animate(withDuration: 0.5, delay: 0.4, options: [],
  animations: {
    self.password.center.x += self.view.bounds.width
  }, 
  completion: nil
)
Copy the code

This will animate the heading and TextField into the screen separately.

Similar UIView. The animate (…). According to the different parameters of the method, there are several different parameters meaning:

WithDuration: animation duration.

Delay: The delay before the animation starts.

Options: an array of uiView. AnimationOptions, used to define the animation variation, more on that later.

Animations: Provides closure for animations, that is, animation code.

Completion: Closure after animation execution is complete.

There is also usingSpringWithDamping and initialSpringVelocity which will be covered in a later section.

Animatable property

Previously, you used Center to create a simple position change view animation.

Not all view properties can be animated, but all view animations, from the simplest to the most complex, can be built from properties on animated views. Let’s take a look at the properties available for animation:

Size of position

bounds frame center

Form (Appearance)

BackgroundColor Alpha: Creates fade-in and fade-out effects.

Translation (Transformation)

Transform: Sets the view’s rotation, scaling, and/or position animation.

These seem like very basic animations that can make surprisingly complex animations! 😉

The animation options

AnimationOptions is an array of UIView.AnimationOptions. Uiview. AnimationOptions is a structure with many constant values.

A few commonly used ones are described below

repeat

.repeat: The animation is always repeated.

.autoreverse: If only. Repeat parameter animation process, like b->e b->e… With. Autoreverse, the animation process is like b-> E -> B -> E… . It’s easy to see the difference by looking at the figure below.

Animation slow

Animation easing, I’m just going to call it Animation easing.

The curve: bending; Bend. I’ll ease it up.

In real life, things don’t just suddenly start and stop moving. Objects like cars or trains will slowly accelerate until they reach their target speed, and unless they hit a brick wall, they will gradually slow down until they come to a complete stop at their final destination.

To make the animation look more realistic, speed it up slowly at the beginning and slow it down at the end, commonly known as ease-in and ease-out.

.curvelinear: Does not apply acceleration or deceleration to animations. .curveeasein: Animation starts slow and ends fast.

UIView.animate(withDuration: 1, delay: 0.6, options: [.repeat, .autoreverse, .curveEaseIn], animations: {
  self.password.center.x += self.view.bounds.width
}, completion: nil)
Copy the code

.curveeaseOut: Animation starts fast and ends slow.

UIView.animate(withDuration: 1, delay: 0.6, options: [.repeat, .autoreverse, .curveEaseOut], animations: {
          self.password.center.x += self.view.bounds.width
      }, completion: nil)
Copy the code

.curveeaseInout: Animation starts and ends slowly, and is fast in the middle

Fade animation of clouds

This one is easy to understand, is the opacity change animation of the CLOUD’s UIImageView. Make the cloud transparent in viewWillAppear() :

cloud1.alpha = 0.0
cloud2.alpha = 0.0
cloud3.alpha = 0.0
cloud4.alpha = 0.0
Copy the code

Then add the animation in viewDidAppear().

UIView.animate(withDuration: 0.5, delay: 0.5, options: [], animations: {
    self.cloud1.alpha = 1.0
}, completion: nil)
UIView.animate(withDuration: 0.5, delay: 0.7, options: [], animations: {
    self.cloud2.alpha = 1.0
}, completion: nil)
UIView.animate(withDuration: 0.5, delay: 0.9, options: [], animations: {
    self.cloud3.alpha = 1.0
}, completion: nil)
UIView.animate(withDuration: 0.5, delay: 1.1, options: [], animations: {
    self.cloud4.alpha = 1.0
}, completion: nil)
Copy the code

2- Spring animation

1- View animation introduction Animation is an action in a single direction, which can be understood as moving one point to another.

This section is the slightly more complicated Springs animation:

Describe spring animation with point changes:

The view goes from point A to point B, oscillating backwards and forwards at point B until the view stops at point B. It was a nice effect that allowed our animation to add a lively, authentic feel.

The beginning project of this chapter, BahamaAirLoginScreen, is the completion project of the previous chapter.

In viewWillAppear() add:

loginButton.center.y += 30.0
loginButton.alpha = 0.0
Copy the code

And then in viewDidAppear() add:

UIView.animate(withDuration: 0.5, delay: 0.5, 
usingSpringWithDamping: 0.5, initialSpringVelocity: 0.0, options: [], animations: {
  self.loginButton.center.y -= 30.0
  self.loginButton.alpha = 1.0
}, completion: nil)
Copy the code

So the Log In button has an animation that moves up instead of an animation that changes both properties at the same time.

UsingSpringWithDamping: damping parameters between 0.0 and 1.0, values closer to 0.0 create a more elastic animation, while values closer to 1.0 create a stiff-looking effect. You can think of this value as the ** “stiffness” ** of the spring.

InitialSpringVelocity: Initial speed. To smooth the start of animation, match this value to the previous view speed of the view.

Effect:

Animations that interact with the user

To animate the logIn button to interact with the user, add the following to the Action logIn() method of the Log In button:

UIView.animate(withDuration: 1.5, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options: [], animations: {
  self.loginButton.bounds.size.width += 80.0
}, completion: nil)
Copy the code

Click on a simple animation that increases in width.

Continue with logIn() and add:

UIView.animate(withDuration: 0.33, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: [], animations: {
  self.loginButton.center.y += 60.0
}, completion: nil) 
Copy the code

Click to enlarge the width while moving down the position.

Another great way to give feedback to users is through color changes. In the animation closure above add:

self.loginButton.backgroundColor = UIColor(red: 0.85, green: 0.83, blue: 0.45, alpha: 1.0)
Copy the code

One last way to give feedback to users: Activity Indicator. The login button should initiate user authentication activities over the network and transfer the user through the chrysanthemum to know that the login operation is taking place.

Continue to add in the animation closure above (spinner is already initialized in viewDidLoad and alpha is set to 0.0) :

self.spinner.center = CGPoint(x: 40.0, y: self.loginButton.frame.size.height/2)
self.spinner.alpha = 1.0
Copy the code

Make the chrysanthemum switch move as the login button moves down, and the final login button looks like this:

Modify the text box animation to spring animation

Put that in viewDidAppear()

UIView.animate(withDuration: 0.5, delay: 0.3, options: [], animations: {
    self.username.center.x += self.view.bounds.width
}, completion: nil)
UIView.animate(withDuration: 1, delay: 0.6, options: [], animations: {
    self.password.center.x += self.view.bounds.width
}, completion: nil)
Copy the code

Is amended as:

UIView.animate(withDuration: 0.5, delay: 0.3, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.0, options: [], animations: {
    self.username.center.x += self.view.bounds.width
}, completion: nil)

UIView.animate(withDuration: 0.5, delay: 0.4, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.0, options: [], animations: {
    self.password.center.x += self.view.bounds.width
}, completion: nil)
Copy the code

Results as follows:

3- Transition animation

Transitions

The beginning project of this chapter is the completion project of the previous chapter.

Examples of transitions

Various animation scenes using transition animation.

Add view

To animate a new view on the screen, call a method similar to the one used in the previous section. The difference this time is that you need to pre-select a predefined transition effect and set up the animation for the animation container view. The transition animation is set on the container view, so the animation works on all child views added to the container view.

Here’s a test (after that, delete the code and continue) :

var animationContainerView: UIView!

override func viewDidLoad(a) {
  super.viewDidLoad()
  // Set the animation container view
  animationContainerView = UIView(frame: view.bounds)
  animationContainerView.frame = view.bounds
  view.addSubview(animationContainerView)
}

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)

  // Create a new view
  let newView = UIImageView(image: UIImage(named: "banner"))
  newView.center = animationContainerView.center

  // Add a new view through transition animation
  UIView.transition(with: animationContainerView, 
    duration: 0.33, 
    options: [.curveEaseOut, .transitionFlipFromBottom], 
    animations: {  
      self.animationContainerView.addSubview(newView)
    }, 
    completion: nil)}Copy the code

Effect:

TransitionFlipFromBottom replaced by transitionFlipFromLeft:

The full predefined transition AnimationOptions are shown below. These AnimationOptions belong to uiview.animationoptions just as the options appeared in the previous two sections.

.transitionFlipFromLeft
.transitionFlipFromRight
.transitionCurlUp
.transitionCurlDown
.transitionCrossDissolve
.transitionFlipFromTop
.transitionFlipFromBottom
Copy the code

Delete the view

The transition animation operation of removing a subview from the screen is similar to adding.

Reference code:

UIView.transition(with: animationContainerView, duration: 0.33,
                  options: [.curveEaseOut, .transitionFlipFromBottom],
                  animations: {
                      self.newView.removeFromSuperview()
                  },
                  completion: nil
)
Copy the code

Hide or show the view

Adding and deleting changes the view hierarchy, which is why you need a container view. Hidden or shown transition animations use the view itself as the animation container.

Reference code:

UIView.transition(with: self.newView, duration: 0.33, 
  options: [.curveEaseOut, .transitionFlipFromBottom], 
  animations: {
    self.newView.isHidden = true
  }, 
  completion: nil
)
Copy the code

One view replaces another view

Reference code:


UIView.transition(from: oldView, to: newView, duration: 0.33, 
  options: .transitionFlipFromTop, completion: nil)
Copy the code

Composite transition animation

This section will simulate some user authentication, with several different animations of progress message changes. Once the user clicks the login button, they are shown messages, including “Connecting…” , “Authorizing” and “Failed”.

Add method showMessage() to ViewController:

func showMessage(index: Int) {
    label.text = messages[index]

    UIView.transition(with: status, duration: 0.33, options: [.curveEaseOut, .transitionCurlDown], animations: {
        self.status.isHidden = false
    }, completion: { _ in})}Copy the code

Add the call self.showMessage(index: 0) to the completion closure of the drawdown animation of the ActionlogIn method of the login button:

UIView.animate(withDuration: 1.5, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options:[], animations: {
    self.loginButton.bounds.size.width += 80.0
}, completion: { _ in
    self.showMessage(index: 0)})Copy the code

The animation option. TransitionCurlDown effect, like a piece of paper turned down, looks like this:

This works well to get the message of a static text label to the user’s attention.

Note: The iPhone emulator provides Slow Animations for quick Animations, Debug/Slow Animations(Command + T).

Add a state message elimination animation method:

func removeMessage(index: Int) {
    UIView.animate(withDuration: 0.33, delay: 0.0, options: [], animations: {
        self.status.center.x += self.view.frame.size.width
    }) { (_) in
        self.status.isHidden = true
        self.status.center = self.statusPosition

        self.showMessage(index: index+1)}}Copy the code

Where is this information elimination method called? After the display of the status information, of course, so add the following completion closure to the showMessage method:

delay(2.0) {
  if index < self.messages.count-1 {
    self.removeMessage(index: index)
  } else {
    //reset form}}Copy the code

Restore initial state

When the “Connecting…” After several information such as “Authorizing” and “Failed” are displayed, you need to delete the information label and restore the original login button.

Add the resetForm() function:

func resetForm(a) {
    // The status information label disappears
    UIView.transition(with: status, duration: 0.2, options: .transitionFlipFromTop, animations: {
        self.status.isHidden = true
        self.status.center = self.statusPosition
    }, completion: nil)
    // The login button and daisies revert to their original state
    UIView.animate(withDuration: 0.2, delay: 0.0, options: [], animations: {
        self.spinner.center = CGPoint(x: -20.0, y: 16.0)
        self.spinner.alpha = 0.0
        self.loginButton.backgroundColor = UIColor(red: 0.63, green: 0.84, blue: 0.35, alpha: 1.0)
        self.loginButton.bounds.size.width -= 80.0
        self.loginButton.center.y -= 60.0
    }, completion: nil)}Copy the code

Call resetForm() at the previous //reset Form.

Combined with previous effects:

Animation of ☁️ in the background

Wouldn’t it be cool if those clouds in the background moved slowly across the screen, moving from left to right, then disappearing to right and slowly moving again to the left? In the previous GIF, you could see the cloud moving, so far, the cloud has only opacity animation, actually because the project was finished when I made the GIF, I made it up, so… 😬)

Add an animateCloud(Cloud: UIImageView) method with the code:

func animateCloud(cloud: UIImageView) {
    // Assuming that the cloud takes about 60.0 seconds from entering the screen to leaving the screen, we can calculate the speed of cloud movement
    let cloudSpeed = view.frame.size.width / 60.0
    // The initial location of the cloud is not necessarily the edge
    let duration:CGFloat = (view.frame.size.width - cloud.frame.origin.x) / cloudSpeed
    UIView.animate(withDuration: TimeInterval(duration), delay: 0.0, options: .curveLinear, animations: {
        cloud.frame.origin.x = self.view.frame.size.width
    }) { (_) in
        cloud.frame.origin.x = -cloud.frame.size.width
        self.animateCloud(cloud: cloud)
    }
}
Copy the code

Code explanation:

  1. First, calculate the average moving speed of ☁️. Assume that the cloud takes about 60.0 seconds from entering the screen to leaving the screen (this time is custom, of course).

  2. Next, calculate how long it takes for ☁️ to move to the right of the screen. Note here that ☁️ does not start at the left edge of the screen, ☁️ moves the distance view.frame.sie.width-cloud.frame.origine.x.

  3. The animate then create animation method (withDuration: delay: options: animations: completion:). Here TimeInterval is a Double alias, and the animation option is used. CurveLinear (neither speed up nor slow down), which is rare, but works well as a slow move for ☁️.

    Animation closures in the cloud. Frame. Origin. X = self. The frame. The size, width, the ☁ ️ move to areas outside the right of the screen.

    Outside the right side of the screen, immediately place ☁️ outside the left edge in the completion closure, cloud.frame.origine.x = -cloud.frame.size. Width.

Finally, don’t forget to add the first four ☁️ animations to viewDidAppear() :

animateCloud(cloud: cloud1)
animateCloud(cloud: cloud2)
animateCloud(cloud: cloud3)
animateCloud(cloud: cloud4)
Copy the code

Overall effect:

4- Practice view animation

This chapter is to practice the animation we learned before.

The beginning project of this chapter, Flight Info, changes several views (several images and a Label) periodically, and the code is very simple:

  func changeFlight(to data: FlightData) {
    
    // populate the UI with the next flight's data
    summary.text = data.summary
    flightNr.text = data.flightNr
    gateNr.text = data.gateNr
    departingFrom.text = data.departingFrom
    arrivingTo.text = data.arrivingTo
    flightStatus.text = data.flightStatus
    bgImageView.image = UIImage(named: data.weatherImageName) snowView.isHidden = ! data.showWeatherEffects// schedule next flight
    delay(seconds: 3.0) {
      self.changeFlight(to: data.isTakingOff ? parisToRome : londonToParis)
    }
  }
Copy the code

Snowflake ❄️ will be learned in the following chapter 26- particle Emitters, and the effect is:

Fade out and fade to Animations (Crossfading Animations)

The first step is to make a smooth transition between the two background images. The first instinct might be to simply fade out the current image and then fade in the new one (opacity change). But when alpha is near zero, this method shows what’s behind the image, and it doesn’t look good. As follows:

Add the background image fade in and out effect to ViewController:

func fade(imageView: UIImageView, toImage: UIImage, showEffects: Bool) {
    UIView.transition(with: imageView, duration: 1.0, options: .transitionCrossDissolve, animations: {
        imageView.image = toImage
    }, completion: nil)

    UIView.animate(withDuration: 1.0, delay: 0.0, options: .curveEaseOut, animations: {
        self.snowView.alpha = showEffects ? 1.0 : 0.0
    }, completion: nil)}Copy the code

The showEffects parameter shows or hides the effect of snowfall.

Add an animated parameter to the changeFlight method, animated, and update the changeFlight method:

func changeFlight(to data: FlightData, animated: Bool = false) {
    summary.text = data.summary
    flightNR.text = data.flightNr
    gateNr.text = data.gateNr
    departingFrom.text = data.departingFrom
    arrivingTo.text = data.arrivingTo
    flightStatus.text = data.flightStatus

    if animated {
        fade(imageView: bgImageView,
             toImage: UIImage(named: data.weatherImageName)! , showEffects: data.showWeatherEffects) }else {
        bgImageView.image = UIImage(named: data.weatherImageName) snowView.isHidden = ! data.showWeatherEffects } }Copy the code

Go ahead and add a code to changeFlight that loops the background image:

delay(seconds: 3.0) {
    self.changeFlight(to: data.isTakingOff ? parisToRome : londonToParis, animated: true)}Copy the code

It now looks like this:

The transition between images is now very smooth compared to how it started, because the snow effect fades in and out of the background image at the same time, making the animation look seamless. You can even see it snowing in Rome! 😝 😝

One important technique I’ve unwittingly learned is that transition animations can be used for the non-animatable properties of views. (1- View Animation Primer does not have image in properties that can be used for animation)

The animation option.transitionCrossresolvent works well for the current project effect, while other options such as.transitionFlipFromLeft do not.

Cube Transitions

Pretend to convert text background color when 3d

This is not a true 3D effect, but it looks very close. You can animate stereo transitions through secondary views. You do this by adding a temporary Label, animating the height of the two labels, and then deleting them.

Add an enumeration to the ViewController:

enum AnimationDirection: Int {
    case positive = 1
    case negative = -1
}
Copy the code

The 1 and -1 of this enumeration will later indicate whether it goes down or up in the Y-axis transformation.

Add a cubeTransition method:

func cubeTransition(label: UILabel, text: String, direction: AnimationDirection) {
    let auxLabel = UILabel(frame: label.frame)
    auxLabel.text = text
    auxLabel.font = label.font
    auxLabel.textAlignment = label.textAlignment
    auxLabel.textColor = label.textColor
    auxLabel.backgroundColor = label.backgroundColor
}
Copy the code

This is constructing a temporary auxiliary Label and copying the original Label attribute to it, except for text with the new value.

Add the following Label to the cubeTransition method:

let auxLabelOffset = CGFloat(direction.rawValue) * label.frame.size.height/2.0
auxLabel.transform = CGAffineTransform(translationX: 0.0, y: auxLabelOffset).scaledBy(x: 1.0, y: 0.1) label.superview? .addSubview(auxLabel)Copy the code

When the text is scaled on the Y-axis alone, it looks like a vertical plane is gradually pushed down, resulting in a faux-perspective effect:

Animation code to continue in the cubeTransition method:

UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseOut, animations: {
    auxLabel.transform = .identity
    // The original Label is converted in the opposite direction on the Y axis
    label.transform = CGAffineTransform(translationX: 0.0, y: -auxLabelOffset).scaledBy(x: 1.0, y: 0.1)
},completion: { _ in
    // Assign the text of the secondary Label to the original Label, then delete the secondary Label
    label.text = auxLabel.text
    label.transform = .identity

    auxLabel.removeFromSuperview()
})
Copy the code

Finally, add this fake 3D rotation animation to the changeFlight method:

if animated {
    fade(imageView: bgImageView,
         toImage: UIImage(named: data.weatherImageName)! , showEffects: data.showWeatherEffects)let direction: AnimationDirection = data.isTakingOff ?
    .positive : .negative
    cubeTransition(label: flightNr, text: data.flightNr, direction: direction)
    cubeTransition(label: gateNr, text: data.gateNr, direction: direction)
} else {
    // No animation required
    bgImageView.image = UIImage(named: data.weatherImageName) snowView.isHidden = ! data.showWeatherEffects flightNr.text = data.flightNr gateNr.text = data.gateNr departingFrom.text = data.departingFrom arrivingTo.text = data.arrivingTo flightStatus.text = data.flightStatus }Copy the code

Finally, the Label conversion effect for the flight number and inbound number (I deliberately extended the animation Duration for easy viewing) :

The transition between fade in and fade out and rebound

Add Fade and bounce Transitions animations for departure and destination labels.

Start by adding the moveLabel method, similar to the one above, to create a secondary Label and copy to it some of the attributes of the original Label.

func moveLabel(label: UILabel, text: String, offset: CGPoint) {
    let auxLabel = UILabel(frame: label.frame)
    auxLabel.text = text
    auxLabel.font = label.font
    auxLabel.textAlignment = label.textAlignment
    auxLabel.textColor = label.textColor
    auxLabel.backgroundColor = .clear

    auxLabel.transform = CGAffineTransform(translationX: offset.x, y: offset.y)
    auxLabel.alpha = 0
    view.addSubview(auxLabel)
}
Copy the code

To animate the original Label with the offset conversion and the opacity decrement, add the following to the moveLabel method:

UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseIn, animations: {
    label.transform = CGAffineTransform(translationX: offset.x, y: offset.y)
    label.alpha = 0.0
}, completion: nil)
Copy the code

Animate the secondary Label and delete it after the animation ends. In the moveLabel method add:

UIView.animate(withDuration: 0.25, delay: 0.1, options: .curveEaseIn, animations: {
    auxLabel.transform = .identity
    auxLabel.alpha = 1.0
}, completion: { _ in
    auxLabel.removeFromSuperview()
    label.text = text
    label.alpha = 1.0
    label.transform = .identity
})
Copy the code

Finally, add if animated {to the changeFlight method:

// Place of origin and destination Label animation
let offsetDeparting = CGPoint(x: CGFloat(direction.rawValue * 80), y: 0.0)
moveLabel(label: departingFrom, text: data.departingFrom, offset: offsetDeparting)
let offsetArriving = CGPoint(x: 0.0, y: CGFloat(direction.rawValue * 50))
moveLabel(label: arrivingTo, text: data.arrivingTo, offset: offsetArriving)
Copy the code

The orientation of the origin and destination Label animations can be modified.

Effect:

Flight status bar animation

You can use the previous fake 3D rotation effect animation to add the following to changeFlight if Animated {:

cubeTransition(label: flightStatus, text: data.flightStatus, direction: direction)

Copy the code

Final results of this chapter:

5- Keyframe animation

Many times, multiple consecutive animations are required. In the previous sections, we have used animation closures and completion closures to contain two animation effects.

This works well for connecting two simple animations, but when we try to combine three, four, or more animations together, it leads to some incredibly confusing and complex code.

Let’s see what it would look like if we wanted to link multiple animations together and move the view in rectangular mode:

Suppose the following effect is implemented:

To do this, you can link several animations with completion closures:

UIView.animate(withDuration: 0.5, 
  animations: {
    view.center.x += 200.0
  }, 
  completion: { _ in
    UIView.animate(withDuration: 0.5, 
      animations: {
        view.center.y += 100.0
      }, 
      completion: { _ in
        UIView.animate(withDuration: 0.5, 
          animations: {
            view.center.x -= 200.0
          }, 
          completion: { _ in
            UIView.animate(withDuration: 0.5, 
              animations: {
                view.center.y -= 100.0})})})}) ' 'Copy the code

Keyframe Animations can be used in Animations instead of Animations that can seem too complicated to look at.

Start the project using the completed project in the previous chapter, Flight Info, by going to ✈️ “Planes come” to learn the keyframe animation.

Let the plane ✈️ take off can be divided into four different stages of animation (of course, the specific classification can depend on the situation) :

  1. Moving on the runway

  2. Give ✈️ a little altitude, incline up

  3. To give the aircraft more tilt and faster speed, tilt up to accelerate flight

  4. In the last 10% the plane fades out of view

The complete animation can be mind-boggling, but breaking it down into stages makes it more manageable. Once the keyframes are defined for each phase, it is easy to solve the problem.

Set up keyframe animation

The plane will take off from its starting position, circle, land and taxi back to its starting point. This animation runs every time the screen switches between flight backgrounds. The full animation will look like this:

Add the planeDepart() method to ViewController:

func planeDepart(a) {
  let originalCenter = planeImage.center

  UIView.animateKeyframes(withDuration: 1.5, delay: 0.0,
    animations: {
      //add keyframes
    }, 
    completion: nil)}Copy the code

And in changeFlight if animated {} call planeDepart() :

if animated {
    planeDepart()

    ...
Copy the code

//add keyFrames:

UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25, animations: {
    self.planeImage.center.x += 80.0
    self.planeImage.center.y -= 10.0
})
Copy the code

AddKeyframe (withRelativeStartTime: relativeDuration: animations:) is different from before animation parameter Settings. WithRelativeStartTime and relativeDuration are both relative time percentages, relative to withDuration.

Using relative values you can specify a small fraction of the total time the keyframe should last; UIKit takes the relative duration of each Keyframe and automatically calculates the exact duration of each keyframe, saving us a lot of work. The code above means that starting at 1.5*0.0, duration 1.5*0.25, ✈️ moves 80.0 to the right and 10.0 to the up.

Then above, add a second keyFrame:

UIView.addKeyframe(withRelativeStartTime: 0.1, relativeDuration: 0.4, animations: {
    self.planeImage.transform = CGAffineTransform(rotationAngle: -.pi/8)})Copy the code

This step is to give ✈️ an upward sloping Angle.

Next, add a third keyframe:

UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.25, animations: {
    self.planeImage.center.x += 100.0
    self.planeImage.center.y -= 50.0
    self.planeImage.alpha = 0.0
})
Copy the code

The step fades away as it moves.

Add a fourth keyframe:

UIView.addKeyframe(withRelativeStartTime: 0.51, relativeDuration: 0.01, animations: {
    self.planeImage.transform = .identity
    self.planeImage.center = CGPoint(x: 0.0, y: originalCenter.y)
})
Copy the code

This step brings ✈️ back to the left edge of the screen at the same height as before, but now with transparency 0.

Add a fifth Keyframe:

UIView.addKeyframe(withRelativeStartTime: 0.55, relativeDuration: 0.45, animations: {
    self.planeImage.alpha = 1.0
    self.planeImage.center = originalCenter
})
Copy the code

Return the plane to its original position.

Now let’s look at the five keyframe start time, they are not one by one, but there is overlap, this is because the step-by-step animation itself has crossed, ✈ ️ on the runway in the process of the mobile will move up, the nose will be gradually inclined upwards, I put the beginning of each step and duration, before this time may need to constantly adjust, See what time separation is more fluent 🙂, the following is a more fluent time separation way.

(0.0, 0.25)
(0.1, 0.4)
(0.25, 0.25)
(0.51, 0.01)
(0.55, 0.45)
Copy the code

Results as follows:

Calculation mode in keyframe animation

Keyframe animations do not support the built-in animation easing available in standard view animations. This is by design; Keyframes should start and end at specific times and flow to each other.

If each stage of the animation above had a gentle curve, the plane would shake instead of moving smoothly from one animation to the next.

Didn’t mention above animateKeyframes (withDuration: delay: options: animations: completion:) method, This method has a parameter (UIViewKeyframeAnimationOptions) options, can provide several kinds of the selection of calculation mode, each mode provides a different kind of method to calculate the animation in the middle of the frame and different optimizer, so as to realize the transformation, the frame before for more detailed information, To view the document UIViewKeyframeAnimationOptions.

Flight departure time animation

Since the departure time of the flight is at the top of the screen, you can simply move it up to the outside of the screen and then move it down to the inside of the screen.

func summarySwitch(to summaryText: String) {
    UIView.animateKeyframes(withDuration: 1.0, delay: 0.0, animations: {
        UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.45, animations: {
            self.summary.center.y -= 100.0
        })
        UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.45, animations: {
            self.summary.center.y += 100.0
        })
    }, completion: nil)

    delay(seconds: 0.5) {
        self.summary.text = summaryText
    }
}
Copy the code

Also call summarySwitch() in If Animated {} of changeFlight.

Final effect of this chapter:

This article is in my personal blog address: System learning iOS animation one: View animation