I’ve caught a very strange problem (THAT ONLY OCCURS ON REAL DEVICES) that has to do with using @NameSpace and MatchedGeometryEffect using NavigationStack. The problem is that if I go from the first screen to the second screen (on the second screen I have a View with MatchedGeometryEffect) , I get a strange bug where the MatchedGeometryEffect animation starts to work very badly (ONLY ON REAL DEVICE), it feels like the animation frame count drops to a minimum, but on simulator or preview everything works fine as it should. However, if I use the screen without NavigationStack, there is no such animation problem. What can this be related to ? And how can this be fixed ? It only takes a couple of lines of code to catch the animation problem, but it took all day to figure out what the problem is.
FirstView
struct NameSpaceTest2Navigation: View {
@State private var nameSpaceTest2Path: [String] = []
var body: some View {
NavigationStack(path: $nameSpaceTest2Path) {
Button(action: {
nameSpaceTest2Path.append("nameSpaceTest2")
}, label: {
Text("Button")
})
.navigationDestination(for: String.self) { path in
NameSpaceTest2()
}
}
}
}
SecondView
struct NameSpaceTest2: View {
@State private var selection: Int = 0
var body: some View {
VStack {
TabView(selection: $selection) {
ForEach(0..<5, id: .self) { _ in
Color.white
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
}
.overlay(alignment: .top) {
NameSpaceTest2Header(selection: $selection)
}
}
}
ThirdView
struct NameSpaceTest2Header: View {
@Binding var selection: Int
@Namespace var sectionUnderline
var body: some View {
ScrollViewReader { scrollReader in //START: ScrollViewReader
ScrollView(.horizontal, showsIndicators: false) { //START: ScrollView
HStack(spacing: 0) { //START: HStack
ForEach(0..<5, id: .self) { index in
VStack { //START: VStack
Text("Name: (index)")
.foregroundStyle(Color.green)
.padding(.horizontal, 24)
.padding(.vertical, 15)
.overlay(alignment: .bottom) {
if selection == index {
Rectangle()
.fill(Color.red)
.frame(height: 5)
.matchedGeometryEffect(id: "sectionUnderline", in: sectionUnderline, properties: .frame)
.transition(.scale(scale: 1))
}
}
.animation(.smooth, value: selection)
} //END: VStack
.onTapGesture {
withAnimation(.smooth) {
scrollReader.scrollTo(index)
selection = index
}
}
.tag(index)
}
} //END: HStack
} //END: ScrollView
.onChange(of: selection, perform: { value in
withAnimation(.smooth) {
scrollReader.scrollTo(value, anchor: .center)
}
})
} //END: ScrollViewReader
}
}
Try to repeat this code and run it on a real device with NavigationStack and you will get a bug with the animation, it will twitch like it has 5-10 frames per second.
Then try to run Second View without Navigation Stack and you will get smooth animation, which is what it should be.
What could be the problem ? How to get the smooth animation back ?
Once again, you should only test on a real device.
I tried wrapping selection = index in DispacthQueue.main.ascync , but it didn’t work. I tried changing the animation, but that didn’t work either. If I set the animation, I can see how the Rectangle changes size almost frame by frame.
Checked the performance in Instruments, nothing highly loaded is happening.
Didn’t expect to have a problem with this.
Can’t imagine what the problem could be, I expected the animation to be smooth, just like on the simulator
Simimi_Dot is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.