coopwatts coopwatts - 1 month ago 9
iOS Question

Programmatically added UITextField - Keyboard causes app to crash on first key press - Swift

I have a "TextInputCell" class that inherits from UITableViewCell and represents a UITableViewCell that contains a UITextField. When using this class in a View Controller to add a "TextInputCell" to a row of a UITableView, I set the delegate to the View Controller itself, and the delegate functions are empty (besides return statements) - which I will post below. When running the app the Text Fields appear exactly as I want them do, and allow me to click in the text field and the keyboard pops up, however the second I press any key on the keyboard the app crashes with error

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull length]: unrecognized selector sent to instance 0x110277af0'
. I tried just adding a regular UITextField to a cell without the class I made and I get the same error. Does anyone know why this is happening or how I can fix it?

TextInputCell class:

//
// TextInputCell.swift
// AdminApp
//
// Created by Cooper Watts on 2016-01-27.
// Copyright © 2016 DefineYoga. All rights reserved.

//
// Based on the DatePickerCell.swift Created by Dylan Vann

import Foundation
import UIKit

/**
* Text Input Cell for table views.
*/
public class TextInputCell: UITableViewCell {

var textField: UITextField!

/**
Creates the TextInputCell

- parameter style: A constant indicating a cell style. See UITableViewCellStyle for descriptions of these constants.
- parameter reuseIdentifier: A string used to identify the cell object if it is to be reused for drawing multiple rows of a table view. Pass nil if the cell object is not to be reused. You should use the same reuse identifier for all cells of the same form.

- returns: An initialized TextInputCell object or nil if the object could not be created.
*/
override public init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

setup()
}

private func setup() {
// The InputField overhangs the view slightly to avoid invalid constraints.
self.clipsToBounds = true

textField = UITextField(frame: CGRect(x: 0, y: 15, width: self.contentView.frame.size.width - 15.0, height: self.contentView.frame.size.height))

textField.placeholder = "PlaceHolder"

textField.autoresizingMask = [.FlexibleRightMargin, .FlexibleLeftMargin, .FlexibleBottomMargin, .FlexibleTopMargin]
textField.translatesAutoresizingMaskIntoConstraints = true

self.contentView.addSubview(textField)

}

/**
Needed for initialization from a storyboard.
- parameter aDecoder: An unarchiver object.
- returns: An initialized TextInputeCell object or nil if the object could not be created.
*/
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}

/**
Update the placeholder specific to the Input Field
- Parameter placeholder: the place holder for the input field
*/
public func updatePlaceHolder(placeholder: String) {
textField.placeholder = placeholder
}

}


And the View Controller when assigning delegates and delegate methods etc..

//Configure the Text Input Cells
var clientNameInput = TextInputCell(style: UITableViewCellStyle.Default, reuseIdentifier: nil)
var invoiceEmailInput = TextInputCell(style: UITableViewCellStyle.Default, reuseIdentifier: nil)
var invoiceAddressInput = TextInputCell(style: UITableViewCellStyle.Default, reuseIdentifier: nil)
var amountChargedInput = TextInputCell(style: UITableViewCellStyle.Default, reuseIdentifier: nil)


In viewDidLoad() :

clientNameInput.textField.delegate = self
invoiceEmailInput.textField.delegate = self
invoiceAddressInput.textField.delegate = self
amountChargedInput.textField.delegate = self

clientNameInput.updatePlaceHolder(mainOptions[0])
invoiceEmailInput.updatePlaceHolder(mainOptions[1])
invoiceAddressInput.updatePlaceHolder(mainOptions[2])
amountChargedInput.updatePlaceHolder(billingOptions[0])


And delegate functions:

/*UITextField Delegate Methods*/

func textFieldDidBeginEditing(textField: UITextField) {
}
func textFieldDidEndEditing(textField: UITextField) {
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return true;
}
func textFieldShouldClear(textField: UITextField) -> Bool {
return true;
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
return true;
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return true;
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true;
}


And I add the TextInputCells to the table like so:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

//Assigns UIDatePickerView and UIPickerView cells
if(indexPath.section == 0 && mainOptions[indexPath.row] == mainOptions[0]) {

return clientNameInput as UITableViewCell

} else if(indexPath.section == 0 && mainOptions[indexPath.row] == mainOptions[1]) {

return invoiceEmailInput as UITableViewCell

} else if(indexPath.section == 0 && mainOptions[indexPath.row] == mainOptions[2]) {

return invoiceAddressInput as UITableViewCell

} //etc....

Answer

Turns out that it was related to a corrupted storyboard or .xib file, The issue was resolved by deleting the Tab Bar controller and creating a new one, with newly configured relationships. The answer was found here.