I have a list with NavigationLinks in it. I wish to programmatically select/tap an item from the list. I tried using NavigationLink(tag..., selection...) API, but looks like it's deprecated from iOS 16.
Code:
import SwiftUI
import Foundation
struct BookItem {
let id: Int
let name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
struct ContentView: View {
let bookList: [BookItem] = [BookItem.init(id: 0, name: "History"), BookItem.init(id: 1, name: "Math"), BookItem.init(id: 2, name: "Science")]
let selectBookItemId: Int = 1
var body: some View {
NavigationStack {
VStack {
List(bookList, id: \.name) { bookItem in
NavigationLink(destination: { DetailsView(bookItem: bookItem) },
label: {
HStack {
Text(bookItem.name)
Spacer()
}
.contentShape(Rectangle())
})
// NOTE: Was going to use this, but it is deprecated
// NavigationLink(
// destination: ,
// tag:,
// selection:,
// label: {})
}
}
}
}
}
struct DetailsView: View {
var bookItem: BookItem
var body: some View {
VStack {
Text(bookItem.name).font(.body.bold()).foregroundColor(Color.blue)
}
}
}
In my sample code above, let's say I wish to programmatically select 2nd item from the list i.e. when I execute the code, it should show me DetailsView with "Math" written on it.
How can it be achieved now since NavigationLink API is deprecated?
Thanks!
Edit: With @son's answer, I was able to solve my original question. I thought it will work with my actual app data, but my data is more complex. I need to pass an array objects to destination view and when I try to do that I am getting an error.
Code:
import SwiftUI
import Foundation
struct BookItem: Hashable {
let id: Int
let name: String
let listing: [String]
init(id: Int, name: String, listing: [String]) {
self.id = id
self.name = name
self.listing = listing
}
}
struct ContentView: View {
let bookList: [BookItem] = [BookItem.init(id: 0, name: "History", listing: ["1", "2"]),
BookItem.init(id: 1, name: "Math", listing: ["1", "2"]),
BookItem.init(id: 2, name: "Science", listing: ["1", "2"])]
let selectBookItemId: Int
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
VStack {
List(bookList, id: \.name) { bookItem in
Button {
path.append(bookItem.listing)
} label: {
HStack {
Text(bookItem.name)
Spacer()
}
.contentShape(Rectangle())
}
}
}
.navigationDestination(for: String.self, destination: { listing in
DetailsView(bookItem: listing)
})
.onAppear { //<- append book at index here
if let book = bookList.first(where: {$0.id == selectBookItemId}) {
path.append(book.listing)
}
}
}
}
}
struct DetailsView: View {
var bookItem: [String]
var body: some View {
VStack {
// Text(bookItem.name).font(.body.bold()).foregroundColor(Color.blue)
}
}
}
Error: Cannot convert value of type 'String' to expected argument type '[String]'
An alternative way is to use NavigationPath
. You could try this:
@State private var path = NavigationPath()
let selectBookItemId: Int
NavigationStack(path: $path) {
VStack {
List(bookList, id: \.name) { bookItem in
Button {
path.append(bookItem)
} label: {
HStack {
Text(bookItem.name)
Spacer()
}
.contentShape(Rectangle())
}
}
}
.navigationDestination(for: BookItem.self, destination: { book in
DetailsView(bookItem: book)
})
.onAppear { //<- here
if let book = bookList.first(where: {$0.id == selectBookItemId}) {
path.append(book)
}
}
}
And the BookItem
must conform Hashable
struct BookItem: Hashable {
...
}
And passing id in such as:
ContentView(selectBookItemId: 1)