SimpuMind
SimpuMind

Reputation: 469

Swift: Insert new CustomTableViewCell row in TableView

need a little help please

i have a Add button in my NavigationController that inserts a new CustomTableviewCell, the custom tableview cell consist of a TextField. I have been able to add new cells successfully, but i cannot get all the text in each textfield and append them to an Array.

var datas: [String] = [""]

static var username = ""

@IBAction func addNewName(_ sender: Any) {
    self.isHandlingAddNewUser = true
    datas.insert(ViewController.username, at: 0)
    tableView.beginUpdates()
    tableView.insertRows(at: [IndexPath.init(row: 0, section: 0)], with: .automatic)
    tableView.endUpdates()
 }

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

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

    if let name = cell.usernameTextField.text{
        ViewController.username = name
    }

    return cell
}

What am i missing, i tried printing out datas, all are empty

Upvotes: 1

Views: 5201

Answers (3)

Qanser
Qanser

Reputation: 39

var row = ["One", "Two", "Three"]

@IBAction func addRow(sender: AnyObject) {
    row.append("Four")
    tableView.reloadData()
}

Upvotes: 0

Rob
Rob

Reputation: 437422

You shouldn't update the model in cellForRowAt. That should only populate the cell's text field's initial value. If you want to find out about changes, you need to set up another mechanism for the cell to inform the view controller when the text field changes.

The basic idea is as follows:

  • Define protocol (which I called UserInputCellDelegate) by which cell can inform view controller of changes;

  • The cellForRowAt should merely update the text field in the cell with the value from the model (your datas). It also defines the view controller as the delegate for the cell (to receive the updates regarding changed values);

  • When the text field is updated (e.g. hooking up the IBOutlet for the "Editing did end"), the cell will inform the view controller of that change by calling the delegate method to inform the view controller of the changes.

  • When the view controller has its didUpdate called, it will update the model accordingly.

Thus:

class ViewController: UITableViewController {

    var datas = [String]()                                  // start with empty array

    @IBAction func didTapAddButton(_ sender: Any) {
        let indexPath = IndexPath(row: 0, section:0)
        datas.insert("", at: indexPath.row)                 // inserting default value (I'm inserting ""; you can insert whatever you want)
        tableView.insertRows(at: [indexPath], with: .automatic)
    }

}

// MARK: - UITableViewDataSource

extension ViewController {

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return datas.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath) as! UserInputCell
        cell.delegate = self
        cell.usernameTextField.text = datas[indexPath.row]
        return cell
    }
}

// MARK: - UserInputCellDelegate

extension ViewController: UserInputCellDelegate {

    func didUpdate(cell: UserInputCell, string: String?) {
        if let indexPath = tableView.indexPath(for: cell) {
            datas[indexPath.row] = string ?? ""             // update `datas` with value user edited
        }

        // For giggles and grins, let's print the array, so we can see what it's doing.
        // In production app, this `print` statement would be removed.

        print("\(datas)")
    }

}

And

protocol UserInputCellDelegate: class {                     // this is class protocol, to allow weak reference

    /// When text field is updated, cell calls this delegate method to inform it of changes
    /// to text field value.
    ///
    /// - Parameters:
    ///   - cell: Cell containing text field that was updated
    ///   - string: String value entered.

    func didUpdate(cell: UserInputCell, string: String?)

}

class UserInputCell: UITableViewCell, UITextFieldDelegate {

    weak var delegate: UserInputCellDelegate?               // note this is weak to avoid strong reference cycle

    @IBOutlet weak var usernameTextField: UITextField!

    // hooked up to "Editing did end" action for text field in IB

    @IBAction func didEndEditing(_ sender: UITextField) {
        delegate?.didUpdate(cell: self, string: sender.text)
    }

}

Upvotes: 3

Grzegorz Krukowski
Grzegorz Krukowski

Reputation: 19792

You are not assigning the data to the cells themselves. I think you should be doing this:

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

    cell.usernameTextField.text = datas[indexPath.row]

    return cell
}

I'm not sure what you try to achieve with that username variable, but I'm sure it shouldn't be in cell configuration method

Upvotes: 1

Related Questions