Matched Geometry Effect SwiftUI
Leveling Up Your UI: How to Use Matched Geometry Effect for Stunning SwiftUI Animations.
Introduction
SwiftUI has revolutionized the way iOS, macOS, watchOS, and tvOS developers create user interfaces. It provides an intuitive and declarative approach to building UI components, making it easier for developers to design seamless user experiences. One of the most powerful features of SwiftUI is the “Matched Geometry Effect,” which allows for smooth transitions and synchronized animations between views. In this article, we will explore the Matched Geometry Effect in SwiftUI and learn how to leverage it to create visually appealing and cohesive user interfaces.
What is the Matched Geometry Effect?
The Matched Geometry Effect in SwiftUI enables the seamless transition of views from one state to another, such as during navigation or animation. It synchronizes the geometry of two views with the same identifier, even if they are located in different containers or hierarchies. This synchronization allows SwiftUI to animate the transition between the two views smoothly, giving the impression that they are the same object transforming into a different state.
Key Concepts
To understand the Matched Geometry Effect better, we need to be familiar with some key concepts:
- ID-based matching: The Matched Geometry Effect relies on the unique identifier assigned to a view, which SwiftUI uses to identify and synchronize views with the same ID.
- Source and destination views: In a transition, SwiftUI designates a source view (initial state) and a destination view (final state).
- The source and destination views should have the same identifier for the Matched Geometry Effect to work effectively.
- AnimatableData protocol: To achieve smooth animations, SwiftUI’s animation engine requires certain properties to be animatable.
- The AnimatableData protocol enables this behavior, allowing SwiftUI to interpolate values and animate changes.
Benefits of Matched Geometry Effect
The Matched Geometry Effect offers several benefits to developers when creating user interfaces:
- Simplified animations: SwiftUI automatically handles the animation between source and destination views, reducing the need for complex and manual animation logic.
- Improved UI consistency: With the Matched Geometry Effect, developers can maintain a consistent visual experience across different transitions and screens.
- Enhanced user experience: Seamless transitions between views improve the overall user experience, making the app feel polished and professional.
Use Cases
The Matched Geometry Effect is a powerful tool with numerous applications in creating user interfaces. Some common use cases include:
- Seamless navigation transitions: When navigating between different screens, we can use the Matched Geometry Effect to ensure that specific elements, like buttons or images, appear to seamlessly morph into the new screen.
- Custom transitions: SwiftUI’s default transitions are visually appealing, but sometimes we may want to create a more personalized animation between views. The Matched Geometry Effect allows us to implement custom transitions that give our app a unique touch.
- Master-detail interfaces: When transitioning from a list of items to a detailed view, we can apply the Matched Geometry Effect to create a smooth animation that focuses users’ attention on the selected item’s details.
Implementation
To implement the Matched Geometry Effect in SwiftUI, follow these steps:
- Import SwiftUI and combine the views you want to synchronize into a container view.
- Assign a unique identifier to both the source and destination views using the
.id()
modifier. - Wrap each view with the
.matchedGeometryEffect()
modifier, passing the identifier as the parameter. - Trigger the animation, such as on a tap or a state change, and let SwiftUI handle the rest.
Example Code:
This code demonstrates how to use the Matched Geometry Effect to create smooth and synchronized animations when expanding and collapsing the profile view. The views with the same identifiers (profileAvatar
, profileName
, and profileJob
) will animate seamlessly between their initial and final states, providing a polished user interface experience.
import SwiftUI
struct ContentView: View {
@State private var isProfileExpanded = false
@Namespace private var profileAnimation
@Namespace private var profileName
@Namespace private var profileAvatar
@Namespace private var profileJob
var body: some View {
VStack {
if isProfileExpanded {
expandedProfileView
} else {
collapsedProfileView
}
videoList
}
}
var collapsedProfileView: some View {
HStack {
profileImage
.matchedGeometryEffect(id: profileAvatar, in: profileAnimation)
.frame(width: 60, height: 60)
VStack(alignment: .leading) {
Text("Profile Name")
.font(.title).bold()
.matchedGeometryEffect(id: profileName, in: profileAnimation)
Text("iOS Developer")
.foregroundColor(.secondary)
.matchedGeometryEffect(id: profileJob, in: profileAnimation)
}
Spacer()
}
.padding()
}
var expandedProfileView: some View {
VStack {
profileImage
.matchedGeometryEffect(id: profileAvatar, in: profileAnimation)
.frame(width: 130, height: 130)
VStack {
Text("Profile Name")
.font(.title).bold()
.matchedGeometryEffect(id: profileName, in: profileAnimation)
Text("iOS Developer")
.foregroundColor(.pink)
.matchedGeometryEffect(id: profileJob, in: profileAnimation)
Text("Check this Cool description, Check this Cool description, Check this Cool description.")
.padding()
}
}
.padding()
}
var profileImage: some View {
Image("avvtr")
.resizable()
.clipShape(Circle())
.onTapGesture {
withAnimation(.spring()) {
isProfileExpanded.toggle()
}
}
}
var videoList: some View {
List {
ForEach(0...5, id: \.self) { _ in
ZStack {
RoundedRectangle(cornerRadius: 8)
.frame(height: 180)
.foregroundColor(.gray.opacity(0.2))
Image(systemName: "play.fill")
.resizable()
.frame(width: 30, height: 30)
.opacity(0.3)
}
.padding(.vertical)
}
.listRowSeparator(.hidden)
}
.listStyle(.plain)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().preferredColorScheme(.dark)
}
}
ContentView
is a SwiftUI view that displays a collapsed profile view and a list of video items.- 2 . It uses the
@State
property wrapper to manage theisProfileExpanded
boolean variable, which determines whether the profile view is expanded or collapsed. - The
@Namespace
property wrappers are used to create unique namespaces for the matched geometry effect. Each view with a different namespace will not interfere with others during animation. collapsedProfileView
is a VStack that displays the collapsed profile information. It consists of a circular profile image and text for the profile name and job title.- The profile image and text are wrapped with the
matchedGeometryEffect
modifier, using different namespaces (profileAvatar
,profileName
, andprofileJob
) to enable synchronized animations during expansion and collapse. expandedProfileView
is a VStack that displays the expanded profile information. It contains a larger circular profile image, the profile name, job title, and a longer description.- Similarly, the views in the expanded profile are wrapped with the
matchedGeometryEffect
modifier, ensuring synchronized animations when transitioning from the collapsed to the expanded state. - The profile image has an
onTapGesture
that toggles theisProfileExpanded
state with a spring animation. videoList
is a List that contains video items, represented by rounded rectangles with a play icon. It is styled with a plain list style and hidden row separators.- The
preferredColorScheme(.dark)
inContentView_Previews
sets the preview to dark mode for testing.
Conclusion
The Matched Geometry Effect in SwiftUI empowers developers to create visually stunning and seamless user interfaces with synchronized animations. Whether it’s for navigation transitions, custom animations, or master-detail interfaces, this feature brings a new level of sophistication to app design. By leveraging the ID-based matching and AnimatableData protocol, SwiftUI can synchronize and animate views with ease. As SwiftUI evolves, developers can expect even more exciting features and enhancements that further enhance the user experience in their apps.
Additional Resources
- https://developer.apple.com/documentation/swiftui/view/matchedgeometryeffect(id:in:properties:anchor:issource:)
- https://www.hackingwithswift.com/quick-start/swiftui/how-to-synchronize-animations-from-one-view-to-another-with-matchedgeometryeffect
- https://www.youtube.com/watch?v=e7OuerHs8PU&t=1s&ab_channel=SeanAllen