Missing random UITableViewCell after tableView.deleteRows

I'm working on an app that displays music records in a table. The table is divided in a section for each day.

The code works as intended, but as soon as an item gets deleted, some rows are rendered white. However, those white rows still can be deleted, but don't respond to the

didSelectRowAt
.


Animation to illustrate the behavior

https://i.stack.imgur.com/r51ia.gif

View Hierarchy Debugger

https://i.stack.imgur.com/9Ekz0.png


Can't find the cell using the view hierarchy debugger either..

https://imgur.com/a/nT1UxLR

My attempt

I've created a

UITableViewController
subclass and implemented the protocols
UITableViewDataSource
&
UITableViewDelegate
.

appData
is a structure, which conforms the
Codable
protocol and is encoded & decoded as the app cycles through it's states.

appData.scannedAlbums
isa dictionary of
String: [ScannedAlbum]
(i.e. "2018-02-21": [ album1, album2, album3 ... ])

Before I refactored the code to use sections, the view controller no problems to render the cell, even after an item was deleted, or at least it did not appear.... :/



HistoryTableViewController

class HistoryTableViewController: UITableViewController {
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    [...]

UITableViewDataSource

override func numberOfSections(in tableView: UITableView) -> Int {
        guard let sections = appDelegate.appData?.scannedAlbums else { return 0 }
        
        return sections.count
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        guard let albumsInSection = appDelegate.appData?.getAlbums(in: section) else { return 0 }
        
        return albumsInSection.count
    }

UITableViewDelegate

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            guard let dateString = appDelegate.appData?.getDateString(for: indexPath.section) else {return}
            guard let albumsInSection = appDelegate.appData?.getAlbums(in: indexPath.section) else {return}
            guard let index = albumsInSection.index(of: albumsInSection[indexPath.row]) else {return}

            if albumsInSection.count == 1 {
                appDelegate.appData?.scannedAlbums.removeValue(forKey: dateString)
                tableView.deleteSections([indexPath.section], with: .automatic)
            }
            else {
                appDelegate.appData?.scannedAlbums[dateString]?.remove(at: index)
                tableView.deleteRows(at: [indexPath], with: .automatic)
            }
        }
    }
   


Wrapping the inner block of commit editinStyle in

DispatchQueue.main.async
removes those empty/white rows, but introduces animation bugs when deleting the row.


Could someone explain me what's going on here?

Replies

Maybe you have a synchronization issue with the data store (DispatchQueue.main.async solving it).


My guess is that it could come from the call to the appDelegate.


Why don't have the data source as a property of the view, loaded in viewDidLoad and saved to appData.scannedAlbum when you leave the view ?


Could you also post the code of tableView(cellForItemAt) ?


Anyway, tableView.deleteRows(at:) should occur in the main thread.

In what thread are you when you call it ?


To solve animation problem when in main thread, did you try another animation like .fade or .none ?