-1

I have a table view in the cell i'm having two text fields in which user can enter the data. Initially i'm showing 5 cells. There is a button on which when user click it add one more cell in the table view. Now when i hit a button it add a cell when textfields are empty. But when i add data in all 5 cell textfields and than hit add button app crashes by showing this error, Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert row 10 into section 0, but there are only 6 rows in section 0 after the update'

The code is try for adding and deleting cell is this,

extension FlashCardViewController: UITableViewDelegate,UITableViewDataSource, UITextFieldDelegate{

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

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

    //cell.termTxt.delegate = self
    //allCellsText[indexPath.row] = cell.termTxt.text!
   // cell.definitionTxt.delegate = self
    return cell
}


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

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    if editingStyle == .delete{
        numberOfCell -= 1
        allCellsText.remove(at: indexPath.row)
        flashCardTableView.beginUpdates()
        flashCardTableView.deleteRows(at: [indexPath], with: .automatic)
        flashCardTableView.endUpdates()
    }
}

func textFieldDidEndEditing(_ textField: UITextField) {
    allCellsText.append(textField.text!)
    print(allCellsText)
}

}

The code for add button is this,

 @IBAction func addCardBtnTapped(_ sender: Any) {

    numberOfCell += 1
    let indexPath = IndexPath(row: allCellsText.count+1, section: 0)
    flashCardTableView.beginUpdates()
    flashCardTableView.insertRows(at: [indexPath], with: .automatic)
    flashCardTableView.endUpdates()
    view.endEditing(true)
}

When i delete any cell it gives me error of index out of range. How can i achieve this goal? The view controllers looks like this, enter image description here

Hamza
  • 231
  • 1
  • 8
  • 22
  • Can u help in this? @Sandeep Bhandari – Hamza Feb 13 '18 at 09:10
  • 1
    Possible duplicate of [UITableView add cell Animation](https://stackoverflow.com/questions/3122934/uitableview-add-cell-animation) – Prashant Tukadiya Feb 13 '18 at 09:13
  • This is different scenario from that above given link. @Prashant Tukadiya – Hamza Feb 13 '18 at 09:20
  • @hamza : You have an answer posted already :) And I think its pretty much helpful :) If you dont get answer here Ill post a link to Github page with code later :) Finally please delete your old question, you cant have duplicate questions in SO – Sandeep Bhandari Feb 13 '18 at 09:20
  • Not related but as always: Delete the `begin- / endUpdates()` lines. They are pointless for a single insert / delete operation. – vadian Feb 13 '18 at 09:23
  • i didn't get it yet, still app is crashing . @SandeepBhandari – Hamza Feb 13 '18 at 09:24
  • yup, when the initial 5 cells are empty and i hit add button, the cell increment correclty, but when i fill all the textfields and than hit add button it crashes. @SandeepBhandari – Hamza Feb 13 '18 at 09:48

3 Answers3

1

The problem is in the way you create an indexPath for inserting a new row, fix it according to this:

@IBAction func addCardBtnTapped(_ sender: Any) {

    numberOfCell += 1
    // create indexPath from numberOfCell, not from allCellsText.count
    let indexPath = IndexPath(row: numberOfCell - 1, section: 0)
    flashCardTableView.beginUpdates()
    flashCardTableView.insertRows(at: [indexPath], with: .automatic)
    flashCardTableView.endUpdates()
    view.endEditing(true)
}

The problem is in creating the IndexPath using IndexPath(row: allCellsText.count+1, section: 0). The insertions and deletions on tableView HAVE to be consistent with the dataSource - if you add a new row, the numberOfRowsInSection HAVE to increase by one, too. Now in your case you increment numberOfCell by one, as you are supposed to do, but then you try to add the new row at an indexPath determined by allCellsText.count+1. The problem is that the allCellsText.count is not consistent with numberOfCell variable (notice that you append a new string everytime textFieldDidEndEditing gets called).

EDIT

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

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = flashCardTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FlashCardTableViewCell
    // configure it with the backing data
    cell.termTxt.text = allCellsTermText[indexPath.row]
    cell.definitionTxt.text = allCellsDefinitionText[indexPath.row]

    // now instead of this you will have to find a way how you will be
    // able to determine the row which needs to be changed and change the model
    // cell.termTxt.delegate = self
    // cell.definitionTxt.delegate = self
    return cell
}


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

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    if editingStyle == .delete {
        allCellsTermText.remove(at: indexPath.row)
        allCellsDefinitionText.remove(at: indexPath.row)
        flashCardTableView.deleteRows(at: [indexPath], with: .automatic)
    }
}

func textFieldDidEndEditing(_ textField: UITextField) {
    // you should not append here, this will add a new row, you have to UPDATE the proper text 
    // allCellsText.append(textField.text!)
}


