How to Set UITableViewCellStyleSubtitle and dequeueReusableCell in Swift?

IosUitableviewSwift

Ios Problem Overview


I'd like a UITableView with subtitle-style cells that use dequeueReusableCellWithIdentifier.

My original Objective-C code was:

static NSString *reuseIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if(!cell)
{
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
}

After searching the few UITableView questions here already on SO, I thought to write it in Swift like so:

tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: "Cell")

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

But that doesn't let me say I want a subtitle style. So I tried this:

var cell :UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "Cell")

Which gives me a subtitle cell, but it doesn't let me dequeueReusableCellWithIdentifier.

I've researched some more and looked at this video tutorial, but he creates a separate subclass of UITableViewCell which I assume is unnecessary as I accomplished this same effect previously in Obj-C.

Any ideas? Thanks.

Ios Solutions


Solution 1 - Ios

Keep in mind that UITableView is defined as an optional in the function, which means your initial cell declaration needs to check for the optional in the property. Also, the returned queued cell is also optional, so ensure you make an optional cast to UITableViewCell. Afterwards, we can force unwrap because we know we have a cell.

var cell:UITableViewCell? = 
tableView?.dequeueReusableCellWithIdentifier(reuseIdentifier) as? UITableViewCell
if (cell == nil)
{
   cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, 
                reuseIdentifier: reuseIdentifier)
}
// At this point, we definitely have a cell -- either dequeued or newly created,
// so let's force unwrap the optional into a UITableViewCell
cell!.detailTextLabel.text = "some text"

return cell

Solution 2 - Ios

If you'd rather avoid optionality, you can make a subclass of UITableViewCell that looks something like this:

class SubtitleTableViewCell: UITableViewCell {

	override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
		super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
	}

	required init?(coder aDecoder: NSCoder) {
		fatalError("init(coder:) has not been implemented")
	}
}

Then register it using:

override func viewDidLoad() {
	super.viewDidLoad()
	self.tableView.register(SubtitleTableViewCell.self, forCellReuseIdentifier: reuseIdentifier)
}

This allows your cell customization code to be really nice:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

	let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)

	cell.textLabel?.text = "foo"
	cell.detailTextLabel?.text = "bar"

	return cell
}

Solution 3 - Ios

Basically the same as other answers, but I get around dealing with nasty optionals (you can't return nil from -tableView:cellForRow:atIndexPath: in Swift) by using a computed variable:

Swift 3

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell: UITableViewCell = {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell") else {
            // Never fails:
            return UITableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: "UITableViewCell")
        }
        return cell
    }()

    // (cell is non-optional; no need to use ?. or !)

    // Configure your cell:
    cell.textLabel?.text       = "Key"
    cell.detailTextLabel?.text = "Value"

    return cell
}

Edit:

Actually, it would be better to dequeue the cell using: tableView.dequeueReusableCell(withIdentifier:for:) instead.

This later variant of the function automatically instantiates a new cell if no one is available for reusing (exactly what my code does explicitly above), and therefore never returns nil.

Solution 4 - Ios

Just building upon memmons' answer by cleaning it up Swift 2 style...

let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) ?? UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: reuseIdentifier)

cell.detailTextLabel?.text = "some text"

return cell

Swift 3:

let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) ?? UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier)

cell.detailTextLabel?.text = ""

return cell

Solution 5 - Ios

Since tableView.dequeueReusableCell(withIdentifier:, for:) return a non-nil cell, the if cell == nil check is always be false. But I found a solution, to makes default style cell become what style(value1, value2 or subtitle) you want, because default style cell's detailTextLabel is nil, so check the detailTextLabel if it's nil, then create new style cell, and give it to dequeue cell, like:

Swift 3:

var cell = tableView.dequeueReusableCell(withIdentifier: yourCellReuseIdentifier, for: indexPath)

if cell.detailTextLabel == nil {
    cell = UITableViewCell(style: .value1, reuseIdentifier: repeatCellReuseIdentifier)
}

cell.textLabel?.text = "Title"
cell.detailTextLabel?.text = "Detail"

return cell

That's works for me.

Hope it's help.

Solution 6 - Ios

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let reuseIdentifier = "cell"
    var cell:UITableViewCell? = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) as UITableViewCell?
    if (cell == nil) {
        cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: reuseIdentifier)
    }
    cell!.textLabel?.text = self.items[indexPath.row]
    cell!.detailTextLabel?.text = self.items[indexPath.row]
    return cell!
}

Solution 7 - Ios

You can use a slightly different syntax than the one from memmons to prevent the forced unwrapping:

let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) as? UITableViewCell ?? UITableViewCell(style: .Subtitle, 
            reuseIdentifier: reuseIdentifier)

cell.detailTextLabel?.text = "some text"

return cell

This is using XCode 6.1 7, Swift 1.2 2.0 syntax where UITableView is no longer an optional.

Solution 8 - Ios

If you are not using your own Custom Cell. Simply register UITableViewCell through code. Then you can prefer code.

Else select storyboard, select your TableViewCell -> Goto Attribute Inspector and choose the desired style as shown below.

enter image description here

Solution 9 - Ios

Make sure that you are not registering any cell to the tableview.

If you did so, dequeueReusableCellWithIdentifier will always give a non optional cell so UITableViewCellStyle.Subtitle will never initiate.

Solution 10 - Ios

Perfect as suggested by Michael G. Emmons, but in Xcode 6.1 using

if !cell { .....

I get this error:

> Optional type '@|value UITableViewCell?' cannot be used as boolean ; > test for '!= nil' instead

The accepted syntax is:

if cell == nil { ...

Solution 11 - Ios

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath:
 NSIndexPath) -> UITableViewCell {  
     var CellIdentifier:String = "Cell"  
     var cell:UITableViewCell? = tableView.dequeueReusableCellWithIdentifier(CellIdentifier) as? UITableViewCell  
     if cell == nil {  
        cell = UITableViewCell(style:UITableViewCellStyle(rawValue:3)!,reuseIdentifier:CellIdentifier)   
      }
    return cell!
}

Solution 12 - Ios

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    
    var cell:UITableViewCell? =
        tableView.dequeueReusableCell(withIdentifier: "cell")
    if (cell != nil)
    {
        cell = UITableViewCell(style: UITableViewCellStyle.subtitle,
                               reuseIdentifier: "cell")
    }
    cell!.textLabel?.text = "ABC"
    cell!.detailTextLabel?.text = "XYZ"
    
    return cell!
    
  }

Solution 13 - Ios

I engage you to look at this little UITableView-Example on Github: https://github.com/YANGReal/UITableView-Swift

They do like follows:

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
   let cell = tableView .dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
   cell.textLabel.text = String(format: "%i", indexPath.row+1)
   // set any other property of your cell here
   return cell
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWunDaiiView Question on Stackoverflow
Solution 1 - IosmemmonsView Answer on Stackoverflow
Solution 2 - IosJeremy MasselView Answer on Stackoverflow
Solution 3 - IosNicolas MiariView Answer on Stackoverflow
Solution 4 - IosjonlambertView Answer on Stackoverflow
Solution 5 - IosMeilbnView Answer on Stackoverflow
Solution 6 - IosGeoffreyView Answer on Stackoverflow
Solution 7 - IoslammertView Answer on Stackoverflow
Solution 8 - IosSudhi 9135View Answer on Stackoverflow
Solution 9 - IosMuraliView Answer on Stackoverflow
Solution 10 - IosGiorgio CalzolatoView Answer on Stackoverflow
Solution 11 - IosAmit Jagesha シView Answer on Stackoverflow
Solution 12 - IosiOS LifeeView Answer on Stackoverflow
Solution 13 - IoszomaView Answer on Stackoverflow