Home > Software design >  Get row and section with textfield delegate method Swift
Get row and section with textfield delegate method Swift

Time:08-09

I'm kind new at swift... I need to pass the indexpath to the texfield delegate method. With "tag" I can pass only row or section.

My code:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "celdaConValores", for: indexPath) as! celdaDomestica

        
        switch indexPath.section {

        case 1:
            cell.titulo.text = checklist.datosDomestica?.titulos[indexPath.row] ?? ""
            cell.valor.tag = indexPath.row
            cell.valor.delegate = self
            return cell
        case 2:
           
            cell.titulo.text = checklist.datosFrigoDomestica?.titulos[indexPath.row] ?? ""
            cell.valor.tag = indexPath.row
            cell.valor.delegate = self
            return cell
        case 3:
           
            cell.titulo.text = checklist.otrosDatosDomestica?.titulos[indexPath.row] ?? ""
            cell.valor.tag = indexPath.row
            cell.valor.delegate = self
            return cell
        default:
            return cell
        }


 func textFieldDidEndEditing(_ textField: UITextField) {
    
    let row = textField.tag
    
    if let texto = textField.text{
        ¿?need the section!
    }

}

I have seen answers like computing the result in the tag value but I don't see it as a good practice

Thank you!

CodePudding user response:

Using tags is quite fragile, and generally not recommended. Instead I recommend using the coordinates of the text field's frame to figure out which cell contains it. Use the following extension to UITableView:

//  Created by Duncan Champney on 12/23/16.
//  Copyright © 2016-2017 Duncan Champney.
//  May be used freely in for any purpose as long as this copyright
//  notice is included.
//
import UIKit

public extension UITableView {
    
    /**
     This method returns the indexPath of the cell that contains the specified view
     - Parameter view: The view to find.
     - Returns: The indexPath of the cell containing the view, or nil if it can't be found
     */
    
    func indexPathForView(_ view: UIView) -> IndexPath? {
        let center = view.center
        
        //The center of the view is a better point to use, but we can only use it if the view has a superview
        guard let superview = view.superview else {
            //The view we were passed does not have a valid superview.
            //Use the view's bounds.origin and convert from the view's coordinate system
            let origin = self.convert(view.bounds.origin, from: view)
            let indexPath = self.indexPathForRow(at: origin)
            return indexPath
        }
        let viewCenter = self.convert(center, from: superview)
        let indexPath = self.indexPathForRow(at: viewCenter)
        return indexPath
    }
}

So your delegate method would look like this:

func textFieldDidEndEditing(_ textField: UITextField) {
    if let indexPath = tableView.indexPathForView(textField) {
        let row = indexPath.row
        let section = indexPath.section
    } else {
        // Can't find the IndexPath. Handle error
    }
}

You can find a working application using that code at this github link: TableViewExtension

CodePudding user response:

You typically don't want to refer to your cells by their indexPath or tag. What happens if a cell is deleted or inserted? You'd have to update all the other values. UIKit and standard design patterns allow us to associate the things that we need with our cells.

What you can do is create a delegate on your cell that passes in the cell whose text changed. Once you have the cell you can get the indexPath from the tableView if you need it.

class ViewController: UIViewController {
    lazy var tableView: UITableView = {
        let tableView = UITableView(frame: .zero, style: .insetGrouped)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.register(SomeCell.self, forCellReuseIdentifier: "SomeCell")
        tableView.backgroundColor = .systemBackground
        tableView.delegate = self
        tableView.dataSource = self
        tableView.estimatedRowHeight = 44
        tableView.rowHeight = UITableView.automaticDimension
        return tableView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(tableView)
        tableView.snp.makeConstraints{ (make) in
            make.edges.equalTo(view.safeAreaLayoutGuide)
        }
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "SomeCell", for: indexPath) as! SomeCell
        cell.delegate = self
        return cell
    }
}

extension ViewController: SomeCellDelegate {
    func someCellTextChanged(_ cell: SomeCell, text: String?) {
        guard let indexPath = tableView.indexPath(for: cell) else { fatalError("Oh no") }
        print("someCellTextChanged at indexPath: \(indexPath) to \(text ?? "")")
    }
}

protocol SomeCellDelegate: AnyObject {
    func someCellTextChanged(_ cell: SomeCell, text: String?)
}

class SomeCell: UITableViewCell, UITextFieldDelegate {
    weak var delegate: SomeCellDelegate?

    lazy var textField: UITextField = {
        let textField = UITextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.backgroundColor = .systemPurple
        return textField
    }()

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

        contentView.addSubview(textField)
        textField.delegate = self
        textField.snp.makeConstraints { make in
            make.edges.equalTo(contentView.layoutMarginsGuide)
        }

        backgroundColor = .systemMint
        contentView.backgroundColor = .systemPink
    }

    required init?(coder: NSCoder) {
        fatalError("nope")
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    func textFieldDidEndEditing(_ textField: UITextField) {
        delegate?.someCellTextChanged(self, text: textField.text)
    }
}
  • Related