0

I have a text field in a tableView. I need to get the position of textfield but the problem is there are multiple section in it. I am able to get only one thing section or row using textfield.tag but I need both.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • I would suggest using a delegation pattern or closure callback similar to the approach shown [here](https://stackoverflow.com/questions/28659845/swift-how-to-get-the-indexpath-row-when-a-button-in-a-cell-is-tapped/38941510#38941510). Using tags or walking the view hierarchy is “icky” – Paulw11 Nov 25 '17 at 11:53
  • bro there is no button its text field – Praveen Srivastava Nov 27 '17 at 07:39
  • That is why I said *similar* - Use delegation or a callback closure to pass the event back to the view controller. Don't use tags or view hierarchy walking. – Paulw11 Nov 27 '17 at 07:41
  • how can i do this? – Praveen Srivastava Nov 27 '17 at 07:46
  • Well, you haven't really explained under what conditions you want to find the index path for the text field; Is it when the text field is edited? When a button is tapped somewhere? What are you actually trying to achieve? – Paulw11 Nov 27 '17 at 07:50
  • In tableview there are number of row and section and each cell contains a UITextField and after editing of text field i need to save this change in to array so i need Section path as well as Row path. – Praveen Srivastava Nov 27 '17 at 08:04
  • So you would use an approach similar to that shown in the question I linked to, except instead of calling the delegate method when a button is tapped, you will call it from an appropriate textfield delegate method, such as `textFieldShouldReturn`. The cell passes itself to the delegate method and in your delegate (which is your tableview) you can use `indexPathForCell` to obtain the index path for the cell where the editing was just completed. – Paulw11 Nov 27 '17 at 08:08
  • bro is there any example of it? – Praveen Srivastava Nov 27 '17 at 08:54

2 Answers2

1

You can find the parent UIResponder of any class by walking up the UIResponder chain; both UITextField and UITableViewCell inherit from UIView, which inherits from UIResponder, so to get the parent tableViewCell of your textfield you can call this function on your textfield:

extension UIResponder {
    func findParentTableViewCell () -> UITableViewCell? {
        var parent: UIResponder = self
        while let next = parent.next {
            if let tableViewCell = parent as? UITableViewCell {
                return tableViewCell
            }
            parent = next
        }
        return nil
    }
}

Then once you have the tableViewCell, you just ask the tableView for its index path with tableView.indexPAth(for:)

You never need to use the tag field:

guard let cell = textField.findParentTableViewCell (),
      let indexPath = tableView.indexPath(for: cell) else {
        print("This textfield is not in the tableview!")
}
print("The indexPath is \(indexPath)")
Josh Homann
  • 15,933
  • 3
  • 30
  • 33
0

You can use a variation of a previous answer that I wrote.

Use a delegate protocol between the cell and the tableview. This allows you to keep the text field delegate in the cell subclass, which enables you to assign the touch text field delegate to the prototype cell in Interface Builder, while still keeping the business logic in the view controller.

It also avoids the potentially fragile approach of navigating the view hierarchy or the use of the tag property, which has issues when cells indexes change (as a result of insertion, deletion or reordering), and which doesn't work where you need to know a section number as well as a row number, as is the case here.

CellSubclass.swift

protocol CellSubclassDelegate: class {
    func textFieldUpdatedInCell(_ cell: CellSubclass)
}

class CellSubclass: UITableViewCell {

@IBOutlet var someTextField: UITextField!

var delegate: CellSubclassDelegate?

override func prepareForReuse() {
    super.prepareForReuse()
    self.delegate = nil
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool
    self.delegate?.textFieldUpdatedInCell(self)
    return yes
}

ViewController.swift

class MyViewController: UIViewController, CellSubclassDelegate {

    @IBOutlet var tableview: UITableView!

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

        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellSubclass

        cell.delegate = self

        // Other cell setup

    } 

    //  MARK: CellSubclassDelegate

    func textFieldUpdatedInCell(_ cell: CellSubclass) {
        guard let indexPath = self.tableView.indexPathForCell(cell) else {
            // Note, this shouldn't happen - how did the user tap on a button that wasn't on screen?
            return
        }

        //  Do whatever you need to do with the indexPath

        print("Text field updated on row \(indexPath.row) of section \(indexPath.section")
    }
} 

You can also see Jacob King's answer using a closure rather than a delegate pattern in the same question.

Paulw11
  • 108,386
  • 14
  • 159
  • 186