Noticed in iOS 18 that List element with animation modifier behaves differently. Seems that on first change of the underlying State property List updates all elements and loses its scroll position even if you insert just one element.
The following code reproduces the behaviour, if you run it on iOS 17 it will behave as expected, you'll see an element being added to the list with an acompanying animation. On iOS 18 you'll lose your scroll position and judging by the animation it seems to update all the other rows in the list even though only one element of the underlaying State property was inserted. After that first update, list element on iOS will behave as expected.
To reproduce the issue scroll down to the ~50th element and tap on a row, compare the behaviour on iOS 17 and iOS 18.
import SwiftUI
struct ContentView: View {
struct ViewState: Equatable {
struct Value: Identifiable, Equatable {
let id: String
var text: String
var value: Bool
}
let list: [Value]
}
@State var viewState: [ViewState.Value] = {
return (0..<100).map { id in
ViewState.Value(
id: "\(id)",
text: "Row number: \(id + 1)",
value: Bool.random()
)
}}()
var body: some View {
list(viewState)
}
@ViewBuilder
private func list(_ list: [ViewState.Value]) -> some View {
List {
ForEach(list) { row in
self.value(row)
}
}
.animation(.default, value: viewState)
}
@ViewBuilder
private func value(_ value: ViewState.Value) -> some View {
Button(action: {
guard let rowIndex = viewState.firstIndex(where: { $0.id == value.id }) else {
return
}
viewState.insert(randomValue(id: rowIndex), at: rowIndex + 1)
}) {
VStack {
Text(value.text)
Text("\(value.value ? "ON" : "OFF")")
.foregroundStyle(value.value ? Color.green : Color.red)
}
}
}
private func randomValue(id: Int) -> ContentView.ViewState.Value {
let id = (id*100 + 1)
return .init(id: "New id: \(id)", text: "Row number: \(id)", value: Bool.random())
}
}
Issues has already been reported using feedback assistant FB16082730