Certainly! If you want to programmatically make a SwiftUI List
move to show a specific row, you can achieve this by embedding the List
inside a ScrollViewReader
. The ScrollViewReader
provides a scrollTo()
method on its proxy that allows you to specify the ID of the row you want to scroll to1. Here’s how you can do it:
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollViewReader { proxy in
VStack {
Button("Jump to #50") {
proxy.scrollTo(50)
}
List(0..<100, id: \.self) { i in
Text("Example \(i)").id(i)
}
}
}
}
}
In the example above, when you press the button labeled “Jump to #50,” the List
will scroll directly to the row with ID 50. You can also provide an optional anchor parameter to control where the row should be positioned within the scroll view (e.g., proxy.scrollTo(50, anchor: .top)
). If you call scrollTo()
inside a withAnimation()
, the movement will be animated1.
For iOS 13 and below, you can try a different approach by flipping the views. This starts the ScrollView
at the bottom, and when adding new data to it, it automatically scrolls the view down. Rotate the outermost view with 180.rotationEffect(.radians(.pi))
and flip it across the vertical plane with .scaleEffect(x: -1, y: 1, anchor: .center)
2.
Here are two effective ways to make a SwiftUI List
scroll automatically in different scenarios:
1. Scrolling to the Bottom When New Items Are Added:
This approach is ideal for situations where you’re dynamically adding items to the list and want it to automatically scroll to the bottom to reveal the new content.
struct MyListView: View {
@State private var items: [String] = []
var body: some View {
List {
ForEach(items) { item in
Text(item)
}
.onAppear { // Scroll to the bottom on initial rendering
withAnimation {
List(items) { item in
Text(item)
}
.scrollTo(items.endIndex - 1) // Scroll to last item index
}
}
}
Button("Add Item") {
items.append("New Item")
}
}
}
Explanation:
- We use a
@State
variableitems
to store the list data. - Inside the
List
, we iterate throughitems
usingForEach
and display each item asText
. - The
onAppear
modifier is used to run code once when the list appears on the screen. - Inside
onAppear
, we create a nestedList
with the same data and usescrollTo
with the index of the last item (items.endIndex - 1
) to scroll to the bottom with animation. - When the “Add Item” button is tapped, a new item is appended to the
items
array, triggering a re-render and automatically scrolling to the bottom again.
2. Scrolling to a Specific Item:
If you want to programmatically scroll to a particular item in the list, you can use ScrollViewReader
:
struct MyListView: View {
@State private var items: [String] = ["Item 1", "Item 2", "Item 3"]
@State private var selectedItem: String? = nil
var body: some View {
ScrollViewReader { reader in
List {
ForEach(items) { item in
Text(item)
.onTapGesture {
selectedItem = item
}
}
.id(item) // Assign unique ID for each item
}
.onAppear {
if let selectedItem = selectedItem {
withAnimation {
reader.scrollTo(selectedItem) // Scroll to the selected item ID
}
}
}
}
Button("Select Item 2") {
selectedItem = "Item 2"
}
}
}
Explanation:
- We use a
ScrollViewReader
to access the scroll view associated with theList
. - Each item in the
List
has a uniqueid
assigned using the item itself. - The
onTapGesture
modifier triggers when an item is tapped, setting theselectedItem
state variable. - In the
onAppear
modifier, we check ifselectedItem
has a value. If so, we usereader.scrollTo(selectedItem)
with animation to scroll to the item with the matching ID. - The button demonstrates programmatically setting
selectedItem
to “Item 2”, which will trigger scrolling to that item.
Remember to choose the appropriate method based on your specific use case. For automatic scrolling with new items, the first approach is suitable. For programmatic scrolling to a specific item, the second approach using ScrollViewReader
is more appropriate.