Can somebody tell me if I'm doing something wrong or SwiftUI's scrollTo(id:anchor:) just doesn't work if the ScrollView is scrolling? I have a trivial example that demonstrates the issue:
import SwiftUI
@main struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct Item: Identifiable {
let id = UUID()
let timestamp: Date
let text: String
}
struct ContentView: View {
@State private var items: [Item] = (0...10_000).map{ .init(timestamp: Date(), text: "Row \($0)") }
@State private var scrollPosition = ScrollPosition(idType: Item.ID.self)
@State private var newMessage: String = ""
var body: some View {
ScrollView {
LazyVStack {
ForEach(items) { item in
ItemView(item: item)
}
}.scrollTargetLayout()
}
.defaultScrollAnchor(.bottom, for: .initialOffset)
.scrollPosition($scrollPosition, anchor: .bottom)
.safeAreaBar(edge: .bottom) {
HStack {
TextField("Type here", text: $newMessage, axis: .vertical)
.textFieldStyle(.roundedBorder)
Button("Send", action: {
let trimmed = newMessage.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return }
newMessage = ""
items.append(.init(timestamp: .now, text: trimmed))
withAnimation(.smooth) {
scrollPosition.scrollTo(id: items.last!.id, anchor: .bottom)
}
})
}.padding()
}
}
}
struct ItemView: View {
let item: Item
var body: some View {
VStack(alignment: .leading) {
Text(item.text)
Text(item.timestamp.formatted())
}.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1)))
}
}
#Preview {
ContentView()
}
Hello @xmollv,
It appears SwiftUI prioritizes user gesture input over pragmatic scroll operations, like scrollTo.
I haven't found this in writing explicitly, but it seems consistent and might be expected, rather than a bug. You can still file an enhancement request through Feedback Assistant with information on how this design impacts you. Bug Reporting: How and Why? explains how you can do that
One idea to work around this is to store the target in state and execute scrollTo inside onScrollPhaseChange once the phase becomes .idle.
The HIG for Scroll views includes some relevant UX considerations for automatic scrolling, this might help give you some ideas as well.
Travis