My project contains a tableview. This tableview consists of 2 sections. The first section contains a single cell that is not editable. The second section is populated by an array called data.
There is a button that starts editing mode and allows the user to delete content from the second section.
I'm trying to end editing mode when the second section is empty so that i can select the cell in my first section.(aesthetic purposes)
My code looks like this (simplified, not actual code):
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
data.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
if(data.count==0) {
self.tableView.setEditing(false, animated: true)
self.tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: .top)
}
This leads to all kinds of weird bugs. Which, according to the documentation, is expected:
You should not call setEditing:animated: within an implementation of this method. If for some reason you must, invoke it after a delay by using the performSelector:withObject:afterDelay: method.
It works, but I feel like this would be a hacky solution. It is not clear to me why I should not end editing when everything is deleted. Can someone explain this? Or perhaps any alternatives?
Related
I am deleting row and want to get next behavior:
If I am deleting row that was selected, I need to get another row selected automatically.
And also if I have an additional trouble, when I am perform deleting, target row that is going to be deleted selecting automatically, I don't need it to be selected as I want to only delete it.
The main question is 1 for now.
I understand that maybe row: 0 does not exists anymore but t's not a point now, I just simplified code to make it more comfortable to discuss the point. I tried performBatchUpdates, and tried to move selectRow(at:section:) to completion closure as well, row selection not works too.
selectRow works perfectly after insertRows function, so I am confused.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: .middle)
}
}
As I understood your question, find this solution:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
}
}
Pass indexPath while deleting a row and after endUpdates, you just need to set the same indexPath as SelectedRow.
Ok, I've found solution for the 1st question (just thought maybe such function can exist and typed didrowat, and xcode suggested this didEndEditingRowAt :)
override func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) {
tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: true, scrollPosition: .middle)
}
Now first part of question is answered, I've inserted selection there.
The second question is still opened, how to make selected row keeping still selected while I am removing another row. I don't need selected row to deselect at all if I am removing another one.
After successfully deleting a row from UITableView, I go to another view controller but when I return to the UITableView the deleted row is back again. Am I missing something? I am relatively new to Swift.
Here is the code:
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .destructive, title: "Delete") { (action, indexPath) in
self.queuelayout.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
print(self.queuelayout)
}
return [delete]
}
I've also tried with this code:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete{
queuelayout.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .right)
}
}
Look forward to your help.
I checked your top codes. They are all right. The only possible reason is as you said you went to another VC , you have already get rid of your tableViewController from the memory. Here is an simple example:
If your tableView is the most right one, and when you go back to middle vc, your right one will be removed from the memory. Then when you click assign button and actually, you are loading a completely new tableViewController.
As a result, viewDidLoad will be called and your tableViewController will init again to the original states.
You may set a breakpoint at func viewDidLoad(), to check this situation. If this one is not called when you go back, you are good to go. Otherwise, everything will be reset. All what you delete will reappear as they should.
This is a simple example. Your case may be a little complicated. But if your tableviewcontroller called ViewDidLoad(), you must have some hassles somewhere.
Hope this help you out.
I have a custom table view cell that has a toggle button on the left side.
When the tableview's isEditing property is set to true, the delete editing option appears properly, but when it's tapped, nothing happens.
The stranger thing is, when I tap and hold, and then drag to one side away from the button, and THEN lift, the cell finally slides over. Which isn't how it's supposed to work at all.
Even when I remove connections to the toggle button from the storyboard and the subclass, it still behaves this way.
Why is it doing this?
First of all, correct me if I'm wrong, but I think these are two different problems.
1.Nothing happens when the "delete editing" button is tapped. I would check if the "delete editing" button properly connected to the class file with an IBAction.
2.The cell slide behavior is not what is desired. I would check to see if the gesture you're recognizing is a tap and not a left swipe.
Hope that helps.
you need to add following methods in your table view controller
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
//remove element from your array providing index.row
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
)
}
}
I am having an issue using a UITableView's didEndDisplayingCell method. I am using this method along with willDisplayCell to handle some extra data tracking for my table. The problem is that didEndDisplayingCell is being called several times in a row, and at times where the cell is actually still being displayed. I cannot figure out why this would happen.
More Details
I am using an NSFetchedResultsController to provide data for the table. Changes come into the table from an NSManagedObjectContext on a different queue and then get merged into the NSManagedObjectContext that the table uses. This all seems to be working fine. Looking at my implementation of the above methods:
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
debugPrint("willDisplay cell at \(indexPath).")
self.dataProvider.addTrackedRelatedRecords(for: indexPath)
}
override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
debugPrint("didEndDisplaying cell at \(indexPath).")
self.dataProvider.removeTrackedRelatedRecords(for: indexPath)
}
The output I'm seeing here is:
"Updating rows in table at [0, 0]". <=== This is an update to the data
"willDisplay cell at [0, 0]." <=== This is what I expect
"didEndDisplaying cell at [0, 0]"
"didEndDisplaying cell at [0, 0]"
"didEndDisplaying cell at [0, 0]"
The cell remains visible in the table and never disappears. There is nothing that I can see that would cause the didEndDisplaying to be called, especially repeatedly.
I can work around this issue by checking to see if the cell is actually still visible, by adding the following to the didEndDisplayingCell method:
override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
debugPrint("didEndDisplaying cell at \(indexPath).")
// WORK AROUND
if let visibleRows = tableView.indexPathsForVisibleRows, visibleRows.contains(indexPath) {
return
}
self.dataProvider.removeTrackedRelatedRecords(for: indexPath)
}
This correctly sees that the cell is still visible, so won't remove my extra tracking. But, if the cell is visible, why is the method being called at all?
Obviously there is a lot of other code at work in my app. I will attempt to cull this down to a very simple case, but thought I'd post this first to see if anyone has seen cases where this might happen. That might help me track it down.
Thanks for any insight.
I found the issue and I'm posting an answer in case anyone else sees this issue and would be helped by what I found.
It turns out that I had inadvertently left a tableView.reloadRows(at: [indexPath], with: .fade) inside a tableView.beginUpdates() and tableView.endUpdates() block.
I believe that the reloadRows was causing the cell to be replaced, which would cause a didEndDisplayingCell. And, since there were a couple updates happening inside the block, these were being queued up and posted once the tableView.endUpdates() was called.
Removing the reloadRows call fixed the issue and all is working as expected.
In my case it was because I was calling reloadData() whenever I was changing the centeredIndexPath. Make sure you are not reloading the data as all the cells will call didEndDisplaying (as you are reloading it).
like this
How can I have a swipe delete cell button like iOS mail application(image + text )?
I found this method but my image is not scale to size:
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let deleteButton = UITableViewRowAction(style: .Destructive, title:"\nDelete", handler: { (action, indexPath) in
self.tableView.dataSource?.tableView?(self.tableView,commitEditingStyle: .Delete,
forRowAtIndexPath: indexPath)
return
})
deleteButton.backgroundColor = UIColor(patternImage: UIImage(named: "icon")!)
return [deleteButton]
}
thanks in advance
I am little confused with your explanation. If you can please provide some more explanation regarding button you want. Do you want Standard button which iOS provides itself or you want a customize button?
I am trying to address both aspects below.
If you need standard operation you can do it like this:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// remove the item from the array if you want
array.remove(at: indexPath.row)
// delete the table view row
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
}
Also, i would like to recommend This.
If u just want to resize button image you can do it like this:
deleteButton.imageView?.contentMode = UIViewContentMode.scaleAspectFill
The table view will do that for you. You just need to have your table view/table view controller live in a navigation stack so there's a navigation bar, and then implement a couple of methods: tableView(_, canEditRowAt:) and tableView(_:,commit:forRowAt:).
You'll probably want to add an edit button to your navigation bar.
I just pushed a demo project called MasterDetailSwift that supports adding and removing cells to a table view. (It also shows the master/detail UI pattern, a good way to handle prepareForSegue using a switch statements, and a few other useful techniques.)