Implement a tablecell fade delete - swift

I'm following this answer to get that nice faded deleted cell. Currently when I swipe to delete, it's sharp, sudden and ugly. Other apps has that nice fade when cell is deleted.
In my app, the cell is not really deleted, the data is removed:
Swift 3, func editingStyle:
let tableSection = sections[sortedSections[indexPath.section]]
let tableItem = tableSection![indexPath.row]
// Removes item from array:
self.incomesDictionary.removeValue(forKey: tableItem.incomeId)
// Reloading the table data:
self.tableView.reloadData()
When I add self.tableView.deleteRows(at: [indexPath], with: .fade) above the ...reloadData() I get an error:
terminating with uncaught exception of type NSException
If I update the function with tableSection?.remove(at: indexPath.row), it still crashes. Any tips to get that smooth effect?
Edit:
I have a programmatically grouped cell and the error I got, after adding the .fade is:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete row 0 from section 0, but there are only 0 sections before the update'

Swift 5.0
https://www.hackingwithswift.com/example-code/uikit/how-to-swipe-to-delete-uitableviewcells
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
self.myArray.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}

Try this:
self.tableView.beginUpdates()
self.tableView.deleteRows(at: [indexPath], with: .fade)
self.tableView.endUpdates()
This will prepare the tableView for your animation

Related

Animate tableview cell when deleting

I have a tableview. The tableview cell contains images, labels. In this cell I have a button to delete the row. I want to animate the cell during deletion(ex. tinder like left or right swipe) or any animation to make the app actractive. How to do this type of animation. Or any recommended library ?
UITableViewCellEditingStyle will do the job for you for i.e
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
//do your necessary operations here
//reload your UITableView here
}
}
Swipe right to left to see the animation.
Typical Animation for your case :
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// your code
tableView.deleteRows(at: [indexPath], with: .automatic)
// your code
}
}
Alternate Way for custom Animation (Updated):
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .top) // Check (.)after for available animation example comment out below -
//tableView.deleteRowsAtIndexPaths([YourIndexPathYouWantToDeleteFrom], with: .left)
//tableView.deleteRowsAtIndexPaths([YourIndexPathYouWantToDeleteFrom], with: .right)
//tableView.deleteRowsAtIndexPaths([YourIndexPathYouWantToDeleteFrom], with: .up)
//tableView.deleteRowsAtIndexPaths([YourIndexPathYouWantToDeleteFrom], with: .down)
//tableView.deleteRowsAtIndexPaths([YourIndexPathYouWantToDeleteFrom], with: .fade)
//tableView.deleteRowsAtIndexPaths([YourIndexPathYouWantToDeleteFrom], with: .middle)
//tableView.deleteRowsAtIndexPaths([YourIndexPathYouWantToDeleteFrom], with: .bottom)
tableView.endUpdates()

Invalid update: invalid number of rows in section 0, the number of rows contained in an existing section after update

