Can someone please help me why the NavigationLink
is not working as intended?
As shown down below (in the code) I use the MarkdownWebView(url: <url>)
with 3 different URL’s.
But when I want to switch between them, the view doesn’t update.
If I open another view in between it’s working.
And on the iPhone (NavigationStack
) it also works.
Section("Legal") {
NavigationLink {
MarkdownWebView(url: "https://<url>/privacy.md", scrollbar: false)
.navigationTitle("Privacy Policy")
} label: {
Text("")
.font(.custom(CustomFonts.FADuotone, size: 20))
.frame(width: 30)
.foregroundColor(.gray)
Text(String(localized: "Privacy Policy", comment: "/"))
}
NavigationLink {
MarkdownWebView(url: "https://<url>/tos.md", scrollbar: false)
.navigationTitle("Terms of use")
} label: {
Text("")
.font(.custom(CustomFonts.FADuotone, size: 20))
.frame(width: 30)
.foregroundColor(.gray)
Text(String(localized: "Terms of Service", comment: "/"))
}
NavigationLink {
MarkdownWebView(url: "https://<url>/licenses.md", scrollbar: false)
.navigationTitle("Licenses")
} label: {
Text("")
.font(.custom(CustomFonts.FADuotone, size: 20))
.frame(width: 30)
.foregroundColor(.gray)
Text(String(localized: "Licenses", comment: "/"))
}
}
This is what the NavigationSplitView looks like:
var body: some View {
NavigationSplitView(columnVisibility: $navigationVM.selectedColumnVisibility) {
column1Form
.navigationTitle(String(localized: "Dashboard", comment: "/"))
.navigationBarTitleDisplayMode(.large)
} content: {
secondForm
}detail: {
detailForm
}
.navigationSplitViewStyle(.balanced)
}
@ViewBuilder
var secondForm: some View {
switch navigationVM.selectedCategory {
case .findWineries: findWineries()
case .profile: ProfileView()
case .stats: StatisticsView()
case .favWines: FavWineView()
case .favWineries: FavWineriesView()
case .cellar: CellarView()
case .orders: OrderListView()
-> case .settings: SettingsView()
case .none: Text("")
}
}
@ViewBuilder
var detailForm: some View {
switch navigationVM.selectedDetail {
case .map: MapView()
case .order: Text("orderTest")
case .orderDetail: OrderDetailView(Status: .delivered)
case .none: Text("")
}
}
On the second column of the SplitView I navigate to the SettingsView() (marked in the code with an arrow).
From there (SettingsView) I want to push the third row with the NavigationLink.
This works fine if I push separate Views. But it doesn’t work with the same View and different parameters (as shown in the post above).
import SwiftUI
import MarkdownUI
struct MarkdownWebView: View {
@State var url: String
@State var scrollbar: Bool
@State var error: Bool = false
@State private var fileContent: String? = nil
var body: some View {
VStack {
if let content = fileContent {
ScrollView(showsIndicators: scrollbar) {
Markdown(content)
}
} else {
if (error) {
VStack(spacing: 20) {
Text("")
.font(.custom(CustomFonts.FADuotone, size: 100, relativeTo: .body))
.foregroundColor(.red)
Text("Document not found")
.font(.title)
}
} else {
VStack(spacing: 20) {
ProgressView()
Text("loading")
}
}
}
}
.onAppear {
loadMarkdownFile(url: url)
}
.padding()
}
private func loadMarkdownFile(url: String) {
DispatchQueue.global().async {
guard let fileUrl = URL(string: url) else {
print("File not found")
self.error = true
return
}
do {
let content = try String(contentsOf: fileUrl)
DispatchQueue.main.async {
self.fileContent = content
}
} catch {
self.error = true
print("Error reading file: \(error)")
}
}
}
}
In the way you use NavigationLink
the .onAppear
in MarkdownWebView
is only called once for the first view. So the content doesn't refresh on other selections, because the view is already visible and .onAppear
isn't called again.
I can suggest two options:
1. quick and dirty
Give each call of MarkdownWebView
a different .id
which forces a redraw:
struct SettingsView: View {
var body: some View {
List {
Section("Legal") {
NavigationLink {
MarkdownWebView(url: "https://www.lipsum.com", scrollbar: false)
.navigationTitle("Privacy Policy")
.id(1) // here
} label: {
Text("")
.font(.system(size: 20))
.frame(width: 30)
.foregroundColor(.gray)
Text(String(localized: "Privacy Policy", comment: "/"))
}
NavigationLink {
MarkdownWebView(url: "https://www.apple.com", scrollbar: false)
.navigationTitle("Terms of use")
.id(2) // here
} label: {
Text("")
.font(.system(size: 20))
.frame(width: 30)
.foregroundColor(.gray)
Text(String(localized: "Terms of Service", comment: "/"))
}
NavigationLink {
MarkdownWebView(url: "https://www.google.com", scrollbar: false)
.navigationTitle("Licenses")
.id(3) // here
} label: {
Text("")
.font(.system(size: 20))
.frame(width: 30)
.foregroundColor(.gray)
Text(String(localized: "Licenses", comment: "/"))
}
}
}
}
}
2. new SwiftUI navigation logic
Use the new init of NavigationLink
with value and provide a .navigationDestination
.
I used Int values here (1,2,3) but you can (and should) also use enum values.
struct SettingsView2: View {
var body: some View {
List {
Section("Legal") {
NavigationLink(value: 1) { // value 1
Text("")
.font(.system(size: 20))
.frame(width: 30)
.foregroundColor(.gray)
Text(String(localized: "Privacy Policy", comment: "/"))
}
NavigationLink(value: 2) { // value 2
Text("")
.font(.system(size: 20))
.frame(width: 30)
.foregroundColor(.gray)
Text(String(localized: "Terms of Service", comment: "/"))
}
NavigationLink(value: 3) { // value 3
Text("")
.font(.system(size: 20))
.frame(width: 30)
.foregroundColor(.gray)
Text(String(localized: "Licenses", comment: "/"))
}
}
}
.navigationDestination(for: Int.self) { value in // define destinations based on value
switch value {
case 1:
MarkdownWebView(url: "https://www.lipsum.com", scrollbar: false)
.navigationTitle("Privacy Policy")
case 2:
MarkdownWebView(url: "https://www.apple.com", scrollbar: false)
.navigationTitle("Terms of use")
case 3:
MarkdownWebView(url: "https://www.google.com", scrollbar: false)
.navigationTitle("Licenses")
default: Text("nothing")
}
}
}
}