Возможно, вы меняете источник данных, а затем перезагружаете строки по индексным путям, которых еще нет.
Это не так просто, но давайте рассмотрим пример: перед тем, как начать вводить, результат поиска будет содержать что-то вроде этого:
["aa", "ab", "ba", "bb"]
Затем вы введете "a"
в строку поиска и источник данных изменится на: ["aa", "ab"]
tableView.deleteRows(at: [IndexPath(row:3, section: 0), IndexPath(row:4, section: 0)], with: .automatic)
, затем вы удаляете все в этой строке поиска, и ваш источник данных изменится на значение по умолчанию: ["aa", "ab", "ba", "bb"]
, поэтому в этом случае вам нужно позвонить:
tableView.insertRows(at: [IndexPath(row:3, section: 0), IndexPath(row:4, section: 0)], with: .automatic)
Я создал несколько рабочих пример - без источника раскадровки, я думаю, довольно просто воссоздать его в соответствии с этим классом.
class SearchCell: UITableViewCell {
@IBOutlet weak var textField:UITextField?
}
class TextCell: UITableViewCell {
@IBOutlet weak var label:UILabel?
}
class ViewController: UIViewController, UITableViewDataSource, UITextFieldDelegate {
@IBOutlet weak var tableView: UITableView?
weak var firstSectionTextField: UITextField?
var originalDataSource:[[String]] = [["aa","ab","ba","bb"], ["aa","ab","ba","bb"]]
var dataSource:[[String]] = []
let skipRowWithSearchInput = 1
override func viewDidLoad() {
super.viewDidLoad()
dataSource = originalDataSource
tableView?.tableFooterView = UIView()
tableView?.tableHeaderView = UIView()
}
func numberOfSections(in tableView: UITableView) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource[section].count + skipRowWithSearchInput
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0, let cell = tableView.dequeueReusableCell(withIdentifier: "search", for: indexPath) as? SearchCell {
cell.textField?.removeTarget(self, action: #selector(textFieldDidChangeText(sender:)), for: .editingChanged)
cell.textField?.addTarget(self, action: #selector(textFieldDidChangeText(sender:)), for: .editingChanged)
if indexPath.section == 0 {
firstSectionTextField = cell.textField
}
return cell
} else if let cell = tableView.dequeueReusableCell(withIdentifier: "text", for: indexPath) as? TextCell {
cell.label?.text = dataSource[indexPath.section][indexPath.row - skipRowWithSearchInput]
return cell
} else {
return UITableViewCell()
}
}
@objc func textFieldDidChangeText(sender: UITextField) {
let section = sender == firstSectionTextField ? 0 : 1
let text = sender.text ?? ""
let oldDataSource:[String] = dataSource[section]
//if the search bar is empty then use the original data source to display all results, or initial one
let newDataSource:[String] = text.count == 0 ? originalDataSource[section] : originalDataSource[section].filter({$0.contains(text)})
var insertedRows:[IndexPath] = []
var deletedRows:[IndexPath] = []
var movedRows:[(from:IndexPath,to:IndexPath)] = []
//resolve inserted rows
newDataSource.enumerated().forEach { (tuple) in let (toIndex, element) = tuple
if oldDataSource.contains(element) == false {
insertedRows.append(IndexPath(row: toIndex + skipRowWithSearchInput, section: section))
}
}
//resolve deleted rows
oldDataSource.enumerated().forEach { (tuple) in let (fromIndex, element) = tuple
if newDataSource.contains(element) == false {
deletedRows.append(IndexPath(row: fromIndex + skipRowWithSearchInput, section: section))
}
}
//resolve moved rows
oldDataSource.enumerated().forEach { (tuple) in let (index, element) = tuple
if newDataSource.count > index, let offset = newDataSource.firstIndex(where: {element == $0}), index != offset {
movedRows.append((from: IndexPath(row: index + skipRowWithSearchInput, section: section), to: IndexPath(row: offset + skipRowWithSearchInput, section: section)))
}
}
//now set dataSource for uitableview, right before you are doing the changes
dataSource[section] = newDataSource
tableView?.beginUpdates()
if insertedRows.count > 0 {
tableView?.insertRows(at: insertedRows, with: .automatic)
}
if deletedRows.count > 0 {
tableView?.deleteRows(at: deletedRows, with: .automatic)
}
movedRows.forEach({
tableView?.moveRow(at: $0.from, to: $0.to)
})
tableView?.endUpdates()
}
}
результат:

Если вам нужно что-то уточнить, не стесняйтесь спрашивать в комментарии.