3

I am building a master detail app. I have a sections view controller (VC) which is a child of master VC. Clicking on rows in the sections VC brings up different detail VCs where users complete required information. Once the information in a detail VC is completed I would like to have a checkmark display in the relevant section VC row. FYI selectedProject is a core data object.

I call a notification in the detail VC viewWillDisappear to refresh the sections VC.

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    let detailCase: String = selectedProject!.section![indexPath.row]
    configureCell(cell, withSectionName: detailCase)
    print("location complete is \(selectedProject!.completeLocation)")
    print("electricity complete is \(selectedProject!.completeElectricity)"
    print(cell.textLabel!.text!)
    print(cell.accessoryType.rawValue)
    return cell
}

func configureCell(_ cell: UITableViewCell, withSectionName sectionName: String) {
        switch sectionName {
        case "Project Details" :
            cell.accessoryType = .checkmark
        case "Location" :
            if selectedProject?.completeLocation == true {
                cell.accessoryType = .checkmark
            }
        case "Electricity Use" :
            if selectedProject?.completeElectricity == true {
                cell.accessoryType = .checkmark
            }
        default :
            cell.accessoryType = .none
        }
        cell.textLabel!.text = sectionName
}

func refreshTable(notification: Notification) -> Void {
    print("Notification ran")
    self.tableView.reloadData()
}

Console:

Notification ran

location complete is true

electricity complete is true

Project Details

3

location complete is true

electricity complete is true

Location

3

location complete is true

electricity complete is true

Electricity Use

3

The 3's = .checkmark

So it appears cellForRowAt knows exactly what it should be doing but then the table is not readily displaying the checkmarks. Instead you either have to click back to the master VC then forward to the sections VC to get the checkmarks displaying, or you have to click different rows in the section VC and the checkmarks will randomly show up.

I've tried calling reloadData in dispatchqueue.main.async with no luck. I tried calling reloadData at different points in the process again without success. I've tried calling tableView.beginUpdates/.endUpdates and reloadSectionData, again without success to get the checkmarks to display.

If I programmatically prompt section VC viewWilDisappear and viewWillAppear in succession, the checkmarks will appear once a different row in sections VC is selected. it "seems" like reloadData() isn't prompting a complete reload of the tableView.

FYI accessory item is set to .none in the storyboard.

Chris Bell
  • 61
  • 1
  • 1
  • 3

4 Answers4

14

UI updates need to be called on the main queue. Try this:

DispatchQueue.main.async {
    self.tableView.reloadData()
}
dylanthelion
  • 1,760
  • 15
  • 23
  • Right solution! – Mannopson Mar 04 '17 at 07:27
  • Thanks for answering. Towards the bottom of my question I mentioned I have tried DispatchQueue.main.async without success. I've tried calling it through the notifier, unless you would sugggest another location that might work. – Chris Bell Mar 04 '17 at 10:07
  • @ChrisBell: in that case, I may be wrong about the core issue (though DO always put UI updates on the main thread). You should update your code to whatever you're currently using, and confirm that refreshTable: is being called. That will help us to narrow down the problem. – dylanthelion Mar 04 '17 at 10:13
2

This fixed it for me. Hope it helps :)

 DispatchQueue.main.async {
            self.tableView.reloadData()
            self.tableView.beginUpdates()
            self.tableView.endUpdates()
        }
Matthew
  • 31
  • 2
  • dylanthelion has already answered about .reloadData() and DispatchQueue.main.async. As for beginUpdates() and endUpdates(), they are not necessary. – Eric Aya Nov 23 '18 at 09:52
  • For myself and the guy who asked the Question (read his replies) .reloadData alone didn't work in our situations. It only worked properly when I added beginUpdates & endUpdates. – Matthew Nov 23 '18 at 16:27
  • Well in this case I think you should explain why you had to do that. This, really, would be the part that could help other people. :) (click on [edit] to do that) – Eric Aya Nov 23 '18 at 19:05
  • I don't know why but i also need this operations sequence to force a tableView refresh. Simply calling reloadData is not enough on my code Thank you – Marco Nov 02 '19 at 18:02
0

Ok so in light of no fixes forthcoming, I went and changed the tableView to static rather than dynamic. I created tableViewCell outlets for all the fixed cells and then added and removed the checkmarks that way.

The code for anyone interested:

var tableCellArray = [UITableViewCell]()

/*-----------------------------------------------------------*/

// MARK: - Outlets

@IBOutlet weak var projectDetails: UITableViewCell!
@IBOutlet weak var location: UITableViewCell!
@IBOutlet weak var electricityUse: UITableViewCell!

// MARK: - Actions

// MARK: - Default functions

override func viewDidLoad() {
    super.viewDidLoad()
    tableCellArray = [projectDetails, location, electricityUse]
    configureCells()
    NotificationCenter.default.addObserver(forName: Notification.Name(rawValue: "refresh"), object: nil, queue: nil, using: refreshTable)
}

// MARK: - Table view data source

func configureCells() {
    var i = 0
    for each in tableCellArray {
        configureCheckmark(each, withIndex: i)
        i = i + 1
    }
}

func configureCheckmark(_ cell: UITableViewCell, withIndex index: Int) {
    var accessoryType: UITableViewCellAccessoryType = .none
    switch index {
        case 0 :
            accessoryType = .checkmark
        case 1 :
            if selectedProject?.completeLocation == true {
                accessoryType = .checkmark
            }
        case 2 :
            if selectedProject?.completeElectricity == true {
                accessoryType = .checkmark
            }
        default :
            accessoryType = .none
        }
    cell.accessoryType = accessoryType
}

func refreshTable(notification: Notification) -> Void {
    configureCells()
    tableView.beginUpdates()
    tableView.endUpdates()
}
Chris Bell
  • 61
  • 1
  • 1
  • 3
0

Make sure you have used these two lines in viewDidLoad():

//registering the xib
self.tableView.register(UINib(nibName: "enter_xib_class_name", bundle: nil), forCellReuseIdentifier: "enter_cell_identifier")
//delegate enabling
self.tableView.delegate=self
//datasource
self.tableView.datasource=self