Marlhex
Marlhex

Reputation: 1987

tableView(_:commit:forRowAt:) not being triggered

This class should be triggering a swipe or slide action into the table view cells, from right to left and that will allow me to delete based on the method tableView(_:commit:forRowAt:), but it is not being triggered and I have no idea why.

Code Class:

import UIKit
import CoreData

class ViewController: UIViewController {
    
    // MARK: - Core Data Properties
    var dataController: DataController!
    
    var fetchedResultsController: NSFetchedResultsController<Garment>!
    
    // MARK: - Properties & Variables
    
    let garmentCellID = "GarmentCellID"
    
    let segmentedControl = UISegmentedControl(items: ["Alpha", "Creation Time"])
    
    let tableView = UITableView(frame: .zero, style: .plain)
    
    // MARK: - Init
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        view.isUserInteractionEnabled = true
        
        //First Setting up the NavBar with it's features
        settingUpNavBar()
        
        setupSegmentedControl()
        
        setupTableView()
        
        setUpFetchedResultsController()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        setUpFetchedResultsController()
        
        if let indexPath = tableView.indexPathForSelectedRow {
            tableView.deselectRow(at: indexPath, animated: false)
            tableView.reloadRows(at: [indexPath], with: .fade)
        }
    }
    
    //Automatic TableView height fit to its content
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        //Initialize the tableView with automatic fit for its height based on its content
        tableView.frame = CGRect(x: tableView.frame.origin.x, y: tableView.frame.origin.y, width: tableView.frame.size.width, height: tableView.contentSize.height)
        
        //Update the table with the actual height needed
        tableView.reloadData()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        fetchedResultsController = nil
    }
    
    fileprivate func settingUpNavBar() {
        let pluNavBarBtn = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(handleAdd))
        
        navigationItem.setRightBarButton(pluNavBarBtn, animated: true)
        
        navigationItem.title = "List"
    }
    
    fileprivate func setupSegmentedControl() {
        //Second Set up the second view, done.
        
        //Third: setup the segment
        segmentedControl.addTarget(self, action: #selector(handleSegmentChange), for: .valueChanged)
        
        view.addSubview(segmentedControl)
        
        segmentedControl.setUpAnchor(top: view.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, topPadding: 100, leftPadding: 10, bottomPadding: 0, rightPadding: 10, width: 0, height: 40)
    }
    
    // MARK: - TableView set up
    fileprivate func setupTableView() {
        tableView.delegate = self
        tableView.dataSource = self
        tableView.alwaysBounceVertical = true
        
        tableView.register(GarmentCell.self, forCellReuseIdentifier: garmentCellID)
        
        view.addSubview(tableView)
        
        tableView.setUpAnchor(top: segmentedControl.bottomAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, topPadding: 10, leftPadding: 0, bottomPadding: 0, rightPadding: 0, width: 0, height: 0)
    }
    
    fileprivate func setUpFetchedResultsController() {
        //Reusing code to use the fetchedResultsController
        //NSFetchRequest is a generic type that's why <> and we specify Garment so it will work with a specific managed object subclass. After the = it will return a fetch request initialized with that entity.
        let fetchRquest: NSFetchRequest<Garment> = Garment.fetchRequest()
        
        //Sort rule: newest to oldest
        
        //Using a descriptor is like a SQL: ORDER by
        let sortDescriptor = NSSortDescriptor(key: "name", ascending: false)
        fetchRquest.sortDescriptors = [sortDescriptor]
        //End of reused code
        
        //Starting the initialzer of resultsFetchedController
        fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRquest, managedObjectContext: dataController.viewContext, sectionNameKeyPath: nil, cacheName: "cach")
        
        fetchedResultsController.delegate = self
        
        do {
            //This throws so we wrap it into a do catch block
            try fetchedResultsController.performFetch()
        } catch {
            fatalError("The fetch could not be performed: \(error.localizedDescription)")
        }
    }
    
    // MARK: - Selectors
    
    @objc func handleSegmentChange() {
        print("segment")
        //Data persistance calls
    }
    
    @objc func handleAdd() {
        print("Plus btn")
        
        let addVC = AddController()
        
        let nav2 = UINavigationController(rootViewController: addVC)
        
        navigationController?.present(nav2, animated: true, completion: nil)
    }
    
    /// Deletes the garment at the specified index path
    func deleteGarment(at indexPath: IndexPath) {
        let garmentToDelete = fetchedResultsController.object(at: indexPath)
        dataController.viewContext.delete(garmentToDelete)
        try? dataController.viewContext.save()
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fetchedResultsController.sections?[section].numberOfObjects ?? 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let aGarment = fetchedResultsController.object(at: indexPath)
        
        let cell = tableView.dequeueReusableCell(withIdentifier: garmentCellID, for: indexPath) as! GarmentCell
        
        cell.textLabel?.text = aGarment.name
        
        return cell
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return fetchedResultsController.sections?.count ?? 1
    }
    
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            deleteGarment(at: indexPath)
        }
    }
    
    override func setEditing(_ editing: Bool, animated: Bool) {
        super.setEditing(editing, animated: animated)
        tableView.setEditing(editing, animated: animated)
    }
}

extension ViewController: NSFetchedResultsControllerDelegate {
    
    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
        switch type {
        case .insert:
            tableView.insertRows(at: [newIndexPath!], with: .fade)
            break
        case .delete:
            tableView.deleteRows(at: [indexPath!], with: .fade)
            break
        case .update:
            tableView.reloadRows(at: [indexPath!], with: .fade)
        case .move:
            tableView.moveRow(at: indexPath!, to: newIndexPath!)
        @unknown default:
            fatalError()
        }
    }
    
    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
        let indexSet = IndexSet(integer: sectionIndex)
        switch type {
        case .insert: tableView.insertSections(indexSet, with: .fade)
        case .delete: tableView.deleteSections(indexSet, with: .fade)
        case .update, .move:
            fatalError("Invalid change type in controller(_:didChange:atSectionIndex:for:). Only .insert or .delete should be possible.")
        @unknown default:
            fatalError()
        }
    }
    
    func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.beginUpdates()
    }
    
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.endUpdates()
    }
}

Upvotes: 0

Views: 425

Answers (1)

Marlhex
Marlhex

Reputation: 1987

Credit to @DonMag: feel free to post it and I'll make it as the answer.

Removed the whole method ViewDidLayoutSubview

//Automatic TableView height fit to its content
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    //Initialize the tableView with automatic fit for its height based on its content
    tableView.frame = CGRect(x: tableView.frame.origin.x, y: tableView.frame.origin.y, width: tableView.frame.size.width, height: tableView.contentSize.height)
    
    //Update the table with the actual height needed
    tableView.reloadData()
}

I just needed to remove the tableView.reloadData() and it will work like a charm.

Upvotes: 1

Related Questions