【发布时间】:2019-10-01 12:53:45
【问题描述】:
我创建了一个 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 模型:
【问题讨论】:
标签: uitableview core-data xcode10 ios12