ios - numero - ¿Cómo eliminar una celda en tableView haciendo clic en un botón en una celda? Usando coreData
numero de filas y columnas de numbers (2)
Creo una aplicación ToDo List.
Usé tableView para enumerar las tareas. Y uso una clase personalizada para celular. En Cell contentView, tengo una etiqueta y un botón hecho. Implementé con éxito la acción de hacer clic de botón en mi código. Funciona bien.
Problema
Cuando hago clic en el botón hecho, borro la última tarea añadida. Pero no el clic. Y cuando intento volver a hacer clic en el botón Hecho, no realiza ninguna acción. Cómo resolver este error
GIF agregado a continuación, haz clic en el enlace
Clase de entidad pendiente
Importar importación Importar CoreData
public class ToDo: NSManagedObject {
public override func awakeFromInsert() {
self.created = NSDate()
}
}
MainVC
import UIKit
import CoreData
class MainVC: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
var controller: NSFetchedResultsController<ToDo>!
@IBOutlet weak var taskTextField: CustomTextField!
@IBOutlet weak var tableView: UITableView!
var toDo: ToDo!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// generateData()
attemptFetch()
}
// to give view to cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell
configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
return cell
}
// custom function
func configureCell(cell: ItemCell, indexPath: NSIndexPath) {
let toDo = controller.object(at: indexPath as IndexPath)
// call the method on the ItemCell
cell.configureCell(toDo: toDo)
// done button click
cell.doneBtn.tag = indexPath.row
cell.doneBtn.addTarget(self, action: #selector(MainVC.donePressed), for: UIControlEvents.touchUpInside)
}
// when select a cell
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// it ensure it have object and atleast one object in there
if let objs = controller.fetchedObjects, objs.count > 0 {
let task = objs[indexPath.row]
performSegue(withIdentifier: "ItemDetailsVC", sender: task)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ItemDetailsVC" {
if let destination = segue.destination as? ItemDetailsVC {
if let task = sender as? ToDo {
destination.taskDetails = task
}
}
}
}
// count of cells
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// we check here if any sections then take info of them and count
if let sections = controller.sections {
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
return 0
}
// column count
func numberOfSections(in tableView: UITableView) -> Int {
if let sections = controller.sections {
return sections.count
}
return 0
}
// give height of a cell
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
// fetching function
func attemptFetch() {
// create a fetch request with fetching Entity
let fetchRequest: NSFetchRequest<ToDo> = ToDo.fetchRequest()
// sorting area
let dateSort = NSSortDescriptor(key: "created", ascending: true)
fetchRequest.sortDescriptors = [dateSort]
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self
self.controller = controller
// actual fetching
do {
try controller.performFetch()
} catch {
let error = error as NSError
print("/(error)")
}
}
// when tableView changes this function starts listen for changes and
// it will handle that for you
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
// this function will listen for when we make change
// insertion, deletion .. etc
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case.insert:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break
case.delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break
case.update:
if let indexPath = indexPath {
let cell = tableView.cellForRow(at: indexPath)
//update the cell data
configureCell(cell: cell as! ItemCell, indexPath: indexPath as NSIndexPath)
}
break
case.move:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break
}
}
@IBAction func addBtnPressed(_ sender: UIButton) {
if taskTextField.text != "" && taskTextField.text != nil {
toDo = ToDo(context: context)
if let task = taskTextField.text {
toDo.title = task
}
ad.saveContext()
taskTextField.text = ""
self.tableView.reloadData()
}
}
// done button
func donePressed() {
if toDo != nil {
context.delete(toDo)
ad.saveContext()
}
}
func generateData() {
let task = ToDo(context: context)
task.title = "alwin"
let task1 = ToDo(context: context)
task1.title = "rambo"
let task2 = ToDo(context: context)
task2.title = "monisha"
let task3 = ToDo(context: context)
task3.title = "wounderlist"
let task4 = ToDo(context: context)
task4.title = "presentation"
let task5 = ToDo(context: context)
task5.title = "roundup"
// to save data
ad.saveContext()
}
}
ItemDetailsVC
import UIKit
class ItemDetailsVC: UIViewController {
var taskDetails: ToDo?
@IBOutlet weak var detailsLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// to clear the <DreamLIst to < only
if let topItem = self.navigationController?.navigationBar.topItem {
topItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.plain, target: nil, action: nil)
// this is execute when tap on an existing cell
if taskDetails != nil {
loadItemData()
}
}
}
func loadItemData() {
if let task = taskDetails {
detailsLbl.text = task.title
}
}
override func viewDidLayoutSubviews() {
detailsLbl.sizeToFit()
}
@IBAction func deletePressed(_ sender: UIBarButtonItem) {
if taskDetails != nil {
context.delete(taskDetails!)
ad.saveContext()
}
_ = navigationController?.popViewController(animated: true)
}
}
guión gráfico, haga clic en el enlace a continuación
ItemCell
import UIKit
class ItemCell: UITableViewCell {
@IBOutlet weak var taskTitle: UILabel!
@IBOutlet weak var doneBtn: UIButton!
var toDo: ToDo?
func configureCell(toDo: ToDo) {
taskTitle.text = toDo.title
}
}
De acuerdo, entonces debes crear un punto de venta de IBAction para tu botón en ItemCell y luego crear un protocolo de esta forma:
protocol ItemDelegate {
func clicked()
}
class ItemCell: UITableViewViewCell {
var delegate : ItemDelegate?
var indexPath: IndexPath?
//call delegate?.clicked() where you have the gesture recogniser
}
Luego en cellForRowAtIndexPath
cell.delegate = self
cell.indexPath = indexPath
Luego implementa la extensión para tu clase:
extension MyTableView: ItemDelegate {
func clicked(indexPath: IndexPath) {
//dismiss cell for indexPath
}
}
OK Actualmente está configurando el selector de su botón hecho fuera de su contenedor (celda), esto es una mala práctica en general, está configurando la celda con un ToDo pero no asignando el opcional dentro de la celda, supuestamente allí para mantener una referencia a el ToDo.
En mi opinión, cambiaría esto ligeramente para que guarde la referencia al ToDo en primer lugar:
func configureCell(toDo: ToDo) {
self.toDo = toDo
taskTitle.text = toDo.title
}
Ahora, en su celda, cree un protocolo, luego configure la celda con un ToDo y un delegado, y luego presione el botón para indicarle al delegado que se presionó su botón con el ToDo relevante ...
protocol ToDoCellDelegate: class {
func toDoCellButtonPressed(todo: ToDo?)
}
Ahora en su celda configure como:
func configureCell(toDo: ToDo, delegate: ToDoCellDelegate) {
self.delegate = delegate
self.toDo = toDo
taskTitle.text = toDo.title
}
y agregue una referencia al delegado en la celda:
weak var delegate: ToDoCellDelegate?
ahora cambie su selector de botones a un func dentro de la celda
func buttonPressed() {
self.delegate?.cellToDoButtonPressed(toDo: toDo)
}
Luego, en su VC se conforma al delegado que pasa auto en la configuración e implementa el delegado:
extension ItemDetailsVC: ToDoCellDelegate {
func toDoCellButtonPress(toDo: ToDo?) {
if let t = toDo {
//tell context to delete todo and remove cell.
}
}
}