如何从 SwiftUI 列表和领域中删除数据
Posted
技术标签:
【中文标题】如何从 SwiftUI 列表和领域中删除数据【英文标题】:How to delete data from SwiftUI List and Realm 【发布时间】:2020-04-12 02:08:09 【问题描述】:以下代码在 SwiftUI List
中正确显示 Realm
数据库中的所有“用户”。我的问题是滑动一行时删除记录。
当我滑动一行并点击删除按钮时,我立即收到 uncaught exception
错误,List
没有更新,但我知道正确的项目从 Realm
数据库中删除,因为我下次运行所选记录未显示的应用。
这是我的代码。
SwiftUI 代码
import RealmSwift
struct ContentView: View
@State private var allUsers: Results<User> = realm.objects(User.self)
var body: some View
VStack
Text("Second Tab")
List
ForEach(allUsers, id:\.self) user in
HStack
Text(user.name)
Text("\(user.age)")
.onDelete(perform: deleteRow)
private func deleteRow(with indexSet: IndexSet)
indexSet.forEach ( index in
try! realm.write
realm.delete(self.allUsers[index])
)
领域模型
import RealmSwift
class User:Object
@objc dynamic var name:String = ""
@objc dynamic var age:Int = 0
@objc dynamic var createdAt = NSDate()
@objc dynamic var userID = UUID().uuidString
override static func primaryKey() -> String?
return "userID"
错误
由于未捕获的异常“RLMException”而终止应用程序,原因:“索引 4 超出范围(必须小于 4)。”
当然,4
会根据 Realm
数据库中的项目数量而变化,在这种情况下,当我滑动并点击删除按钮时,我有 5 条记录。
我的期望是每次allUsers
@State 变量更改时List
都会更新,我知道我的问题不是完全理解绑定的工作原理。
我做错了什么?
【问题讨论】:
在哪个确切的操作上,你得到了这个异常? 错误指向 AppDelegate -Thread 1: signal SIGABRT
第一个问题是 List 或 ForEach 中传递的数据必须符合 Identifiable 协议。此外,由于 Results 对象的性质,Realm 并不是非常适合 SwiftUI - 您可能暂时希望依靠通知来处理 UI 更新。一旦 Realm 有了 Frozen Objects,实现 Realm 功能就会更容易。
那么,如果我理解正确的话,您的意思是,到目前为止,Realm 和 SwiftUI 不能很好地协同工作?在 Realm 端或 SwiftUI 上学习通知?感谢您指出 List 或 ForEach 中传递的数据必须符合 Identifiable 协议这一事实。
这是我们迄今为止的经验。这并不是 Realm 的“问题”,因为结果按预期工作 - 它是与 SwiftUI 本质的交互。 SO上有许多similar questions,但没有不是解决方法的实际答案-其中大多数都与利用通知相关。
【参考方案1】:
我的期望是每次列表都会更新 allUsers @State 变量变化
这是正确的,但状态没有改变......以下应该可以工作
private func deleteRow(with indexSet: IndexSet)
indexSet.forEach ( index in
try! realm.write
realm.delete(self.allUsers[index])
)
self.allUsers = realm.objects(User.self) // << refetch !!
注意:下面只是分配initial状态值
@State private var allUsers: Results<User> = realm.objects(User.self)
【讨论】:
不幸的是,按照您的建议从 Realm 删除记录后重新获取不起作用,我得到了同样的错误。 @fs_tigre 不幸的是,assigning initial state value
不正确,因为 Results 对象的性质。它们是实时更新的——这意味着领域结果对象与其底层对象保持实时连接,如果将对象添加到领域,结果将包括该对象。如果删除对象,则该对象将不再包含在这些结果中。可以观察结果对象的变化,然后可以在添加、修改或删除对象时采取行动。我很确定 @State
在这里不是正确的。
知道在哪里可以找到有关如何重组代码的信息吗?
@fs_tigre 有时越简单越好,所以我只需将 allUsers 结果设为类 var,然后将观察者添加到该 var。在观察者块中,只需在发生变化时刷新 tableView。如果您的 tableView 中不需要动画 UI 元素,那就可以了。如果你这样做了,那么你可以利用观察者闭包在更细粒度的行级别控制 tableView 更新。
@fs_tigre,我越想越倾向于认为原因在其他地方......如果崩溃是由 SwiftUI 引起的,那将是一些 ios 起源的东西......比如UIKit 内部异常,或 swift 库...但是您声明它是 RLMException 并且这是领域内部异常,而通过提供的代码,一切都应该是正确的,因为在写入事务中执行删除...并且结果应该按照设计和更新立即记录。【参考方案2】:
这是使用 Realm 时非常常见的错误。它也发生在“传统”视图控制器中。
我找到的唯一可靠的解决方案,它大大提高了应用程序的稳定性,是始终使用界面项而不是领域对象。
此外,在现实生活中,我们需要做一些处理,从服务器加载头像图像,添加一些复选框、选择或其他任何东西,而且我们通常不需要显示对象的所有属性。不知何故,这是一种 MVVM 方法。
顺便说一句,你可以在循环之前调用realm.write
,不确定,但我认为最好尽量减少上下文切换。
使用这种技术,无论是与 SwiftUI 还是 UIViewControllers,都可以很好地解决 RLM 崩溃问题。
struct ContentView: View
struct UserItem: Hashable
var id: String
var name: String
var age: Int
private var allUsers: Results<User> = realm.objects(User.self)
@State private var userItems: [UserItem] = []
func updateItems()
userItems = allUsers.map
UserItem(id: $0.userID, name: $0.name, age: $0.age)
var body: some View
VStack
Text("Second Tab")
List
ForEach(userItems, id:\.self) user in
HStack
Text(user.name)
Text("\(user.age)")
.onDelete(perform: deleteRow)
.onAppear()
updateItems()
private func deleteRow(with indexSet: IndexSet)
try! realm.write
indexSet.forEach
realm.delete(self.allUsers[$0])
updateItems()
【讨论】:
以上是关于如何从 SwiftUI 列表和领域中删除数据的主要内容,如果未能解决你的问题,请参考以下文章
如何以编程方式从 SwiftUI 列表中删除行并刷新列表视图?
如何从 SwiftUI 和 Realm 中另一个列表中的对象中添加和删除列表中的对象
如何从 SwiftUI 的详细信息视图(编辑项目视图)中删除核心数据条目?
如何从 WatchOS 的 SwiftUI 列表中删除分隔符?