@IBAction func addCardBtnTapped(_ sender: Any) {

    // create a new row by appending new empty strings
    allCellsTermText.append("")
    allCellsDefinitionText.append("")

    let indexPath = IndexPath(row: allCellsTermText.count - 1, section: 0)
    flashCardTableView.insertRows(at: [indexPath], with: .automatic)
    view.endEditing(true)
}
Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90
  • i'm getting this error while tried ur code Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert row 6 into section 0, but there are only 6 rows in section 0 after the update' – Hamza Feb 13 '18 at 09:35
  • @Hamza it was my mistake, should have been `numberOfCell - 1` instead of just `numberOfCell` – Milan Nosáľ Feb 13 '18 at 09:37
  • now it is adding a new cell but one small issue is still coming. @Milan Nosáľ – Hamza Feb 13 '18 at 09:53
  • when i enter data in the cell textfield and than hit add button it add the new cell but the textfield is pre filled by the data enter as in above textfields. @Milan Nosáľ – Hamza Feb 13 '18 at 09:58
  • @Hamza update your question with your real `cellForRow` implementation – Milan Nosáľ Feb 13 '18 at 10:08
  • @Hamza is the `allCellsText` supposed to hold the data from the cells? – Milan Nosáľ Feb 13 '18 at 10:12
  • actually, i want to send the data from all the textfields to the server thats why i'm getting the text from textfields in an array name allCellsText. @Milan Nosáľ – Hamza Feb 13 '18 at 10:21
  • @Hamza I'm gonna assume the answer is yes – Milan Nosáľ Feb 13 '18 at 10:22
  • yup its yes.But why new cell textfields are pre filled with data? @Milan Nosáľ – Hamza Feb 13 '18 at 10:24
  • @Hamza most probably they were prefilled because you were reusing the cells but not resetting their state – Milan Nosáľ Feb 13 '18 at 10:34
  • what is this bro ? allCellsTermText . @Milan Nosáľ – Hamza Feb 13 '18 at 10:37
  • @Hamza you need to distinguish between term texts and definition texts to be able to fill the textFields correctly (notice that in `cellForRowAt` you need to set text in both `termTxt` and `definitionTxt` – Milan Nosáľ Feb 13 '18 at 10:39
  • but now it is not showing me 5 cells initially.Table view loads empty @Milan Nosáľ – Hamza Feb 13 '18 at 10:43
  • @Hamza it is showing you as many cells as there are texts in `allCellsTermText` and `allCellsDefinitionText` - so just initiate these two arrays using `["", "", "", "", ""]` literal – Milan Nosáľ Feb 13 '18 at 10:45
  • Thats perfect big big thanks for ur support and time.:) @Milan Nosáľ – Hamza Feb 13 '18 at 10:50
1

If you want to use button to delete tableviewCell, any table view that allows rows to be deleted

Image TableViewCell delete with button

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

        cell.deleteButton.addTarget(self, action: #selector(nHapusTap(_:)), for: .touchUpInside)            

        return cell
    }

  @objc func nHapusTap(_ sender: UIButton) {
        let hitPoint = sender.convert(CGPoint.zero, to: tableView)
        if let indexPath = tableView.indexPathForRow(at: hitPoint) {

            self.dataArray.remove(at: indexPath.row)
            tableView.beginUpdates()
            tableView.deleteRows(at: [indexPath], with: .automatic)
            tableView.endUpdates()

        }

    }
Jeri P.M
  • 399
  • 5
  • 15
0

You're doing it incorrect. You've added self as delegate to the termtxt and definitiontxt here.

cell.termTxt.delegate = self
    cell.definitionTxt.delegate = self

As many times as you end editing in these inputboxes, your delegate method gonna get hit where you're just appending the text in a array.

func textFieldDidEndEditing(_ textField: UITextField) {
    allCellsText.append(textField.text!)
    print(allCellsText)
}

When you call the add row button, the allCellsText size is 10 because endEditing has been called from two types of inputboxes. (10 is incorrect, you can't add 10th row after 5th row).

let indexPath = IndexPath(row: allCellsText.count+1, section: 0)

Solution: Either add some checks in your delegate method before appending anything in allCellsText array OR update your logic in addCardBtnTapped function, remove the dependency from allCellsText arrow. use something like:

let indexPath = IndexPath(row: numberOfCell+1, section: 0)
Ahmad Raza
  • 2,850
  • 1
  • 21
  • 37
  • when i add this line of code let indexPath = IndexPath(row: numberOfCell+1, section: 0) the app still crashed. @Ahmad Raza – Hamza Feb 13 '18 at 09:27
  • I missed one line in your code. You already doing numberOfCell += 1 above. So just use let indexPath = IndexPath(row: numberOfCell, section: 0). Ignore +1 here. – Ahmad Raza Feb 13 '18 at 10:12