Skip to content

Instantly share code, notes, and snippets.

@kayoslab
Last active May 31, 2021 17:59
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kayoslab/088aac932870bdedcdd254652384f8ca to your computer and use it in GitHub Desktop.
Save kayoslab/088aac932870bdedcdd254652384f8ca to your computer and use it in GitHub Desktop.
Reorder UITableViewCells and modify the underlying CoreData Model
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
navigationItem.rightBarButtonItem = editButtonItem
}
// MARK: - Fetched results controller
var managedObjectContext = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext
var fetchedResultsController: NSFetchedResultsController<Object>? {
if let instance = instanceFetchedResultsController {
return instance
}
let fetchRequest: NSFetchRequest<Object> = Object.fetchRequest()
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = fetchBatchSize
// Edit the sort key as appropriate.
let dispatchSortDescriptor = NSSortDescriptor(key: "sort", ascending: true)
fetchRequest.sortDescriptors = [dispatchSortDescriptor]
if let managedObjectContext = managedObjectContext {
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: "destination", cacheName: nil)
aFetchedResultsController.delegate = self
instanceFetchedResultsController = aFetchedResultsController
do {
try instanceFetchedResultsController?.performFetch()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
return instanceFetchedResultsController
}
return nil
}
fileprivate var instanceFetchedResultsController: NSFetchedResultsController<Object>?
}
// MARK: - UITableViewDelegate
extension TableViewController {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "cellShowDetail", sender: tableView.cellForRow(at: indexPath))
}
override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
guard let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.newBackgroundContext() else { return }
/**
Switches two existing rows sort key
- Parameter sourceIndexPath: First Row
- Parameter destinationIndexPath: Second Row
*/
func switchRows(surceIndexPath: IndexPath, destinationIndexPath: IndexPath) {
guard let fetchedSourceObject = fetchedResultsController?.object(at: sourceIndexPath) else { return }
guard let fetchedDestinationObject = fetchedResultsController?.object(at: destinationIndexPath) else { return }
guard let storedSourceObject = context.object(with: fetchedSourceObject.objectID) as? Object else { return }
guard let storedDestinationObject = context.object(with: fetchedDestinationObject.objectID) as? Object else { return }
guard let sourceSort = fetchedSourceObject.sortId else { return }
guard let destinationSort = fetchedDestinationObject.sortId else { return }
storedSourceObject.sortId = destinationSort
storedDestinationObject.sortId = sourceSort
}
/**
Stores the destination's sort Key into the origin-object
- Parameter origin: An Int defining the row of the current row
- Parameter destination: An Int defining the row from which the data is loaded
*/
func incrementSortIndex(forOriginRow origin: Int, destinationRow destination: Int) {
let mutableSourceIndex = IndexPath(row: origin, section: destinationIndexPath.section)
let mutableDestinationIndex = IndexPath(row: destination, section: destinationIndexPath.section)
guard let fetchedSourceObject = fetchedResultsController?.object(at: mutableSourceIndex) else { return }
guard let fetchedDestinationObject = fetchedResultsController?.object(at: mutableDestinationIndex) else { return }
guard let storedSourceObject = context.object(with: fetchedSourceObject.objectID) as? Object else { return }
guard let destinationSort = fetchedDestinationObject.sortId else { return }
storedSourceObject.sortId = destinationSort
}
/**
Save the current BackgroundContext
*/
func saveContext() {
do {
if context.hasChanges {
try context.save()
}
} catch {
print(error.localizedDescription)
}
}
if sourceIndexPath == destinationIndexPath {
// Nothing to do here, drag-and-drop and both rows are the same.
return
} else if abs(sourceIndexPath.row - destinationIndexPath.row) == 1 {
// If the rows were just switched
switchRows(surceIndexPath: sourceIndexPath, destinationIndexPath: destinationIndexPath)
return
} else if sourceIndexPath.row < destinationIndexPath.row {
// Move rows upwards
guard let fetchedSourceObject = fetchedResultsController?.object(at: sourceIndexPath) else { return }
guard let fetchedDestinationObject = fetchedResultsController?.object(at: destinationIndexPath) else { return }
// iterate over the unmoved rows, which are pushed downwards
for row in sourceIndexPath.row + 1 ..< destinationIndexPath.row {
incrementSortIndex(forOriginRow: row, destinationRow: row - 1)
}
// drag Source-Object upwards
guard let storedSourceObject = context.object(with: fetchedSourceObject.objectID) as? Object else { return }
guard let destinationSort = fetchedDestinationObject.sortId else { return }
storedSourceObject.sortId = destinationSort
} else if sourceIndexPath.row > destinationIndexPath.row {
// Move rows downwards
guard let fetchedSourceObject = fetchedResultsController?.object(at: sourceIndexPath) else { return }
guard let fetchedDestinationObject = fetchedResultsController?.object(at: destinationIndexPath) else { return }
// iterate over the unmoved rows, which are pushed upwards
for row in destinationIndexPath.row ..< sourceIndexPath.row {
incrementSortIndex(forOriginRow: row, destinationRow: row + 1)
}
// Source-Object is moved downwards
guard let storedSourceObject = context.object(with: fetchedSourceObject.objectID) as? Object else { return }
guard let destinationSort = fetchedDestinationObject.sortId else { return }
storedSourceObject.sortId = destinationSort
}
// Save the current Context
saveContext()
}
override func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath {
if sourceIndexPath.section != proposedDestinationIndexPath.section {
var row = 0
if sourceIndexPath.section < proposedDestinationIndexPath.section {
row = self.tableView(tableView, numberOfRowsInSection: sourceIndexPath.section) - 1
}
return IndexPath(row: row, section: sourceIndexPath.section)
}
return proposedDestinationIndexPath
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment