【问题标题】:How to make a Realm collection observer section aware like `NSFetchedResultsController`?如何让领域集合观察者部分像`NSFetchedResultsController`一样感知?
【发布时间】:2021-07-25 06:59:49
【问题描述】:

我是 Realm 的新手,我希望它能在 CoreData 中做一些简单的事情。首先,我将解释它是如何与 CoreData 一起工作的,然后我会问是否或如何使用 Realm 来完成它。

TL;DR:我希望 Realm 的功能类似于您使用 NSFetchedResultsController 获得的功能,特别是它如何轻松管理表格视图部分。如果您知道答案,您可能可以跳过下面的详细信息。

这是场景。假设我们有一个这样的数据模型:

┌────────────────┐
│Category        │
│ - name         │             ┌────────────────┐
│ - description  │┼─────┐      │Item            │
│ - items        │      │     ┌│ - name         │
│ - dateCreated  │      └────○┼│ - description  │
└────────────────┘            └│ - category     │
                               │ - dateCreated  │
                               └────────────────┘
                               

一个Category 可以有多个Items。一个Item 有一个Category

假设我们在表格视图中显示了一些Items。表格视图由Category 划分。所以它看起来像这样:

┌──────────────┐
│Camping       │
│ Axe          │
│ Firestartor  │
│Clothing      │
│ Bug shirt    │
│ Long pants   │
│              │
└──────────────┘

是的,有一个错字。我们会解决的。 Camping 是Category,显示在部分标题中。它的Items 是该部分中的行。与 Clothing 相同,它是 Category,位于部分标题中,其 Items 是该部分中的行。

使用 CoreData,这很容易通过 NSFetchedResultsController 来实现,如下所示:

let fr = NSFetchRequest<Item> = NSFetchRequest(…)
fr.predicate = …
fr.sortDescriptors = …
let frc = NSFetchedResultsController(fetchRequest: fr, managedObjectContext: moc, sectionNameKeyPath: “category.name”, cacheName: nil)

获取请求 fr 很像 Realm 查询。它指定实体以及谓词和排序描述符。在这种情况下,假设排序描述符是按照类别名称中的项目名称进行排序。

但是对于部分,关键是为sectionNameKeyPath 参数提供一个值。该值与您在获取请求中指定的实体相关。所以在这种情况下category.name 是相对于Item 的。 Item 有一个 Category 和一个 Category 有一个名字。所以这些部分将是Category 名称。

现在,在cellForRowAt 我只是这样做

let item = frc.object(at: indexPath)
cell.textField.text = item.name

自从我们提供了sectionNameKeyPath,获取结果控制器已经完成了所有的工作,并且已经对部分进行了分类并且知道哪些Items 在哪些部分中。这就是使用NSFetchedResultsController 加载分段表视图的全部内容。

此外,就像 Realm 集合观察者一样,NSFetchedResultsController 监听变化。在这种情况下,它正在侦听对 Items 的更改。假设用户点击选项卡栏上的另一个选项卡并转到应用程序的其他位置,注意到“firestartor”的拼写错误并修复它。另一个视图控制器上的frc 将注意到该更改并调用其委托方法,它将该行的 indexPath 传递给我,我可以只更新单元格。我不需要做任何处理来找出索引路径,它只是将正确的索引路径传递给我。

问题:

  1. Realm 是否有类似 NSFetchedResultsController 的机制,可以轻松处理部分,还是部分是否喜欢完全取决于开发人员?

RealmCollectionChange 的文档说...

更改信息有两种格式:集合中针对每种更改类型的简单行索引数组,以及适合直接传递给 UITableView 的批量更新方法的请求部分中的索引路径数组。

  1. 我得到的只是行索引的简单数组。你如何获得其他格式?具有索引路径数组的那个?

