Animations: Bringing the Host Passport to Life on iOS | by Anne Lu | The Airbnb Tech Weblog | Could, 2024
How Airbnb enabled hosts and friends to attach and introduce themselves by the Host Passport.
By: Anne Lu
In Could 2023 we launched the Host Passport as a part of our Summer Release. We wished to offer Hosts a method to introduce themselves, and begin constructing a extra private reference to their friends. To that finish, we created the Host Passport, which seems within the backside nook of every Personal Room itemizing outcome with a photograph of the Host on the duvet. Friends can faucet it to totally open the Host Passport and be taught extra concerning the Host and get a way for the actual dwell individual they’d be staying with.
The Host Passport presents Hosts a method to introduce themselves and set visitor expectations, and permits friends to rapidly begin discovering who they might be sharing an area with.
Delivering this animation with excessive pixel accuracy, fluidity, excessive efficiency, and a spark of pleasure led us to come across and resolve many novel technical points distinctive to every shopper platform that we help. Whereas the Host Passport seems within the net, Android, and iOS apps, this text focuses particularly on the iOS implementation.
Whereas we’ve nearly fully converted to SwiftUI relating to constructing new parts and screens in our app, we opted to make use of UIKit for the passport animation. We did this for a few causes. Firstly, on the time of this writing, SwiftUI doesn’t have APIs supporting customized transitions and navigation patterns, so our display screen navigation and transition layer stays in UIKit. And secondly, whereas keyframe timing was launched for SwiftUI animations with iOS 17, our model help prolonged again to iOS 15 on the time of launch.
UIKit gives a ready-to-use framework that allows the event of easy, polished animations. Mixed with our in-house declarative transition framework, we had been beginning with a strong basis that we might leverage to create advanced animations. The work lay in bridging the hole between established patterns and our novel necessities; whereas we had been already skilled in creating pleasant two-dimensional animations, three-dimensional animation was uncharted territory.
The complexity of this animation lies in its many shifting components. The problem lies not in animating single properties, however moderately coordinating many for a cohesive impact that’s not solely purposeful, but additionally pleasant.
The anchor point is a property of a view’s bounds, defaulting to the relative [0.5, 0.5], or precise heart. Rotation animations rotate round this anchor level, so by default, views rotate round their midpoints, which provides a card rotating impact moderately than a web page flipping one.
To attain the specified web page rotation, we confronted a dilemma with the anchor level. Shifting the anchor level to [0, 0.5] within the coordinate area might accomplish the web page turning impact by shifting it to the view’s main facet, however that method had the potential to disrupt different elements of the animation — it’s because the anchor level is used not solely as the idea for rotation, however for different transforms, corresponding to scaling and translation. Altering the anchor level for three-dimensional rotation has a knock-on impact on these different transforms, inflicting sudden unwanted side effects we’d then should work round.
With this in thoughts, we used another method: as a substitute of instantly manipulating the anchor level, we created clear views the place the seen content material occupied solely half of the area. Because the rotation happens, the view seemingly rotates across the left edge, whereas nonetheless leveraging the default heart level for the precise rotation.
With this, we’re in a position to animate our guide web page rotation with out introducing issues to the opposite transforms. See the instance beneath, the place there’s a border added across the whole view, together with the clear half, to point out its precise dimension.
With the rotation solved, we subsequent had to consider the best way to compose the view to appear to be a guide. We ended up conducting that impact through the use of a compound view. At a primary stage, the booklet consists of a entrance web page and two inside pages. That meant we wanted three separate views:
By stitching them collectively, we create the Passport booklet.
To create the impression of a web page flip, we wanted to make use of one other trick; whereas actual life pages have a entrance and a again, the identical is just not true of a view. Due to this fact, with a view to make it look like a web page turning, we timed it in order that in the course of the web page flip, the entrance view is swapped for the again view on the precise level when the web page is totally orthogonal to the viewer’s perspective. This creates the phantasm of a entrance and a again. Et voila!
At this level, we had a passport booklet with the flexibility to flip open in three dimensions.
With the intention to accomplish the following step within the animation we wanted to combine our guide animation with our declarative animation framework, which dealt with transitioning the animating passport from the itemizing outcomes view onto the modal view. Our animation framework permits us to carry out a shared component transition, the place a view animates seamlessly between two separate screens, in only a few strains of code.
First, we created a transition definition that describes the kind of animation we wished:
let passportTransition: TransitionDefinition = [
SharedElementIdentifiers.Passport.passport(listingId): .sharedElement
]
Subsequent, we hooked up these identifiers to the supply view (the passport within the itemizing search outcomes) and the vacation spot (the open passport card within the context sheet.) We set the transition definition on the modal presentation, and from there, the framework created the animation that moved our passport view from its beginning location within the itemizing outcomes to its closing location within the modal.
Beneath typical circumstances, our framework captures a “snapshot” of the view by rendering it as a static picture. The snapshot is then animated from the preliminary place to the ultimate place, whereas the unique supply and vacation spot views are hidden in the course of the animation. This enables us to play the animation of the view shifting from one place to a different in a performant means whereas holding the view hierarchy intact.
In our case, nevertheless, a static snapshot didn’t have the performance we wanted, which was the flexibility to play the web page flip animation alongside the shared component transition. Due to this fact, we created a customized snapshot that we used rather than the default static snapshot. This tradition snapshot was a duplicate of the view that did have animation capabilities, that we then triggered to play alongside the animated transition in order that they’d be completely in sync. Enter UIViewPropertyAnimator: a category that permits us to outline animation blocks and dynamically management their playback. It gives the pliability to begin, cease, or modify animations in real-time.
It neatly encapsulated our animations inside a single object, which might then be handed alongside to our animation framework. As our framework dealt with the display screen to display screen transition, it triggered the customized animation to play in sync with that transition.
It isn’t solely the place a view strikes that determines realism, but additionally very importantly when. The passport opens within the span of a second, however the easy class belies the complexity beneath.
On a more in-depth look, our animation consists of many synchronized particular person animations. The passport grows in dimension, strikes alongside the x and y axis, rotates its pages in 3D area, and shadows transfer to simulate gentle and motion. To get issues good, we use a separate timing curve for every property.
However we’d like much more specificity than that; our design requires these to begin and cease at completely different factors alongside the animation period. For that, we time particular occasions to relative factors throughout the timing curve through keyframes. To develop on our earlier instance, right here is our animator with keyframes set.
let animator = UIViewPropertyAnimator(period: 2.0, curve: .easeInOut) {// Allow keyframe animations, inheriting the period of the
// guardian property animator
UIView.animateKeyframes(withDuration: 0, delay: 0) {
// At the beginning of the animation, translate the view 100 pixels downwards
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
cardView.remodel = CGAffineTransform(translationX: 0, y: 100)
}
// On the midway level, flip the colour to coincide with the turning
// level of our view.
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0) {
cardView.backgroundColor = .pink
}
// Return to authentic place
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
cardView.remodel = .identification
}
}
}
animator.startAnimation()
Subsequent, let’s take a more in-depth take a look at spring timings and their distinctive traits. When creating animations, we’ve got the choice of several types of easing features for a naturalistic really feel.
Easing features like linear and cubic are widespread timing curves, as depicted within the graphs beneath. They offer us the flexibility to specify the pace of our animation over time.
Linear