I have some screen in iOS app with NavigationStack (or NavigationView) which contains different elements. One of them is searchable List which is wrapped in NavigationLink. So this searchable list appears in new screen.
And when user selects some element of List he should to be automatically returned back to previous screen without additional two clicks on Cancel (if use search) and then "Back" buttons.
Does exist any right way to do this? Thanks for any advice!
import SwiftUI
struct TheItem: Identifiable {
let id = UUID()
let name: String
}
class SomeModel: ObservableObject {
@Published var selectedId: UUID?
@Published var searchText: String = ""
var items: [TheItem] = [
TheItem(name:"Item 1"),
TheItem(name:"Item 2"),
TheItem(name:"Item 3"),
// ...
TheItem(name:"Item N")
]
var filteredItems: [TheItem] {
guard !searchText.isEmpty else { return self.items }
return self.items.filter { item in
item.name.lowercased().contains(self.searchText.lowercased())
}
}
var selectedItemName: String {
if selectedId != nil {
return filteredItems.first(where: {$0.id == selectedId})?.name ?? "-"
}
return "-"
}
init () {
selectedId = items.first?.id
}
}
struct TestNavigationLinkBackkView: View {
@EnvironmentObject private var model: SomeModel
var body: some View {
NavigationStack {
NavigationLink(model.selectedItemName) {
List {
Picker("Item", selection: $model.selectedId) {
ForEach(model.filteredItems) { item in
Text(item.name).tag(item.id)
}
}
}
.searchable(text: $model.searchText, placement: .navigationBarDrawer(displayMode: .always))
.pickerStyle(.inline)
}
}
}
}
#Preview {
TestNavigationLinkBackkView()
.environmentObject(SomeModel())
}
If you factor out the List
part to a separate View
then you can call dismiss
when the selection changes. This will pop the current view from the NavigationStack
.
The documentation to dismiss
states:
The specific behavior of the action depends on where you call it from.
This is why it is necessary to factor out the child view to a separate View
.
// TestNavigationLinkBackkView
NavigationStack {
NavigationLink(model.selectedItemName) {
ChildView()
}
}
struct ChildView: View {
@EnvironmentObject private var model: SomeModel
@Environment(\.dismiss) private var dismiss // 👈 added
var body: some View {
List {
Picker("Category", selection: $model.selectedId) {
ForEach(model.filteredItems) { item in
Text(item.name).tag(item.id)
}
}
}
.searchable(text: $model.searchText, placement: .navigationBarDrawer(displayMode: .always))
.pickerStyle(.inline)
.onChange(of: model.selectedId) { // 👈 added
dismiss()
}
}
}