We're trying to implement transitions between TabView pages similar to what the Fitness app does on watchOS. As the user swipes or uses the Digital Crown to move between pages, the large rings gauge in the center transitions smoothly to a toolbar item. The code to implement this transition is described in two places in Apple's documentation:
- https://developer.apple.com/documentation/watchos-apps/creating-an-intuitive-and-effective-ui-in-watchos-10 (See the section “Provide continuity with persistent elements”)
- The WWDC23 session "Design and build apps for watchOS 10", around 9:30
However, copying the code from Apple's documentation doesn't give animation as reliable as what we're seeing in the Fitness app. Any slight reversal of motion causes the transition animation to jump back to the starting state. Has anyone else figured out how to replicate what the Fitness app is doing on watchOS?
I've included our View implementation below. Debug prints show what's going wrong: the page variable decrements immediately the user moves backward by any amount. But somehow, the Fitness app gets around this problem. How?
struct ContentView: View {
@Namespace var namespace
@State var page = 0
var body: some View {
NavigationStack {
TabView(selection: $page) {
globeView
.containerBackground(Color.blue.gradient, for: .tabView)
.navigationTitle("One")
.matchedGeometryEffect(
id: "globe",
in: namespace,
properties: .frame,
isSource: page == 0)
.tag(0)
Text("Page two")
.containerBackground(Color.green.gradient, for: .tabView)
.navigationTitle("Two")
.tag(1)
}
.tabViewStyle(.verticalPage)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
globeView
.matchedGeometryEffect(
id: "globe",
in: namespace,
properties: .frame,
isSource: page == 1)
}
}
}
}
@ViewBuilder
var globeView: some View {
Image(systemName: "globe")
.resizable()
.scaledToFit()
}
}
Thanks for any help!
—Chris