0

I have an UITextField in one of the UITableViewCell rows. When I select the row, the delegate methods of the text field are getting called. But, when I tap on the UITextField directly, the app crashes with the following error,

fatal error: unexpectedly found nil while unwrapping an Optional value

The app crashes only when the UITextField is tapped. The tableView is loaded correctly with the given data.

This is the code,

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "Cell")

    cell.detailTextLabel?.hidden = true

    let txtFld = UITextField()
    cell.contentView.addSubview(txtFld)
    txtFld.tintColor = appGlobals.appThemeColor
    txtFld.delegate = self
    txtFld.textColor = appGlobals.appThemeColor
    txtFld.returnKeyType = UIReturnKeyType.Done
    txtFld.keyboardType = UIKeyboardType.EmailAddress
    txtFld.autocorrectionType = UITextAutocorrectionType.No
    txtFld.autocapitalizationType = UITextAutocapitalizationType.None
    txtFld.translatesAutoresizingMaskIntoConstraints = false
    txtFld.text = self.appGlobals.getSelectedContactEmail()
    txtFld.tag = 99
    cell.addConstraint(NSLayoutConstraint(item: txtFld, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: cell.textLabel, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 15))
    cell.addConstraint(NSLayoutConstraint(item: txtFld, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.Width, multiplier: 1, constant: cell.frame.size.width - 30))
    cell.addConstraint(NSLayoutConstraint(item: txtFld, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.Height, multiplier: 1, constant: 20))
    cell.addConstraint(NSLayoutConstraint(item: txtFld, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: cell.textLabel, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 0))

    cell.selectionStyle = UITableViewCellSelectionStyle.None

    return cell
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let txtField = cell.viewWithTag(99) as! UITextField
    txtField.becomeFirstResponder()
}

And the UITextField Delegate methods,

func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
    return true
}

func textFieldDidBeginEditing(textField: UITextField) {
    NSLog("text field became first responder")
}

UPDATE1:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "Cell")

    cell.detailTextLabel?.hidden = true
    if(cell.contentView.viewWithTag(99) == nil){
        let txtFld = UITextField()
        cell.contentView.addSubview(txtFld)
        txtFld.tintColor = appGlobals.appThemeColor
        txtFld.delegate = self
        txtFld.textColor = appGlobals.appThemeColor
        txtFld.returnKeyType = UIReturnKeyType.Done
        txtFld.keyboardType = UIKeyboardType.EmailAddress
        txtFld.autocorrectionType = UITextAutocorrectionType.No
        txtFld.autocapitalizationType = UITextAutocapitalizationType.None
        txtFld.text = self.appGlobals.getSelectedContactEmail()
        txtFld.tag = 99

        cell.selectionStyle = UITableViewCellSelectionStyle.None

        return cell
    }
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    if let txtField = cell.contentView.viewWithTag(99) as? UITextField {
        txtField.becomeFirstResponder()
    }
}

UPDATE2:

This is the custom tableview cell's code,

import UIKit

class TxtFieldTableViewCell: UITableViewCell {

let appGlobals = AppGlobals()

let txtFld = UITextField(frame: CGRectMake(15, 27, AppGlobals().getScreenWidth() - 30, 20))

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)

    txtFld.tintColor = appGlobals.appThemeColor
    txtFld.textColor = appGlobals.appThemeColor
    txtFld.returnKeyType = UIReturnKeyType.Done
    txtFld.autocorrectionType = UITextAutocorrectionType.No
    txtFld.autocapitalizationType = UITextAutocapitalizationType.None
    self.addSubview(txtFld)
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func awakeFromNib() {
    super.awakeFromNib()
}

override func setSelected(selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
}

}

And I've used it in my code like this,

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

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

    if(cell!.viewWithTag(99) == nil){
        cell!.txtFld.delegate = self
        cell!.txtFld.keyboardType = UIKeyboardType.EmailAddress
        cell!.txtFld.text = self.appGlobals.getSelectedContactEmail()
        cell!.txtFld.tag = 99
        cell!.selectionStyle = UITableViewCellSelectionStyle.None
    }
}

