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)
}
}