One of the new ScrollView modifiers introduced for iOS 17 was .scrollPosition(id:)
, which allows us to bind the ID of the scrolled-to view. From Apple's documentation, we can do:
@Binding var items: [Item]
@Binding var scrollPosition: Item.ID?
ScrollView {
LazyVStack {
ForEach(items) { item in
ItemView(item)
}
}
.scrollTargetLayout()
}
.scrollPosition(id: $scrollPosition)
But let's say we'd like to have scrollPosition
represent the index of each item
in items
. For example, when we're scrolled to the first ItemView
, scrollPosition
should be 0; when we scroll to the second ItemView
, it should be 1, and so on.
How can we achieve this in SwiftUI (with this modifier)?
Try this approach, declare scrollPosition: Int?
,
use an items.enumerated()
in the ForEach
loop, and an id(index)
, such as:
@Binding var scrollPosition: Int?
and
ForEach(Array(items.enumerated()), id: \.1.id) { index, item in
ItemView(item).id(index)
}
EDIT-1:
Here is my answer complete test code that shows how to use .scrollPosition(id:...)
when
....scrollPosition represent the index of each item
struct ContentView: View {
@State var items = [MyItem(name: "item-0"), MyItem(name: "item-1"), MyItem(name: "item-2"),
MyItem(name: "item-3"), MyItem(name: "item-4"), MyItem(name: "item-5"),
MyItem(name: "item-6"), MyItem(name: "item-7"), MyItem(name: "item-8")]
@State var scrollPosition: Int?
var body: some View {
VStack {
Text("\(scrollPosition ?? 0)")
ScrollView {
LazyVStack {
ForEach(Array(items.enumerated()), id: \.1.id) { index, item in
ItemView(item: item).id(index)
}
}
.scrollTargetLayout()
}
.scrollPosition(id: $scrollPosition)
}
}
}
struct ItemView: View {
var item: MyItem
var body: some View {
ZStack {
Rectangle()
.fill(Color.green.gradient)
.frame(height: 333)
Text(item.name)
}
}
}
struct MyItem: Identifiable {
let id = UUID()
var name: String
}