SwiftUI - ForEach 删除转换始终仅应用于最后一项 [英] SwiftUI - ForEach deletion transition always applied to last item only

查看:26
本文介绍了SwiftUI - ForEach 删除转换始终仅应用于最后一项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试向我的 ForEach 添加删除动画,以便其中的每个 Card 在删除时都会向外扩展.这是我目前所拥有的:

I'm trying to add a delete animation to my ForEach, so that each Card inside it scales out when removed. This is what I have so far:

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

The problem is that no matter which Card is pressed, it's always the last one that animates. And sometimes, the text inside each card has a weird sliding/morphing animation. Here's my code:

/// Ran into this problem: "SwiftUI ForEach index out of range error when removing row"
/// `ObservableObject` solution from https://stackoverflow.com/a/62796050/14351818
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)
    }
}

如何扩展被点击的Card,而不是最后一个?

How can I scale out the Card that is clicked, and not the last one?

推荐答案

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

The reason you're seeing this behavior is because you use an index as an id for ForEach. So, when an element is removed from the cards array, the only difference that ForEach sees is that the last index is gone.

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

You need to make sure that the id uniquely identifies each element of ForEach.

如果您必须使用索引并标识每个元素,您可以使用 enumerated 方法或 zip 将数组及其索引放在一起.我喜欢后者:

If you must use indices and have each element identified, you can either use the enumerated method or zip the array and its indices together. I like the latter:

ForEach(Array(zip(cards.indices, cards)), id: \.1) { (index, card) in 
   //...
}

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

The above uses the object itself as the ID, which requires conformance to Hashable. If you don't want that, you can use the id property directly:

ForEach(Array(zip(cards.indices, cards)), id: \.1.id) { (index, card) in
  //...
}


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


For completeness, here's the enumerated version (technically, it's not an index, but rather an offset, but for 0-based arrays it's the same):

ForEach(Array(cards.enumerated()), id: \.1) { (index, card) in 
   //...
}

这篇关于SwiftUI - ForEach 删除转换始终仅应用于最后一项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