Reputation: 1430
I have a few components, but I will simplify the code here. Essentially I have a WebView class that builds up a webview and sets the webview variable in the parent class, then I can act on that webview to do things like "goBack" and such. The problem is, even though the method is being called correctly, the webview is always nil, and I can't figure out why...
BasicView...
struct BasicView: View {
@State var webView: WebView? = nil
var body: some View {
NavigationView {
VStack {
WebView(url: "myurl here") { view in
print("setView called. Setting webView")
self.webView = view
}
}
.navigationTitle("My App")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(
leading:
Button(action: {
guard let view = webView else {
print("webView not set")
return
}
view.goBack()
}) {
Text("Back")
}
)
}
}
}
}
Now in my WebView...
struct WebView: UIViewRepresentable {
let url: URL
let setParent: (WebView) -> Void
var webView: WKWebView
init(
url: URL,
setParent: @ecaping (WebView) -> Void = {_ in },
webView: WKWebView = WKWebView()
)
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> some WKWebView {
webView.navigationDelegate = context.coordinator
webView.uiDelegate = context.coordinator
webView.load(URLRequest(url: url))
setParent(self)
return webView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
func goBack() {
//logic for going back
}
class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate {
var parent: WebView
init(_ parent: WebView) {
self.parent = parent
}
//all my override functions for navigation and whatnot here
}
}
So, now when I run my app I do see in the logs "setView called. Setting webView" and then if I click on the back button in the navigation bar I see "webView not set"
So the setParent is being called and self.webView
should be set because that method is being called, but when I click the button in the navigation bar, I see the webView not set
show up. So somehow the state variable is not being updated when the setParent is being called.
Not sure how to proceed as this is the first time I have seen a state variable not update and be usable. Any help is greatly appreciated. Thank you
Upvotes: -3
Views: 118
Reputation: 36995
Try this approach using a NavigationLink
to achieve what you ask ...to allow the webview to go "back" meaning the webview loads to a specific URL
.
Note, SwiftUI is what is called a declarative system, you declare what you want to see, and then you change the data that the view represents.
Fully working example test code:
import Foundation
import SwiftUI
import WebKit
struct ContentView: View {
var body: some View {
BasicView()
}
}
struct BasicView: View {
let swiftUIUrl = URL(string: "https://developer.apple.com/xcode/swiftui/")!
let navStackUrl = URL(string: "https://developer.apple.com/documentation/swiftui/navigationstack/")!
var body: some View {
NavigationStack {
VStack(spacing: 55) {
NavigationLink(destination: WebView(url: swiftUIUrl)) {
Text("Go to SwiftUI")
}
NavigationLink(destination: WebView(url: navStackUrl)) {
Text("Go to NavigationStack")
}
}
.navigationTitle("My App")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct WebView: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
let request = URLRequest(url: url)
uiView.load(request)
}
}
Another working example code using a data model:
// the data model
struct MyData: Identifiable {
let id = UUID()
var url: URL?
var name: String
}
struct BasicView: View {
// the data
let urls = [
MyData(url: URL(string: "https://developer.apple.com/xcode/swiftui/"), name: "SwiftUI"),
MyData(url: URL(string: "https://developer.apple.com/documentation/swiftui/navigationstack/"), name: "NavigationStack")
]
var body: some View {
NavigationStack {
List(urls) { data in
NavigationLink(destination: WebView(url: data.url)) {
Text("Go to \(data.name)")
}
}
.navigationTitle("My App")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct WebView: UIViewRepresentable {
let url: URL?
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
if let url {
let request = URLRequest(url: url)
uiView.load(request)
}
}
}
Upvotes: 0