刚开始在 iOS 项目中使用 Realm,我查看了示例和文档,但似乎无法弄清楚如何为一系列 Realm 结果获取细粒度的通知。
例如,如果它只是一个 Results 对象,您可以这样做
// Observe Results Notifications
notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.tableView else { return }
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
tableView.reloadData()
break
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed, so apply them to the UITableView
tableView.beginUpdates()
tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}),
with: .automatic)
tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.endUpdates()
break
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
break
}
}
这对于没有节的普通表格 View 来说工作得很好,因为它只是将新单元格插入节 0。
但是,看看 GroupedTableView (带有某些部分的 TableView )示例,他们只是向 Realm 对象本身添加了一个通知 block 。它会通知您任何更改,而不是特定的插入/删除等。
像这样:
// Set realm notification block
notificationToken = realm.addNotificationBlock { [unowned self] note, realm in
self.tableView.reloadData()
}
虽然这可行,但它确实不是最佳解决方案,因为您会失去 iOS 免费提供的精美动画。
我的问题真的只是,我怎样才能将细粒度的通知添加到结果数组中
var objectsBySection = [Results<DemoObject>]()
我考虑过遍历数组并向每个结果对象添加一个通知 block ,但是由于可以将新的结果对象添加到此二维数组,这似乎不是一个好的解决方案。
有没有人有使用 Realm 和分段表格 View 的经验,它的分段/单元格的数量是动态增长的?
~~~~~~~~~~~~~~~~~~~~~~~~~~更新答案~~~~~~~~~~~~~~~ ~~~~~~
感谢@bogdanf,我找到了解决方案。我在这里发布我的解决方案,因为它与@bogdanf 建议的不完全相同,但他的回答让我找到了解决方案,所以在这里。
首先,在我的应用程序中,部分并不是完全无限的。用户在添加对象时会添加到它们上,但数量有限。也就是说,我可以创建一个数组并将我的实际 Realm 对象附加到它们,从而允许我通过适当的部分对对象进行分组。
这就是第一步,我创建了一个包含所有部分的数组,在我的应用程序中这总共有 48 个部分,因此在添加通知时运行时不会太差。
创建我的部分数组后,我查询 Realm 以找到与部分对应的正确对象,如下所示:
func initObjectsBySection() {
print("Initializing")
for (index, section) in sections.enumerated() {
let unsorted = realm.objects(Object.self).filter("section == %@" , section)
let sorted = unsorted.sorted(byProperty: "year", ascending: false)
objectsBySection.append(sorted)
registerNotification(for: objectsBySection[index], in: index)
}
}
和registerNotifcation
是 bogdanf 建议的,有一些变化:
func registerNotification(for objects: Results<Object>, in section: Int) {
let token = objects.addNotificationBlock { [unowned self] (changes: RealmCollectionChange) in
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
self.tableView.reloadData()
break
case .update:
// Query results have changed, so apply them to the UITableView
self.tableView.beginUpdates()
self.tableView.reloadSections(IndexSet.init(integer: section), with: .automatic)
self.tableView.endUpdates()
break
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
break
}
}
notifTokens.append(token)
}
我只是简单地重新加载部分而不是删除或插入特定行的原因是因为这做同样的事情,它更简洁,一个关键因素是它允许重新计算部分标题/高度。
由于我从大约 48 个部分的数组开始,这意味着如果用户要从新安装开始,他们将有 48 个空部分,这看起来很糟糕。
相反,我所做的只是将该部分的标题设置为 0,即:
override func numberOfSections(in tableView: UITableView) -> Int {
return objectsBySection.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectsBySection[section].count
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return objectsBySection[section].count > 0 ? 44 : 0
}
基本上就是这样。
最佳答案
我会像这样,按照您的建议遍历数组:
var objectsBySection = [Results<DemoObject>]()
// Fill the objectsBySection array like in your example
...
for (index, objects) in objectsBySection.enumerated() {
registerNotifications(for: objects, in: index)
}
registerNotifications(for:in:)
方法定义如下:
func registerNotifications(for results: Results<DemoObject>, in section:Int) {
let notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.tableView else { return }
switch changes {
...
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed, so apply them to the UITableView
tableView.beginUpdates()
tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: section) }), with: .automatic)
tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: section)}), with: .automatic)
tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: section) }), with: .automatic)
tableView.endUpdates()
break
...
}
}
notificationTokens.append(notificationToken)
}
我们假设 var notificationTokens
是在类级别定义的。
现在,您提到可以随时添加新的部分,所以我们也来处理一下。因此,我们添加了普通的旧非细粒度通知 block ,并检查是否添加了新部分。
notificationToken = realm.addNotificationBlock { [unowned self] note, realm in
// Let's see what the section list looks like now
let sections = Set(realm.objects(DemoObject.self).value(forKey: "sectionTitle") as! [String])
if !Set(sectionTitles).isSuperset(of: sections) {
sectionTitles = Array(sections)
self.tableView.reloadData()
}
}
因此,在我的简单方法中,它仅在添加新部分时才重新加载所有内容。如果您想从漂亮的插入动画中受益,您可以检查添加了哪些部分,将它们一一插入到表格中,然后将新对象添加到它们。
注意:我检查是否添加部分的方法非常密集,基本上它会遍历数据库中的所有对象,因此您可能希望在您的应用程序中使用实际负载来检查它。不幸的是,在 Realm 允许 distinct
或 group by
查询之前,这是我能想到的解决此问题的唯一方法。
关于ios - 向 Realm 结果数组添加通知,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40365792/