Post marked as solved
Click to stop watching this thread.
You have stopped watching this post. Click to start watching again.
contentPostList.repliessolved.tooltip
@Frameworks Engineer I've refined your code a bit with suggestions from other answers here to try and explain the question better.
struct ContentView: View {
@State var data: [String] = (0 ..< 25).map { String($0) }
@State var dataID: String?
var body: some View {
ScrollView {
VStack {
Text("Header")
LazyVStack {
ForEach(data, id: \.self) { item in
Color.red
.frame(width: 100, height: 100)
.overlay {
Text("\(item)")
.padding()
.background()
}
}
}
.scrollTargetLayout()
}
}
.scrollPosition(id: $dataID)
.safeAreaInset(edge: .bottom) {
VStack {
Text("\(Text("Scrolled").bold()) \(dataIDText)")
HStack {
Button {
scrollTo(data.first)
} label: {
Label("Top", systemImage: "arrow.up")
.frame(maxWidth: .infinity)
}
Button {
scrollTo(data.last)
} label: {
Label("Bottom", systemImage: "arrow.down")
.frame(maxWidth: .infinity)
}
Menu {
Button("Prepend") {
let items = createMoreItems()
data.insert(contentsOf: items, at: 0)
}
Button("Append") {
let items = createMoreItems()
data.append(contentsOf: items)
}
Button("Remove First") {
data.removeFirst()
}
Button("Remove Last") {
data.removeLast()
}
} label: {
Label("More", systemImage: "ellipsis.circle")
.frame(maxWidth: .infinity)
}
}
}
.background(Material.ultraThin)
}
}
var dataIDText: String {
dataID.map(String.init(describing:)) ?? "None"
}
private func scrollTo(_ dataID: String?,
animation: Animation? = .easeInOut,
position: UnitPoint = .bottom) {
withAnimation(animation) {
withTransaction(\.scrollTargetAnchor, position) {
self.dataID = dataID
}
}
}
private func createMoreItems(count: Int = 10) -> [String] {
return (0..<count).map { String(data.count + $0) }
}
}
#Preview {
ContentView()
}
While most of the times when adding a single item (append, insert) the dataID scrolled position is kept correctly, when adding multiple items (like a page) on top (prepend) it fails to maintain the right content offset, pushing the whole content down.
Notice that on this scenario dataID won't change its value.
There are a lot of hacky ways to try fix this like transforming the ScrollView and its subviews, accessing UIScrollView directly using introspect,, or re-setting dataID few moments after appending a page, but none of them is robust and most of the times they create weird scroll jumps.
I've also tried to create a custom ScrollTargetBehavior updating the scroll target to the old page content offset, but it works only when new content is added while drugging, and also creates weird scroll jumps.
Unfortunately achieving reverse scroll behavior in SwiftUI 5 feels impossible ATM.