Still getting the same error.

  • at which line does the crash occur? – Ramy Kfoury Jan 22 '16 at 12:18
  • I suspect that text field is not properly initiated UITextField(). Try to init it with some frame, like UITextField(frame: someRect) – Prcela Jan 22 '16 at 12:25
  • Also try cell.contentView.viewWithTag(99). Note that txtField is subview of contentView, not a cell. – Prcela Jan 22 '16 at 12:28
  • This code is non-optimal. Every time `cellForRowAtIndexPath` is called, you are creating a new text field and adding it into a table view cell... and if you are scrolling that table view, cells get reused, text fields get added over and over again into the same (recycled) cell. You should probably come up with a different way to add in your text field (e.g. a subclassed UITableViewCell?). – Michael Dautermann Jan 22 '16 at 12:29
  • There is no particular line where the crash occurs. If the UITextField is tapped directly, then the app crashes. @RamyKfoury – Deepika Masilamani Jan 22 '16 at 13:28
  • It seems the problem is not with the didSelectRowAtIndexPath. This delegate is not even called, when I tap on the UITextField. @Prcela – Deepika Masilamani Jan 22 '16 at 13:31
  • I have initialized the UITextField with a frame and I am adding the textfield to the cell, only when it is not added to the cell. But still, the app crashes. @Prcela – Deepika Masilamani Jan 22 '16 at 13:33
  • have you implemented the UITextField delegate methods? – Ramy Kfoury Jan 22 '16 at 13:36
  • Yes.. But the delegate methods are not even called. – Deepika Masilamani Jan 22 '16 at 13:37
  • The proper way should be that you have a CustomTableViewCell: UITableviewCell. Inside your storyboard choose this custom class and connect the text field as outlet property. Then, inside the source you cast to that custom class: let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! CustomTableviewClass – Prcela Jan 22 '16 at 13:41
  • I am not using storyboard in my project. I will try to subclass the uitableviewcell programmatically and let you know the result. – Deepika Masilamani Jan 22 '16 at 13:43
  • I have updated my code as per your suggestion. Still app crashes :( @Prcela – Deepika Masilamani Jan 22 '16 at 14:35
  • Possible duplicate of [Fatal error: unexpectedly found nil while unwrapping an Optional values](http://stackoverflow.com/questions/24643522/fatal-error-unexpectedly-found-nil-while-unwrapping-an-optional-values) – GoZoner Jan 22 '16 at 15:04
  • My problem is different from the problem in the mention link. In my case, the problem occurs only when I tap on the UITextField. The tableView is displayed in the view without any problem. – Deepika Masilamani Jan 22 '16 at 15:11

2 Answers2

0

The only place in your piece of code where your error could occur is this

let txtField = cell.viewWithTag(99) as! UITextField
txtField.becomeFirstResponder()

Please change it to the following and try it again

if let txtField = cell.contentView.viewWithTag(99) as? UITextField {
    txtField.becomeFirstResponder()
}

If this solves the Problem, your TextField is not initiated well... Try to give it a frame like

txtFld.frame = CGRectMake(0,0,40,40)

before you add it as a subview

Also look at Michael Dautermanns comment... TableViewCells are reused... So you need to check if there already is a subview with tag 99, and only add a new subview, if there is no subview with tag 99

Dennis Weidmann
  • 1,932
  • 1
  • 12
  • 16
0

I have found the reason why my app was crashing. I was using an uninitialised variable in textFieldShouldBeginEditing. That variable was initialised in the didSelectRowAtIndexPath method.

I've declared a variable like this,

var selectedIndex: NSIndexPath!

And I've initialised it like this,

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let cell = tableView.cellForRowAtIndexPath(indexPath)! as UITableViewCell
    selectedIndex = indexPath
    if let txtField = cell.viewWithTag(99) as? UITextField {
        txtField.becomeFirstResponder()
    }
}

And the variable was used like this,

func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
    if(selectedIndex.section != 0){
          //Some Operation
    }
}

Since selectedIndex was not initialised when I tap on the text field directly, my app crashed. That was the problem.

Thank you @Michael Dautermann and @Neo for suggesting optimal solutions.