13
votes

So, I'm building a Detail View Controller App that presents a Table with a two-part cell: the label and the Text Field.

I'm trying to retrieve the Text Field value and add it to an array. I tried to use the "textField.superview.superview" technique but it didn't worked.

func textFieldDidEndEditing(textField: UITextField!){
    var cell: UITableViewCell = textField.superview.superview
    var table: UITableView = cell.superview.superview
    let textFieldIndexPath = table.indexPathForCell(cell)
}

Xcode fails to build and presents that "UIView is not convertible to UITableViewCell" and "to UITableView". The referring table has two sections, of four and two rows, respectively.

Thanks in advance.

EDIT: added ".superview" at the second line of the function.

4
have you tried casting? try adding "as UITableViewCell" at the end of the .superview.superview lineConnor
iOS 7 changed the UITableViewCell hierachy, something.superview.superview no longer works. You need to use the [sender convertPoint:CGRectZero toView:self.tableView] to get root point, then go NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:rootPoint]; Of course, you need to do this using Swift code, which I don't know (just showing the algorithm here that's all).Zhang

4 Answers

10
votes

You'll want to cast the first and second lines in your function, like this:

func textFieldDidEndEditing(textField: UITextField!){
    var cell: UITableViewCell = textField.superview.superview as UITableViewCell
    var table: UITableView = cell.superview as UITableView
    let textFieldIndexPath = table.indexPathForCell(cell)
}

superview returns a UIView, so you need to cast it to the type of view you expect.

28
votes

While the currently accepted answer might work, it assumes a specific view hierarchy, which is not a reliable approach since it is prone to change.

To get the indexPath from a UITextField that is inside a cell, it's much better to go with the following:

func textFieldDidEndEditing(textField: UITextField!){
    let pointInTable = textField.convert(textField.bounds.origin, to: self.tableView)
    let textFieldIndexPath = self.tableView.indexPathForRow(at: pointInTable)
    ...
}

This will continue to work independent of eventual changes to the view hierarchy.

9
votes

Using superview and typecasting isn't a preferred aaproach. The best practice is to use delegate pattern. If you have a textField in DemoTableViewCell which you are using in DemoTableViewController make a protocol DemoTableViewCellDelegate and assign delegate of DemoTableViewCell to DemoTableViewController so that viewcontroller is notified when eiditing ends in textfield.

protocol DemoTableViewCellDelegate: class {
  func didEndEditing(onCell cell: DemoTableViewCell)
}

class DemoTableViewCell: UITableViewCell {
  @IBOutlet var textField: UITextField!

  weak var delegate: DemoTableViewCellDelegate?

  override func awakeFromNib() {
    super.awakeFromNib()
    textField.delegate = self
  }
}

extension DemoTableViewCell: UITextFieldDelegate {
  func textFieldDidEndEditing(_ textField: UITextField) {
    delegate.didEndEditing(onCell: self)
  }
}

class DemoTableViewController: UITableViewController {

  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: DemoTableViewCell.self, for: indexPath)
    cell.delegate = self
    return cell
  }

}

extension DemoTableViewController: DemoTableViewCellDelegate {
  func didEndEditing(onCell cell: DemoTableViewCell) {
    //Indexpath for the cell in which editing have ended.
    //Now do whatever you want to do with the text and indexpath.
    let indexPath = tableView.indexPath(for: cell)
    let text = cell.textField.text
  }
}
-2
votes

You can use tag property of UITableViewCell

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "UpdateTableViewCell", for: indexPath) as! UpdateTableViewCell

        cell.tag = indexPath.row
        cell.setCellData()

        return cell
}

now in UITableViewCell

func textFieldDidEndEditing(textField: UITextField!){
    let textFieldIndexPath = self.tag
}