More serioulsy this bug seems to be solved on iOS 16.4, as the scroll will happen and the app will not crash.
However the selection will not visually happen unless is made after the scroll has happened, by wrapping it inside a DispatchQueue.main.async call.
Does anyone has a better ideas how to do this?
Code sample follows:
///A simple data model for the demo. Only stores an UUID.
struct DataModel: Identifiable, Hashable {
let id: UUID = UUID()
var nameUUID: String {
id.uuidString
}
}
struct ContentView: View {
///Array with some data to show
@State private var data: [DataModel] = []
///Selected row
@State private var selection: DataModel?
var body: some View {
VStack(alignment: .leading) {
HStack {
//Create a new array for showing in the list.
//This array will be bigger than the last one.
//The selection will be the last element of the array (triggering the bug)
Button {
//Increment the size of the new List by 5
let numberElements = data.count + 5
//Create a new Array of DataModel with more 5 elements that the previous one
let newData = (0 ..< numberElements).map { _ in DataModel() }
//Update STate for the new values
data = newData
} label: {
Text("Randomize & Select Last")
}
Spacer()
Text(selection?.id.uuidString ?? "no selection")
Spacer()
//Create a new array for showing in the list.
//This array will be bigger than the last one.
//The selection will be the a random element of the array (only triggering the bug when the element is )
Button {
//Increment the size of the new List by 5
//If empty will start with 40 (reducing the odds of triggering the bug)
let numberElements = data.count == 0 ? 40 : data.count + 5
//Create a new Array of DataModel with more 5 elements that the previous one
let newData = (0 ..< numberElements).map { _ in DataModel() }
//Select a random element of the array/list.
//This will scroll if the element is 'inside' the previous list
//Otherwise will crash
let newSelection = newData.randomElement()
//Update State for the new values
data = newData
selection = newSelection
} label: {
Text("Randomize & Select Random")
}
}
.padding()
//MARK: ScrollViewReader and List
ScrollViewReader {
proxy in
List(data, selection: $selection) {
dataElement in
//Row (is only the UUID for the rows
Text(dataElement.id.uuidString)
.id(dataElement)
.tag(dataElement)
}
//action that fires when data changes
//it will scroll to the selection
.onChange(of: data, perform: { newValue in
let lastValue = newValue.last
proxy.scrollTo(lastValue)
DispatchQueue.main.async {
//Select the last element of the array/list.
self.selection = lastValue
}
})
}
}
}
}