Reputation: 1987
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
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