Initialiser le membre dans la structure de vue
Je souhaite avoir un WKWebView avec gestion JavaScript dans SwiftUI. À partir de l' initialisation des variables Swift , je fais ce qui suit: (J'utilisehttps://github.com/kylehickinson/SwiftUI-WebViewpour fournir un wrapper pour WKWebViewSwiftUI qui ajoute également de précieuses contraintes de mise en page.)
struct ContentView: View {
var body: some View {
WebView(webView: myWebView)
.onAppear {
let url = Bundle.main.url(forResource: "index", withExtension: "html")!
myWebView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
myWebView.load(request)
}
}
class JSHandler : NSObject, WKScriptMessageHandler {
var contentView: ContentView?
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("documentReady")
contentView!.myWebView.evaluateJavaScript("<Long Script to Transfer Data>") { (result, error) in
}
}
}
let myJSHandler = JSHandler()
var myWebView: WKWebView = {
let config = WKWebViewConfiguration()
let controller = WKUserContentController()
controller.add(myJSHandler , name: "documentReady")
config.userContentController = controller
return WKWebView(frame: .zero, configuration: config)
}()
}
Mais ensuite, j'ai appris du membre d'instance ne peut pas être utilisé sur le type que cela ne fonctionne pas car la fermeture n'a pas de référence à soi. J'ai besoin que WKWebView ait un objet de configuration dédié, donc je ne peux pas simplement utiliser l'autre constructeur. J'ai besoin d'une référence pour le faire evaluateJavaScript.
Comment faire fonctionner cela?
EDIT 1 : Ajoutez bodyet mentionnez le framework utilisé pour encapsuler WKWebView.
EDIT 2 : Ajout d'un code pour clarifier que j'ai besoin de communications bidirectionnelles depuis l' WKWebViewapplication native via WKScriptMessageHandler(pour être averti lorsque le document HTML est prêt) et depuis l'application native vers WKWebViewvia evaluateJavaScript(pour transférer des données lorsque le document HTML est prêt).
Réponses
Une solution possible consiste à configurer WKWebView dans init
struct ContentView: View {
private var myWebView: WKWebView
init() {
let config = WKWebViewConfiguration()
let controller = WKUserContentController()
controller.add(myJSHandler , name: "documentReady")
config.userContentController = controller
myWebView = WKWebView(frame: .zero, configuration: config)
}
var body: some View {
WebView(webView: myWebView)
.onAppear {
let url = Bundle.main.url(forResource: "index", withExtension: "html")!
myWebView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
myWebView.load(request)
}
}
class JSHandler : NSObject, WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("documentReady")
message.webView?.evaluateJavaScript("<Long Script to Transfer Data>") { (result, error) in
}
}
}
let myJSHandler = JSHandler()
}
Faites simplement de myWebView une lazyvariable, cela garantit que le jsHandler est initialisé avant WebView.
lazy var myWebView: WKWebView = {
let config = WKWebViewConfiguration()
let controller = WKUserContentController()
controller.add(myJSHandler , name: "documentReady")
config.userContentController = controller
return WKWebView(frame: .zero, configuration: config)
}()