Reordering controls on UITableView while not in editing mode?
I have a UITableView and I want to be able to reorder the rows when I'm not in edit mode, meaning I don't want 开发者_运维技巧to see the delete icons until I press Edit. I want to be able to see and use the reordering controls all the time. Is that possible?
Do I have to keep the UITableView in editing mode all the time and enable/disable the delete icon manually? How to disable the swipe to delete in that case?
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.
Reorder without deletion and no indentation.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewCellEditingStyleNone;
}
- (BOOL)tableView:(UITableView *)tableview shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath {
return NO;
}
- (BOOL)tableView:(UITableView *)tableview canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
Reference: Solution posted by Stefan von Chossy here
As far as I'm aware you can't do it using just the editing flag. However, it's not hard to achieve a similar effect.
Have your own boolean ie deleting
. Set the tableview cell to cell.showsReorderControl = YES
.
Then implement all the methods required for moving cells. The bit of code I think you're looking for is.
– tableView:editingStyleForRowAtIndexPath:
if deleting == YES
return UITableViewCellEditingStyleDelete
otherwise return UITableViewCellEditingStyleNone
.
Finally, set tableView.editing = YES
always. If before and after changing the deleting
bool you run [tableView beginUpdates]
and [tableView endUpdates]
everything should work fine. Let me know if this doesn't make sense, and I'll try again.
Finally can show Reorder Control with Swipe-To-Delete in Non-Edit-Mode for row managed by Core Data NSFetchedResultsController in Swift 4.1 on Xcode 9.3 like this:
class MyTableViewCell: UITableViewCell {
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
setupAccessoryView()
}
func setupAccessoryView() {
if accessoryView == nil {
if let reorderCtrl = subviews.first(where: { (subView) -> Bool in
subView.description.contains("Reorder")
}) {
accessoryView = reorderCtrl
}
}
}
}
class MyTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var frc: NSFetchedResultsController<MyRow>!
override func viewDidLoad() {
super.viewDidLoad()
do {
// Setup the NSFetchedResultsController here
try frc.performFetch()
setEditing(true, animated: false) // Enable for cell reorder control creation on first load
} catch {
print(error)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setEditing(false, animated: false)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
setEditing(true, animated: false) // Enable for cell reorder control creation when managed object is updated or inserted on other view
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
// your data source update here for swipe to delete
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
// your data source update here for sort property value change
}
}
I am not sure if this works with actual editing controls on top, but for just showing reordering controls all the time, this seems to be working best:
override func layoutSubviews() {
showsReorderControl = true
super.layoutSubviews()
}
精彩评论