2016-06-11 104 views
2

当我尝试从Realm数据库中删除项目时,我无法适当更新UICollection View。删除Realm对象后更新UICollectionView

让我们假设一个境界容器List<Child>类型的children

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.' 

T他只能通过使用collectionView.reloadData()来获取更新的collectionView,但这不是我想要的,因为缺少单元格删除的动画。

然而,当我只能在这个容器中,在indexPath.row通过移除子(不从数据库中删除它):

try! realm.write { 
    children.removeAtIndex(indexPath.row) 
} 

collectionView.deleteItemsAtIndexPaths([indexPath])作品更新的CollectionView没有问题。

从数据库中删除项目后,更新UICollectionView的最佳方法是什么?

+0

问题是你先从children.removeAtIndex(indexPath.row)中删除它。那么如何在indexPath.row中使用该对象? –

+0

这是真的。但事实上,我不想使用'removeAtIndex',因为我想从数据库中删除它。我提到它是因为这是我能够使collectionView更新工作的唯一方法。 – Gerard

+0

要重新加载集合视图,您必须从子列表中删除对象。这样集合视图就会得到新的列表。 –

回答

2

当您继续访问已被删除的对象时,会出现您正面临的错误。所以,你可能在某处存储了一个对象的引用,这本身就很好,但是在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 
    } 

} 
+0

删除的对象确实在“layoutSubviews”的UICollectionView单元格中调用。我没想到这种方法甚至在删除后被调用,但使用setter而不是直接引用解决了问题!关于您使用通知的建议,我仍然遇到了实施该问题的问题。我收到错误:'...在更新之后(5),现有部分中包含的项目数量必须等于更新前部分中包含的项目数量(6),加上或减去从该部分插入或删除的项目数量(插入1个,1删除)...' – Gerard

+0

由于我只删除一个项目,我不明白为什么通知处理程序显然插入一个项目。你能否在这里给我一点提示? – Gerard

+0

您是否在代码中的任何其他地方执行批量更新? – marius