SwiftUI WebView无法执行前进后退操作问题排查
问题:SwiftUI中WebView前进后退按钮始终禁用
在SwiftUI中使用WebView加载网页内容,已实现前进、后退按钮的逻辑,但输入URL并点击页面内链接后,这两个按钮始终处于禁用状态,无法正常使用。
相关代码
ContentView代码
import SwiftUI struct ContentView: View { @State private var selection = 0 var body: some View { TabView(selection: $selection) { NavigationView { WebListView().navigationBarTitle("Web View ", displayMode: .inline) .toolbarBackground(Color.white,for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar) .accentColor(.red) .onAppear() { UITabBar.appearance().barTintColor = .white } }.tabItem { Image(systemName: "person.crop.circle") Text("Web View") }.tag(2) } } }
WebListView代码
import SwiftUI struct WebListView: View { @StateObject var model = WebViewModel() var body: some View { WebContentView() .font(.system(size: 30, weight: .bold, design: .rounded)) .toolbar { ToolbarItemGroup(placement: .automatic) { Button(action: { model.goBack() }, label: { Image(systemName: "chevron.left") }) .disabled(!model.canGoBack) .font(.system(size: 20)) Button(action: { model.goForward() }, label: { Image(systemName: "chevron.right") }) .disabled(!model.canGoForward) .font(.system(size: 20)) Spacer() } } } }
UIViewRepresentable实现代码
struct WebView: UIViewRepresentable { typealias UIViewType = WKWebView let webView: WKWebView func makeUIView(context: Context) -> WKWebView { return webView } func updateUIView(_ uiView: WKWebView, context: Context) { } }
WebContentView代码
import Combine import WebKit import SwiftUI @MainActor struct WebContentView: View { @StateObject var model = WebViewModel() var body: some View { ZStack(alignment: .bottom) { Color.blue VStack(spacing: 0) { HStack(spacing: 10) { HStack { TextField("Enter url", text: $model.urlString) .keyboardType(.URL) .autocapitalization(.none) .disableAutocorrection(true) .padding(8) .font(.system(size: 15)) Spacer() } .background(Color.white) .cornerRadius(30) Button("GO", action: { model.loadUrl() }) .foregroundColor(.white) .padding(10) .font(.system(size: 15)) .background(.blue) }.padding(10) ZStack { WebView(webView: model.webView) if model.isLoading { ProgressView() .progressViewStyle(CircularProgressViewStyle()) } } } } } } struct WebContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
WebViewModel代码
@MainActor class WebViewModel: ObservableObject { let webView: WKWebView private let navigationDelegate: WebViewNavigationDelegate init() { let configuration = WKWebViewConfiguration() configuration.websiteDataStore = .nonPersistent() webView = WKWebView(frame: .zero, configuration: configuration) navigationDelegate = WebViewNavigationDelegate() webView.navigationDelegate = navigationDelegate setupBindings() } @Published var urlString: String = "" @Published var canGoBack: Bool = false @Published var canGoForward: Bool = false @Published var isLoading: Bool = false private func setupBindings() { webView.publisher(for: \.canGoBack) .assign(to: &$canGoBack) webView.publisher(for: \.canGoForward) .assign(to: &$canGoForward) webView.publisher(for: \.isLoading) .assign(to: &$isLoading) } func loadUrl() { guard let url = URL(string: urlString) else { return } webView.load(URLRequest(url: url)) } func goForward() { webView.goForward() } func goBack() { webView.goBack() } }
导航代理代码
import WebKit class WebViewNavigationDelegate: NSObject, WKNavigationDelegate { func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { // TODO decisionHandler(.allow) } func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { // TODO decisionHandler(.allow) } }
问题原因及解决方案
核心问题
WebListView和WebContentView各自创建了独立的WebViewModel实例:
- 工具栏按钮绑定的是
WebListView中的model - 实际加载网页、处理导航的是
WebContentView中的model
两者状态完全隔离,导致按钮无法感知WebView的导航变化。
修复步骤
- 修改
WebContentView,改为接收外部传入的WebViewModel:
@MainActor struct WebContentView: View { // 替换@StateObject为@ObservedObject,接收外部传入的model @ObservedObject var model: WebViewModel var body: some View { // 原有视图代码不变 ZStack(alignment: .bottom) { Color.blue VStack(spacing: 0) { HStack(spacing: 10) { HStack { TextField("Enter url", text: $model.urlString) .keyboardType(.URL) .autocapitalization(.none) .disableAutocorrection(true) .padding(8) .font(.system(size: 15)) Spacer() } .background(Color.white) .cornerRadius(30) Button("GO", action: { model.loadUrl() }) .foregroundColor(.white) .padding(10) .font(.system(size: 15)) .background(.blue) }.padding(10) ZStack { WebView(webView: model.webView) if model.isLoading { ProgressView() .progressViewStyle(CircularProgressViewStyle()) } } } } } }
- 在
WebListView中,将自己的model传递给WebContentView:
struct WebListView: View { @StateObject var model = WebViewModel() var body: some View { // 传递model到WebContentView WebContentView(model: model) .font(.system(size: 30, weight: .bold, design: .rounded)) .toolbar { ToolbarItemGroup(placement: .automatic) { Button(action: { model.goBack() }, label: { Image(systemName: "chevron.left") }) .disabled(!model.canGoBack) .font(.system(size: 20)) Button(action: { model.goForward() }, label: { Image(systemName: "chevron.right") }) .disabled(!model.canGoForward) .font(.system(size: 20)) Spacer() } } } }
这样两者共享同一个WebViewModel实例,按钮的canGoBack/canGoForward状态会正确同步WebView的导航变化,按钮就能正常启用/禁用了。
内容的提问来源于stack exchange,提问作者user8705895
相关产品推荐
相关产品推荐

