55

Consider the following simple view controller:

class ViewController: UIViewController, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView!

    var items = ["One", "Two", "Three"]

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.registerClass(CustomTableViewCell.self, forCellReuseIdentifier: "customCell")
        self.tableView.dataSource = self
    }

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

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = self.tableView.dequeueReusableCellWithIdentifier("customCell") as CustomTableViewCell
        cell.titleLabel!.text = self.items[indexPath.row]
        return cell
    }
}

And custom cell view:

class CustomTableViewCell: UITableViewCell {
    @IBOutlet weak var titleLabel: UILabel!   
}

This code causes the following error.

fatal error: unexpectedly found nil while unwrapping an Optional value

titleLabel is nil — it's not clear why. Setting default properties of UITableViewCell (like textLabel) work just fine.

I'm using a nib for the custom cell.

Both the labels and table views are correctly connected to their IBOutlets.

label table view

Both the prototype cell and the custom nib view are marked as having a CustomTableViewCell class.

I'm new to iOS development, am I missing something obvious?

Sample Xcode project available.

pkamb
  • 33,281
  • 23
  • 160
  • 191
David Chouinard
  • 6,466
  • 8
  • 43
  • 61

6 Answers6

62

First off, you're using a nib file to load your custom cell into the table. That's probably going to be more of a headache than it's worth if you're new to Swift/Cocoa. I would move everything over to storyboard for the time being. Instead of using a nib file click, go to Storyboard, click on your UITableView and make sure the TableView's content setting is Dyanamic Prototypes:

enter image description here

Next, click on the prototype cell (the only one in the table view) and set the class to CustomTableViewCell and set its reuse identifier to customCell:

enter image description here enter image description here

Next, add a label to your prototype cell and link it to the IBOutlet in your CustomTableViewCell class. You don't need to register your customCell so long as you've set the reuse identifier in storyboard. Delete this line:

self.tableView.registerClass(CustomTableViewCell.self, forCellReuseIdentifier: "customCell")

and it should run.

kellanburket
  • 12,250
  • 3
  • 46
  • 73
  • I've commented out that line, the issue is still present. (there are no warnings and the reuse identifier is correctly set in the attributes inspector) – David Chouinard Mar 17 '15 at 14:22
  • Oh I see, you're using a nib for your CustomViewCell. You need to mention that in the question of it's misleading. Is there any reason you have for not just generating the cell in storyboard? – kellanburket Mar 17 '15 at 14:26
  • Oh, sorry, I assumed this was the only way to do it (following a tutorial). Updated the question. Happy to do it a different way. – David Chouinard Mar 17 '15 at 14:29
  • It's just more of a headache to do it with a nib file, I'll update my answer and show you how to do it in storyboard. – kellanburket Mar 17 '15 at 14:30
  • Just read your update. The whole point of custom cell is for a custom layout, how can I use interface builder to build my custom cell layout? I realize you can programmatically manipulate the layout, constraints, etc. but I'd much rather set that visually. – David Chouinard Mar 17 '15 at 15:05
  • You can do it just like you can in a nib file. What do you want it to do? – kellanburket Mar 17 '15 at 15:14
  • I spent a bit of time fiddling with your suggestions and things make *a lot* more sense now. I got rid of my nib and the custom cells work fine now. Thank you. – David Chouinard Mar 17 '15 at 15:35
  • For me ClassName.self did not work and only UINib() worked eg - tableView.register(UINib(nibName: reuseIdentifier, bundle: nil), forCellReuseIdentifier: reuseIdentifier) – Bishal Ghimire Apr 28 '17 at 08:00
  • 9
    I was coding the "register" and setting the reuse ID storyboard. This consistently returned NIL properties of the custom-cell (e.g., "label", "imageview", etc.). When I removed the register coding, it works as expected. – mobibob Aug 22 '17 at 20:56
  • 7
    The key is here is to link the cell view on the storyboard with reuse identifier and remove the line `collectionView.register(...)` – MK Yung Nov 18 '18 at 16:31
  • 1
    That's not a good answer because it doesn't answer the question. I don't want to know how do to it that way. I want to know how to do it the way the questioner asked, without being told 'it's a bad idea because I'm new to Swift.' I don't care about that. Who green checks these things when they don't answer the question? – johnrubythecat Jun 05 '19 at 21:26
44

try this

import UIKit

class ViewController: UIViewController, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView!

    var items = ["One", "Two", "Three"]

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.registerNib(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "customCell")// CustomTableViewCell.self, forCellReuseIdentifier: "customCell")
        self.tableView.dataSource = self
    }

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

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = self.tableView.dequeueReusableCellWithIdentifier("customCell", forIndexPath: indexPath) as CustomTableViewCell
        cell.titleLabel!.text = self.items[indexPath.row]
        return cell
    }
}
LDNZh
  • 1,136
  • 8
  • 14
12

Register your nib like this:

let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "PickerCell", bundle: bundle)
collectionViewPicker.register(nib, forCellWithReuseIdentifier: "cell")
Arnav
  • 668
  • 6
  • 14
6

You should use dequeueReusableCellWithIdentifier:forIndexPath to dequeue the cells.

If you are using storyboard to create the cells, you should not need to register the class for reuse as storyboard does it for you if you set the reuseIdentifier.

Rory McKinnel
  • 7,936
  • 2
  • 17
  • 28
  • That doesn't fix it, same issue. I've updated the sample XCode project in the question with your suggestion. – David Chouinard Mar 17 '15 at 14:24
  • Have you got `self.tableView.delegate = self` anywhere? Seems to be missing in `viewDidLoad`? You assign the dataSource delegate but not the delegate? – Rory McKinnel Mar 17 '15 at 14:30
  • Just looked at your source. You have no delegate as mentioned above and no methods for heightForRowAtIndexPath etc... This will not be helping things. – Rory McKinnel Mar 17 '15 at 15:13
  • I tried pulling the smallest subset of code that would replicate the issue from my main project. I do have the rest of the plumbing in my real project — is there any indication that these things are causing the issue? They're present in my main project. – David Chouinard Mar 17 '15 at 15:18
  • Not if they are present in the main code. Just noticed they were missing. Only other suggestion is what happens if you remove the "weak" statement from the IBOutlet? – Rory McKinnel Mar 17 '15 at 15:27
1

Please make sure you are not doing any mistake while registering your nib(custom cell) in the viewdidload. Nib name and reuseIdentifiers should not be wrong.

Narasimha Nallamsetty
  • 1,215
  • 14
  • 16
-10

Works for me without !

EDIT

    let challenge = self.challenges![indexPath.row]

    let title = challenge["name"]
    if title != nil {
        cell.titleLabel.text = title
    }
iOSfleer
  • 408
  • 6
  • 20