【问题标题】:Send update to child view (WKWebView) using swiftUI?使用 swiftUI 向子视图 (WKWebView) 发送更新?
【发布时间】:2021-10-27 16:27:29
【问题描述】:

我想使用 SwiftUI 在视图中制作一个非常简单的浏览器。

会发生什么:

在我的 ContentView 中,有一个 WKWebView,导航栏左侧有一个“返回”按钮。如果按下“返回”按钮,webView 将返回上一页。

ContentView.swift:

struct ContentView: View {
    let defaultUrl = "https://www.apple.com"
    @State var needsGoBack = false
    
    var body: some View {
        WebView(urlString: defaultUrl, needsGoBack: $needsGoBack)
            .navigationBarItems(leading:
                Button(action: {
                    print("button pressed...set needsGoBack = true")
                    needsGoBack = true
                }) {
                    Text("Go Back")
                })
    }
}

WebView.swift

struct WebView: UIViewRepresentable {
    
    let urlString: String
    let navigationHelper = WebViewHelper()
    @State var myWebView = WKWebView()
    @Binding var needsGoBack: Bool
    
    func makeUIView(context: Context) -> WKWebView {
        if let url = URL(string: urlString) {
            let request = URLRequest(url: url)
            myWebView.load(request)
        }
        
        myWebView.navigationDelegate = navigationHelper
        myWebView.uiDelegate = navigationHelper
        
        return myWebView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        print("webview updateUIView")
        
        print("needsGoBack", needsGoBack)
        
        if needsGoBack {
            myWebView.goBack()
            needsGoBack = false // this line has problem
        }
    }
    
    typealias UIViewType = WKWebView
    
}

// https://gist.github.com/joshbetz/2ff5922203240d4685d5bdb5ada79105
class WebViewHelper: NSObject, WKNavigationDelegate, WKUIDelegate {
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        print("webview didFinishNavigation")
    }
    
    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        print("didStartProvisionalNavigation")
    }
    
    func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
        print("webviewDidCommit")
    }
    
    func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        print("didReceiveAuthenticationChallenge")
        completionHandler(.performDefaultHandling, nil)
    }
}

实际发生的情况:

在WebView.swift的updateUIView函数中,有一条警告信息“Modifying state during view update, this will cause undefined behavior”。对于这行代码:“needsGoBack = false”。并且“needsGoBack”变量不会设置为“false”。 “返回”按钮首次工作。但是,因为“needsGoBack”之后总是“true”,所以不会第二次通知子视图(WebView)(调用updateUIView方法)。

【问题讨论】:

    标签: swiftui wkwebview


    【解决方案1】:

    通过将您的WKWebView 存储在一个单独的对象(在本例中为NavigationState)中,您的ContentView 都可以访问该对象,您可以直接访问goBack() 方法。这样,您就避免了尝试使用 Bool 来表示一次性事件的棘手问题,这不仅在实践中不起作用(正如您所发现的那样),而且在语义上思考起来也有点有趣关于。

    
    class NavigationState : NSObject, ObservableObject {
        @Published var currentURL : URL?
        @Published var webView : WKWebView
        
        override init() {
            let wv = WKWebView()
            self.webView = wv
    
            super.init()
            wv.navigationDelegate = self
        }
        
        func loadRequest(_ urlRequest: URLRequest) {
            webView.load(urlRequest)
        }
    }
    
    extension NavigationState : WKNavigationDelegate {
        func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
            self.currentURL = webView.url
        }
    }
    
    struct WebView : UIViewRepresentable {
        
        @ObservedObject var navigationState : NavigationState
        
        func makeUIView(context: Context) -> WKWebView  {
            return navigationState.webView
        }
        
        func updateUIView(_ uiView: WKWebView, context: Context) {
            
        }
    }
    
    struct ContentView: View {
        @StateObject var navigationState = NavigationState()
        
        var body: some View {
            VStack {
                Text(navigationState.currentURL?.absoluteString ?? "(none)")
                WebView(navigationState: navigationState)
                    .clipped()
                HStack {
                    Button("Back") {
                        navigationState.webView.goBack()
                    }
                    Button("Forward") {
                        navigationState.webView.goForward()
                    }
                }
            }.onAppear {
                navigationState.loadRequest(URLRequest(url: URL(string: "https://www.google.com")!))
            }
        }
    }
    

    【讨论】:

    • 你的回答更有意义,因为我的意图实际上不是 UI 的东西。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-08
    • 2019-08-03
    • 2020-04-15
    相关资源
    最近更新 更多