Всем привет. Я новичок. Решил написать простенькое приложение на основе TableView. В каждой ячейке есть счётчик, который меняет своё значение по нажатию кнопок + и -, которые находятся в той же ячейке. В CoreData создана сущность Counter с одним свойством value. Хочу сделать так, чтоб таблицу можно было редактировать(isEditing = true). Ниже код, в котором я попытался реализовать функционал методами tableView. Перемещение вроде бы проходит нормально, а вот при удалении элемента, таблица ведёт себя не адекватно: меняет значения соседних ячеек, либо приложение падает(видно на прикреплённом видео). Как нужно правильно реализовывать отображение ячеек при isEditing = true используя CoreData? Можно ли как-то использовать для этого NSFetchedResultsControllerDelegate?
https://youtu.be/oaExcob7B2s - видео
import UIKit
import CoreData
class CountersTableViewController: UITableViewController, CounterValueManager, NSFetchedResultsControllerDelegate{
var fetchController: NSFetchedResultsController<Counter>!
lazy var context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext
var counters = [Counter]()
@IBAction func addCounterButton(_ sender: UIBarButtonItem) {
let counter = Counter(context: context!)
counter.value = 0
do {
try context?.save()
counters.append(counter)
tableView.reloadData()
} catch {
print(error.localizedDescription)
}
}
@IBAction func editButtonPressed(_ sender: UIBarButtonItem) {
self.isEditing = !self.isEditing
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
let fetchRequest: NSFetchRequest<Counter> = Counter.fetchRequest()
let sortDescriptor: NSSortDescriptor = NSSortDescriptor(key: "value", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context!, sectionNameKeyPath: nil, cacheName: nil)
//fetchController.delegate = self
do {
try fetchController.performFetch()
counters = fetchController.fetchedObjects!
tableView.reloadData()
} catch {
print(error)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return counters.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? CountersTableViewCell
cell?.delegate = self
cell?.increaseButton.tag = indexPath.row
cell?.decreaseButton.tag = indexPath.row
cell?.valueLabel.text = String(counters[indexPath.row].value)
return cell!
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let counter = self.counters[indexPath.row]
context?.delete(counter)
do {
try context?.save()
self.counters.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
} catch {
print(error.localizedDescription)
}
} 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
}
}
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
let itemToMove = counters[fromIndexPath.row]
counters.remove(at: fromIndexPath.row)
counters.insert(itemToMove, at: to.row)
do {
try context?.save()
tableView.reloadData()
} catch {
print(error.localizedDescription)
}
}
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
// MARK: - FetchedResultsDelegate
// func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
// tableView.beginUpdates()
// }
// func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
// switch type {
// case .insert: guard let indexPath = newIndexPath else {break}
// tableView.insertRows(at: [indexPath], with: .fade)
// case .delete: guard let indexPath = indexPath else {break}
// tableView.deleteRows(at: [indexPath], with: .fade)
// case .update: guard let indexPath = indexPath else {break}
// tableView.reloadRows(at: [indexPath], with: .fade)
// tableView.reloadData()
//
// default:
// tableView.reloadData()
// }
// counters = controller.fetchedObjects as! [Counter]
// }
//
// func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
// tableView.endUpdates()
// }
// MARK: - CounterValueManager funcs
func increaseAt(index: Int) {
let counter = counters[index]
counter.value += 1
do {
try context?.save()
tableView.reloadData()
} catch {
print(error.localizedDescription)
}
}
func decreaseAt(index: Int) {
let counter = counters[index]
guard counter.value >= 1 else {return}
counter.value -= 1
do {
try context?.save()
tableView.reloadData()
} catch {
print(error.localizedDescription)
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}