【问题讨论】:

  • 不确定问题是否超级清楚,一个好的答案将取决于您如何创建一个部分。例如,在您的示例中,您有带有 A、B、C... 的部分,然后用户名位于与字母匹配的部分中。如果用户的姓氏从 Smith 更改为 Jones,则该对象将传递给观察者,您可以将其从 Smith 部分中删除并将其插入 Jones 部分 - 这是文档拼写顺序的原因之一; 在集合通知处理程序中,始终按以下顺序应用更改:删除、插入、然后修改
  • @Jay 在将 Smith 更改为 Jones 的情况下,RealmCollectionChange 对象没有提供足够的信息。它返回一个简单的行索引数组。当然,我可以只使用reloadData(),但是没有动画。我希望有办法获得新的 indexPath。
  • @Jay 我把你关于不太清楚的评论放在心上,我已经编辑了这个问题。现在我在全面描述 CoreData 可以完成的事情,然后询问 Realm 是否有类似的功能。

标签: ios swift core-data realm nsfetchedresultscontroller


【解决方案1】:

我相信问题是如何使用 Realm 观察者处理谨慎的插入、删除和修改,以便在 tableView 中应用动画。

让我笼统地解决这种问题,因为我们不知道 tableView 部分和行是如何填充的。

假设我们有一堆 Person 对象存储在 Realm 中,每个 Person 都有一个 name 属性。我们将这些人加载到 tableView 数据源中,并对这些结果进行观察。这是展示如何通过索引访问每个对象的“胆量”

func observePeople() {
    self.peopleToken = self.peopleResults!.observe { changes in
            switch changes {
            case .initial(let people):
                print("  initial object load complete")
                for person in people {
                    print("\(person.name)")
                }

            case .update(let persons, let deletions, let insertions, let modifications):
                for index in deletions {
                    let deletedPerson = persons[index]
                    print("  deletedAccount: \(deletedPerson.name)")
                }

                for index in insertions {
                    let insertedPerson = persons[index]
                    print("  insertedAccount: \(insertedPerson.name)")
                }

                for index in modifications {
                    let modifiedPerson = persons[index]
                    print("  modifiedAccount: \(modifiedPerson.name)")
                }

            case .error(let error):
                print(error.localizedDescription)
            }
    }
}

假设用户将其姓氏从 Jones 修改为 Smith。这段代码会执行

for index in modifications {
   let modifiedPerson = persons[index]
   print("  modifiedPerson: \(modifiedPerson.name)")
}

这将打印到控制台

modifiedPerson: Smith

要向具有行和节的 tableView 添加动画,请将 case 语句中的上述代码替换或扩充为:

case .update(_, let deletions, let insertions, let modifications):
     ... determine the section. for example, first char is A=0, B=1, C=2 etc
     tableView.beginUpdates()behavior.
     tableView.deleteRows(at: deletions.map({ getPath(for: $0) )}),
                          with: .automatic)
     tableView.insertRows(at: insertions.map({ getPath(for: $0) }),
                          with: .automatic)
     tableView.reloadRows(at: modifications.map({ getPath(for: $0) }),
                          with: .automatic)
     tableView.endUpdates()

然后是确定此对象在数据源中的位置并返回该对象的索引路径以支持动画的函数。

func getPathFor(myPerson: PersonClass) {
   //determine which object this is in the array and return it's indexPath
   return indexPath
}

因此,例如,如果我们使用 A、B、C 部分,并且用户 Jones 是部分 J 中的第一个人,那将是第 9 部分,第 0 行。例如让 jones = IndexPath(row: 0, section: 9).

还有许多其他方法可以执行此过程,但这个对我们有用。

【讨论】:

  • 也许最初不是很清楚(我很抱歉,但希望现在更好)我有 2 个问题。根据您的代码,我认为您是在说我的第一个问题的答案是没有与NSFetchedResultsController 等效的东西,并且我们负责表格视图部分的管理。那是对的吗?如果是这样,您的示例将很有帮助。谢谢你。这是第一个问题。关于RealmCollectionChange 的其他格式,您有问题2 的答案吗?
猜你喜欢
  • 2018-08-09
  • 1970-01-01
  • 1970-01-01
  • 2019-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
相关资源
最近更新 更多