Swift3: ошибка в TableView при удалении ячеек, содержащих значения UserDefault

Я выполнил все шаги, указанные здесь: Правильный способ удаления строк из UITableView и обновления массива из NSUserDefaults в Swift/iOS

Однако возникает ошибка, говорящая;

'Недопустимое обновление: недопустимое количество строк в разделе 0. Количество строк, содержащихся в существующем разделе после обновления (2), должно быть равно количеству строк, содержащихся в этом разделе до обновления (2), плюс или минус количество строк, вставленных или удаленных из этого раздела (0 вставленных, 1 удаленных) и плюс или минус количество строк, перемещенных в этот раздел или из него (0 добавленных, 0 удаленных).

Я создал

класс TableViewController: UITableViewController {

struct Section {
    var sectionName: String!
    var words: [String]!

    init(title: String, word: [String]) {
        self.sectionName = title
        self.words = word

    }
}

var arrayForRows = [String]()

var sections = [Section]()
func getData() -> [String] {
if let data = userDefaultDataSave.stringArray(forKey: "data") {
    return data
}
return []
}
override func viewDidLoad() {
    super.viewDidLoad()

    tableView.dataSource = self

    guard let data = dataDefaults.stringArray(forKey: "data") else {
        return
    }
    arrayForRows = data as [String]
    tableView.reloadData()

    sections = [
        Section(title: "A", word: []), // 1
        Section(title: "B", word: []), //2
        Section(title: "C", word: []),
        Section(title: "D", word: []),
        Section(title: "E", word: []),
        Section(title: "F", word: []),
        Section(title: "G", word: []),
        Section(title: "H", word: []),
        Section(title: "I", word: []),
        Section(title: "J", word: []),
        Section(title: "K", word: []),
        Section(title: "L", word: []),
        Section(title: "M", word: []),
        Section(title: "N", word: []),
        Section(title: "O", word: []),
        Section(title: "P", word: []),
        Section(title: "Q", word: []),
        Section(title: "R", word: []),
        Section(title: "S", word: []),
        Section(title: "T", word: []),
        Section(title: "U", word: []),
        Section(title: "V", word: []),
        Section(title: "W", word: []),
        Section(title: "X", word: []),
        Section(title: "Y", word: []),
        Section(title: "Z", word: [])
    ]


    let getAlphabetData = getData()

    for a in getAlphabetData {

        if a.hasPrefix("A") || a.hasPrefix("a") {
            if sections[0].sectionName == "A" {
                sections[0].words.append(a as String)
            }
        }

        if a.hasPrefix("B") || a.hasPrefix("b") {
            if sections[1].sectionName == "B" {
                sections[1].words.append(a as String)
            }
        }

        if a.hasPrefix("c") || a.hasPrefix("c") {
            if sections[2].sectionName == "C" {
                sections[2].words.append(a as String)
            }
        }
        if a.hasPrefix("D") || a.hasPrefix("d") {
            if sections[3].sectionName == "D" {
                sections[3].words.append(a as String)
            }
        }
        if a.hasPrefix("E") || a.hasPrefix("e") {
            if sections[4].sectionName == "E" {
                sections[4].words.append(a as String)
            }
        }
        if a.hasPrefix("F") || a.hasPrefix("f") {
            if sections[5].sectionName == "F" {
                sections[5].words.append(a as String)
            }
        }
        if a.hasPrefix("G") || a.hasPrefix("g") {
            if sections[6].sectionName == "G" {
                sections[6].words.append(a as String)
            }
        }
        if a.hasPrefix("H") || a.hasPrefix("h") {
            if sections[7].sectionName == "H" {
                sections[7].words.append(a as String)
            }
        }
        if a.hasPrefix("I") || a.hasPrefix("i") {
            if sections[8].sectionName == "I" {
                sections[8].words.append(a as String)
            }
        }
        if a.hasPrefix("J") || a.hasPrefix("j") {
            if sections[9].sectionName == "J" {
                sections[9].words.append(a as String)
            }
        }
        if a.hasPrefix("K") || a.hasPrefix("k") {
            if sections[10].sectionName == "K" {
                sections[10].words.append(a as String)
            }
        }
        if a.hasPrefix("L") || a.hasPrefix("l") {
            if sections[11].sectionName == "L" {
                sections[11].words.append(a as String)
            }
        }
        if a.hasPrefix("M") || a.hasPrefix("m") {
            if sections[12].sectionName == "M" {
                sections[12].words.append(a as String)
            }
        }
        if a.hasPrefix("N") || a.hasPrefix("n") {
            if sections[13].sectionName == "N" {
                sections[13].words.append(a as String)
            }
        }
        if a.hasPrefix("O") || a.hasPrefix("o") {
            if sections[14].sectionName == "O" {
                sections[14].words.append(a as String)
            }
        }
        if a.hasPrefix("P") || a.hasPrefix("p") {
            if sections[15].sectionName == "P" {
                sections[15].words.append(a as String)
            }
        }
        if a.hasPrefix("Q") || a.hasPrefix("q") {
            if sections[16].sectionName == "Q" {
                sections[16].words.append(a as String)
            }
        }
        if a.hasPrefix("R") || a.hasPrefix("r") {
            if sections[17].sectionName == "R" {
                sections[17].words.append(a as String)
            }
        }
        if a.hasPrefix("S") || a.hasPrefix("s") {
            if sections[18].sectionName == "S" {
                sections[18].words.append(a as String)
            }
        }
        if a.hasPrefix("T") || a.hasPrefix("t") {
            if sections[19].sectionName == "T" {
                sections[19].words.append(a as String)
            }
        }
        if a.hasPrefix("U") || a.hasPrefix("u") {
            if sections[20].sectionName == "U" {
                sections[20].words.append(a as String)
            }
        }
        if a.hasPrefix("V") || a.hasPrefix("v") {
            if sections[21].sectionName == "V" {
                sections[21].words.append(a as String)
            }
        }
        if a.hasPrefix("W") || a.hasPrefix("w") {
            if sections[22].sectionName == "W" {
                sections[22].words.append(a as String)
            }
        }
        if a.hasPrefix("X") || a.hasPrefix("x") {
            if sections[23].sectionName == "X" {
                sections[23].words.append(a as String)
            }
        }
        if a.hasPrefix("Y") || a.hasPrefix("y") {
            if sections[24].sectionName == "Y" {
                sections[24].words.append(a as String)
            }
        }
        if a.hasPrefix("Z") || a.hasPrefix("z") {
            if sections[25].sectionName == "Z" {
                sections[25].words.append(a as String)
            }
        }
    }

}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return sections.count
}

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

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows

    return arrayForRows.count
}


