Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView in Swift

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     } 
like image 532
DBoyer Avatar asked Jun 03 '14 18:06

DBoyer


People also ask

What is table View Controller in Swift?

The TableViewController provides the tableview property to access the tableview in the storyboard. It conforms to the UITableViewDelegate and UITableViewDatasource protocol by default. The subclass overrides the delegate and DataSource methods to provide the tableview implementation.

What is UITableView delegate?

Methods for managing selections, configuring section headers and footers, deleting and reordering cells, and performing other actions in a table view.

What is the difference between UITableView and UICollectionView?

Tableiw is a simple list, which displays single-dimensional rows of data. It's smooth because of cell reuse and other magic. 2. UICollectionView is the model for displaying multidimensional data .


2 Answers

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 
like image 95
Sulthan Avatar answered Sep 18 '22 07:09

Sulthan


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:.

like image 40
matt Avatar answered Sep 19 '22 07:09

matt