Property Animators

As the documentation indicates, to animate a view you create and start a property animator, which is an instance of UIViewPropertyAnimator. This allows you to animate one of the animatable view properties that you see listed.

Basic animations

In Mandala, you are first going to animate the highlight view on the ImageSelector. When a mood is selected, the highlight will move smoothly instead of simply appearing behind the selected mood. Later in this chapter, you will animate color changes on both the highlight view and the add mood button.

Open Mandala.xcodeproj and navigate to ImageSelector.swift. Update imageButtonTapped(_:) to animate the highlight view.

Listing 19.1  Animating the highlight view’s frame (ImageSelector.swift)

@objc private func imageButtonTapped(_ sender: UIButton) {
    guard let buttonIndex = imageButtons.firstIndex(of: sender) else {
        preconditionFailure("The buttons and images are not parallel.")
    }

    selectedIndex = buttonIndex

    let selectionAnimator = UIViewPropertyAnimator(
        duration: 0.3,
        curve: .easeInOut,
        animations: {
            self.selectedIndex = buttonIndex
            self.layoutIfNeeded()
        })
    selectionAnimator.startAnimation()

    sendActions(for: .valueChanged)
}

You initialize the property animator with a duration in seconds, a curve called a timing function (more on that shortly), and a closure that contains the changes to be animated. After the property animator is created, you call startAnimation() on it to begin the animation.

Recall that the highlightViewXConstraint is updated whenever the selectedIndex changes. Animating constraints is a bit different than animating other properties. If you update a constraint within an animation block, no animation will occur. Why? After a constraint is modified, the system needs to recalculate the frames for all the related views in the hierarchy to accommodate the change. It would be expensive for any constraint change to trigger this automatically. (Imagine if you updated quite a few constraints – you would not want it to recalculate the frames after each change.) Instead, the changes are batched up, and the system recalculates all the frames just before the next time the screen is redrawn.

But in this case, you do not want to wait – you want the frames to be recalculated as soon as selectedIndex changes and highlightViewXConstraint is updated. So you must explicitly ask the system to recalculate the frames, which you do in the closure by calling the method layoutIfNeeded() on the button’s view. This will force the view to lay out its subviews based on the latest constraints.

Build and run the application. Select different images and you will see the highlight view slide to its new position (Figure 19.2).

Figure 19.2  Highlight view animating

Highlight view animating

Timing functions

The acceleration of the animation is controlled by its timing function. The animation you created uses an ease-in/ease-out timing function (.easeInOut). This means that the animation accelerates smoothly from rest to a constant speed and then gradually slows down before coming to rest again.

Other timing functions include .linear (a constant speed from beginning to end), .easeIn (accelerating to a constant speed and then ending abruptly), and .easeOut (beginning at full speed and then slowing down at the end). Figure 19.3 shows the progress over time of these four timing functions. Try replacing the timing function in Mandala with the other options to see the effect. (You can increase the duration to make the difference more obvious.)

Figure 19.3  Timing functions

Timing functions

You can also create your own timing functions using a cubic Bézier curve, but that is beyond the scope of this book. If you are interested, look at the UIViewPropertyAnimator initializer init(duration:controlPoint1:controlPoint2:animations:).

Spring animations

iOS has a powerful physics engine built in. An easy way to harness this power is by using a spring animation. Spring animations define their own timing functions to add an oscillation to the end of a movement that looks like the moved element is settling into place.

Replace the basic animation for the highlight view with a spring animation.

Listing 19.2  Using a spring animation (ImageSelector.swift)

let selectionAnimator = UIViewPropertyAnimator(
    duration: 0.3,
    curve: .easeInOut,
    dampingRatio: 0.7,
    animations: {
        self.selectedIndex = buttonIndex
        self.layoutIfNeeded()
    })

The damping ratio is a measure of the springiness of the spring animation. It is a value between 0.0 and 1.0. A value closer to 0.0 will be more springy and so will oscillate more. A value closer to 1.0 will be less springy and so will oscillate less.

Play around with the values for the duration and damping ratio until you find a combination you like.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset