在视图结构中初始化成员
Posted
技术标签:
【中文标题】在视图结构中初始化成员【英文标题】:Initialize member in view struct 【发布时间】:2021-03-27 18:54:46 【问题描述】:我想在 SwiftUI 中使用带有 javascript 处理的 WKWebView。从Swift Variables Initialization,我正在执行以下操作:(我正在使用https://github.com/kylehickinson/SwiftUI-WebView 为SwiftUI 中的WKWebView
提供一个包装器,它还添加了有价值的布局约束。)
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)
()
但后来我从Instance member cannot be used on type 得知这不起作用,因为闭包没有对 self 的引用。我需要 WKWebView 有专用的配置对象,所以我不能只使用其他构造函数。我需要参考它来做evaluateJavaScript
。
如何做到这一点?
编辑 1:添加 body
并提及用于包装 WKWebView 的框架。
编辑 2:添加了代码以阐明我需要通过 WKScriptMessageHandler
从 WKWebView
到本机应用程序的两种方式通信(以便在 HTML 文档准备好时得到通知)和从本机应用程序到WKWebView
via evaluateJavaScript
(在 HTML 文档准备好时传输数据)。
【问题讨论】:
将WKWebView
包装到 UIViewRepresentable 中,就像在 ***.com/a/59790493/12299030 中一样。
@Asperi 在 struct WebView 中,您使用的是var webview: WKWebView = WKWebView()
,但我需要使用带有自定义配置的构造函数。那我不会遇到同样的问题吗? (我已经使用github.com/kylehickinson/SwiftUI-WebView 将 WKWebView 包装到 UIViewRepresentable 中。)
这里是备用***.com/a/60173992/12299030。你可以使用任何适合你的。
@Asperi 感谢您的解决方案。我想指出,由于配置是在包装器内完成的,所以如果我想用一组不同的 JS 方法制作另一个 WKWebView,我将不得不制作另一个包装器!另一个问题是我需要在我的ContentView
中引用WKWebView
,以便我可以调用evaluateJavaScript
!
【参考方案1】:
一种可能的解决方案是在init
中配置WKWebView
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()
【讨论】:
我刚刚想出了一个解决方案,将myWebView
移动到 JSHandler
中,但是我不知道如何命名 JSHandler
,因为它不仅处理 JavaScript 回调,而且还用作WKWebView 的包装器。这要好得多。感谢您使用message.webView?
而不是顺便引用ContentView
的提示。
你不能使用对 ContentView 的引用,因为它不是引用类型,它会是一个副本。
我没有意识到这一点。我认为将var contentView: ContentView?
放入JSHandler
就像我最初所做的工作一样,因为ContentView?
可能是nil
。【参考方案2】:
只需将 myWebView 设为 lazy
变量,这样可以确保 jsHandler 在 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)
()
【讨论】:
这不适用于 SwiftUI 视图,因为它没有可修改的 self。以上是关于在视图结构中初始化成员的主要内容,如果未能解决你的问题,请参考以下文章
在C中参考命名的结构成员直接初始化或分配变量数组的结构成员?