英文:
How do I refresh a WKWebView via @StateObject stored in another class after altering a property on an @ObservedObject?
问题
我有一个基本的模型,像这样:
```swift
public final class WebSite: ObservableObject {
@Published
var URL: String
// 让我们假设这将是一个有效的 URL
}
和一个 webview。它使用站点 URL 来呈现一个 UIViewRepresentable
(我的站点视图必须是一个类,因为 WKWebView
协议):
class WebSiteWebView: WKWebView {
@ObservedObject
var webSite: WebSite
init() { /* 初始化。这里不相关 */ }
func loadUrl(url: String) {
self.load(URLRequest(url: URL(string: url)!))
}
func render() -> some View {
let representable = WebSiteWebView.Representable(webView: self)
do {
try representable.webView.loadUrl(url: self.webSite.URL)
} catch { ... }
}
}
现在,我有一个父 ContentView 如下:
struct ContentView: View {
@StateObject var webSite: WebSite = WebSite(URL: "stackoverflow.com")
var body: some View {
WebSiteView(webSite: webSite)
}
}
和一个显示 web 视图的 WebSiteView
:
struct WebSiteView: View {
@ObservedObject
var webSite: WebSite
var body: some View {
let webView: WebSiteWebView = WebView(webSite: webSite)
VStack {
webView.render()
Button("Switch URL") {
webSite.URL = "google.com"
}
}
}
}
为了完整性,UIViewRepresentable
的实现:
extension WebSiteWebView {
struct Representable: UIViewRepresentable {
let webView: WebSiteWebView
func makeUIView(context: Context) -> WebSiteWebView {
self.webView
}
func updateUIView(_ uiView: WebSiteWebView, context: Context) {}
}
}
简而言之,发生了以下情况:
- 父 ContentView 初始化一个包含名为
URL
的发布的String
的@StateObject
。 - ContentView 有一个名为
WebSiteView
的子视图,将网站类作为@ObservedObject
。 - 该子视图从
@ObservedObject
加载URL
。 - 该子视图还有一个更改
URL
的按钮。
我期望在按下按钮后,WebSiteView
将重新渲染,因为它将 webSite
声明为 @ObservedObject
。而且它确实(从技术上)做到了。webView.render()
使用新的 URL
调用,但 WKWebView
不会刷新。有人知道这是为什么吗?
<details>
<summary>英文:</summary>
I have a basic model like this"
public final class WebSite: ObservableObject {
@Published
var URL: String
// Let us please assume that this will be a valid URL
}
And a webview. It uses the site url to render a `UIViewRepresentable` (my site view must be a class bc of the `WKWebView` protocol):
class WebSiteWebView: WKWebView {
@ObservedObject
var webSite: WebSite
init() { /* initialization. Not relevant here*/ }
loadUrl(url: String) {
self.load(URLRequest(url: url))
}
render() -> some View {
let representable = WebSiteWebView.Representable(webView: self)
do {
try representable.webView.loadUrl(url: self.webSite.url)
} catch { ... }
}
}
Now, I have a parent ContentView as follows:
struct ContentView: View {
@StateObject var webSite: WebSite = WebSite(URL: "stackoverflow.com")
var body: some View {
WebSiteView(webSite: webSite)
}
}
and a `WebSiteView` that displays a web view:
struct WebSiteView: View {
@ObservedObject
var webSite: WebSite
var body: some View {
let webView: WebSiteWebView = WebView(webSite: webSite)
VStack {
webView.render()
Button("Switch URL") {
webSite.URL = "google.com"
}
}
}
}
The `UIViewRepresentable` impl for completeness
extension WebSiteWebView {
struct Representable: UIViewRepresentable {
let webView: WebSiteWebView
func makeUIView(context: Context) -> WebSiteWebView {
self.webView
}
func updateUIView(_ uiView: WebSiteWebView, context: Context) {}
}
}
In a nutshell, this is what's going on:
1. The parent ContentView initializes a `@StateObject` that contains a published `String` named `URL`.
2. The ContentView has a child view named `WebSiteView` that takes the web site class as an `@ObservedObject`
3. That child view loads the `URL` from the `@ObservedObject`
4. That child view also has a button that changes the `URL`
I would expect that after the button is pressed, the `WebSiteView` would re-render, since it declares the `webSite` as an `@ObservedObject`. And, it does (technically). `webView.render()` is called with the new `URL` but the `WKWebView` does not refresh. Does anyone know why this is happening?
</details>
# 答案1
**得分**: 1
代码部分如下:
```swift
struct WebSiteWebView: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> WKWebView {
WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
if uiView.url != url {
uiView.load(url)
}
}
}
英文:
things are a bit backwards, it needs to be like this:
struct WebSiteWebView: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> WKWebView {
WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
if uiView.url != url {
uiView.load(url)
}
}
}
Basically the WebSiteWebView
struct can be recreated many times and your job is to update the WKWebView
object with any change to the properties. updateUIView
is called the first time and also any time the url
changes.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论