当我尝试从Realm数据库中删除项目时,我无法正确更新UICollection视图。
让我们假设一个children
类型的Realm容器List<Child>
:
var children = realm.objects(Parent).first!.children
当我想通过以下方式从数据库中删除此子项时:
try! realm.write {
realm.delete(children[indexPath.row])
}
按collectionView.deleteItemsAtIndexPaths([indexPath])
更新collectionView会出现以下错误:
Got error: *** Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.'
我更新collectionView的唯一方法是使用collectionView.reloadData()
,但这不是我想要的,因为缺少单元格删除的动画。
但是,当我只通过indexPath.row
从这个容器中删除一个孩子时(不将其从数据库中删除):
try! realm.write {
children.removeAtIndex(indexPath.row)
}
使用collectionView.deleteItemsAtIndexPaths([indexPath])
更新collectionView没有问题。
从数据库中删除项目后更新UICollectionView的最佳方法是什么?
答案 0 :(得分:3)
当您继续访问已删除的对象时,会出现您所面临的错误。因此,您可能会在某个地方存储对象的引用,这本身就很好,但在invalidated
之后继续访问它。
可能发生这种情况,例如在您的自定义子类UICollectionViewCell
中。我建议在您的单元格上实现一个setter,并从该方法中将属性值拉入视图组件。您甚至可以在单元格中使用KVO来更新这些内容。 (我们在我们的回购中已经an example based on ReactKit了。)当对象可能在以后的某个时间点被删除时,您无法继续访问这些属性,例如:如果您的单元格需要绘制,或者在淡出时需要进行布局。
我建议订阅您用于填充收藏视图单元格的精细粒度通知,并仅以这种方式将更新传播到集合视图。通过这种方式,您可以确保根据要求使用漂亮的动画删除您的项目,并自动处理。所有这些都可以看起来如下所示。在我们的回购中,您会找到complete runnable sample。
class Cell: UICollectionViewCell {
@IBOutlet var label: UILabel!
func attach(object: DemoObject) {
label.text = object.title
}
}
class CollectionViewController: UICollectionViewController {
var notificationToken: NotificationToken? = nil
lazy var realm = try! Realm()
lazy var results: Results<DemoObject> = {
self.realm.objects(DemoObject)
}()
// MARK: View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Observe Notifications
notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
guard let collectionView = self?.collectionView else { return }
switch changes {
case .Initial:
// Results are now populated and can be accessed without blocking the UI
collectionView.reloadData()
break
case .Update(_, let deletions, let insertions, let modifications):
// Query results have changed, so apply them to the UITableView
collectionView.performBatchUpdates({
collectionView.insertItemsAtIndexPaths(insertions.map { NSIndexPath(forRow: $0, inSection: 0) })
collectionView.deleteItemsAtIndexPaths(deletions.map { NSIndexPath(forRow: $0, inSection: 0) })
collectionView.reloadItemsAtIndexPaths(modifications.map { NSIndexPath(forRow: $0, inSection: 0) })
}, completion: { _ in })
break
case .Error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
break
}
}
}
deinit {
notificationToken?.stop()
}
// MARK: Helpers
func objectAtIndexPath(indexPath: NSIndexPath) -> DemoObject {
return results[indexPath.row]
}
// MARK: UICollectionViewDataSource
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return results.count
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let object = objectAtIndexPath(indexPath)
try! realm.write {
realm.delete(object)
}
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let object = objectAtIndexPath(indexPath)
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! Cell
cell.attach(object)
return cell
}
}