I found a solution. UITableViewCell refuses to draw the reorder control unless it's in editing mode. Fortunately, UITableViewCell and UITableView track editing separately, and crucially, UITableView actually handles reordering regardless of its own editing mode. We just need to trick the cells into drawing the reorder controls and we're home free.
Subclass UITableViewCell like this:
class ReorderTableViewCell: UITableViewCell {
override var showsReorderControl: Bool {
get {
return true // short-circuit to on
}
set { }
}
override func setEditing(editing: Bool, animated: Bool) {
if editing == false {
return // ignore any attempts to turn it off
}
super.setEditing(editing, animated: animated)
}
}
Now simply set editing = true on cells for which you want to enable reordering. You can make it conditional on -tableView:canMoveRowAtIndexPath:.
In your table view data source:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
// Configure the cell...
:
cell.editing = self.tableView(tableView, canMoveRowAtIndexPath: indexPath) // conditionally enable reordering
return cell
}
The only drawback is that this is incompatible with the table view allowsMultipleSelectionDuringEditing option; the edit control is incorrectly always shown. A workaround is to enable multiple selection only during actual table view editing.
In your table view controller:
override func setEditing(editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
self.tableView.allowsMultipleSelectionDuringEditing = editing // enable only during actual editing to avoid cosmetic problem
}
Also, in -viewDidLoad or your storyboard, set the initial value of allowsMultipleSelectionDuringEditing to false.