I have a LazyVGrid
inside a NavigationView
.
NavigationView {
ScrollView {
LazyVGrid(columns: columns) {
ForEach(items) { item in
NavigationLink(tag: item, selection: $displayedItem) {
DetailView(item)
} label: {
GridItemView(item)
}
}
}
}
}
The referenced variables are defined as follows on the view:
@State var displayedItem: Item?
let columns: [GridItem] = Array(repeating: .init(.flexible()), count: 2)
Now I want to show the detail view for a specific item. I do this by simply assigning this item to the displayedItem
property:
func showDetailView(for item: Item) {
displayedItem = item
}
This works great when the respective item is visible on the LazyVGrid
at the moment when I call this function. However, when the item is not visible, I first need to scroll to the item for the NavigationLink
to fire. I know why this is happening (because the items are loaded lazily, it's a lazy grid after all), but I don't know how to make the LazyVGrid
load the specific item when I need it.
I have also tried to programmatically scroll to the target item by wrapping the entire ScrollView
inside a ScrollViewReader
and appending the following modifier:
.onChange(of: displayedItem) { item in
if let item = item {
scrollProxy.scrollTo(item.id)
}
}
Unfortunately, this has the same problem: Scrolling to a given item doesn't work until the item is loaded.
Is there any way to make this work, i.e. to trigger a NavigationLink
for an item that is not currently visible in the LazyVGrid
? (It's important for me as I need this functionality to deep-link to a specific item's DetailView
.)
An possible approach can be like in this topic - use one link somewhere in background of ScrollView and activate it by tapGesture/button from user or assigning corresponding value programmatically.
Tested with Xcode 13.4 / iOS 15.5
Main part:
ScrollView {
LazyVGrid(columns: columns) {
ForEach(items) { item in
RoundedRectangle(cornerRadius: 16).fill(.yellow)
.frame(maxWidth: .infinity).aspectRatio(1, contentMode: .fit)
.overlay(Text("Item \(item.value)"))
.onTapGesture {
selectedItem = item
}
}
}
}
.padding(.horizontal)
.background(
NavigationLink(destination: DetailView(item: selectedItem), isActive: isActive) {
EmptyView()
}
)
.toolbar {
Button("Random") { selectedItem = items.randomElement() }
}