Implementing iOS 11 Drag and Drop in a table view. If I don't want the first row to be dragged, I assume I return an empty array from tableView(:itemsForBeginning:)
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
if indexPath.row == 0 {
return []
}
let dragItems = self.dragItems(forRowAt: indexPath)
print("dragging row index \(String(describing: dragItems.first?.localObject as? Int))")
return dragItems
}
Return an empty array when you do not allow the user to drag content from the specified index path.
But even when it's verified that [ ] is returned, the drag still happens. That means either I screwed up or the feature is not implemented as documented. I'm always hesitant to think it's someone else, so my question is if the return of [ ] as implemented should in fact prevent the drag of the 0 row? Anyone else verify this or show it as working as documented?
Thanks
Edit: Sample code from WWDC video includes this chunk:
if tableView.isEditing {
// User wants to reorder a row, don't return any drag items. The table view will allow a drag to begin for reordering only.
return []
}
Is this saying that if you don't return any drag items, the table view will still allow dragging?!?! Then how would one prevent a row from being dragged at all?
Thanks to @Losiowaty for pointing in useful direction.
I knew that in tableView, the drop delegate would look to tableView(:moveRowAt:)
if there was just one UIDragItem
. What I didn't see documented anywhere was that it also checked with tableView(:canMoveRowAt:)
, though that now seems obvious in retrospect.
My canMoveRowAt
looked like this:
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable. If coded that the first row is non-editable, that row is by definition also non-re-orderable
return true
}
Note the comment inside the method. I don't know if I wrote the comment or copied it from somewhere. I'd prevented row 0 from being editable (deletable and reorder-able in edit mode) and let that override canMoveRowAt
, but that is ignored with iOS 11 drag and drop apparently. So the solution is to be explicit, as in:
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
if indexPath.row == 0 {return false}
return true
}
An additional complexity in diagnosing this is that the same code in an iMessage app on iPad is not reaching tableView(:moveRowAt:)
, but is reaching there on iPhone. For an iOS app, tableView(:moveRowAt:)
is reached on both iPad and iPhone, though this may be separate issue.