I am trying to use the new scroll position API with the tabs. Following is my test code:
import SwiftUI
struct ContentView: View {
@State private var selectedTab = 0
@State private var position = ScrollPosition(edge: .top)
@State private var shouldScrollToBottom = false
var body: some View {
ScrollView {
VStack {
Picker("Tabs", selection: $selectedTab) {
Text("Collection 1").tag(0)
Text("Collection 2").tag(1)
Text("Collection 3").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
.padding()
contentForSelectedTab()
}
}
.scrollPosition($position)
.onChange(of: selectedTab) { oldTab, newTab in
position.scrollTo(edge: .bottom)
}
}
@ViewBuilder
private func contentForSelectedTab() -> some View {
if selectedTab == 0 {
listItems
} else {
Text("unknown list")
}
}
var listItems: some View {
LazyVStack(spacing: 0) {
ForEach(1...100, id: \.self) { item in
VStack(spacing: 0) {
Text("Item \(item)")
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
Divider()
}
}
}
}
}
#Preview {
ContentView()
}
As you can see, I want to achieve an effect that every time users switch to a different tab, the scroll view automatically scrolls to the bottom of the list of the tab.
However, this does not work very well somehow.
I also tried to introduce some delay on calling the position.scrollTo(edge: .bottom)
but no luck.
If I use onAppear on the listItems and place the scrollTo function there, I can see it works only the first time. Any tab switching later will not scroll to the bottom.
What confused me is that if I add a print function in the onChange and onAppear, I can see the prints at the desired timing.
So my only explanation to this is that the scrollTo function cannot correctly recognise the sizes of scroll view when its content has changed.
Looks like a bug to me.
The answer to this is in the documentation:
When configuring a scroll position, SwiftUI will attempt to keep that position stable. For an edge, that means keeping a top aligned scroll view scrolled to the top if the content size changes. For a point, SwiftUI won’t attempt to keep that exact offset scrolled when the content size changes nor will it update to a new offset when that changes.