iosswiftuitableviewuikituicontextualaction

UIContextualAction triggers setEditing = true, how to disable this?


I just implemented trailingSwipeActionsConfigurationForRowAt and leadingSwipeActionsConfigurationForRowAt to add swipe actions to my UITableViewCells.

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration

This triggers (on my UITableViewController) func setEditing(_ editing: Bool, animated: Bool) to be called with editing = true, which in turn also triggers my editing animation and other changes that I want when going into edit mode. (The contextual actions are not related to editing/deleting).

I don't want this, but have yet to find a way to disable this behaviour, even just detecting that setEditing is called via the swipe actions.

Any ideas?


Solution

  • I was able to solve this by triggering the "real" edit mode differently.

    On my UITableViewController:

    class MyTableViewController: UITableViewController {
      var realEditMode: Bool = false
    
      func setRealEditing(_ editing: Bool) {
        realEditMode = editing
        setEditing(realEditMode, animated: true)
      }
    
      // See Note 1 below
      @available(iOS 11.0, *)
      override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration {
        guard let item = itemForIndexPath(indexPath) else {
          return UISwipeActionsConfiguration(actions: [])
        }
    
        if realEditMode {
          return UISwipeActionsConfiguration(actions: [
            buildActionConfiguration(.delete, fromIndexPath: indexPath)
          ])
        } else {
          return UISwipeActionsConfiguration(actions: [
            buildActionConfiguration(.read, fromIndexPath: indexPath)
          ])
        }
      }
    
      @available(iOS 11.0, *)
      func buildActionConfiguration(_ action: MyCellActionEnum, fromIndexPath indexPath: IndexPath) -> UIContextualAction {
        // logic to build `UIContextualAction`
      }
    }
    

    And in my UITableViewCell check if the editing flag is set by manual triggering or by swipe edit triggering:

    class MyCell: UITableViewCell {
      var myTableViewController: MyTableViewController?
    
      override func setEditing(_ editing: Bool, animated: Bool) {
        if editing && !(myTableViewController?.realEditMode ?? true) {
            return
        }
    
        super.setEditing(editing, animated: animated)
      }
    }
    

    Then on the edit buttons in the UI I changed setEditing(true/false, animated: true) to setRealEditing(true/false) instead.

    Note 1

    One problem I found was that when using trailingSwipeActionsConfigurationForRowAt was that the delete button (⛔️) didn't work anymore. Tapping it did not trigger the confirmation swipe.

    I found that there had to exist a trailingSwipeActionsConfigurationForRowAt with a UIContextualAction that was initialized with UIContextualAction(style: .destructive) (i.e had destructive style). This is the item that is then used for displaying the delete confirmation.

    However I did not want that item to be visible when the regular swipe actions were used, so to only show it one "real edit mode" I used the realEditMode flag.

    This worked for me and didn't seem too hacky. If anything more official pops up I'm more than happy to change the accepted answer.