SwiftUI含`dismiss`环境值的视图点击NavigationLink时冻结
问题背景
在嵌套了NavigationSplitView和NavigationStack的SwiftUI应用中,当MainView持有@Environment(\.dismiss)环境变量时,点击NavigationLink会导致应用无限冻结。移除@Environment(\.dismiss)后恢复正常,但会失去视图关闭能力。经排查,问题根源是DismissAction不遵循Equatable协议,导致视图在导航触发时陷入无限重绘循环:
- NavigationLink的destination闭包捕获了MainView的状态和dismiss环境值
- dismiss每次重绘都会被视为新值,触发destination重新计算
- destination中的ChildView通过闭包调用
foo()更新MainView的action状态,引发MainView重绘 - 循环往复最终导致应用冻结
解决办法
方案1:使用navigationDestination替代内嵌destination(推荐)
这是SwiftUI 4及以上版本官方推荐的导航方式,能从根源避免闭包捕获带来的循环依赖问题:
struct MainView: View { @Environment(\.dismiss) private var dismiss @State var action: String = "Open child" @State private var showChild = false var body: some View { List { NavigationLink(action: { showChild = true }) { Text(action) } } .navigationDestination(isPresented: $showChild) { ChildView(action: { _ in foo() }) } } func foo() { self.action = "Updated action" } }
方案2:用LazyView延迟加载目标视图
通过LazyView包装目标视图,避免提前创建视图时捕获到不稳定的环境值,打断重绘循环:
首先定义LazyView:
struct LazyView<Content: View>: View { private let content: () -> Content init(_ content: @autoclosure @escaping () -> Content) { self.content = content } var body: some View { content() } }
然后修改MainView的目标视图创建逻辑:
@ViewBuilder func listDestination() -> some View { LazyView(ChildView(action: { _ in foo() })) }
方案3:封装DismissAction为ObservableObject
将dismiss环境值封装到可观察对象中,减少环境值变化对视图重绘的触发频率:
class DismissHandler: ObservableObject { private let dismiss: DismissAction init(dismiss: DismissAction) { self.dismiss = dismiss } func dismissCurrentView() { dismiss() } } struct MainView: View { @Environment(\.dismiss) private var dismiss @StateObject private var dismissHandler: DismissHandler! @State var action: String = "Open child" var body: some View { List { NavigationLink { ChildView(action: { _ in foo() }) } label: { Text(action) } } .onAppear { dismissHandler = DismissHandler(dismiss: dismiss) } } func foo() { self.action = "Updated action" } }
后续需要关闭视图时,调用dismissHandler.dismissCurrentView()即可。
内容的提问来源于stack exchange,提问作者Brandon Stillitano
相关产品推荐
相关产品推荐

