I am trying to scroll to a newly appended view in a SwiftUI List using ScrollViewReader but keep crashing with EXC_BAD_INSTRUCTION in scrollTo(_:) after adding a few items. I am using Xcode 14.0.1 and iOS 16.0 simulator.
Here is a minimal demo that exhibits the issue:
struct ContentView: View {
@State var items = [Item]()
@State var scrollItem: UUID? = nil
var body: some View {
NavigationView {
ScrollViewReader { proxy in
List {
ForEach(items) { item in
Text(item.id.uuidString)
.id(item.id)
}
}
.listStyle(.inset)
.onChange(of: scrollItem) { newValue in
proxy.scrollTo(newValue)
}
}
.navigationTitle("List Demo")
.toolbar {
Button("Add") {
addItem()
}
}
}
}
func addItem() {
items.append(Item())
scrollItem = items.last?.id
}
}
struct Item: Identifiable {
let id = UUID()
}
I can get past the issue using a ScrollView instead of a List, but I would like to use the native swipe-to-delete functionality in the real project.
CodePudding user response:
List
is not supported well in ScrollViewReader
. See this thread.
This solution is ugly, but works. The bad thing is that list blinks when you add a new item. I used one of the ideas from the thread above.
import SwiftUI
struct ContentView: View {
@State var items = [Item]()
@State var scrollItem: UUID? = nil
@State var isHidingList = false
var body: some View {
NavigationView {
VStack(alignment: .leading) {
if isHidingList {
list.hidden()
} else {
list
}
}
.onChange(of: scrollItem) { _ in
DispatchQueue.main.async {
self.isHidingList = false
}
}
.navigationTitle("List Demo")
.toolbar {
Button("Add") {
addItem()
}
}
}
}
var list: some View {
ScrollViewReader { proxy in
List {
ForEach(items) { item in
Text(item.id.uuidString)
.id(item.id)
}
}
.listStyle(.inset)
.onChange(of: scrollItem) { newValue in
guard !isHidingList else { return }
proxy.scrollTo(newValue)
}
.onAppear() {
guard !isHidingList else { return }
proxy.scrollTo(scrollItem)
}
}
}
func addItem() {
isHidingList = true
items.append(Item())
scrollItem = items.last?.id
}
}
struct Item: Identifiable {
let id = UUID()
}