'Read Data From Text Field in TableView Dynamic Cell in Swift

I want to read the text in the text fields in my dynamic cells in the table view.

Each cell has two text fields and the user can add more cells.

My question : How can I read the values from text fields (textField1 & textField2) and add them to my cards struct?

In the Cards struct there are two variables. frontLabel and backLabel as string

My table View:

class AddFCViewController: UIViewController  {

   
    var numberOfTextFields = 1
    var cards = [Cards]()
    
    
    
    // Table View
    let tableView : UITableView = {
        let tableView = UITableView()
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.backgroundColor = .purple
        
        return tableView
    }()
    
   
    // Add Button
    let addButtton : UIButton = {
       let btn = UIButton()
        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.backgroundColor = .link
        btn.setTitle("Ekle", for: .normal)
        return btn
    }()
    
  
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureTableView()
        configureAddButton()


        
    }
    
    
   
    
    // MARK: - Configure Add Button
    func configureAddButton() {
        view.addSubview(addButtton)
       
        addButtton.addTarget(self, action: #selector(addButtonTapped(sender:)), for: .touchUpInside)
    }
    
    // MARK: - Add Button Tapped MEthod
    @objc func addButtonTapped(sender: UIButton){
        numberOfTextFields += 1
        tableView.reloadData()
    }
    
 
    // MARK: - Configure Table View
    func configureTableView() {
        view.addSubview(tableView)
        setTableViewDelegates()
        
        tableView.rowHeight = 100
        tableView.register(AddFCCell.self, forCellReuseIdentifier: "AddFCCell")
       
        
    }
    
    // MARK: - Table View Delegates
    func setTableViewDelegates() {
        tableView.delegate = self
        tableView.dataSource = self
    }
}


// MARK: - Table View Extension & Delegate Methods

extension AddFCViewController: UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return numberOfTextFields
    }
    
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "AddFCCell") as! AddFCCell
        cell.selectionStyle = .none
        cell.textField1.tag = indexPath.row
        cell.textField2.tag = indexPath.row
        print(cell.textField1.tag)
        print(cell.textField2.tag)
        
//        cell.textField1.text = cards[indexPath.row].frontLabel
//        cell.textField2.text = cards[indexPath.row].backLabel
  

        cell.textField1.delegate = self
        

        cell.backgroundColor = .purple

        return cell
    }
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 10
    }
    
    func textFieldDidBeginEditing(_ textField: UITextField) {
//        textField.addTarget(self, action: #selector(valueChanged), for: .editingChanged)
    }
   
  
    
}

Here is my custom Cell Class.

class AddFCCell: UITableViewCell {
   
    
    // MARK: - Text Field 1
    let textField1 : UITextField = {
        let textField = UITextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.backgroundColor = .white
        textField.isUserInteractionEnabled = true
        textField.placeholder = "TYPE HERE SOMETHING"
        textField.font = UIFont.systemFont(ofSize: 20)

        return textField
    }()
    
    
    // MARK: - Text Field 2
    let textField2 : UITextField = {
        let textField = UITextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.backgroundColor = .white
        textField.isUserInteractionEnabled = true
        textField.placeholder = "TYPE HERE SOMETHING"
        textField.font = UIFont.systemFont(ofSize: 20)

        return textField
    }()

    // MARK: - view Did Load
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        contentView.addSubview(textField1)
        contentView.addSubview(textField2)
        
        configureTextBox1(textField: textField1)
        configureTextBox2(textField: textField2)
    }
  
    
    // MARK: - Configure Text Box 1 Function
    func configureTextBox1(textField : UITextField) {
        
        textField.layer.cornerRadius = 20
     
    }
    
    // MARK: - Configure Text Box 2 Function
    func configureTextBox2(textField : UITextField) {
        
        textField.layer.cornerRadius = 20
     
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

PS - I deleted some of my constraints code as it was just too much code there.



Solution 1:[1]

I would change couple of things,

1. Get rid of numberOfTextFields

2. Use var cards = [Cards]() to return the number of cells in tableView

3. Initialize cards with a single Cards instance and set its property to nil. So I would have Cards implementation as

struct Cards {
    var frontLabel: String?
    var backLabel: String?
}

4. Initialize cards array as

var cards = [Cards()]

5. Return number of rows from arrays count

    func numberOfSections(in tableView: UITableView) -> Int {
        return cards.count
    }

6. I would migrate text field delegate responsibilities to Cell instead of moving it to ViewController and I would have a delegate in AddFCCell to update the ViewController about text changes

@objc protocol AddFCCellDelegate {
    func updateCard(with frontText: String?, backText: String?, for cell: AddFCCell)
}

class AddFCCell: UITableViewCell {
    weak var delegate:AddFCCellDelegate?
    //.... other codes of yours
}

extension AddFCCell: UITextFieldDelegate {
    func textFieldDidEndEditing(_ textField: UITextField) {
        self.delegate?.updateCard(with: self.textField1.text, backText: self.textField2.text, for: self)
    }
}

7. Finally, implement/confirm to AddFCCellDelegate in viewController and reload TableView bu updating text

extension ViewController: AddFCCellDelegate {
    func updateCard(with frontText: String?, backText: String?, for cell: AddFCCell) {
        guard let indexPath = self.tableView.indexPath(for: cell) else {
            return
        }
        self.cards[indexPath.row].frontLabel = frontText
        self.cards[indexPath.row].backLabel = backText
        self.tableView.reloadRows(at: [indexPath], with: .automatic)
    }
}

8. Update CellForRowAtIndexPath

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

        let cell = tableView.dequeueReusableCell(withIdentifier: "AddFCCell") as! AddFCCell
        cell.selectionStyle = .none
        cell.delegate = self
        cell.textField1.text = cards[indexPath.row].frontLabel
        cell.textField2.text = cards[indexPath.row].backLabel


        cell.backgroundColor = .purple

        return cell
    }

You can obviously implement a efficient protocol and return/update only specific variable in Cards struct based on which textField was modified n all, but the code posted here is only to give you an idea and to get started with

EDIT 1:

Make sure you have set your TextFields delegate right

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        contentView.addSubview(textField1)
        textField1.delegate = self
        contentView.addSubview(textField2)
        textField2.delegate = self

        configureTextBox1(textField: textField1)
        configureTextBox2(textField: textField2)
    }

Solution 2:[2]

You can add placeholder text to both text filed, make sure both place holder text are different, here I am trying to assign uniq identifier to textfield with tag.

&

retrieve text field value by comparing place holder text in textFieldDidBeginEditing.

cell.textField1.tag = indexPath.row

cell.textField2.tag = indexPath.row

cell.textField1.placeholder = "1"

cell.textField2.placeholder = "2"



func textFieldDidBeginEditing(_ textField: UITextField) {

      if(textField.tag && placeholder){
      }

 }

It is not a proper solution but by this way you can differentiate both text filed with same tag.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Arasuvel