The Goal
Let's say I have a List or a LazyVGrid that displays multiple items nested inside a ScrollView. I use a ForEach view to generate the individual item views:
ForEach(items) { item in
ItemView(item)
}
The items array might be a @State property on the view itself or a @Published property on a view model that conforms to @ObservableObject (I'll go with the first in this example).
Now when I change the items array by inserting or removing elements, I want the changes to be animated in a particular fashion, so I add a transition and an animation modifier as follows:
ScrollView {
LazyVGrid(columns: 2) {
ForEach(items) { item in
ItemView(item)
.transition(.scale)
}
}
}
.animation(.default, value: items)
This works beautifully.
The Problem
The only hiccup is that this code also causes the entire ScrollView to scale from zero to its full size when the view first appears. (It makes sense as the items array is empty initially before the items are fetched from the store, so the array does change in deed.)
Solution Attempt
To solve the problem, I obviously need to make the animation dependent on a property that does not change before the view has appeared and the items array is loaded. So I created such a property as a plain Boolean and toggle it whenever the items array changes, but only after didAppear has been called:
@State var changedState: Bool = false
@State var didAppear: Bool = false
@State var items: [Item] = [] {
didSet {
if didAppear {
changedState.toggle()
}
}
}
Then I change the value of the animation modifier to this new property:
.animation(.default, value: changedState)
✅ That solves the problem. However, it feels very "ugly" and like a lot of overhead.
The Question
Is there any other (more elegant/concise) way to disable the initial scale animation?
Edit: Minimal Code Example
struct ContentView: View {
@State var items: [Int] = []
var body: some View {
NavigationView {
ScrollView {
LazyVGrid(columns: [GridItem(), GridItem()]) {
ForEach(items, id: \.self) { item in
Rectangle()
.frame(height: 50)
.foregroundColor(.red)
.transition(.scale)
}
}
}
.animation(.default, value: items)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
let newItem = items.last.map { $0 + 1 } ?? 0
items.append(newItem)
} label: {
Text("Add Item")
}
}
}
}
.onAppear {
items = [Int](0...10)
}
}
}
This is how the initial animation looks like:


