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

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的导航变化。

修复步骤

  1. 修改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())
                    }
                }
            }
        }
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 01:20:20