【发布时间】:2017-11-22 09:44:28
【问题描述】:
我正在尝试创建一个自动更新的 TableView,这通常在 Results.observe 的帮助下很容易做到(替换 .addNotificationBlock)
我面临的问题是我无法弄清楚如何处理具有多个部分的 tableView,以及可以从一个部分移动到另一个部分的单元格。
以下表为例:(见UITableView with Multiple Sections using Realm and Swift)
斗牛犬
查理
最大
德国牧羊犬
贝拉
朋友
莫莉
金毛寻回犬
贝利
西伯利亚哈士奇
菊花
有
class Dog: Object {
@objc dynamic var name String?
@objc dynamic var race: String?
}
然后是类似的东西:
let results = realm.objects(Dog.self)
let token = dogs.observe { changes in
switch changes {
case .initial(let dogs):
break
case .update:
// HANDLE MOVING CELL TO DIFFERENT SECTION HERE
break
case .error:
break
}
}
假设我有上面的 tableView,但 'Molly' 遇到了身份危机,结果证明是一只金毛猎犬,所以我们从细节屏幕中更改了比赛。
我将如何处理 Observe 块中的这种变化?
我尝试使用 1 个结果列表 / 令牌,当我们更改比赛属性时会触发修改。但是除了一个完整的 reloadData(),因为我需要动画,我无法使用它,我无法弄清楚如何在 2 个不同的部分处理删除和插入,因为我们无法访问“狗”中的先前数据'-目的。因此,我不知道如何确定一个单元格是否应该移动到不同的部分以及前一部分是什么。
我也尝试使用每个部分的结果列表,但这会导致不一致。当我更改 race 属性时,它会触发修改(狗对象已更改)、删除(上一节的 resultList.count 为 -1)和插入(新节的 resultList.count = +1 )。这些通知不会同时触发,从而导致错误:
'尝试从第x节中删除项目x,但只有x 更新前的部分'
有没有人想出如何优雅地处理这个问题?在我正在实习的项目中,我实际上需要在多个 tableView 中使用类似的东西。
提前致谢
(第一次发帖,如有不合标准请及时指正)
----- 使用更具体的示例代码进行编辑 -----
我正在使用的数据类删除了一些不重要的属性
class CountInfo: Object, Encodable {
@objc dynamic var uuid: String?
@objc dynamic var productName: String?
// TableView is split in 2 sections based on this boolean-value
@objc dynamic var inStock: Bool = false
}
viewDidLoad() 中的代码存根我想用 2 个部分来更新我的 tableView
self.countListProducts = realm.objects(CountInfo.self)
self.token = self.countListProducts.observe {
changes in
AppDelegate.log.debug(changes)
if let tableView = self.tableView {
switch changes {
case .initial:
// if countInfo.isCounted = true: insert in section 0, if false: insert in section 1
// Currently handled by cellForRowAt
tableView.reloadData()
case .update(_, let deletions, let insertions, let modifications):
// Remove deletion rows from correct section
// Insert insertions into correct section
// Reload Cell if modification didn't change 'isCounted' property
// Remove from old section and insert in new section if 'isCounted' property changed
tableView.beginUpdates()
tableView.insertRows(at: insertions.map({ /* GET ROW TO INSERT */ }),
with: .automatic)
tableView.deleteRows(at: deletions.map({ /* GET ROW TO DELETE */ }),
with: .automatic)
tableView.reloadRows(at: modifications.map({ /* UPDATE NAME OR MOVE TO OTHER SECTION IF 'inStock' value Changed */ }),
with: .automatic)
tableView.endUpdates()
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
}
}
【问题讨论】:
-
您是否使用数组作为您的 tableView 数据源?当用户进行更改时,数据是在什么时候写入 Realm 的,您是手动更新数据源还是通过观察重新加载?
-
我在 dog 类下面添加了一些代码,显示我希望如何处理更改。希望这能澄清我的意思
-
您不使用tableView函数insertRowsAtIndexPaths:withRowAnimation:和deleteRowsAtIndexPaths:withRowAnimation:的任何原因?
-
因为更新通知没有告诉我更改了哪些属性。它也不会给我以前的种族属性,我需要弄清楚要删除哪一行,尽管我想我可以从 didSet 存储一个“上一节”值来解决这部分问题。
-
哦!您的 Realm 对象通常会有一个 Primary Key,它是标识该领域对象的唯一属性。当通知触发时,该对象被传入(通过 insertions 数组),使用 .map 对其进行迭代,并使用每个对象的键来处理 dataSource 数组中的该对象,更新/相应地删除它并在 tableView 上执行插入/删除。