SwiftUI - ForEach 删除转换始终仅应用于最后一项

ahe*_*eze 1 swift swiftui swiftui-foreach

我正在尝试向我的 中添加删除动画ForEach,以便其中的每个Card动画在删除时都会缩放。这是我到目前为止所拥有的:

问题是,无论Card按下哪一个,总是最后一个动画。有时,每张卡片内的文本都有奇怪的滑动/变形动画。这是我的代码:

/// Ran into this problem: "SwiftUI ForEach index out of range error when removing row"
/// `ObservableObject` solution from /sf/answers/4395723531/
class Card: ObservableObject, Identifiable {
    let id = UUID()
    @Published var name: String

    init(name: String) {
        self.name = name
    }
}

struct ContentView: View {
    @State var cards = [
        Card(name: "Apple"),
        Card(name: "Banana "),
        Card(name: "Coupon"),
        Card(name: "Dog"),
        Card(name: "Eat")
    ]
    
    var body: some View {
        ScrollView(.horizontal) {
            HStack {
                
                ForEach(cards.indices, id: \.self) { index in
                    CardView(card: cards[index], removePressed: {
                        
                        withAnimation(.easeOut) {
                            _ = cards.remove(at: index) /// remove the card
                        }
                        
                    })
                    .transition(.scale)
                }
                
            }
        }
    }
}

struct CardView: View {
    
    @ObservedObject var card: Card
    var removePressed: (() -> Void)?
    
    var body: some View {
        Button(action: {
            removePressed?() /// call the remove closure
        }) {
            VStack {
                Text("Remove")
                Text(card.name)
            }
        }
        .foregroundColor(Color.white)
        .font(.system(size: 24, weight: .medium))
        .padding(40)
        .background(Color.red)
    }
}
Run Code Online (Sandbox Code Playgroud)

如何横向扩展Card被单击的而不是最后一个?

New*_*Dev 6

您看到此行为的原因是因为您使用索引作为idfor ForEach。因此,当从数组中删除一个元素时cards,看到的唯一区别ForEach是最后一个索引消失了。

您需要确保id唯一标识 的每个元素ForEach

如果必须使用索引并标识每个元素,则可以同时使用该enumerated方法或zip数组及其索引。我喜欢后者:

ForEach(Array(zip(cards.indices, cards)), id: \.1) { (index, card) in 
   //...
}
Run Code Online (Sandbox Code Playgroud)

上面使用对象本身作为ID,这需要符合Hashable。如果你不想这样,你可以id直接使用该属性:

ForEach(Array(zip(cards.indices, cards)), id: \.1.id) { (index, card) in
  //...
}
Run Code Online (Sandbox Code Playgroud)

为了完整起见,这是enumerated版本(从技术上讲,它不是索引,而是偏移量,但对于基于 0 的数组来说是相同的):

ForEach(Array(cards.enumerated()), id: \.1) { (index, card) in 
   //...
}
Run Code Online (Sandbox Code Playgroud)