override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return sections[section].sectionName
}



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

    // Cellに値を設定する.
    cell.textLabel?.font = UIFont.systemFont(ofSize: 20)
    cell.textLabel?.textColor = UIColor.black

    cell.textLabel!.text = sections[indexPath.section].words[indexPath.row]

    return cell
}

// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    if editingStyle == .delete {
        // Delete the row from the data source

        tableView.beginUpdates()

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

        let userDefaults = UserDefaults.standard
        userDefaults.set(arrayForRows, forKey: "data")
        tableView.endUpdates()
    } 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
    }

}

}

Из другого контроллера значения добавляются в массив words секции, записывая таким образом;

  class InputDataViewController: UIViewController {


 // this function is written inside UITextView in this class. 
 //But I omitted here. 

 func addData(text: String) {

    var data = self.getData()
    data.insert(text as NSString, at: 0)
    dataDefault.set(data, forKey: "data")
}


func getData() -> [String] {
    if let data = dataDefault.stringArray(forKey: "data") {
        return data
    }
    return []
 }

}

В чем проблема? Из того, как я это вижу, я могу понять, если бы я явно не написал tableView.reloadData() или tableView.beginUpdates() и endUpdates(), но я это сделал. Какова причина возникновения этой ошибки? Не могли бы вы помочь мне?

Спасибо.

Обновлено

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return sections[section].words.count
}

Кроме того, я обнаружил, что если я пишу таким образом, это работает. Однако выбранный не был удален из моего представления таблицы. Первый индекс слова был удален из моего представления таблицы независимо от того, что я выбрал и удалил строку в разделе. Удаленная строка находилась в другом разделе.

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

    if editingStyle == .delete {
        // Delete the row from the data source
        //            removeHistory(index: sections[indexPath.section].words[indexPath.row])

        tableView.beginUpdates()

        let indexSet = NSMutableIndexSet()
        indexSet.add(indexPath.section)


        //let indexSet = sections[indexPath.section].words[indexPath.row] as IndexSet
        //sections.remove(at: indexPath.section)
        sections[indexPath.section].words.remove(at: indexPath.row)
        var data = getData()
        data.remove(at: indexPath.row)
        dataDefault.set(data, forKey: "data")
        dataDefault.synchronize()

        //tableView.deleteSections(indexSet, with: UITableViewRowAnimation.fade)
        tableView.deleteRows(at: [indexPath], with: .fade)

        tableView.endUpdates()
        //



    } 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
    }
}

person Ryo    schedule 30.10.2016    source источник
comment
Почему вы устанавливаете источник данных для табличного представления после вызова reloadData? Было бы полезно, если бы вы включили реализацию tableView:numberOfRowsInSection: в свой фрагмент.   -  person crizzis    schedule 30.10.2016
comment
@crizzis, пожалуйста, проверьте обновление   -  person Ryo    schedule 30.10.2016


Ответы (2)


