I am creating a custom list displaying time information (minutes and seconds, not used in the snippet below to simplify the code). I managed to implement a nice animation when the user adds an entry to the list, but deleting an entry has no animation (1st GIF).
With iOS 14, the animation is working, however, the animation only removes the last rectangle from the list and then updates the text in each row (2nd GIF). That's not what I want - My goal is if a row has been deleted, the other rows should fill up that space and move accordingly - with an animation.
Probably something is wrong with the IDs of the rows but I just wasn't able to fix that. Thanks for helping!
struct ContentView: View {
@State var minutes = [0]
@State var seconds = [0]
@State var selectedElement = 0
var body: some View {
ScrollView(){
VStack{
ForEach(minutes.indices, id: \.self){ elem in
ZStack{
EntryBackground()
Text("\(self.minutes[elem])")
.transition(AnyTransition.scale)
HStack{
Button(action: {
withAnimation(.spring()){
self.seconds.remove(at: elem)
self.minutes.remove(at: elem)
}
})
{
Image(systemName: "minus.circle.fill")
.foregroundColor(Color.red)
.font(.system(size: 22))
.padding(.leading, 10)
}
Spacer()
}
}
.padding(.horizontal)
.padding(.top)
.contentShape(Rectangle())
.onTapGesture {
withAnimation(.spring()){
self.selectedElement = elem
}
}
}
}
Spacer()
Button(action: {
withAnimation{
self.minutes.append(self.minutes.count)
self.seconds.append(0)
}
})
{
ZStack{
EntryBackground()
Text("Add")
HStack{
Image(systemName: "plus.circle.fill")
.foregroundColor(Color.green)
.font(.system(size: 22))
.padding(.leading, 10)
Spacer()
}
}.padding()
}
}
}
}
struct EntryBackground: View {
var body: some View {
Rectangle()
.cornerRadius(12)
.frame(height: 40)
.foregroundColor(Color.gray.opacity(0.15))
}
}
You need to make each row uniquely identified, so animator know what is added and what is removed, so animate each change properly.
Here is possible approach. Tested with Xcode 12 / iOS 14
struct TimeItem: Identifiable, Equatable {
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.id == rhs.id
}
let id = UUID() // << identify item
let minutes: Int
let seconds: Int = 0
}
struct ContentView: View {
@State var items = [TimeItem]()
@State var selectedElement: TimeItem?
var body: some View {
ScrollView(){
VStack{
ForEach(items){ elem in // << work by item
ZStack{
EntryBackground()
Text("\(elem.minutes)")
.transition(AnyTransition.scale)
HStack{
Button(action: {
self.items.removeAll { $0.id == elem.id }
})
{
Image(systemName: "minus.circle.fill")
.foregroundColor(Color.red)
.font(.system(size: 22))
.padding(.leading, 10)
}
Spacer()
}
}
.padding(.horizontal)
.padding(.top)
.contentShape(Rectangle())
.onTapGesture {
withAnimation(.spring()){
self.selectedElement = elem
}
}
}
}
Spacer()
Button(action: {
self.items.append(TimeItem(minutes: self.items.count))
})
{
ZStack{
EntryBackground()
Text("Add")
HStack{
Image(systemName: "plus.circle.fill")
.foregroundColor(Color.green)
.font(.system(size: 22))
.padding(.leading, 10)
Spacer()
}
}.padding()
}
}.animation(.spring(), value: items) // << animate changes
}
}
struct EntryBackground: View {
var body: some View {
Rectangle()
.cornerRadius(12)
.frame(height: 40)
.foregroundColor(Color.gray.opacity(0.15))
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With