I keep getting this error when I swipe and delete a UITableCell
Terminating app due to uncaught exception
NSInternalInconsistencyException, reason: Invalid update: invalid
number of rows in section 0. The number of rows contained in an
existing section after the update (11) must be equal to the number of
rows contained in that section before the update (11), plus or minus
the number of rows inserted or deleted from that section (0 inserted,
1 deleted) and plus or minus the number of rows moved into or out of
that section (0 moved in, 0 moved out).
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = deleteAction(at: indexPath)
tableView.reloadData()
print(listOfCoins)
print(listOfCoins.count)
return UISwipeActionsConfiguration(actions: [delete])
}
func deleteAction(at indexPath: IndexPath) -> UIContextualAction {
let action = UIContextualAction(style: .destructive, title: "Delete") { (action, view, nil) in
self.listOfCoins.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
self.tableView.reloadData()
}
action.title = "Delete"
action.backgroundColor = .red
return action
}
I've tried taking out "self.tableView.deleteRows" which allows the app to run and deletes the indexPath from my source, but I don't know why the cell doesn't disappear when 'tableView.reloadData()' is called afterwards.
You only need
self.listOfCoins.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
So remove this
let delete = deleteAction(at: indexPath)
tableView.reloadData() // remove this line
And
self.tableView.deleteRows(at: [indexPath], with: .automatic)
self.tableView.reloadData() // remove this line
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = deleteAction(at: indexPath)
print(listOfCoins)
print(listOfCoins.count)
return UISwipeActionsConfiguration(actions: [delete])
}
func deleteAction(at indexPath: IndexPath) -> UIContextualAction {
let action = UIContextualAction(style: .destructive, title: "Delete") { (action, view, nil) in
self.listOfCoins.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
action.title = "Delete"
action.backgroundColor = .red
return action
}

Can't delete cells from UITableView, just the data from said cells

I am making an app that stores events and their data, and it was working flawlessly until I tried to add Core Data, since then, I've been having issues with it. One is the fact that using the delete func doesn't really delete the tablecell, just the data inside. If I try to delete the blank cell, it does nothing. Below is the code for the delete function I am using.
Also, my app for some reason starts with one of these blank cell, which can't be deleted.
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
PersistenceService.context.delete(events[indexPath.row])
do {
try PersistenceService.context.save()
eventLog.reloadData()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
}
You have to remove the item from the context and from the data source array and it's highly recommended to use deleteRows rather than reloading the entire table view to take advantage of the animation
PersistenceService.context.delete(events[indexPath.row])
events.remove(at: indexPath.row)
eventLog.deleteRows(at: [indexPath], with: .fade)
... save context

Variable crashes code

Sorry for the bad title, and I feel really dumb for asking this question.
I'm deleting a cell from my table and the first block of code runs just perfect, but when shortening the line by creating a variable the code crashes. Why?
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
emojisByCategories[indexPath.section].remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
This small change will cause a "libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)" error
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
var emojis = emojisByCategories[indexPath.section]
emojis.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
Build on Xcode Version 9.0 (9A235)
You have an array of arrays. Array in Swift is a struct which is a value type. When you assign an array to another variable, you are actually creating a copy of the array.
When you do:
var emojis = emojisByCategories[indexPath.section]
emojis.remove(at: indexPath.row)
you are modifying the copy in emojis. Nothing in emojisByCategories is actually being changed as a result of this code.
So now you tell the table view that a row has been deleted but your data source hasn't actually changed at all so you get the crash telling you about an invalid number of rows in a section.
The line:
emojisByCategories[indexPath.section].remove(at: indexPath.row)
doesn't have the same problem because you are not making a copy of any array and the values in emojisByCategories are being updated as expected.
You can make your second set of code work by adding a third line:
var emojis = emojisByCategories[indexPath.section]
emojis.remove(at: indexPath.row)
emojisByCategories[indexPath.section] = emojis
That 3rd line updates emojisByCategories with the updated emojis array so now your code won't crash.

Delete table row with animation

I want to delete a row with animation in my table view controller.
I use the following code:
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == .Delete) {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
let LM_ITEM = lebensmittel[indexPath.row]
managedObjectContext?.deleteObject(lebensmittel[indexPath.row])
self.DatenAbrufen()
}
}
but after press on delete, I get this error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
*** First throw call stack:
(0x1856382d8 0x1973040e4 0x185638198 0x1864eced4 0x18a296e5c 0x10010e278 0x10010ef9c 0x18a2b0ea4 0x18a3a6880 0x18a0e5398 0x18a0ce474 0x18a0e4d34 0x18a0a3f54 0x18a0de82c 0x18a0ddee4 0x18a0b1120 0x18a3522b8 0x18a0af634 0x1855f0240 0x1855ef4e4 0x1855ed594 0x1855192d4 0x18ef6f6fc 0x18a116f40 0x100134420 0x1979aea08)
libc++abi.dylib: terminating with uncaught exception of type NSException
You need to update your model before call tableView.deleteRowsAtIndexPaths(..)
like this,
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == .Delete) {
let LM_ITEM = lebensmittel[indexPath.row]
managedObjectContext?.deleteObject(lebensmittel[indexPath.row])
self.DatenAbrufen()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
Swift 5
tableView.deleteRows(at: [indexPath], with: .automatic)
Use this for Swift 4
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
it will delete the tableView row with animation similar to the iPhone messaging application.
Swift 4.2
tableView.deleteRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
Try the following:
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == .Delete) {
let LM_ITEM = lebensmittel[indexPath.row]
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
managedObjectContext?.deleteObject(LM_ITEM)
self.DatenAbrufen()
}
}