1

I'm using trailingSwipeActionsConfigurationForRowAt UITableView's delegate method for handling when user swipe to delete certain cell (item).

My UIContextualAction for delete event looks like this. I first remove item from Realm database and then I delete row from my table view

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let deleteAction = UIContextualAction(style: .destructive, title: nil) { _,_,_ in
        Repository.shared.deleteItem(self.items![indexPath.row])
        tableView.deleteRows(at: [indexPath], with: .fade)
    }
    ...
}

But from some reason when I delete cell, it isn't deleted immediately and it returns to position between swipe. Then when I scroll table view, cell disappeared like you can see in gif below:

enter image description here

I can "fix" this by reloading data of table view right after I remove certain item from database, but this is not what I want to, I want to use deleteRow(at:with:) because of animation.


One my guess is, that it has something to do with deleting item from Realm database.

I'm deleting item using delete method

func deleteItem(_ item: Item) {
    do {
        try realm.write {
            realm.delete(item)
        }
    } catch {
        print(error)
    }
}

then as data source array for my table view I'm using Realm's Results

var items: Results<Item>?

Does anybody have any idea why this happens? Thank you.

Community
  • 1
  • 1
Robert Dresler
  • 10,580
  • 2
  • 22
  • 40
  • 1
    One option is to add a Realm observer to your results dataset. So when you delete an object from realm, your app receives a notification of that event and can update the UI accordingly. It also goes hand-in-hand with adding or editing - the observer closure receives those events and can process them. Keeps the UI fresh and constantly updated. – Jay Jan 07 '19 at 20:11
  • Where are you calling `realm.delete`? You only show the code for deleting the table view rows, but don't actually show where you modify the data source. – Dávid Pásztor Jan 07 '19 at 21:52
  • @DávidPásztor it's just method which calls `realm.delete(item)` inside *do-try-catch* block – Robert Dresler Jan 07 '19 at 21:53
  • @Jay okay, that's not bad idea. Thank you for your suggestion. – Robert Dresler Jan 07 '19 at 21:54
  • Have you tried adding `beginUpdates()` and `endUpdates()` before and after you call `deleteRows`? – hardik parmar Jan 10 '19 at 07:54

1 Answers1

1

I was facing the exactly the issue you had. I'm using Realm too, and I also used Results<Item> as my tableView datasource.

My implementation for swipe-to-delete-cell in tableView(_:, trailingSwipeActionsConfigurationForRowAt:) method is exactly same as yours: delete the swiped item in realm.write{} block, then call UITableView.deleteRows(at:with:) method to update the tableView. There's no need to update the datasource items as Realm Results<T> auto updates itself.

All is working well on iOS 13 and above, but when I tested it on iOS 12.4, iPhone 6 simulator, I had the same issue as you described in the question.

I figured out a solution while reading this article, it turned out that all you need to do is to call the completion handler provided in UIContextualAction.Handler after you have finished your updates, please refer to the code below:

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { _, _, completion in
        Repository.shared.deleteItem(self.items![indexPath.row])  // delete item from Realm
        tableView.deleteRows(at: [indexPath], with: .fade)  // update tableView
        completion(true) // notify the tableView that the swipe action has completed so now it can perform UI updates (this line is no longer needed on iOS 13+)
    }
    ...
}

This solved my problem, hope it works for you too.

Daniel Hu
  • 423
  • 4
  • 11