简体   繁体   English

Swift3:删除包含UserDefault值的单元格时,TableView中出现错误

[英]Swift3: Error in TableView when deleting cells holding UserDefault values

I followed all steps provided here: The proper way to delete rows from UITableView and update array from NSUserDefaults in Swift / iOS 我遵循了此处提供的所有步骤: 在Swift / iOS中从UITableView删除行并从NSUserDefaults更新数组的正确方法

However, an error is caused, saying; 但是,会导致错误。

'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).' '无效的更新:第0节中无效的行数。更新(2)之后,现有节中包含的行数必须等于更新(2)之前该节中包含的行数,加上或减去从该部分插入或删除的行数(插入0,已删除1),加上或减去移入或移出该部分的行数(移入0,移出0)。

I have created 我创造了

class TableViewController: UITableViewController { 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
    }

}

} }

From another Controller, values are added into the Section's words array, by writing this way; 通过另一种编写方式,可以从另一个Controller将值添加到Section的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 []
 }

}

What is the problem? 问题是什么? From the way I see it, I can understand if I did not explicitly write tableView.reloadData() or tableView.beginUpdates() and endUpdates() , but I did. 从我的观察角度,我可以理解是否没有显式编写tableView.reloadData()tableView.beginUpdates()endUpdates() ,但是我做到了。 What is the reason for causing this error? 导致此错误的原因是什么? Could you help me? 你可以帮帮我吗?

Thanks. 谢谢。

Updated 更新

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

Also, I found out that if I write this way, it worked. 另外,我发现如果我用这种方式编写,它会起作用。 However, the selected one was not deleted from my table view. 但是,所选的那个并没有从我的表格视图中删除。 The first index of word was deleted from my table view regardless of the fact that I chose and deleted a row in a section. 无论我选择并删除某节中的一行,都是从表视图中删除单词的第一个索引。 The deleted row was in another section. 删除的行在另一部分中。

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

You must make sure that every time you add or delete rows, you keep your data source consistant. 您必须确保每次添加或删除行时,都必须使数据源保持一致。 In your case you're removing object fron arrayForRows but your numberOfRows is using a completely different data source: sections[section].words.count 在您的情况下,您将删除arrayForRows对象,但numberOfRows使用的是完全不同的数据源: sections[section].words.count

So you should either 所以你应该

  1. Keep numberOfRows as it is and remove from sections[indexPath.section].words array 保持numberOfRows sections[indexPath.section].words并从sections[indexPath.section].words数组中删除
  2. Change your nubmerOfRows method to return arrayForRows.count 更改您的nubmerOfRows方法以返回arrayForRows.count

Just make your choice what is your dataSource and pick the right option. 只需选择什么是数据源,然后选择正确的选项即可。 Since you have multiple sections, you should follow the first suggestion. 由于您有多个部分,因此应遵循第一个建议。

Edit: 编辑:

You're getting index out of bounds error because it seems you never update your sections array. 您正在获取index out of bounds错误,因为似乎您从未更新过sections数组。 You should fill all those words arrays for every section, right? 您应该为每个部分填充所有这些words数组,对吗? But judging by the code you provided they are always empty. 但是从您提供的代码来看,它们始终为空。

Basically your implementation should look like this: 基本上,您的实现应如下所示:

  1. ViewController A displays a list of sections with words starting from a particular letter in every section ViewController A显示一个节列表,每个节中的单词都以一个特定字母开头
  2. ViewController B creates new elements and save them to UserDefaults. ViewController B创建新元素并将其保存到UserDefaults。
  3. VC A should update sections array every time in viewWillAppear method. VC A应该每次在viewWillAppear方法中更新sections数组。 Just make a loop through all elements in the array from UserDefaults and put every element into the right Section object. 只需从UserDefaults中遍历数组中的所有元素,然后将每个元素放入正确的Section对象即可。
  4. Every time a row is deleted in VC A you must remove it from a corresponding Section object, otherwise app will crash. 每次在VC A中删除一行时,都必须将其从相应的Section对象中删除,否则应用程序将崩溃。 Also you should remove the same object from UserDefaults array to keep those changes persistent. 另外,您应该从UserDefaults数组中删除同一对象,以使这些更改保持不变。

Example for a function to insert a word into your sections array: 将单词插入您的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)
        }
    }
}

Maybe the problem is that you delete row from table view before deleting data? 也许问题是您在删除数据之前从表视图中删除了行? try to delete object from your array and only then delete row from tableView 尝试从数组中删除对象,然后再从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()

Also try to look at your numberOfRowsInSection method. 还尝试查看您的numberOfRowsInSection方法。 I guess it should return 我想它应该回来

arrayForRows.count

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM