Search code examples
iosswiftuiscrollview

How to use scrollPosition(id:) to get index of item in ScrollView in iOS 17


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)?


Solution

  • 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
    }