【问题标题】:How to animate the deletion of an item in list using SwipeAction? Realm, Swiftui如何使用 SwipeAction 动画删除列表中的项目?领域,Swiftui
【发布时间】:2021-09-26 15:56:11
【问题描述】:

我正在尝试在列表中向左滑动时为删除项目设置动画,但它不起作用。我正在使用 Realm 来保存任务。即使操作包含在 withAnimation 中,也会立即应用删除而不使用动画。

我注意到,当我使用 onDelete 修饰符时,动画播放良好,但没有使用 swipeAction 的动画

import SwiftUI
import RealmSwift

struct TasksView: View {
    
    @StateObject var tasksViewModel: TasksViewModel = TasksViewModel()
    
    var body: some View {
        
        List {
            if let tasks = tasksViewModel.tasks {
                ForEach(tasks.freeze(), id: \.id) { task in
                    Text(task.title)
                        .swipeActions(edge: .trailing, allowsFullSwipe: true) {
                            Button(role: .destructive) {
                                withAnimation {
                                    if let taskToDelete = task.thaw() {
                                        tasksViewModel.archive(taskToDelete)
                                    }
                                }
                            } label: {
                                Image("delete")
                            }
                            .tint(.red)
                        }
                }
            }
        }

    }
}

struct TasksViewNew_Previews: PreviewProvider {
    static var previews: some View {
        TasksView()
    }
}
import Foundation
import RealmSwift

@MainActor
class TasksViewModel: ObservableObject {

    @Published var tasks: List<Task>?
    
    var token: NotificationToken?

    init() {
        let realm = try! Realm()
        let group = Group()
        do {
            try realm.write {
                let tasks = [Task(title: "TaskA"),
                             Task(title: "TaskB"),
                             Task(title: "TaskC"),
                             Task(title: "TaskD"),]
                realm.add(group)
                group.tasks.append(objectsIn: tasks)
                self.tasks = group.tasks
            }
        } catch {
            print("error")
        }
        
        token = group.observe({ (changes) in
              switch changes {
              case .error(_):
                  break
              case .change(_, _):
                  self.objectWillChange.send()
                  break
              case .deleted:
                  break
              }
              
          })
        
    }
    
    func archive(_ task: Task) {
        do {

            let realm = try Realm()
            try realm.write {
                realm.delete(task)
            }

        } catch {
            print(error)
        }
    }
    
}
class Task: Object {
    @Persisted var id: String = UUID().uuidString
    @Persisted var title: String

    convenience init(title: String) {
        self.init()
        self.title = title
    }
}

class Group: Object, ObjectKeyIdentifiable {
    @Persisted var _id = ObjectId.generate()
    @Persisted var tasks = List<Task>()
}

【问题讨论】:

  • 几点建议;您可以通过在列表中使用 @ObservedRealmObject 属性包装器来简化代码。这会根据需要自动打开写入事务。此外,尚不清楚为什么这些物体会被冻结。也可能将.onDelete(perform: 添加到列表项也可以简化代码。
  • 任何运气/进展?看来我遇到了同样的问题,用一个简单的ArrayStrings 作为我的模型。所以我怀疑动画问题与Realm(?)有关。

标签: swiftui realm


【解决方案1】:

作为对原始问题的comment 的跟进,我在这里发布了我的代码,其中我遇到了一个类似的问题,即使用.swipeAction() 触发的删除动画。为了更好地比较,我的视图允许在自定义删除和使用.onDelete() 的删除之间切换。结果显示了这样的差异:

代码基于 Paul Hudson 的 example,但他仅打印出一条删除消息以显示该按钮已被点击。

// Friends.swift

import Foundation

class Friends: ObservableObject {
    @Published var list = ["Antoine", "Bas", "Curt", "Dave", "Erica"]
    
    func remove(friend: String) {
        if let index = list.firstIndex(of: friend) {
            list.remove(at: index)
        }
    }
    
    func remove(at indexSet: IndexSet) {
        list.remove(atOffsets: indexSet)
    }
}
// ContentView.swift

import SwiftUI

struct ContentView: View {
    @EnvironmentObject var friends: Friends
    
    @State private var usingOnDelete = false
    
    var body: some View {
        List {
            Section {
                Toggle("Using onDelete", isOn: $usingOnDelete)
            }
            
            Section {
                if usingOnDelete {
                    listUsingOnDelete
                } else {
                    listNotUsingOnDelete
                }
            }
        }
        .navigationTitle("Deletion test")
    }
    
    private var listUsingOnDelete: some View {
        ForEach(friends.list, id: \.self) { friend in
            Text(friend)
        }
        .onDelete { friends.remove(at: $0) }
    }
    
    private var listNotUsingOnDelete: some View {
        ForEach(friends.list, id: \.self) { friend in
            Text(friend)
            .swipeActions(allowsFullSwipe: true) {
                Button(role: .destructive) {
                    withAnimation {
                        friends.remove(friend: friend)
                    }
                } label: {
                    Label("Delete", systemImage: "trash.fill")
                }
                
                Button {
                    print("Muting conversation")
                } label: {
                    Label("Mute", systemImage: "bell.slash.fill")
                }
                .tint(.indigo)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            ContentView()
        }
        .environmentObject(Friends())
    }
}
// MainApp.swift

import SwiftUI

@main
struct MainApp: App {
    @StateObject var friends = Friends()
    
    var body: some Scene {
        WindowGroup {
            NavigationView {
                ContentView()
            }
            .environmentObject(friends)
        }
    }
}

【讨论】:

  • 这个问题有解决办法吗?还是我们在等待更新... :)
  • 恐怕我还没找到解决办法。
【解决方案2】:

这似乎是 SwiftUI 列表中的一个错误。 在列表中添加简单的样式可以解决这个问题,但在我看来它看起来不太好。

.listStyle(.plain)

我已为此问题提交了反馈,因为它可以使用原版 SwiftUI 重现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-29
    • 1970-01-01
    相关资源
    最近更新 更多