在SwiftUI中使用ForEach插入,更新和删除动画

时间:2020-04-28 17:37:32

标签: swift listview animation foreach swiftui

我设法对ForEach中显示的项目进行了很好的插入和删除动画处理(通过.transition(...)上的Row完成)。但是可悲的是,当我只是在观察到的数组中更新Item的名称时,也会触发该动画。当然,这是因为它实际上是一个新视图(您可以看到,因为onAppear()中的Row被调用了。)

众所周知,推荐的管理带有炫酷动画的列表的方法是List,但我认为许多人都希望避免使用标准UI或该元素附带的限制。

已附上有效的SwiftUI 示例代码段(使用Xcode 11.4构建)

所以,问题是

是否有一种聪明的方法来抑制动画(或保留另一个动画),使其保持相同的位置?是否有很酷的可能性“重用”行并进行更新?

还是答案“让我们等待下一个WWDC,让我们看看苹果是否会解决...”? ;-)

干杯,
奥兰多?


修改

当您可以区分编辑/添加/删除(例如通过手动用户操作)时,

时髦的答案实际上是一个好方法。 items数组在后台更新后(例如,通过视图模型中来自Core Data的同步更新),您不知道这是否是更新。但是,在这种情况下,答案可能是在视图模型中手动实现插入/更新/删除案例。


struct ContentView: View {

    @State var items: [Item] = [
        Item(name: "Tim"),
        Item(name: "Steve"),
        Item(name: "Bill")
    ]

    var body: some View {
        NavigationView {
            ScrollView {
                VStack {
                    ForEach(items, id: \.self) { item in
                        Row(name: item.name)
                    }
                }
            }
            .navigationBarItems(leading: AddButton, trailing: RenameButton)
        }
    }

    private var AddButton: some View {
        Button(action: {
            self.items.insert(Item(name: "Jeff"), at: 0)
        }) {
            Text("Add")
        }
    }

    private var RenameButton: some View {
        Button(action: {
            self.items[0].name = "Craigh"
        }) {
            Text("Rename first")
        }
    }
}

struct Row: View {

    @State var name: String

    var body: some View {
        HStack {
            Text(name)
            Spacer()
        }
        .padding()
        .animation(.spring())
        .transition(.move(edge: .leading))
    }
}

struct Item: Identifiable, Hashable {

    let id: UUID
    var name: String

    init(id: UUID = UUID(), name: String) {
        self.id = id
        self.name = name
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

1 个答案:

答案 0 :(得分:3)

幸运的是,这实际上确实很容易做到。只需删除行上的.animation(.spring()),然后将所有更改包装在withAnimation(.spring()) { ... }中即可。

因此添加按钮将如下所示:

private var AddButton: some View {
    Button(action: {
        withAnimation(.spring()) {
            self.items.insert(Item(name: "Jeff"), at: 0)
        }
    }) {
        Text("Add")
    }
}

您的行将如下所示:

struct Row: View {

    @State var name: String

    var body: some View {
        HStack {
            Text(name)
            Spacer()
        }
        .padding()
        .transition(.move(edge: .leading))
    }
}