28

I'm updating a UITableViewController to use the new UITableViewDiffableDataSource, I have everything working except Swipe to delete.

This is an example of how I use swipe to delete

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

   let lockedAction = UIContextualAction(style: .normal, title: "TEST") { (_, _, completion) in
       print("tapped....")
       completion(true)
   }

    return UISwipeActionsConfiguration(actions: [lockedAction])
}

But this doesn't not work in a UITableViewController that has UITableViewDiffableDataSource

There is no swipe, a break point within the method is never called either

I thought this was a beta bug, but I updated to Xcode 11 GM and that same thing is occurring.

Thanks for any advice

DogCoffee
  • 19,820
  • 10
  • 87
  • 120
  • 2
    I have the same problem and submitted a bug report using Feedback Assistant. As far as I can tell it's some kind of iOS bug. – timschmitz Sep 13 '19 at 17:08

3 Answers3

50

It's true that the docs for tableView(_:canEditRowAt:) say:

The method permits the data source to exclude individual rows from being treated as editable. Editable rows display the insertion or deletion control in their cells. If this method is not implemented, all rows are assumed to be editable

However UITableViewDiffableDataSource, does implement that method and it seems to return false by default (though I can't find that documented anywhere).

In fact the sample code from WWDC 2019 sessions 215 and 220 implements a custom UITableViewDiffableDataSource subclass like this:

class DataSource: UITableViewDiffableDataSource<SectionType, ItemType> {
    // ... 
    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }
    // ...
}
Alex Robinson
  • 1,698
  • 16
  • 13
42

You should subclass UITableViewDiffableDataSource and return true for the rows you want to enable this for in:

override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool
Ash Cameron
  • 1,898
  • 1
  • 10
  • 11
  • 1
    This doesn't not solve the issue. "The method permits the data source to exclude individual rows from being treated as editable. Editable rows display the insertion or deletion control in their cells. If this method is not implemented, all rows are assumed to be editable" Thanks for trying to help. – DogCoffee Sep 16 '19 at 22:23
  • @DogCoffee have you tried my solution? Apple's docs aren't accurate when it comes to the new diffable data sources introduced in iOS 13. Their default implementation seems to return false and assume them to *not* be editable. – Ash Cameron Sep 26 '19 at 22:03
  • Works - Well done... ill send you the bonus 50 when it unlocks in 23 hrs :) – DogCoffee Oct 11 '19 at 07:16
  • 1
    This does work. This was very annoying... I spent an hour messing around until i started to add iOS 13 in my google searches and found this found stack page. Thanks @AshCameron – Dan Oct 12 '19 at 19:33
  • Thanks, works for me as well. Apple needs to update their documentation. – crashoverride777 Oct 16 '19 at 20:09
  • This works. Frustrating. My tableview delegate is set as a UITableViewController. My diffable data is a subclass. I was setting canEditRowAt in my tableview delegate. It was not being called, so trailingSwipeActionsConfigurationForRowAt was also not being called. As the solution suggests, I had to set canEditRowAt in the data source subclass of instead of the the tableview delegate. Makes no sense! Thanks for solution. – Small Talk Feb 16 '20 at 12:34
-1

If your custom UITableViewDiffableDataSource class inherits from another custom UITableViewDiffableDataSource class, you need to implement the trailingSwipeActionsConfigurationForRowAt method in the parent class with default implementation and then override it in the child class in order to get called:

Parent class:

class ParentDataSource: UITableViewDiffableDataSource<SectionType, ItemType> {
    // ... 

    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        return nil
    }
    // ...
}

Child class:

class ChildDataSource: ParentDataSource {
    // ... 
    override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let lockedAction = UIContextualAction(style: .normal, title: "TEST") { (_, _, completion) in
            print("tapped....")
            completion(true)
        }

        return UISwipeActionsConfiguration(actions: [lockedAction])
    }
    // ...
}

I had the same issue and this is the only solution it worked for me.

pableiros
  • 14,932
  • 12
  • 99
  • 105