Вы должны убедиться, что каждый раз, когда вы добавляете или удаляете строки, вы поддерживаете согласованность источника данных. В вашем случае вы удаляете объект из arrayForRows, но ваш numberOfRows использует совершенно другой источник данных: sections[section].words.count

Так что вы должны либо

  1. Оставить numberOfRows как есть и удалить из массива sections[indexPath.section].words
  2. Измените свой метод nubmerOfRows, чтобы он возвращал arrayForRows.count

Просто сделайте свой выбор, какой у вас источник данных, и выберите правильный вариант. Поскольку у вас есть несколько разделов, вы должны следовать первому предложению.

Изменить:

Вы получаете ошибку index out of bounds, потому что кажется, что вы никогда не обновляете свой массив sections. Вы должны заполнить все эти words массивов для каждого раздела, верно? Но, судя по приведенному вами коду, они всегда пусты.

В основном ваша реализация должна выглядеть так:

  1. ViewController A отображает список разделов со словами, начинающимися с определенной буквы в каждом разделе.
  2. ViewController B создает новые элементы и сохраняет их в UserDefaults.
  3. VC A должен обновлять массив sections каждый раз в методе viewWillAppear. Просто сделайте цикл по всем элементам массива из UserDefaults и поместите каждый элемент в правильный объект Section.
  4. Каждый раз, когда строка удаляется в VC A, вы должны удалять ее из соответствующего объекта Section, иначе приложение выйдет из строя. Также вы должны удалить тот же объект из массива UserDefaults, чтобы сохранить эти изменения.

Пример функции для вставки слова в массив sections:

func insert(word: String) {
    guard word.characters.count > 0 else { return }
    let firstLetter = String(describing: word.characters.first).lowercased()
    for var section in sections {
        if section.title.lowercased().hasPrefix(firstLetter) {
            section.words.append(word)
        }
    }
}
person alexburtnik    schedule 30.10.2016
comment
При удалении объекта или в numberOfRows? - person alexburtnik; 30.10.2016
comment
Покажите свой обновленный код, и я скажу вам, в чем ошибка. - person alexburtnik; 30.10.2016
comment
Я связал ячейку с другим TableViewController, где написан приведенный выше код. И когда я нажал на ячейку, он говорит об ошибке. Я думаю, должно быть что-то не так в другом файле. Я просто хочу, чтобы UITableVIEW располагался в алфавитном порядке с разделами. Они будут содержать слова в каждом разделе. Эти значения сохраняются с помощью UserDefault и поступают из UITextView. Какой подход подходит для этого? - person Ryo; 30.10.2016
comment
Спасибо за редактирование. Да, точно. Это в основном то, как написан мой код. Как поместить каждый элемент в правильный объект Section? Например, для a в данных { if a.hasPrefix(A) || a.hasPrefix(a) { if section[0].sectionName == A { //здесь код? } } } - person Ryo; 30.10.2016
comment
Спасибо за код, но я до сих пор не понимаю, как работает мой код. Вроде постоянно вылезает ошибка. Поэтому я скопировал и вставил свой код в два контроллера представления. Не могли бы вы взглянуть на это. Я так раздражен на самом деле в течение недели, и я расстроен. Я ценю, если вы могли бы дать мне решение. Это будет огромная благодарность. @alexburtnik - person Ryo; 31.10.2016
comment
@alexburnik привет. Я снова обновился. Было бы полезно, если бы вы могли мне помочь. - person Ryo; 31.10.2016

Может быть, проблема в том, что вы удаляете строку из представления таблицы перед удалением данных? попробуйте удалить объект из вашего массива и только потом удалить строку из tableView

    tableView.beginUpdates()
    arrayForRows.remove(at: indexPath.row)
    self.tableView.deleteRows(at: [indexPath], with: .automatic)
    let userDefaults = UserDefaults.standard
    userDefaults.set(arrayForRows, forKey: "data")
    tableView.endUpdates()

Также попробуйте посмотреть на свой метод numberOfRowsInSection. Я думаю, он должен вернуться

arrayForRows.count
person valivaxa    schedule 30.10.2016
comment
Нет. Это не работает. Я пробовал это тоже, но не удалось. Я ищу решение... Я не вижу проблем с моим кодом, но есть ошибка... - person Ryo; 30.10.2016
comment
@Ryo попробуйте изменить numberOfRowsInSection так, как я предлагаю. Проблема в том, что вы удаляете данные из arrayForRows, но количество строк вашего tableView не зависит от этого массива - person valivaxa; 30.10.2016
comment
И теперь он сказал, что индекс вне диапазона - person Ryo; 30.10.2016
comment
@Ryo, у тебя есть только один раздел в tableView? - person valivaxa; 30.10.2016
comment
нет, у меня много разделов, как вы можете видеть в моем посте. Есть 25 или 24 секций. И они содержат пустые массивы, пока значения не будут добавлены в их массив слов - person Ryo; 30.10.2016