UITableView customize delete button function - ios

As default behavior, once you want to delete any single cell of table view, you will tap on delete button at left of cel, the delete confirmation button will be shown at right of cell, and then continue tapping on this button, this row will be deleted. For this behavior, you need to have 2 steps to delete a row. Is there any way can only tap on delete button(at the left of cell) to delete the cell without tapping on the confirmation button?

Do you mean deleting the row only with swapping left? Ignoring the delete button?
You could use UISwipeGestureRecognizer, like this:
class YourViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
var swipe = UISwipeGestureRecognizer(target: self, action: #selector(self.didSwipe))
self.tableView.addGestureRecognizer(swipe)
}
func didSwipe(recognizer: UIGestureRecognizer) {
if swipe.state == UIGestureRecognizerState.Ended {
let swipeLocation = swipe.locationInView(self.tableView)
if let swipedIndexPath = tableView.indexPathForRowAtPoint(swipeLocation) {
if let swipedCell = self.tableView.cellForRowAtIndexPath(swipedIndexPath) {
self.cellObjectsArray.remove(at: swipedIndexPath.row)
tableView.deleteRows(at: [swipedIndexPath], with: .fade)
}
}
}
}
}

You can delete the cell by implementing the following delegate methods, but this is a two step process,
Swipe the cell to find the delete confirmation button on right side
Click Delete (commitEditingStyle will be called)
If you want to do it in a single step/click, Add a custom button in UITableViewCell and in its selector get the indexpath of UITableViewCell and delete the object from datasource and reload table, This is similar to the code implemented in commitEditingStyle method.
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{
return #"Delete";
}
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
//Delete the object at `indexPath` from datasource
//Update UI, by reloading section/entire table
}

Related

Deselect table view row when returning to app

I have a table view and one of the table view cells opens another app. When I return to my app the table view cell is still highlighted. What is the best way to deselect a table view cell when returning to the app?
Edit: The issue is that -viewWillAppear or -viewDidAppear does not get called when returning from an app since the view is already visible.
Set notification in viewDidLoad
final override func viewDidLoad() {
super.viewDidLoad()
// add notification observers
NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
}
Create method didBecomeActive
func didBecomeActive() {
if let indexPath = tableView.indexPathForSelectedRow {
deselectRow(at: indexPath, animated: true)
}
}
UIKit documentation
UIApplicationDidBecomeActive
UITableView - indexPathForSelectedRow
UITableView - deselectRow
You have 2 options for solve your issue
Option 1
Deselect row in didSelectRow method
Example code
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
// Open app code here
}
Option 2
Find selected row and deselect row,
Put below code in viewDidAppear method
for (NSIndexPath *indexPath in tableView.indexPathsForSelectedRows) {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}

Changing background Color of TableViewCell while editing Cells

I have a tableView with a few cells that have a black background color. On tapping the Edit Button in the navigation Bar, I would like to change the background color of area that allows re-ordering to black instead of the current white color that it shows?
I am able to change the edit button to Done using the function below but I am not sure how to change that specific area of the cells. I sense this is where I change it but i am not show how.
override func setEditing (editing:Bool, animated:Bool)
{
super.setEditing(editing,animated:animated)
if (self.editing) {
self.editButton.title = "Done"
self.navigationItem.setHidesBackButton(true, animated: true)
} else {
self.editButton.title = "Edit"
self.navigationItem.setHidesBackButton(false, animated: false)
}
}
This is the image of what I am referring to.
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.backgroundColor = [UIColor redColor];
}
In this "swipe to delete" mode the table view does not display any insertion, deletion, and reordering controls. This method gives the delegate an opportunity to adjust the application's user interface to editing mode.
according to the comment you want to capture the event when user does not delete the cell but swipe to end the editing mode. For this, we have the following delegate:
- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.backgroundColor = originalColor;
}
public class MyTableSource : UITableViewSource
{
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
// abbreviation
// :
// :
cell.BackgroundColor = UIColor.Black;
return cell;
}
}
It turns out setting the cell's background color in code i.e. cellForRowAtIndexPath got the color to uniformly change to black.
I had originally done this from the UITableViewCell XIB file used.

UITableView commitEditingStyle only on particular cases

Is it possible to make editable the UITableView by adding the commitEditingStyle method only under certain circumstances ?
I have a controller.m/.h file that is doing stuff for 3 differents storyboards viewcontrollers. I want only 2 of the 3 to be able to commitEditingStyle. I can distinguish them using the self.restorationIdentifier.
you can check tableview tag..
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(tableview.tag==1 || tableview.tag==2)
return UITableViewCellEditingStyleDelete;
return UITableViewCellEditingStyleNone;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
//here your code
}
}
public override UITableViewCellEditingStyle EditingStyleForRow(UITableView tableView, NSIndexPath indexPath)
{
//here we show and hide the delete for particular row
if (indexPath.Row ==1)
{
return UITableViewCellEditingStyle.Delete;
}
else {
return UITableViewCellEditingStyle.None;
}
}
public override void CommitEditingStyle(UITableView tableView, UITableViewCellEditingStyle editingStyle, NSIndexPath indexPath)
{
if (editingStyle == UITableViewCellEditingStyle.Delete)
{
//here we handle delete button action of the tableview
}
}
Well, seems that I had simply to subclass and add the commitEditingStyle on the subclass. Then change the class in the storyboard to the subclass and that's all.

Swipe to delete cell does not cancel UIButton action

My UITableView has the swipe to delete feature enabled. Each cell has a UIButton on it that performs an action (in this case, perform a segue).
I'd expect that if I swipe the cell by touching the button, the button's action would be canceled/ignored, and only the swipe would be handled. What actually happens, however, is that both gestures (swipe + tap) are detected and handled.
This means that if I just want to delete one cell and "accidentally" swipe by touching the button, the app will go to the next screen.
How can I force my app to ignore the taps in this case?
august's answer was nice enough for me, but I figured out how to make it even better:
Checking if the table was on edit mode to decide if the button should perform its action will make it behave as it should, but there will still be an issue in the user experience:
If the user wants to exit the editing mode, he should be able to tap anywhere in the cell to achieve that, including the button. However, the UIButton's action is still analyzed first by the app, and tapping the button will not exit editing mode.
The solution I found was to disable the button's user interaction while entering edit mode, and reenabling it when it's done:
// View with tag = 1 is the UIButton in question
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath {
[(UIButton *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:1] setUserInteractionEnabled:NO];
}
- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath {
[(UIButton *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:1] setUserInteractionEnabled:YES];
}
This way, dragging the button to enter edit mode will not trigger the button's action, and taping it to exit edit mode will indeed exit edit mode.
One elegant way would be to ignore button taps as long as a cell has entered editing mode. This works because the swipe to delete gesture will cause willBeginEditingRowAtIndexPath to be called before the button tap action is invoked.
- (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
self.isEditing = YES;
}
- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
self.isEditing = NO;
}
// button tapped
- (IBAction)tap:(id)sender
{
if (self.isEditing) {
NSLog(#"Ignore it");
}
else {
NSLog(#"Tap");
// perform segue
}
}
It's possible to do it also in your cell's subclass:
override func setEditing(editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
actionButton?.userInteractionEnabled = !editing
}
Because my function being called from a button press was a delegate onto my main UITableViewController's class and connected to the UITableViewCell as an IBAction, the button in my UITableViewCell was still firing on a swipe that had the UIButton pressed as part of the swipe.
In order to stop that I used the same UITableView delegates as the accepted answer, but had to set a file level variable to monitor if editing was occurring.
// in custom UITableViewCell class
#IBAction func displayOptions(_ sender: Any) {
delegate?.buttonPress()
}
// in UITableViewController class that implemented delegate
fileprivate var cellSwiped: Bool = false
func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath) {
cellSwiped = true
}
func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) {
cellSwiped = false
}
func displayContactsSheet(for contact: Contact) {
if cellSwiped {
return
}
// proceed with delegate call button press actions
}

UITableView, replace cell view on selection

Is it possible to replace a cell view for the selected cell? I have registered my controller as a data source and delegate. Requests for cell views, row numbers, selection status, etc, all come in nicely. In the cell selection callbacks, I'm trying to have the table view reload the cell with a new view (not the actual data!). Basically, I want the cell view to expand and show more of the underlying data, if it is selected. The problem is that I have no clue how to make the table view ask my controller for a new view. I can see that the view is requesting the cell height, but nothing more.
Calling reloadData on the view works, but it's inefficient and comes with a set of its own problems (animation possibilities, maintaining selection state, etc).
Here is the approach I would take to do this.
#property (nonatomic, strong) NSIndexPath *selectedIndexPath;
Set a property of type NSIndexPath to your controller to store which index path was selected.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedIndexPath = indexPath;
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation: UITableViewRowAnimationNone];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath compare:self.selectedIndexPath] == NSOrderedSame) {
// Create your custom cell here and return it.
}
else {
// Should create a normal cell and return it.
}
}
Exactly. Note too that you probably want to deselect. Here's the full code in Swift. Use selectedIndexPath in cellForRowAtIndexPath as appropriate.
// Selecting TableViewController
import UIKit
class SelectingTableViewController: UITableViewController
{
internal var selectedIndexPath:NSIndexPath? = nil
override func viewDidLoad()
{
super.viewDidLoad()
tableView.estimatedRowHeight = 68.0
tableView.rowHeight = UITableViewAutomaticDimension
self.clearsSelectionOnViewWillAppear = false;
}
override func tableView
(tableView:UITableView, didSelectRowAtIndexPath indexPath:NSIndexPath)
{
print("did select....")
// in fact, was this very row selected,
// and the user is clicking to deselect it...
// if you don't want "click a selected row to deselect"
// then on't include this clause.
if selectedIndexPath == indexPath
{
print("(user clicked on selected to deselect)")
selectedIndexPath = nil
tableView.reloadRowsAtIndexPaths(
[indexPath],
withRowAnimation:UITableViewRowAnimation.None)
tableView.deselectRowAtIndexPath(indexPath, animated:false)
return
}
// in fact, was some other row selected??
// user is changing to this row? if so, also deselect that row
if selectedIndexPath != nil
{
let pleaseRedrawMe = selectedIndexPath!
// (note that it will be drawn un-selected
// since we're chaging the 'selectedIndexPath' global)
selectedIndexPath = indexPath
tableView.reloadRowsAtIndexPaths(
[pleaseRedrawMe, indexPath],
withRowAnimation:UITableViewRowAnimation.None)
return;
}
// no previous selection.
// simply select that new one the user just touched.
// note that you can not use Apple's willDeselectRowAtIndexPath
// functions ... because they are freaky
selectedIndexPath = indexPath
tableView.reloadRowsAtIndexPaths(
[indexPath],
withRowAnimation:UITableViewRowAnimation.None)
}
}
Have you tried:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
Then you can specify only your updated cell for reloading. You can also tell your tableview to remember or forget selection status upon reload with:
tableViewController.clearsSelectionOnViewWillAppear = NO;
Then you will also have to deal with the custom view inside
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath

Resources