问题描述
我正在尝试制作一个非常准系统的移动浏览器来练习 swiftui 和 wkwebview(WKwebview 包含在 UIViewRepresentable 中)。但是,当尝试为浏览器实现多个选项卡时,我遇到了一个奇怪的错误。默认的 webview 可以工作,但是当按下按钮添加新选项卡时,我注意到新的 webview 实际上从未运行过它的 makeuiview() 函数。新选项卡仅显示第一个 webview 加载的最后一个页面,但保持静态且无法接收导航。我不确定是什么导致 uiviewrepresentable 无法运行它的 makeuiview 方法。还有更多代码,但我发布了我认为最重要的内容。
内容视图 (添加新标签的逻辑所在)参考 Tablist.newTab() 函数
import SwiftUI
import CoreData
class TabList: ObservableObject {
@Published var tabs: [browserPage] = [browserPage()]
func newTab(){
tabs.append(browserPage())
}
}
struct ContentView: View {
@Environment(\.managedobjectContext) private var viewContext
@StateObject var tablist: TabList = TabList()
@State var selection = 0
@State var showTabs: Bool = false
var body: some View {
vstack {
tablist.tabs[selection]
Button("next") {
withAnimation {
selection = (selection + 1) % tablist.tabs.count
}
}
Divider()
Button("add") {
withAnimation {
tablist.newTab()
selection = tablist.tabs.count - 1
}
}
Divider()
Text("\(selection)")
Text("\(tablist.tabs.count)")
}
.frame( maxWidth: .infinity,maxHeight: .infinity,alignment: .topLeading)
.sheet(isPresented: $showTabs,content: {
})
}
}
Webviewmodel 类
import Foundation
import Combine
class Webviewmodel: ObservableObject{
var webViewNavigationPublisher = PassthroughSubject<WebViewNavigation,Never>()
var showWebTitle = PassthroughSubject<String,Never>()
var showLoader = PassthroughSubject<Bool,Never>()
var valuePublisher = PassthroughSubject<String,Never>()
var url: String = "https://www.google.com"
@Published var urltoEdit: String = "https://www.google.com"
}
enum WebViewNavigation {
case backward,forward,reload,load
}
WebsiteView(WKWebview 的 UIViewRepresentable)
import Foundation
import UIKit
import SwiftUI
import Combine
import WebKit
protocol WebViewHandlerDelegate {
func receivedJsonValueFromWebView(value: [String: Any?])
func receivedStringValueFromWebView(value: String)
}
struct WebView: UIViewRepresentable,WebViewHandlerDelegate {
func receivedJsonValueFromWebView(value: [String : Any?]) {
print("JSON value received from web is: \(value)")
}
func receivedStringValueFromWebView(value: String) {
print("String value received from web is: \(value)")
}
@Observedobject var viewmodel: Webviewmodel
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
let preferences = WKPreferences()
let configuration = WKWebViewConfiguration()
configuration.userContentController.add(self.makeCoordinator(),name: "iOSNative")
configuration.preferences = preferences
let webView = WKWebView(frame: CGRect.zero,configuration: configuration)
webView.navigationDelegate = context.coordinator
//remove after debugging...
webView.allowsBackForwardNavigationGestures = true
webView.scrollView.isScrollEnabled = true
webView.load(URLRequest(url: URL(string: viewmodel.url)!))
print("UUUUUUUUUUUUUUUUUUUUU " + viewmodel.url)
return webView
}
func updateUIView(_ webView: WKWebView,context: Context) {
}
class Coordinator : NSObject,WKNavigationDelegate {
var parent: WebView
var valueSubscriber: AnyCancellable? = nil
var webViewNavigationSubscriber: AnyCancellable? = nil
var delegate: WebViewHandlerDelegate?
init(_ uiWebView: WebView) {
self.parent = uiWebView
}
deinit {
valueSubscriber?.cancel()
webViewNavigationSubscriber?.cancel()
}
func webView(_ webView: WKWebView,didFinish navigation: WKNavigation!) {
// Get the title of loaded webcontent
webView.evaluateJavaScript("document.title") { (response,error) in
if let error = error {
print("Error getting title")
print(error.localizedDescription)
}
guard let title = response as? String else {
return
}
self.parent.viewmodel.showWebTitle.send(title)
}
valueSubscriber = parent.viewmodel.valuePublisher.receive(on: RunLoop.main).sink(receiveValue: { value in
let javascriptFunction = "valueGotFromIOS(\(value));"
webView.evaluateJavaScript(javascriptFunction) { (response,error) in
if let error = error {
print("Error calling javascript:valueGotFromIOS()")
print(error.localizedDescription)
} else {
print("Called javascript:valueGotFromIOS()")
}
}
})
// Page loaded so no need to show loader anymore
self.parent.viewmodel.showLoader.send(false)
}
/* Here I implemented most of the WKWebView's delegate functions so that you can kNow them and
can use them in different necessary purposes */
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
// Hides loader
parent.viewmodel.showLoader.send(false)
}
func webView(_ webView: WKWebView,didFail navigation: WKNavigation!,withError error: Error) {
// Hides loader
parent.viewmodel.showLoader.send(false)
}
func webView(_ webView: WKWebView,didCommit navigation: WKNavigation!) {
self.parent.viewmodel.urltoEdit = webView.url!.absoluteString
// Shows loader
parent.viewmodel.showLoader.send(true)
}
func webView(_ webView: WKWebView,didStartProvisionalNavigation navigation: WKNavigation!) {
// Shows loader
parent.viewmodel.showLoader.send(true)
self.webViewNavigationSubscriber = self.parent.viewmodel.webViewNavigationPublisher.receive(on: RunLoop.main).sink(receiveValue: { navigation in
switch navigation {
case .backward:
if webView.canGoBack {
webView.goBack()
}
case .forward:
if webView.canGoForward {
webView.goForward()
}
case .reload:
webView.reload()
case .load:
webView.load(URLRequest(url: URL(string: self.parent.viewmodel.url)!))
print("IIIIIIIIIIIIIIIIIIIII " + self.parent.viewmodel.url)
}
})
}
// This function is essential for intercepting every navigation in the webview
func webView(_ webView: WKWebView,decidePolicyFor navigationAction: WKNavigationAction,decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// Suppose you don't want your user to go a restricted site
// Here you can get many information about new url from 'navigationAction.request.description'
decisionHandler(.allow)
}
}
}
// MARK: - Extensions
extension WebView.Coordinator: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController,didReceive message: WKScriptMessage) {
// Make sure that your passed delegate is called
if message.name == "iOSNative" {
if let body = message.body as? [String: Any?] {
delegate?.receivedJsonValueFromWebView(value: body)
} else if let body = message.body as? String {
delegate?.receivedStringValueFromWebView(value: body)
}
}
}
}
浏览器页面视图
import SwiftUI
struct browserPage: View,Identifiable{
let id: UUID = UUID()
@Observedobject var viewmodel = Webviewmodel()
var body: some View {
vstack{
URLBarView(viewmodel: viewmodel)
HStack{
WebView(viewmodel: viewmodel)
}
BottomNavBarView(viewmodel: viewmodel)
.padding(.bottom,25)
}
}
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)