Reputation: 1250
I am learning Swift by writing a single table app view which lists the content of a Core Data table (entity) upon start-up. Then the user can reorder the rows in the table view.
I need to be able to save the newly ordered rows such that they replace the previous database table, so when the user starts the app again, the new order is shown.
The editing (re-ordering) feature is activated by a long press and calls
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
self.projectTableView.moveRow(at: sourceIndexPath, to: destinationIndexPath)
}
A second long press then inactivates the editing feature:
// Called when long press occurred
@objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer){
if gestureRecognizer.state == .ended {
let touchPoint = gestureRecognizer.location(in: self.projectTableView)
if let indexPath = projectTableView.indexPathForRow(at: touchPoint) {
if self.projectTableView.isEditing == true {
self.projectTableView.isEditing = false
db.updateAll() //this is a stub
} else {
self.projectTableView.isEditing = true
}
}
}
}
The call to db.updateAll() in 'handleLongPress' above is just a blank, and I don't know how to update the database. Is there a way to read the content of the tableView in the new sequence into an array, then replace the table in the db? Feels a little "brute force" but can't see any other solution.
Upvotes: 0
Views: 777
Reputation: 1398
Ok you can achieve that in several ways :
1- Using NSFetchedResultsController
, here you can automatically synchronizing changes made to your core data persistence store with a table view,
so quickly here are the steps :
NSFetchedResultsControllerDelegate
NSFetchedResultsController
with you core data model NSFetchRequest
, call NSFetchedResultsController
initializer with the request, then assign it to your instance declared before performFetch
method on your instance And now you can implement the delegates, here you want didChange
, so something like that :
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChange anObject: Any,
at indexPath: IndexPath?,
for type: NSFetchedResultsChangeType,
newIndexPath: IndexPath?) {
switch type {
/*
....
*/
case .move:
if let deleteIndexPath = indexPath {
self.tableView.deleteRows(at: [deleteIndexPath], with: .fade)
}
if let insertIndexPath = newIndexPath {
self.tableView.insertRows(at: [insertIndexPath], with: .fade)
}
}
}
2- Second option which personally i prefer it over the NSFetchedResultscontroller
You can add a property in your model (core data model). That can be an Int for example "orderNum". So when you fetch request you can order the result using this prperty.
So if your table view cell re-arranged, after implementing moveItem method you can update this property for all your objects(loop over them) and they will be as they are displayed.
try to save your managed object context now,
Next time when you want to fetch request you can use a sort descriptor to sort on the "orderNum".
Upvotes: 4
Reputation: 1117
Maybe updating your data source (by removing and re-inserting the item) when moveRowAt is called would be better?
So something like:
// assuming your data source is an array of names
var data = ["Jon", "Arya", "Tyrion", "Sansa", "Winterfell"]
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
self.projectTableView.moveRow(at: sourceIndexPath, to: destinationIndexPath)
let item = self.data.remove(at: sourceIndexPath.row)
if sourceIndexPath.row > destinationIndexPath.row {
// "Sansa" was moved to be between "Jon" and "Arya"
self.data.insert(item, at: destinationIndexPath.row
} else {
// if the new destination comes after previous location i.e. "Sansa"
// was moved to the end of the list
self.data.insert(item, at: destinationIndexPath.row - 1
}
}
Upvotes: 1