tableView.cellForRowAtIndexPath(indexPath) return nil
Asked Answered
G

3

7

I got a validation function that loop through my table view, the problem is that it return nil cell at some point.

for var section = 0; section < self.tableView.numberOfSections(); ++section {
    for var row = 0; row < self.tableView.numberOfRowsInSection(section); ++row {
        var indexPath = NSIndexPath(forRow: row, inSection: section)
        if section > 0 {
            let cell = tableView.cellForRowAtIndexPath(indexPath) as! MyCell
            // cell is nil when self.tableView.numberOfRowsInSection(section) return 3 for row 1 and 2
            // ... Other stuff
        }
    }
}

I'm not really sure what I'm doing wrong here, I try double checking the indexPath row and section and they are good, numberOfRowsInSection() return 3 but the row 1 and 2 return a nil cell... I can see my 3 cell in the UI too.

Anybody has an idea of what I'm doing wrong?

My function is called after some tableView.reloadData() and in viewDidLoad, is it possible that the tableview didn't finish reloading before my function is executed event though I didn't call it in a dispatch_async ??

In hope of an answer. Thank in advance

--------------------------- Answer ------------------------

Additional explanation :

cellForRowAtIndexPath only return visible cell, validation should be done in data model. When the cell is constructed in

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

It should change itself according to the validation state.

Grimona answered 2/7, 2015 at 15:35 Comment(0)
R
17

As stated in the documentation, cellForRowAtIndexPath returns:

An object representing a cell of the table, or nil if the cell is not visible or indexPath is out of range.

Hence, unless your table is fully displayed, there are some off screen rows for which that method returns nil.

The reason why it returns nil for non visible cells is because they do not exist - the table reuses the same cells, to minimize memory usage - otherwise tables with a large number of rows would be impossible to manage.

Reservation answered 2/7, 2015 at 15:47 Comment(8)
Yea, but I don't hide any of the cell, All I do during the process is change their backgroundColor to help the user know if it is valid or not... is there a edge case were my cell could be hidden without me knowing? Can the reloadData() cause this? Is there a way to ensure that my tableview finished rendering before lunching the validation ?Grimona
Do you mean that all rows are displayed on screen and you don't have to scroll?Reservation
No, there are some cell that are not on screen actually. What should I do to make my validation work?Grimona
I meen I want to change the background of the cell that are not valid, is there a way to proceed?Grimona
I don't know what kind of validation you are doing... is it something that you actually need to do in the cell, or can it be done with the data you use to populate teh cells?Reservation
Ok if you want to change the background, then the right place to do it is in tableView:cellForRowAtIndexPath:Reservation
I guest the correct place to do that is in func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {, I should validate in a seperated function according to my data model. Thank for the guideline.Grimona
I have a case when all cells ARE on screen and, yet, cellForRowAtIndexPath does return nil :-[[[[[Simple
A
1

So, to handle that error just do optional binding:

// Do your dataSource changes above

if let cell = tableView.cellForRow(at: indexPath) as? MyTableViewCell {
    // yourCode
}

If the cell is visible your code got applied or otherwise, the desired Cell gets reloaded when getting in the visible part as dequeueReusableCell in the cellForRowAt method.

Allopatric answered 23/5, 2020 at 12:44 Comment(0)
B
0

I too experienced the issue where cellForRowAtIndexPath was returning nil even though the cells were fully visible. In my case, I was calling the debug function (see below) in viewDidAppear() and I suspect the UITableView wasn't fully ready yet because part of the contents being printed were incomplete with nil cells.

This is how I got around it: in the viewController, I placed a button which would call the debug function:

public func printCellInfo() {
    for (sectionindex, section) in sections.enumerated() {
        for (rowIndex, _) in section.rows.enumerated() {
            let cell = tableView.cellForRow(at: IndexPath(row: rowIndex, section: sectionindex))
            let cellDescription = String(describing: cell.self)

            let text = """
            Section (\(sectionindex)) - Row (\(rowIndex)): \n
            Cell: \(cellDescription)
            Height:\(String(describing: cell?.bounds.height))\n
            """

            print(text)
        }
    }
}

Please note that I'm using my own data structure: the data source is an array of sections, each of them containing an array of rows. You'll need to adjust accordingly.

If my hypothesis is correct, you will be able to print the debug description of all visible cells. Please give it a try and let us know if it works.

Bombastic answered 30/1, 2019 at 18:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.