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)!")
}
}
In order to access an optional's value (if it has one at all), you need to unwrap it. An optional value can be unwrapped safely or forcibly. If you force-unwrap an optional, and it didn't have a value, your program will crash with the above message. Xcode will show you the crash by highlighting a line of code.
99% of the time the above error is caused by a force-unwrapped optional that is nil. You can fix it by avoiding it becomes nil, which is sometimes not possible, so then you use optional binding or optional chaining. Avoid using implicitly unwrapped optionals when you can, unless you have a specific need to use it.
Unwrap an optional type with the nil coalescing operator If a nil value is found when an optional value is unwrapped, an additional default value is supplied which will be used instead. You can also write default values in terms of objects.
It means that something was nil where it should not be. how I can fix the error. Check all the place you use Implicitly Unwrapped Optional type (!) and forced unwrapping (!). In your code shown, there are no forced unwrappings, so you may have some properties with Implicitly Unwrapped Optional type.
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).
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!
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With