You have to write your code assuming that the initializer of the View
in SwiftUI will be called many times.
You write the initialization process in makeUIView(context:)
in this case.
See:
https://developer.apple.com/documentation/swiftui/uiviewrepresentable/makeuiview(context:)
For example, I wrote the following code based on this answer. I added a toggle height button to this referenced code.
the -----> makeUIView
log is only output once,
but the -----> webview init
logs are output every time the toggle button is pressed.
import SwiftUI
import WebKit
struct ContentView: View {
var body: some View {
MyWebView()
}
}
class WebViewModel: ObservableObject {
@Published var title = ""
}
struct WebView: UIViewRepresentable {
let wkWebView = WKWebView()
init(viewModel: WebViewModel) {
print("\n-----> webview init")
}
func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {
print("\n-----> makeUIView")
if let url = URL(string: "https://www.google.com") {
let request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad)
wkWebView.load(request)
}
return wkWebView
}
func updateUIView(_ webview: WKWebView, context: UIViewRepresentableContext<WebView>) { }
}
struct MyWebView: View {
@State private var toggleHight = false
@ObservedObject private var viewModel = WebViewModel()
init() {
print("\n-----> root init")
}
var body: some View {
VStack {
WebView(
viewModel: viewModel
)
.frame(
height: { toggleHight ? 600 : 300 }()
)
Button(
"toggle",
action: {
toggleHight.toggle()
}
)
}
}
}
Furthermore, I realized after I wrote example code that WebView: UIViewRepresentable
should not have an instance variable of wkWebView
.
Please do it all(create instance and configuration) in makeUIView(context:)
, as shown below.
This is because instance variables are recreated every time the initializer is called.
import SwiftUI
import WebKit
struct ContentView: View {
var body: some View {
MyWebView()
}
}
class WebViewModel: ObservableObject {
@Published var title = ""
}
struct WebView: UIViewRepresentable {
init(viewModel: WebViewModel) {
print("\n-----> webview init")
}
func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {
print("\n-----> makeUIView")
let wkWebView = WKWebView()
if let url = URL(string: "https://www.google.com") {
let request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad)
wkWebView.load(request)
}
return wkWebView
}
func updateUIView(_ webview: WKWebView, context: UIViewRepresentableContext<WebView>) { }
}
struct MyWebView: View {
@State private var toggleHight = false
@ObservedObject private var viewModel = WebViewModel()
init() {
print("\n-----> root init")
}
var body: some View {
VStack {
WebView(
viewModel: viewModel
)
.frame(
height: { toggleHight ? 600 : 300 }()
)
Button(
"toggle",
action: {
toggleHight.toggle()
}
)
}
}
}
I struggled with this tight constraint when I was developing with UIViewControllerRepresentable
. With the help of my colleagues, I managed to finish the code.
Your code has been called 6 times, so there may be some problem. but I cannot tell what the problem is from the code you provided.
It is common for init
to be called multiple times in SwiftUI
. We need to write code to deal with this. If your init
is being called too often, you may want to look for the root cause. The code I referred to and the code I wrote are only once at startup.