Я создал приложение для iOS, в котором хранится список (скажем, имена). Кроме того, я добавил функциональные возможности, такие как удаление, поиск и изменение порядка строк таблицы. Ниже скриншот приложения:
Проблема, с которой я сталкиваюсь, заключается в том, что, когда я переупорядочиваю список, сначала нажимая кнопку редактирования, кажется, что код работает нормально. На экранах ниже я поменял первые две строки, которые, кажется, делают так, как я хочу.
Посмотрите на два скриншота ниже:
Но когда я выполняю функцию поиска, чередующиеся строки возвращаются в исходное положение, как показано на первом изображении. Так как я использую CoreData в качестве постоянного хранилища. Я пытаюсь найти решение этой проблемы, но пока нет успеха. Вот как это выглядит после выполнения функции поиска:
Это мой код:
import UIKit
import CoreData
class ViewController: UIViewController, UISearchBarDelegate, UISearchDisplayDelegate {
// IBOutlets
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var editButton: UIBarButtonItem!
// Global declaration
var people: [NSManagedObject] = []
// Below is a computed property
var appDelegate: AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
override func viewDidLoad() {
super.viewDidLoad()
// The below line is for giving a title to the View Controller.
// title = "The List"
searchBar.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
// The below line hides empty rows in a tableView
tableView.tableFooterView = UIView()
}
//MARK: - IBAction addName implementation
@IBAction func addName(_ sender: UIBarButtonItem) {
let alert = UIAlertController(title: "New Name", message: "Add a new name", preferredStyle: .alert)
let saveAction = UIAlertAction (title: "Save", style: .default) {
[unowned self] action in
guard let textField = alert.textFields?.first, let nameToSave = textField.text else {
return
}
self.save(name: nameToSave)
self.tableView.reloadData() // this is to reload the table data
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
// The below code handles the validation operation. In this case, we are checking wheather the field is empty and if it is 'save' button is disabled.
alert.addTextField(configurationHandler: { (textField) in
textField.text = ""
textField.placeholder = "Enter something...."
saveAction.isEnabled = false
NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification, object: textField, queue: OperationQueue.main) { (notification) in
saveAction.isEnabled = textField.text!.count > 0
}
})
alert.addAction(saveAction)
alert.addAction(cancelAction)
present(alert, animated: true)
}
// MARK: - SAVING TO CORE DATA
// CoreData kicks in here!
func save(name: String) {
// 1
let managedContext = appDelegate.persistentContainer.viewContext
// 2
let entity = NSEntityDescription.entity(forEntityName: "Person", in: managedContext)!
let person = NSManagedObject(entity: entity, insertInto: managedContext)
// 3
person.setValue(name, forKeyPath: "name")
// 4
do {
try managedContext.save()
people.append(person)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
// MARK: - FETCHING FROM CORE DATA
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 1
let managedContext = appDelegate.persistentContainer.viewContext
// 2
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Person")
// 3
do {
people = try
managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
// MARK: - searchBar functionality implementation
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText != "" {
var predicate: NSPredicate = NSPredicate()
predicate = NSPredicate(format: "name contains[c] '\(searchText)'")
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
fetchRequest.predicate = predicate
do {
people = try context.fetch(fetchRequest) as! [NSManagedObject]
} catch {
print("Could not get search data!")
}
} else {
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
do {
people = try context.fetch(fetchRequest) as! [NSManagedObject]
} catch {
print("Error in loading data.")
}
}
tableView.reloadData() // This line reloads the table whether search is performed or not.
}
// This function closes the search bar when cancel button is tapped.
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
}
@IBAction func editButton(_ sender: Any) {
tableView.isEditing = !tableView.isEditing
// This switch case is for changing the title when editing
switch tableView.isEditing {
case true:
editButton.title = "Done"
case false:
editButton.title = "Edit"
}
}
}
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let person = people[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = person.value(forKeyPath: "name") as? String
return cell
}
// This function sets the height for a row programatically.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 55.0
}
// Determine whether a given row is eligible for reordering or not.
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}
// Process the row move. This means updating the data model to correct the item indices.
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let itemToMove = people.remove(at: sourceIndexPath.row)
people.insert(itemToMove, at: destinationIndexPath.row)
tableView.reloadData()
}
}
// MARK: - UITableViewDelegate
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// handle delete (by removing the data from your array and updating the tableview)
// MARK: - Delete the person from core data
let person = people[indexPath.row]
let managedContext = appDelegate.persistentContainer.viewContext
managedContext.delete(person)
try? managedContext.save() // This the short version of do-catch block used in above functions to save and fetch data
// remove the person from cache / CoreData
people.remove(at: indexPath.row)
// delete row from table view
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
}
Буду признателен за любую помощь, поскольку я пробую это несколько недель.
Моя модель CoreData: