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

ors*_*aef 9 foreach animation listview swift swiftui

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

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

附加了一个有效的 SwiftUI示例片段(使用 Xcode 11.4 构建)

所以,问题:

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

或者答案是“让我们等待下一个 WWDC,让我们看看 Apple 是否会修复它......”?;-)

干杯,
奥兰多


编辑

当您可以区分编辑/添加/删除(例如,通过手动用户操作)时,笨拙的前辈答案实际上是一种很好的方法。一旦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()
    }
}

Run Code Online (Sandbox Code Playgroud)

bon*_*onk 12

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

所以添加按钮将如下所示:

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

你的行将如下所示:

struct Row: View {

    @State var name: String

    var body: some View {
        HStack {
            Text(name)
            Spacer()
        }
        .padding()
        .transition(.move(edge: .leading))
    }
}
Run Code Online (Sandbox Code Playgroud)