简体   繁体   中英

How do I avoid delay or adjust timing of coordinator.drop when using a Swift Realm Results<> or List<> instance?

I am using Realm Swift to build a todo list while learning iOS development. I'm running into a problem with my collectionView that happens when I try to re-order items using coordinator.drop(item.dragItem, toItemAt: destinationIndexPath) .

When I include the coordinator the dropped item first displays the contents of the cell that was originally at that index and then after a delay is replaced by the contents of the cell that was dragged. If I don't include the coordinator then the drag and drop works as intended and the item is correctly swapped when dropped (but the animation is that of a cell shrinking down to the center and a bit jarring).

When I reoder items in the collectionView:

func reorderItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
        if let item = coordinator.items.first,
            let sourceIndexPath = item.sourceIndexPath {
                do {
                    try self.realm.write {
                          // standard category is of type Category
                          // items is of type List<Task>
                          standardCategory?.items.remove(at: sourceIndexPath.item)
                          standardCategory?.items.insert(item.dragItem.localObject as! Task, at: destinationIndexPath.item)
                    }
                } catch {
                    print("error reording new items, \(error)")
                }
            coordinator.drop(item.dragItem, toItemAt: destinationIndexPath)
        }
    }

Inside of viewDidLoad() to handle updating the collectionView:

 self.notificationToken = tasks!.observe {
            (changes: RealmCollectionChange) in
            switch changes {
            case .initial(_):
                self.collectionView.reloadData()
            case .update(_, let deletions, let insertions, let modifications):
                 self.collectionView.performBatchUpdates({
                    self.collectionView.deleteItems(at: deletions.map({ IndexPath(item: $0, section: 0) }))
                    self.collectionView.insertItems(at: insertions.map({ IndexPath(item: $0, section: 0) }))
                    self.collectionView.reloadItems(at: modifications.map({ IndexPath(item: $0, section: 0) }))
                 }, completion: { status in
                    self.collectionView.reloadData()
                 })
            case .error(_):
                print("error")
        }

I think I have it working now. Assuming edge cases don't break it here's the answer:

I needed to handle the model updates and collectionView updates outside of tasks.observe so that I could control the order of events more exactly. You can do that by using try realm.commitWrite(withoutNotifying: [notificationToken!])

The key I think was to reorder the sequence of deleting and updating the items to: 1. delete from model 2. remove from collection view 3. inset into model 4. use coordinator to drop item 5. insert into collection view 6. reload section (optional I think, I needed it for UI changes)

func reorderItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
        if let item = coordinator.items.first {
            if let localObject = item.dragItem.localObject as? Task {
                if let sourceIndexPath = item.sourceIndexPath {
                    do {
                        try self.realm.write {
                            standardCategory?.items.remove(at: sourceIndexPath.item)
                            try realm.commitWrite(withoutNotifying: [notificationToken!])
                        }
                    } catch { print("error reording new items, \(error)") }
                    collectionView.deleteItems(at: [sourceIndexPath])
                }
                do {
                    try self.realm.write {
                        standardCategory?.items.insert(localObject, at: destinationIndexPath.item)
                        try realm.commitWrite(withoutNotifying: [notificationToken!])
                    }
                } catch { print("error reording new items, \(error)") }

                self.collectionView.performBatchUpdates({
                    coordinator.drop(item.dragItem, toItemAt: destinationIndexPath)
                    collectionView.insertItems(at: [destinationIndexPath])
                }, completion: { status in
                    let indexSet = IndexSet(integer: destinationIndexPath.section)
                    collectionView.reloadSections(indexSet)
                })
            }
        }
    }

SOLUTION: It turns out you can't use Results when sorting using drag and drop (not 100% sure why but that would be nice to know). Here's the current working solution using List instead:

func reorderItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
        if let item = coordinator.items.first {
            if let localObject = item.dragItem.localObject as? Task {
                if let sourceIndexPath = item.sourceIndexPath {
                    self.collectionView.performBatchUpdates({
                        do {
                            standardCategory!.realm!.beginWrite()

                            standardCategory!.items.remove(at: sourceIndexPath.item)
                            collectionView.deleteItems(at: [sourceIndexPath])

                            self.standardCategory!.items.insert(localObject as Task, at: destinationIndexPath.item)

                            coordinator.drop(item.dragItem, toItemAt: destinationIndexPath)

                            collectionView.insertItems(at: [destinationIndexPath])
                            try standardCategory!.realm!.commitWrite(withoutNotifying: [notificationToken!])

                        } catch { print("error reording new items, \(error)") }
                    }, completion: nil)
                }
            }
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM