61

I'm struggling to figure out what's wrong with this code snippet. This is currently working in Objective-C, but in Swift this just crashes on the first line of the method. It shows an error message in console log: Bad_Instruction.

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!  {
        var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell

        if (cell == nil) {
            cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "Cell")
        }

        cell.textLabel.text = "TEXT"
        cell.detailTextLabel.text = "DETAIL TEXT"

        return cell
    }
Brian Tompsett - 汤莱恩
  • 5,195
  • 62
  • 50
  • 120
DBoyer
  • 3,012
  • 4
  • 20
  • 32

17 Answers17

82

Also see matt's answer which contains the second half of the solution

Let's find a solution without creating custom subclasses or nibs

The real problem is in the fact that Swift distinguishes between objects that can be empty (nil) and objects that can't be empty. If you don't register a nib for your identifier, then dequeueReusableCellWithIdentifier can return nil.

That means we have to declare the variable as optional:

var cell : UITableViewCell?

and we have to cast using as? not as

//variable type is inferred
var cell = tableView.dequeueReusableCellWithIdentifier("CELL") as? UITableViewCell

if cell == nil {
    cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL")
}

// we know that cell is not empty now so we use ! to force unwrapping but you could also define cell as
// let cell = (tableView.dequeue... as? UITableViewCell) ?? UITableViewCell(style: ...)

cell!.textLabel.text = "Baking Soda"
cell!.detailTextLabel.text = "1/2 cup"

cell!.textLabel.text = "Hello World"

return cell
Community
  • 1
  • 1
Sulthan
  • 118,286
  • 20
  • 194
  • 245
  • 1
    Thanks for your answer! I was struggling to understand what 'optionals' were in Swift since your right, creating a subclass of a table view cell with no customization is pointless. I will definitely try this! – DBoyer Jun 04 '14 at 22:18
  • @Sulthan I'm using code identical to yours but I'm getting an EXC_BAD_INSTRUCTION on the dequeueReusableCellWithIdentifier line. Any idea why? –  Jun 05 '14 at 12:44
  • @kmcgrady There must be a `nil` somewhere where it is not expected. I have just replaced `self.tableView` with `tableView` (parameter `tableView` can't be `nil` but `self.tableView` could be). Make sure you have `as?` and not `as`. – Sulthan Jun 05 '14 at 12:50
  • @Sulthan Thanks for the reply. You were right it was a 'nil' issue. The text I was setting for me label was getting nil from the dictionary. Thanks! –  Jun 05 '14 at 13:40
  • You can no longer `if !cell` because Optionals do not conform to `BooleanType`. I went: `if cell == nil`. – huwr Nov 18 '14 at 00:58
  • After trying very hard from many SO posts, i found it working when i put `cell != nil` for a similar issue like above. Why is it so? – iamprem Apr 08 '15 at 20:41
  • 1
    This method expects a UITableViewCell to be returned. Not an optional. So are you meant to force unwrap it when you return? – Daniel Wood Aug 07 '15 at 15:09
  • shows warning in swift 3: conditional downcast from 'UITableViewCell?' to 'UITableViewCell' does nothing – Abdul Hannan Jul 24 '17 at 14:06
64

Sulthan's answer is clever, but the real solution is: don't call dequeueReusableCellWithIdentifier. That was your mistake at the outset.

This method is completely outmoded, and I'm surprised it has not been formally deprecated; no system that can accommodate Swift (iOS 7 or iOS 8) needs it for any purpose whatever.

Instead, call the modern method, dequeueReusableCellWithIdentifier:forIndexPath:. This has the advantage that no optionals are involved; you are guaranteed that a cell will be returned. All the question marks and exclamation marks fall away, you can use let instead of var because the cell's existence is guaranteed, and you're living in a convenient, modern world.

You must, if you're not using a storyboard, register the table for this identifier beforehand, registering either a class or a nib. The conventional place to do that is viewDidLoad, which is as early as the table view exists at all.

Here's an example using a custom cell class:

override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.registerClass(MyCell.self, forCellReuseIdentifier: "Cell")
}

// ...

override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath:indexPath) as MyCell
    // no "if" - the cell is guaranteed to exist
    // ... do stuff to the cell here ...
    cell.textLabel.text = // ... whatever
    // ...
    return cell
}

But if you're using a storyboard (which most people do), you don't even need to register the table view in viewDidLoad! Just enter the cell identifier in the storyboard and you're good to go with dequeueReusableCellWithIdentifier:forIndexPath:.

matt
  • 447,615
  • 74
  • 748
  • 977
  • 3
    If the reusableCell is called "Cell" then where is the MyCell coming from in your example? – JoshL Aug 22 '14 at 00:10
  • @JoshL Your question makes no sense. The cell reuse identifier and the cell class have nothing to do with one another. – matt Aug 22 '14 at 03:47
  • This should be marked the correct answer. @Sulthan's answer is where I was previously, struggling to shoehorn an dequeue method that returned an optional into a datasource method that returned a non-optional. This solution makes all that go away. – Echelon Oct 28 '15 at 10:30
18

@Sulthan's answer is spot on. One possible convenience modification would be to cast the cell as a UITableViewCell!, rather than a UITableViewCell.

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
    var cell = tableView.dequeueReusableCellWithIdentifier("CELL") as UITableViewCell!
    if !cell {
        cell = UITableViewCell(style:.Default, reuseIdentifier: "CELL")
    }
    // setup cell without force unwrapping it
    cell.textLabel.text = "Swift"
    return cell
}

Now, you can modify the cell variable without force unwrapping it each time. Use caution when using implicitly unwrapped optionals. You must be certain that the value you are accessing has a value.

For more information, refer to the "Implicitly Unwrapped Optionals" section of The Swift Programming Language.

aselvia
  • 181
  • 3
8

Try this:

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
    cell.textLabel.text = "\(indexPath.row)"

    return cell
}

Note that you should register you UITableViewCell and ID when creating instantiating your UITableView:

tableView.delegate = self
tableView.dataSource = self
tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: "Cell")
Oscar Swanros
  • 19,010
  • 5
  • 30
  • 48
  • 1
    That works, so it definately gets me closer, but this requires you to register a cell since you can't assign to cell if you use 'let' and registering a cell prevents that ability to use CellStyleValue1 – DBoyer Jun 03 '14 at 18:59
  • 2
    If you want a to change the cell style. You need to just create a custom cell subclass and use the style you want in the init method. – CW0007007 Jun 03 '14 at 19:07
  • 1
    For me, different cell styles assumes different behaviour for each one, thus creating a custom `UITableViewCell` subclass makes more sense to me. – Oscar Swanros Jun 03 '14 at 19:11
  • 2
    Alright that makes sense to me. You were right It did work perfectly, luckily swift makes it a lot easier to sneak another subclass into the same file – DBoyer Jun 03 '14 at 19:12
  • 3
    Yes! You don't have to import any of your custom classes anymore! It's beautiful :-) – Oscar Swanros Jun 03 '14 at 19:13
  • @DBoyer Would you mind posting what you wrote to solve this problem please, and how you created a custom UITableViewCell subclass to use a custom style, please? It would be greatly appreciated :) – WunDaii Jun 04 '14 at 01:01
  • -1. No explanation or solution for the problem. Just a workaround. – Sulthan Jun 04 '14 at 12:05
8

Here is what I wrote to get it working...

First Register the table view cell with the table view

self.tableView.registerClass(MyTableViewCell.self, forCellReuseIdentifier: "Cell")

Then configure cellForRowAtIndexPath

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!  {
    var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as MyTableViewCell

    cell.textLabel.text = "Cell Text"
    cell.detailTextLabel.text = "Cell Detail Text in Value 1 Style"

    return cell
}

I then defined a custom cell subclass write at the bottom of the file (since its so much easier now)

class MyTableViewCell : UITableViewCell {

    init(style: UITableViewCellStyle, reuseIdentifier: String!) {
        super.init(style: UITableViewCellStyle.Value1, reuseIdentifier: reuseIdentifier)
    }

}
DBoyer
  • 3,012
  • 4
  • 20
  • 32
6

Here is a simple way to define table cell in swift 2:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let identifier = "cell"
    let cell = tableView.dequeueReusableCellWithIdentifier(identifier) ??
        UITableViewCell.init(style: UITableViewCellStyle.Default, reuseIdentifier: identifier)
    cell.textLabel!.text = "my text"
    return cell
}

Swift 3:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let identifier = "cell"
    let cell = tableView.dequeueReusableCell(withIdentifier: identifier) ??
        UITableViewCell(style: .default, reuseIdentifier: identifier)
    cell.textLabel!.text = "my text"
    return cell
}
david72
  • 6,327
  • 2
  • 31
  • 54
4

There's a few answers here, but I don't think any of them are ideal, because after the declaration you're ending up with an optional UITableViewCell, which then needs a cell!... in any declarations. I think this is a better approach (I can confirm this compiles on Xcode 6.1):

var cell:UITableViewCell

if let c = tableView.dequeueReusableCellWithIdentifier("cell") as? UITableViewCell {
    cell = c
}
else {
    cell = UITableViewCell()
}
2

Well, I have done this way:

Steps for UITableView using Swift:

  • Take UITableView in ViewController
  • Give Referencing Outlets in ViewController.swift class
  • Give Outlets dataSource & delegate to ViewController

Now Swift code in ViewController.swift class:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var mTableView: UITableView!

    var items: [String] = ["Item 1","Item 2","Item 3", "Item 4", "Item 5"]

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.mTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.items.count;
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell:UITableViewCell = self.mTableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell

        cell.textLabel?.text = self.items[indexPath.row]
         println(self.items[indexPath.row])

        return cell
    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        println("You have selected cell #\(indexPath.row)!")
    }
}

Now it's time to Run your program.

Done

Hiren Patel
  • 48,538
  • 20
  • 161
  • 144
  • i get an error about not having "override" before "func" in each of these. i guess because I'm using a UITableViewController, not UITableViewDelegate. – Randy L Aug 28 '15 at 16:24
  • @the0ther, yes. can you please follow my steps for the same. – Hiren Patel Aug 28 '15 at 16:45
1

Actually in the Apple's TableView Guide document and Sample Code you will find the sentence below:

If the dequeueReusableCellWithIdentifier: method asks for a cell that’s defined in a storyboard, the method always returns a valid cell. If there is not a recycled cell waiting to be reused, the method creates a new one using the information in the storyboard itself. This eliminates the need to check the return value for nil and create a cell manually.

So,we could just code like this:

var identifer: String = "myCell"
var cell = tableView.dequeueReusableCellWithIdentifier(identifer) as UITableViewCell
cell.textLabel.text = a[indexPath.row].name
cell.detailTextLabel.text = "detail"

I think this is a suitable way to use tableView

S.Captain
  • 167
  • 1
  • 9
  • This is correct, but unless you specify that the cell style is `subtitle` you'll never get the `detailLabel` to show up. So, how to tell Xcode to use `tableView.dequeueReusableCellWithIdentifier` and `subtitle` style? – kakubei Aug 05 '15 at 11:24
1

Using "as" keyword would do the following two steps:
1.creating a optional value which wrap a variable of UITableViewCell;
2.unwrapping the optional value.

So,by doing this

var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Component") as UITableViewCell

you would get a "plain" UITableViewCell type variable: cell.Theoretically speaking, it's ok to do this.But the next line

if (cell == nil) {}

makes trouble, because in swift, only the optional value can be assigned with nil.

So, to solve this problem, you have to make cell a variable of Optional type. just like this:

var cell = tableView.dequeueReusableCellWithIdentifier("Component") as? UITableViewCell

using the keyword "as?" would create a Optional variable, and this, undoubtedly, can be assigned with nil.

li_shengdm
  • 43
  • 5
0

For cell template:

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
        let myCell : youCell =  youCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
        return myCell
    }
Lola
  • 2,401
  • 6
  • 15
  • 44
0

bro, please take a look at the sample https://github.com/brotchie/SwiftTableView

haithngn
  • 694
  • 9
  • 28
0

Why not this?

(please delete if i am not in the goal...)

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

    if let cell: UITableViewCell = theTableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as? UITableViewCell {
        // cell ok
    }else{
       // not ok
    }
}
Mk3d
  • 88
  • 5
0

I have done in following way: to show detailTextLabel. text value

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

    let CellIdentifier: String = "cell"

    var cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier) as? UITableViewCell

    if cell == nil {
        cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: CellIdentifier)
    }

    //cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator

    // parse the value of records
    let dataRecord = self.paymentData[indexPath.row] as! NSDictionary

    let receiverName = dataRecord["receiver_name"] as! String
    let profession = dataRecord["profession"] as! String
    let dateCreated = dataRecord["date_created"] as! String
    let payAmount = dataRecord["pay_amount"] as! String

    println("payment \(payAmount)")
    cell!.textLabel?.text = "\(receiverName)\n\(profession)\n\(dateCreated)"
    cell!.detailTextLabel?.text = "$\(payAmount)"
    cell!.textLabel?.numberOfLines = 4

    return cell!

}// end tableview
Vinod Joshi
  • 7,094
  • 46
  • 49
0

UITableView Demo using Playground

//: Playground - noun: a place where people can play

import UIKit
import PlaygroundSupport

class TableviewDemoDelegate:NSObject,UITableViewDataSource,UITableViewDelegate {


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }

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

        var cell:UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath)

        if cell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
        }


        cell?.textLabel?.text = "Item \(indexPath.row+1)"

        return cell!
    }


    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("You have selected cell #\(indexPath.row)!")

    }
}

var tableView = UITableView(frame:CGRect(x: 0, y: 0, width: 320, height: 568), style: .plain)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")

let delegate = TableviewDemoDelegate()
tableView.delegate = delegate
tableView.dataSource = delegate


PlaygroundPage.current.liveView = tableView
Eric Aya
  • 68,765
  • 33
  • 165
  • 232
Kirit Vaghela
  • 12,245
  • 4
  • 72
  • 79
0

I went through your codes and most probably the reason for the crash is you are trying to typecast an optional value which is not assigned

Now consider the line of code below

var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell

When there are no cells in the tableview you are still trying to typecast as UITableView.When the compiler tries to typecast nil value you face this issue

The correct statement should be

var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") 

You can use if else statement to typecast for values which holds

Uma Madhavi
  • 4,601
  • 5
  • 34
  • 70
-3

Try this code

var cell:CustomTableViewCell = tableView.dequeueReusableCellWithIdentifier("CustomTableViewCell") as CustomTableViewCell
cell.cellTitle.text="vijay"

https://github.com/iappvk/TableView-Swift

Vijay
  • 7
  • 1
  • 3