Seong Lee
Seong Lee

Reputation: 10580

Saving textfield values in array in the same order as TableView Cell even after some cells are deleted

I have a TableView that allows user to add cells for text entry.

I implemented textfield delegate method that watches the change in the cell's textfield and store the text value in array.

I currently assign indexPath.row of cellForRowAtIndexPath method as a tag value of cell's textfields. And I use that tag as the index of array for updating values.

But this approach causes issue once I delete some cells and add new cells to store new value. Values get stored in random index of array.

How do I save values in array in the same index order as the table cell even after some cells are deleted?

var stepCount = 1
var stepOrder = ["1"]
var steps = [""]

@IBAction func stepTextFieldDidChange(sender: UITextField) {
    steps[stepTextField.tag] = sender.text!
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    var cell:UITableViewCell?

    if tableView == self.stepTableView {
        let aCell = tableView.dequeueReusableCellWithIdentifier("StepCell", forIndexPath: indexPath) as! StepCell
        stepTextField = aCell.getTextField()
        stepTextField.tag = indexPath.row
        aCell.stepTextField.delegate = self
        aCell.configureStepCell(stepOrder[indexPath.row], step: steps[indexPath.row])
        cell = aCell
    }

    return cell
}

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if tableView == stepTableView {

        if editingStyle == .Delete {
            // Delete the row from the data source
            if stepOrder.count > 1 {
                steps.removeAtIndex(indexPath.row)
                stepTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
                stepTableView.reloadData()
            }   
        } else if editingStyle == .Insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
        }
    }
}

@IBAction func addStep(sender: AnyObject) {
    stepCount++
    stepOrder.append("\(stepCount)")
    steps.append("")
    stepTextField.becomeFirstResponder()
    stepTableView.reloadData()
}

enter image description here

Upvotes: 1

Views: 1914

Answers (1)

Paulw11
Paulw11

Reputation: 115041

Using tags is difficult because, as you have found out, the index will change as you manipulate the table data.

Although it is a bit of overhead for a simple string, I would create a Step class and store an array of these. Then you can store a reference to the Step instance in your custom cell and update it from your delegate method.

On the subject of the delegate, you should move the UITextField delegate method into your cell class and provide a new protocol to let your view controller know about the change.

class Step {
    var stepValue = ""

    convenience init(_ value:String) {
        self.init()
        self.stepValue=value
    }
}

protocol StepDelegate {
    func stepValueChanged(step:Step, newValue:String) -> Void
}

In your cell class you will have:

var delegate : StepDelegate?
var step: Step!

func stepTextFieldDidChange(sender: UITextField) {
    self.step.stepValue = = sender.text!
    self.delegate?.stepValueChanged(self.step,newValue:self.step.stepValue)
}

In your view controller you will have:

var steps = [Step]()

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    var cell:UITableViewCell?

    if tableView == self.stepTableView {
        let aCell = tableView.dequeueReusableCellWithIdentifier("StepCell", forIndexPath: indexPath) as! StepCell

        let step=self.steps[indexPath.row]

        aCell.delegate=self
        aCell.step=step

        aCell.configureStepCell(step)
        cell = aCell
    }

    return cell
}

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if tableView == stepTableView {

        if editingStyle == .Delete {
            // Delete the row from the data source
                steps.removeAtIndex(indexPath.row)
                stepTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 
        } else if editingStyle == .Insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
        }
    }
}

@IBAction func addStep(sender: AnyObject) {

    let newStep=Step("\(self.steps.count+1)")
    let newPath=NSIndexPath(forRow: self.steps.count, inSection: 0)
    steps.append(newStep)
    self.stepTableView.insertRowsAtIndexPaths([newPath], withRowAnimation: .Automatic)
}

func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
    if (self.steps.count > 1) {
        return .Delete 
    } else {
        return .None     // Don't allow deletion of the last item in the table
    }
}

Upvotes: 1

Related Questions