SwiftUI导航时matchedGeometryEffect不生效的问题排查
我希望导航到新页面时,视图能平滑动画到新页面的对应位置(类似Hero效果)。有文章表明该效果可通过matchedGeometryEffect实现,但在Xcode 15.0、iOS17环境下,以下代码仅呈现常规导航动画,未达到预期效果。请问这是Bug还是我遗漏了什么?注:示例为简化场景,实际业务更复杂。
代码:
struct ContentView: View { var imageNames = ["circle", "cloud"] @Namespace var n var body: some View { NavigationStack { List { ForEach(imageNames) { name in NavigationLink { ZStack { Color.blue.opacity(0.3) Image(systemName: name) .resizable() .frame(width: 200, height: 200) .matchedGeometryEffect(id: name, in: n) } } label: { Image(systemName: name) .resizable() .frame(width: 50, height: 50) .padding() .matchedGeometryEffect(id: name, in: n) } } } } } } extension String: Identifiable { public var id: String { self } }
问题原因与解决方法
这不是Bug,是iOS17的NavigationStack默认转场动画优先级更高,覆盖了matchedGeometryEffect的Hero动画效果。只需做以下调整即可生效:
禁用目标页面的默认导航转场动画
在目标页面的根视图上添加.transaction修饰符,取消系统默认的导航动画,让Hero动画正常播放。确保
matchedGeometryEffect配置一致
两个关联视图的id和namespace必须完全匹配,若自定义了properties或anchor参数,两端也要保持一致。
修改后的代码:
struct ContentView: View { var imageNames = ["circle", "cloud"] @Namespace var n var body: some View { NavigationStack { List { ForEach(imageNames) { name in NavigationLink { ZStack { Color.blue.opacity(0.3) Image(systemName: name) .resizable() .frame(width: 200, height: 200) .matchedGeometryEffect(id: name, in: n) } .transaction { transaction in transaction.disablesAnimations = true // 关闭默认导航动画 } } label: { Image(systemName: name) .resizable() .frame(width: 50, height: 50) .padding() .matchedGeometryEffect(id: name, in: n) } } } } } } extension String: Identifiable { public var id: String { self } }
补充说明:如果实际业务中需要保留其他动画,可以在.transaction里针对性控制,而非全局禁用所有动画。
内容的提问来源于stack exchange,提问作者f3dm76
相关产品推荐
相关产品推荐

