使用ForEach删除动画的SwiftUI自定义列表不起作用

时间:2020-08-11 18:47:04

标签: ios swift swiftui

我正在创建一个显示时间信息的自定义列表(分钟和秒,下面的代码段中未使用以简化代码)。当用户向列表中添加条目时,我设法实现了一个不错的动画,但是删除条目没有动画(第一个GIF)。

在iOS 14上,动画正在运行,但是,动画仅从列表中删除最后一个矩形,然后更新每行中的文本(第二个GIF)。那不是我想要的-我的目标是如果已删除一行,则其他行应填充该空间并相应地移动-并带有动画。

行ID可能有问题,但是我无法解决。感谢您的帮助!

enter image description here

enter image description here

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))
    }
}

1 个答案:

答案 0 :(得分:5)

您需要使每一行都得到唯一标识,以便动画师知道添加了什么和删除了什么,从而正确地动画化每个更改。

这是可行的方法。经过Xcode 12 / iOS 14的测试

enter image description here

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))
    }
}