I'm trying to implement drag and drop in my UITableView
. The drag part is working - and I'm able to drag outside my app into the reminders app and see the String that is included in the NSItemProvider
. Despite implementing the UITableViewDropDelegate
methods and adding self as the dropDelegate object in viewDidLoad, the performDropWith method is never being called (either in simulator or on a real device).
I've successfully implemented Drag and Drop in another UITableView
in my app. The only difference between where this table is and that one is this one is inside a ViewController container so I can implement my own splitview but as far as I know that shouldn't make any difference. I've read every tutorial on the net on this and I can't see what I'm missing. I've stripped down my code to the bare minimum and it's not working.
I have an @IBOutlet to tableView, and that's working fine because lots of other things in the ViewController class utilize it. I'm using Swift 4.2 in Xcode 10.1.
Class declaration:
class FolderMasterViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITableViewDropDelegate, UITableViewDragDelegate {
inside viewDidLoad:
self.tableView.dropDelegate = self
self.tableView.dragDelegate = self
self.tableView.dragInteractionEnabled = true
The relevant drag/drop methods inside the class:
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
if let folder = folders?[indexPath.row] {
Logger.log("DRAGGING FOLDER with name: \(folder.name)")
let itemProvider = createItemProviderToDrag(with: folder.folderID)
let dragItem = UIDragItem(itemProvider: itemProvider)
// This stores the local object so we don't have to re-fetch it.
dragItem.localObject = folder
return [dragItem]
}
// we shouldn't ever get here; but returning an empty array means nothing should be dragged
return []
}
private func createItemProviderToDrag(with itemID: String) -> NSItemProvider {
let itemProvider = NSItemProvider()
itemProvider.registerDataRepresentation(forTypeIdentifier: kUTTypePlainText as String, visibility: .all) { completion in
let data = itemID.data(using: .utf8)
completion(data, nil)
return nil
}
return itemProvider
}
func tableView(_ tableView: UITableView, canHandle session: UIDropSession) -> Bool {
// NOTE: This returns true
print("Can I handle it? \(session.canLoadObjects(ofClass: NSString.self))")
return session.canLoadObjects(ofClass: NSString.self)
}
func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal {
print("Can I drop") // NEVER PRINTS
return UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}
func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
print("PERFORM DROP") // NEVER PRINTS
}
here's what I see in the log output:
05-May-2019 11:11:49:049 [FolderMasterViewController.swift:204] tableView(_:itemsForBeginning:at:) -> DRAGGING FOLDER with name: folder2
Can I handle it? true
2019-05-05 11:11:50.633390-0700 CWP[34092:10212965] PBItemCollectionServicer connection disconnected.
I don't believe the PBItemCollectionServicer message is related - I've see those in the log on the other table I've successfully implemented drag and drop on, and other stackoverflow posts say you can pretty much ignore them.
Further digging and starting over with a very empty tableView and static data source and adding back functionality bit by bit - I discovered that the way I'm reloading the tableView is causing this break in the drag and drop functionality.
When I changed the list of folders that were displayed, I was using this:
tableView.reloadSections([0], with: UITableView.RowAnimation.right)
when I change that to
tableView.reloadData()
suddenly dropping items in the table works again and I can see the prints from tableView(_:performDropWith:)
. Even if put the tableView.reloadData() into an animated block like this, it still works:
UIView.transition(with: tableView,
duration: 0.5,
options: .transitionCrossDissolve,
animations: { self.tableView.reloadData() })
Does anyone know why using tableView.reloadSections(_:with:)
doesn't allow the Drop half of Drag & Drop to work? (My table only has 1 section in it)
dropSessionDidUpdate
will trigger untiltableView.reloadSections()
is called. However, using reloadData() works just fine. - StonedStudio