Hi, I wanted to detect that a NavigationLink was selected, so I wrote a ViewModifier using list($selection:) and navigationDestination(isPresent:).
If you are not using $path in your NavigationStack(path:), you might be able to use this ViewModifier idea for data loading with onChange(of: selection) {}.
I attach below the sample code for a recursive FileListViewer.
You can get this ViewModifier and other sample code from https://github.com/hmuronaka/NavigationDestinationSelectedViewModifier.
I hope this idea will be helpful to you.
import SwiftUI
import NavigationDestinationSelectedViewModifier
struct PlainFileList2<Destination: View>: View {
let current: URL
let paths: [URL]
@ViewBuilder let destination: (URL) -> Destination
@State private var selection: URL?
@State private var childPaths: [URL]?
var body: some View {
List(selection: $selection) {
ForEach(paths, id: \.self) { url in
if FileManager.default.isDirectory(url: url) {
NavigationLink(value: url) {
Label(url.lastPathComponent, systemImage: "folder")
}
} else {
self.destination(url)
}
}
}
.navigationDestination(selection: $selection, item: $childPaths ) { childPaths in
if let selection {
PlainFileList2(current: selection, paths: childPaths, destination: self.destination)
}
}
.onChange(of: selection, perform: { newValue in
if let newValue, FileManager.default.isDirectory(url: newValue) {
self.childPaths = try! FileManager.default.contentsOfDirectory(at: newValue, includingPropertiesForKeys: [.parentDirectoryURLKey, .creationDateKey, .fileSizeKey], options: [])
} else {
self.childPaths = nil
}
})
.navigationTitle(current.lastPathComponent)
}
}
import SwiftUI
fileprivate struct NavigationDestinationViewModifier<SelectionValue: Hashable, Value: Equatable, Destination: View>: ViewModifier {
@Binding var selection: SelectionValue?
@Binding var item: Value?
@ViewBuilder let destination: (Value) -> Destination
func body(content: Content) -> some View {
content
.navigationDestination(isPresented: .init(get: {
item != nil
}, set: { newValue in
if !newValue {
item = nil
}
})) {
if let selected = item {
destination(selected)
} else {
EmptyView()
}
}
.onChange(of: item) { newValue in
if newValue == nil && selection != nil {
selection = nil
}
}
}
}
public extension View {
func navigationDestination<SelectionValue: Hashable, Value: Equatable, Destination: View>(selection: Binding<SelectionValue?>, item: Binding<Value?>, @ViewBuilder destination: @escaping (Value) -> Destination) -> some View {
return self.modifier(NavigationDestinationViewModifier(selection: selection, item: item, destination: destination))
}
}