I'm trying to get this to animate nicely when I add elements to my array.
Currently the button jumps down after the foreach elements are added.
Here's my code:
import SwiftUI
class ForEachAnimationViewModel: ObservableObject {
@Published var array: [String] = []
var count = 5
init() {
array = (0..<count).map({ "Element \($0)" })
}
func addMore() {
let newItems = (count..<count+5).map({ "Element \($0)" })
array.append(contentsOf: newItems)
}
}
struct ForEachAnimationView: View {
@ObservedObject var viewModel: ForEachAnimationViewModel
var body: some View {
ScrollView {
ForEach(viewModel.array, id: \.self) { element in
Text(element)
}
Button(action: {
withAnimation {
viewModel.addMore()
}
}) {
Text("Button")
}
}
}
}
#Preview {
ForEachAnimationView(viewModel: ForEachAnimationViewModel())
}
There is a problem with the way that new entries are being added to the array. The variable count
is not being updated when more entries are added, so this results in errors being shown in the console about duplicate ids. One way to fix would be to change the function addMore
to base the new ids on the existing count:
func addMore() {
let count = array.count
let newItems = (count..<count+5).map({ "Element \($0)" })
array.append(contentsOf: newItems)
}
The animation can then be improved with these changes:
.animation
modifier to the ScrollView
.transition
modifier to the Text
itemsminWidth
on the Text
, to avoid wrapping issues when the width of one row is larger than the width of the previous row.struct ForEachAnimationView: View {
@ObservedObject var viewModel: ForEachAnimationViewModel
var body: some View {
ScrollView {
ForEach(viewModel.array, id: \.self) { element in
Text(element)
.frame(minWidth: 200)
.transition(.opacity)
}
Button("Button") {
viewModel.addMore()
}
}
.animation(.easeIn(duration: 1), value: viewModel.array)
}
}
You can try other types of transition, but if you want to see the rows appear one by one, it would be better to add them one by one instead of in batches of 5.