SwiftUI使用onChange关闭UIViewRepresentable无效问题
问题原因
dismiss操作失效核心是两个问题:
- 你当前读取的
presentationMode不属于被推入的WebView页面环境:.onChange虽然挂在WebView上,但闭包捕获的是MainView的环境变量,调用dismiss时找不到正确的导航栈节点,自然无法关闭页面。 - 额外存在隐藏缺陷:
updateUIView会在每次视图状态更新时触发,你在里面写了固定加载首页的逻辑,会打断网页正常跳转,导致状态触发时机和预期不符。
修复方案
不要依赖手动调用presentationMode.wrappedValue.dismiss(),直接修改控制NavigationLink显隐的isShowWebView状态即可——SwiftUI的导航栈会自动跟随绑定状态变化,自动弹出被推入的页面,从根源避免环境变量读取错误的问题。同时移除updateUIView里的重复加载逻辑,修复网页跳转异常。
修复后主视图代码
class ViewModel:ObservableObject { @Published var urlHasChanged:Bool = false @Published var isShowWebView:Bool = false } struct MainView : View { @ObservedObject var viewModel = ViewModel() var body: some View { NavigationView { VStack { Button { viewModel.isShowWebView = true } label: { Text("show web view") } .background(NavigationLink( destination: WebView(viewModel: viewModel) .onChange(of: viewModel.urlHasChanged) { newValue in guard newValue else { return } print("trigger close") // 直接修改导航绑定状态,触发页面自动弹出 viewModel.isShowWebView = false // 重置标记位,避免下次打开WebView时异常触发 viewModel.urlHasChanged = false }, isActive: $viewModel.isShowWebView, label: { EmptyView() } ).opacity(0)) } } } }
修复后WebView代码
struct WebView: UIViewRepresentable { var viewModel : ViewModel func makeCoordinator() -> Coordinator { Coordinator(viewModel: viewModel) } func makeUIView(context: Context) -> WKWebView { let webView = WKWebView() webView.navigationDelegate = context.coordinator // 仅在WebView初始化时加载一次首页 webView.load(URLRequest(url: URL(string: "https://www.google.com/")!)) return webView } func updateUIView(_ webView: WKWebView, context: Context) { // 删除原有重复加载逻辑,避免状态更新时网页被重置 } class Coordinator : NSObject, WKNavigationDelegate { private let viewModel : ViewModel // 初始化时强制传入viewModel,去掉可选类型避免取值失败 init(viewModel: ViewModel) { self.viewModel = viewModel } func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { guard let urlStr = navigationAction.request.url?.absoluteString else { decisionHandler(.cancel) return } if urlStr.contains("google.com") { decisionHandler(.allow) } else { viewModel.urlHasChanged = true decisionHandler(.cancel) } } } }
补充说明:如果要使用
presentationMode实现dismiss,必须在WebView内部的视图层级中读取环境变量(比如给WebView包一层容器视图,在容器视图上声明@Environment(\.presentationMode)),否则读取到的永远是外层MainView的环境状态,无法触发正确的关闭操作。但直接绑定导航显隐状态是SwiftUI中最稳定、兼容性最好的实现方式。
内容的提问来源于stack exchange,提问作者Dilan
相关产品推荐
相关产品推荐

