Search code examples
iosiphoneswiftuitableviewuiscrollview

Scrolling Tableview data reshuffle in UITextfield in Swift


I have table view in which cell consist of multiple sections, each section have different number of rows and every row have a text field. When I wrote in it and scroll down and up the data lost or reshuffled. So I am trying to save textfield data into the 2 dimensional array but I can’t solve this problem.

Here is code in Custom cell:

class CustomCell: UITableViewCell {

    @IBOutlet weak var userName: UITextField!
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }
}

View controller code:

extension ViewController: UITableViewDelegate, UITableViewDataSource {

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let label = UILabel()
        label.text = "Header"
        return label
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 7
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = self.tableview.dequeueReusableCell(withIdentifier: CustomCell.identifier, for: indexPath) as! CustomCell
        return cell

    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80.0
    }
}

Solution

  • Your cells cannot be expected to maintain data (the contents of your UITextField) as they get removed from memory once they are outside the visible bounds of your table view.

    You have to look into the UITableViewDataSource protocol and store the contents of your cell’s UITextFields in a class which will remain in memory for the duration of your table view’s view controller.

    Typically, people use the view controller to be the Data Source as you have done.

    Steps are as follows:

    • In your view controller's initialization, create and prepare a data structure (array / dictionary keyed on IndexPaths) that will contain the contents of the text you need to store
    • When dequeuing a cell (in your cellForRowAt function), configure the cell with the necessary string from your data structure, if content exists for that particular indexPath.
    • When the text is changed by the user in the cell, notify your data source of new contents for the cell's index path

    Example:

    Define the following protocol:

    protocol DataSourceUpdateDelegate: class {
        func didUpdateDataIn(_ sender: UITableViewCell, with text: String?)
    }
    

    Ensure your UITableViewCell declares a delegate variable and uses it:

    class MyCell: UITableViewCell, UITextFieldDelegate {
        @IBOutlet var myTextField: UITextField!
        weak var dataSourceDelegate: DataSourceUpdateDelegate?
    
        func configureCellWithData(_ data: String?, delegate: DataSourceUpdateDelegate?)
        {
            myTextField.text = data
            myTextField.delegate = self
            dataSourceDelegate = delegate
        }
    
        override func prepareForReuse() {
            myTextField.text = ""
            super.prepareForReuse()
        }
    
        func textFieldDidEndEditing(_ textField: UITextField) {
            dataSourceDelegate?.didUpdateDataIn(self, with: textField.text)
        }
    }
    

    Make sure your View Controller conforms to DataSourceUpdateDelegate and initialize a variable to manage the data:

    class MyViewController: UITableViewController, UITableViewDataSource, DataSourceUpdateDelegate {
        var data = [IndexPath : String]()
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = MyCell() // Dequeue your cell here instead of instantiating it like this
            let cellData = data[indexPath]
            cell.configureCellWithData(cellData, delegate: self)
            return cell
        }
    
        func didUpdateDataIn(_ sender: UITableViewCell, with text: String?) {
            data[tableView.indexPath(for: sender)!] = text
        }
    }