Peter Pik
Peter Pik

Reputation: 11193

cant return cell in cellForRowAtIndexPath

i'm trying to return different cells in a tableView. Normally in this case i would return different cells and then return nil at the bottom, but in this case it gives me and error. I've tried returning an empty cell as well, but also gives me and error.

What i've tried

return nil

and

 var cell: UITableViewCell!
  return cell

but both returned errors. How can i fix this?

cellForRowAtIndex

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

    
    
    
    if indexPath.row == 0 {


        let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("imageCell", forIndexPath: indexPath) as UITableViewCell
        var imageFile = cell.viewWithTag(100) as PFImageView
        imageFile.image = itemFile


        cell.selectionStyle = UITableViewCellSelectionStyle.None
        return cell
        
    } else if indexPath.row == 1 {
        let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("segmentCell", forIndexPath: indexPath) as UITableViewCell
        
        var titleLabel = cell.viewWithTag(101) as UILabel?
        titleLabel?.text = itemTitle
        
        let segmentControl = cell.viewWithTag(102) as UISegmentedControl
        segmentControl.selectedSegmentIndex = segment
        segmentControl.setTitle("Beskrivelse", forSegmentAtIndex: 0)
        segmentControl.setTitle("Sælger", forSegmentAtIndex: 1)
        segmentControl.setTitle("Lokation", forSegmentAtIndex: 2)
        segmentControl.tintColor = UIColor(rgba: "#619e00")
        var font = UIFont(name: "Lato-Regular", size: 11)
        var attributes:NSDictionary = NSDictionary(object: font , forKey: NSFontAttributeName)
        segmentControl.setTitleTextAttributes(attributes, forState: UIControlState.Normal)
        segmentControl.addTarget(self, action: "segmentAction:", forControlEvents: .ValueChanged)
        

        cell.selectionStyle = UITableViewCellSelectionStyle.None
        
        return cell
      
    } else if indexPath.row == 2 {
        switch segment {
        case 0:
            let cell = tableView.dequeueReusableCellWithIdentifier("CellZero", forIndexPath: indexPath) as DescViewCell
            return cell
        case 1:
            let cell = tableView.dequeueReusableCellWithIdentifier("CellOne", forIndexPath: indexPath) as SellerViewCell
            return cell
        case 2:
            let cell = tableView.dequeueReusableCellWithIdentifier("CellTwo", forIndexPath: indexPath) as LocationViewCell
            return cell
        default:
            break
        
        }
    }
        
    
    var cell: UITableViewCell!
    return cell
}

Upvotes: 2

Views: 8994

Answers (4)

Imanou Petit
Imanou Petit

Reputation: 92439

As indicated in a previous answer for a similar question, -tableView:cellForRowAtIndexPath: must return a UITableViewCell. So you can't return nil. However, I would also recommend to avoid returning the following codes at the end of -tableView:cellForRowAtIndexPath: when you use if else or switch statements inside it:

//bad code design
var cell: UITableViewCell!
return cell

or:

//bad code design
var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
return cell

You can do better code than creating an UITableViewCell instance that is never called just in order to silent Xcode warnings!

So what would be the solution?

The key thing is to be sure that your last possible value for an if else statement is set in else (not in else if). In the same way, the key thing is to be sure that your last possible value for a switch statement is set in default: (not in case XXX:).

Thus, your code should look like this:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    if indexPath.row == 0 {
        let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("CellZero", forIndexPath: indexPath) as UITableViewCell
        /* ... */
        return cell
    } else if indexPath.row == 1 {
        let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("CellOne", forIndexPath: indexPath) as CellOne
        /* ... */
        return cell
    } else { //set your last indexPath.row case in "else", not in "else if indexPath.row == 2"!!!
        switch segment {
        case 0:
            let cell = tableView.dequeueReusableCellWithIdentifier("CellTwo", forIndexPath: indexPath) as CellTwo
            /* ... */
            return cell
        case 1:
            let cell = tableView.dequeueReusableCellWithIdentifier("CellThree", forIndexPath: indexPath) as CellThree
            /* ... */
            return cell
        default: //set your last segment case in "default:", not in "case 2:"!!!
            let cell = tableView.dequeueReusableCellWithIdentifier("CellFour", forIndexPath: indexPath) as CellFour
            /* ... */
            return cell
        }
    }
    //No need for a fictive "return cell" with this code!!!
}

If segment is not an optional, thanks to tuples, you can even reduce the previous code to this:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    switch (indexPath.row, segment) {
    case (0, _):
        let cell = tableView.dequeueReusableCellWithIdentifier("CellZero", forIndexPath: indexPath) as UITableViewCell
        /* ... */
        return cell
    case (1, _):
        let cell = tableView.dequeueReusableCellWithIdentifier("CellOne", forIndexPath: indexPath) as CellOne
        /* ... */
        return cell
    case (2, 0):
        let cell = tableView.dequeueReusableCellWithIdentifier("CellTwo", forIndexPath: indexPath) as CellTwo
        /* ... */
        return cell
    case (2, 1):
        let cell = tableView.dequeueReusableCellWithIdentifier("CellThree", forIndexPath: indexPath) as CellThree
        /* ... */
        return cell
    default: //case (2, 2)
        let cell = tableView.dequeueReusableCellWithIdentifier("CellFour", forIndexPath: indexPath) as CellFour
        /* ... */
        return cell
    }
}

Upvotes: 5

Nate Cook
Nate Cook

Reputation: 93276

You need to declare the cell before your if...then logic, then return it after. You don't have to initialize the cell if you use var:

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

    var cell: UITableViewCell!

    if indexPath.row == 0 {
        cell = tableView.dequeueReusableCellWithIdentifier("imageCell", forIndexPath: indexPath) as UITableViewCell
        // ...
    } else if indexPath.row == 1 {
        cell = tableView.dequeueReusableCellWithIdentifier("segmentCell", forIndexPath: indexPath) as UITableViewCell
        // ...
    } else if indexPath.row == 2 {
        // ...
    } 

    return cell
}

(Just make sure you catch all cases - if your return cell without initializing you'll get a runtime error.)

Upvotes: 1

Istvan
Istvan

Reputation: 1627

You mustn't return nil from cellForRowAtIndexPath. You always should return a valid cell.

 var cell: UITableViewCell!

This line of code doesn't create any cell, it just a UITableViewCell variable with nil content, you have to assign a value to it:

var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
return cell

Upvotes: 2

Grimxn
Grimxn

Reputation: 22487

You are only declaring the last var cell: UITableViewCell! - you are not initialising it. You need to do something like

var cell = UITableViewCell(style: someStyle, reuseIdentifier: someID)
return cell

Upvotes: 1

Related Questions