In my app there's a list in a NavigationStack for selecting an object for the next view. This is working well. But after restarting the app the selected item should be preselected. I've tried the following code:
@AppStorage("selectedIndex") private var selectedIndex: Int?
var body: some View {
NavigationStack {
List(selection: $selectedIndex) {
ForEach(0..<myObjectsArray.count, id: \.self) { index in
NavigationLink(destination: MyObjectView(selectedObject: myObjectsArray[index]) {
VStack(alignment: .leading) {
HStack{
Text(myObjectsArray[index].name)
Text("- \(myObjectsArray[index].numbers) Employees")
}
Text("last Update: \(myObjectsArray[index].updated.ISO8601Format())")
.font(.footnote)
}
}
}
}
}
}
After restart of my app, the item of selectedIndex is highlighted but the corresponding view isn't shown. Where's my error?
Try this approach using NavigationPath
and NavigationLink(value: index)
as shown in the example code. Note you should not use index
etc.. in the ForEach
loop,
use the actual Item
.
// for testing
struct TheItem: Identifiable, Hashable {
let id = UUID()
var name: String
var numbers: Int
}
struct ContentView: View {
var body: some View {
SelectionMode()
}
}
struct SelectionMode: View {
@AppStorage("selectedIndex") private var selectedIndex: Int?
// for testing
@State private var myObjectsArray = [
TheItem(name: "item-1", numbers: 11),
TheItem(name: "item-2", numbers: 22)
]
@State private var path = NavigationPath() // <--- here
var body: some View {
NavigationStack(path: $path) {
List {
ForEach(0..<myObjectsArray.count, id: \.self) { index in
NavigationLink(value: index) { // <--- here
VStack(alignment: .leading) {
HStack{
Text(myObjectsArray[index].name)
Text("- \(myObjectsArray[index].numbers) Employees")
}
Text("last Update)")
.font(.footnote)
}
}
}
}
.navigationDestination(for: Int.self) { index in // <--- here
MyObjectView(selectedObject: myObjectsArray[index])
.onAppear {
selectedIndex = index
}
}
}
.onAppear {
if let ndx = selectedIndex {
path.append(ndx) // <--- here
}
}
}
}
struct MyObjectView: View {
let selectedObject: TheItem
var body: some View {
Text("MyObjectView \(selectedObject.name)")
}
}
EDIT-1
Another more robust approach without using index
, is to store the id
of the item
instead of the index.
For example:
struct TheItem: Identifiable, Hashable {
let id: Int // <--- here
var name: String
var numbers: Int
}
struct ContentView: View {
@AppStorage("selectedItem") private var selectedItem: Int?
// for testing
@State private var myObjectsArray = [
TheItem(id: 0, name: "item-0", numbers: 0),
TheItem(id: 1,name: "item-1", numbers: 1)
]
@State private var path = NavigationPath() // <--- here
var body: some View {
NavigationStack(path: $path) {
List {
ForEach(myObjectsArray) { item in // <--- here
NavigationLink(value: item) { // <--- here
VStack(alignment: .leading) {
HStack {
Text(item.name)
Text("- \(item.numbers) Employees")
}
Text("last Update)").font(.footnote)
}
}
}
}
.navigationDestination(for: TheItem.self) { item in // <--- here
MyObjectView(selectedObject: item)
.onAppear {
selectedItem = item.id
}
.onDisappear {
selectedItem = -1 // <--- if required
}
}
}
.onAppear {
if let item = myObjectsArray.first(where: {$0.id == selectedItem ?? -1}) {
path.append(item) // <--- here
}
}
}
}
struct MyObjectView: View {
let selectedObject: TheItem
var body: some View {
Text("MyObjectView: \(selectedObject.name) \(selectedObject.numbers)")
}
}