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

时间:2016-10-30 18:07:36

标签: ios swift tableview userdefaults

我按照此处提供的所有步骤操作:The proper way to delete rows from UITableView and update array from NSUserDefaults in Swift / iOS

然而,导致错误,说;

  

'无效更新:第0部分中的行数无效。数量   更新(2)后必须包含在现有部分中的行   等于之前该部分中包含的行数   update(2),加上或减去插入或删除的行数   该部分(0插入,1删除)和加号或减号的数量   移入或移出该部分的行(0移入,0移出)。'

我创建了

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

}

}

通过这种方式,从另一个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 []
 }

}

有什么问题?从我看到它的方式来看,我可以理解,如果我没有明确地写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
    }
}

2 个答案:

答案 0 :(得分:1)

每次添加或删除行时,都必须确保数据源保持一致。在您的情况下,您要移除arrayForRows对象,但您的numberOfRows使用完全不同的数据来源:sections[section].words.count

所以你应该

  1. 保持numberOfRows不变,然后从sections[indexPath.section].words数组
  2. 中删除
  3. 将您的nubmerOfRows方法更改为返回arrayForRows.count
  4. 只需选择您的dataSource是什么,然后选择正确的选项。由于您有多个部分,因此您应该遵循第一个建议。

    修改

    您收到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数组中删除相同的对象,以保持这些更改持久。
    5. 将单词插入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)
              }
          }
      }
      

答案 1 :(得分:0)

也许问题是你在删除数据之前从表视图中删除了行?尝试从数组中删除对象,然后从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