You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 01:54:34