Reputation: 597
I'm getting an error message "Unexpectedly found nil
while unwrapping an Optional
" from Swift, with the below class. The error occurs on the line:
(cell.contentView.viewWithTag(1) as UILabel).text = object["firstName"] as? String
I have a custom cell class with 2 UILabels tagged 1 and 2, the outlets are set
import UIKit
import Foundation
class dictionaryTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
var objects = NSMutableArray()
var dataArray = [["firstName":"Debasis","lastName":"Das","email":"[email protected]"],["firstName":"John","lastName":"Doe","email":"[email protected]"],["firstName":"Jane","lastName":"Doe","email":"[email protected]"],["firstName":"Mary","lastName":"Jane","email":"[email protected]"]]
@IBOutlet
var tableView: UITableView!
var items: [String] = ["We", "Heart", "Swift"]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count;//self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
//cell.textLabel?.text = self.items[indexPath.row]
//return cell
let cell = tableView.dequeueReusableCellWithIdentifier("MyCell", forIndexPath: indexPath) as UITableViewCell
let object = dataArray[indexPath.row] as NSDictionary
(cell.contentView.viewWithTag(1) as UILabel).text = object["firstName"] as? String
(cell.contentView.viewWithTag(2) as UILabel).text = object["lastName"] as? String
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
}
Upvotes: 8
Views: 17462
Reputation: 437432
If you want to use a custom cell layout, I would suggest
Subclass UITableViewCell
:
// CustomTableViewCell.swift
import UIKit
class CustomTableViewCell: UITableViewCell {
@IBOutlet weak var firstNameLabel: UILabel!
@IBOutlet weak var lastNameLabel: UILabel!
}
Create table view in storyboard. Add cell prototype to tableview in storyboard. Design that cell prototype (adding whatever labels you want).
Specify the storyboard identifier for the cell prototype to be whatever you want (MyCell
in your example).
Specify the base class for the cell prototype to be, in this example, CustomTableViewCell
:
Hook up the IBOutlet
references between your cell prototype and your UITableViewCell
subclass.
Notice the solid bullets to the left of the IBOutlet
references. That indicates that the outlet was hooked up correctly.
Implement cellForRowAtIndexPath
in your data source, e.g. in Swift 3:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! CustomTableViewCell
let person = dataArray[indexPath.row]
cell.firstNameLabel.text = person["firstName"]
cell.lastNameLabel.text = person["lastName"]
return cell
}
Or in Swift 2:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCell", forIndexPath: indexPath) as! CustomTableViewCell
let person = dataArray[indexPath.row]
cell.firstNameLabel.text = person["firstName"]
cell.lastNameLabel.text = person["lastName"]
return cell
}
Note, if using cell prototypes, you do not want to call registerClass
in viewDidLoad
. The storyboard will automatically register your custom class with the reused identifier for you (because of what you have done in steps 3 and 4).
Upvotes: 23
Reputation: 100
All I had to do was:
cell.subviews[0].subviews[0].viewWithTag(//your tag//)
Upvotes: 0
Reputation: 3485
EDIT2: This solution is maybe better for your purpose. With this you create your own class for your own UITableViewCell and use that with reusing pattern. Comments are in the code:
class dictionaryTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
// The data
var dataArray = [["firstName":"Debasis","lastName":"Das","email":"[email protected]"],["firstName":"John","lastName":"Doe","email":"[email protected]"],["firstName":"Jane","lastName":"Doe","email":"[email protected]"],["firstName":"Mary","lastName":"Jane","email":"[email protected]"]]
// The outlet to your tableview in the ViewController in storyboard
@IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Create a nib for reusing
let nib = UINib(nibName: "MyCell", bundle: nil)
// Register the nib for reusing within the tableview with your reuseidentifier
tableView.registerNib(nib, forCellReuseIdentifier: "MyCell")
// Set delegate and datasource to self (you can do that in interface builder as well
tableView.delegate = self
tableView.dataSource = self
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count; // return the number of datasets
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// create a reusable cell with the reuse identifier you registered in viewDidLoad and cast it to MyCell
let cell = tableView.dequeueReusableCellWithIdentifier("MyCell") as MyCell
let object = dataArray[indexPath.row] as NSDictionary
// set the strings matching your array
cell.label1.text = object["firstName"] as? String
cell.label2.text = object["lastName"] as? String
// return the cell
return cell
}
// some example for the delegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
}
// class for you own UITableViewCell
// has own xib file
class MyCell: UITableViewCell {
// two outlets representing two labels in the xib file
@IBOutlet private weak var label1: UILabel!
@IBOutlet private weak var label2: UILabel!
}
For this to work you have to create a xib-file with the right name (in this case MyCell). It should something similar to this:
It is very important to set the class of the custom view and the outlets.
------------------------------------- OTHER SOLUTION --------------------------------------
EDIT: As I see it you don't initialize your own class ´MyCell´ but Apple's class ´UITableViewCell´ with the reuseIdentifier ´MyCell´. So that is the solution for using UITableViewCell:
The line (cell.contentView.viewWithTag(1) as UILabel)
is not only a cast to a specific class it is also unwrapping the Optional (UIView). You do that in other cases with '!'.
So the problem is: you are creating an instance of UITableViewCell, a class Apple created. You want to get subviews inside of that view with specific tags. How do you know apple gave them these tags? What you really want is creating an instance of UITableViewCell with a special style and set the values of the textLabels like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Create an instance of UITableViewCell. Style is .Subtitle but could be others as well
var cell = tableView.dequeueReusableCellWithIdentifier("MyCell") as? UITableViewCell
let object = dataArray[indexPath.row] as NSDictionary
// set the strings matching your array
cell?.textLabel?.text = object["firstName"] as? String
cell?.detailTextLabel?.text = object["lastName"] as? String
// return the cell
return cell!
}
Upvotes: 5