Airpods Pro Control Center animation with Matched Geometry Effect

Viennarz Curtiz
2 min readFeb 24, 2023

When I learned SwiftUI, I thought of replicating the animation of the Airpods Pro in Control Center.

I learned that with Matched Geometry Effect, some animations are easily achieved.

Build the content

As of writing, I use Xcode 14.2.

   enum Mode {
case noiseCancellation
case transparency
case off
}
@Namespace private var circleAnimate

Apple Docs says Namespace is

A dynamic property type that allows access to a namespace defined by the persistent identity of the object containing the property (e.g. a view).

@State private var selectedMode: Mode = .off

Let’s use ZStack with the Circle below of the Image, instead of using the modifier .background(Circle()) .

var body: some View {
HStack {
ZStack {
if selectedMode == .noiseCancellation {
Circle()
.fill(Color.blue)
.frame(width: 60, height: 60)
}

Image(systemName:
"person.fill")
.foregroundColor(.white)
.padding()
}

Spacer()

ZStack {
if selectedMode == .off {
Circle()
.fill(Color.gray)
.frame(width: 60, height: 60)

}

Image(systemName: "person.fill")
.foregroundColor(.white)
.padding()
}

Spacer()

ZStack {
if selectedMode == .transparency {
Circle()
.fill(Color.blue)
.frame(width: 60, height: 60)
}

Image(systemName: "person.fill")
.foregroundColor(.white)
.padding()
}

}
.padding(4)
.frame(width: 300)
.background(Capsule())
.font(.title2)
}

It should look like this.

Note that there is no SF Symbol that is exactly the same on the icons of the Control Center — Airpods Pro.

Let’s add tap gesture with animation on each Icon View

ZStack {
...
}
.onTapGesture {
withAnimation {
selectedMode = .noiseCancellation

}
}

The animation is still not the one we wanted.

Matched Geometry Effect

  • allows for synchronized animation between views that have the same ID and belong to different view hierarchies. It enables the creation of smooth transitions when elements are added, removed, or moved between screens.

Apple Documentation

Add this modifier to each Circle() view, after the .frame modifier.

.matchedGeometryEffect(id: "selection", in: circleAnimate)

Now it should behave like this.

Conclusion

Although MatchedGeometryEffect has made certain animations easier than ever before, I’ve also encountered some limitations when using it. While I haven’t used it extensively, I’m excited to see what improvements will be made in the future.

Hurray 🎉. Now go explore and experiment more!

Project Repository

https://github.com/viennarzc/AirpodsProLikeAnimation

--

--