How To Make A Swipeable Table View Cell With Actions – Without Going Nuts With Scroll Views

So you want to make a swipeable table view cell like in Mail.app? This tutorial shows you how without getting bogged down in nested scroll views. By Ellen Shapiro.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 6 of this article. Click here to view the first page.

A List Of Ingredients for a Swipeable Table View Cell

So what does this mean for you? Well, at this point you have a list of obvious ingredients for cooking up a UITableViewCell subclass with your own custom buttons.

Going in reverse z-order with the items at the “bottom” of the view stack first, you have the following:

  1. The contentView as your base view, since it's required that you add subviews to this view.
  2. Any UIButtons you want to display after the user swipes.
  3. A container view above the buttons to hold all of your content.
  4. Either a UIScrollView to hold your container view, like Apple use, or you could use a UIPanGestureRecognizer. This can also handle the swipes to reveal/hide the buttons. You'll take the latter approach in your project.
  5. Finally, the views with your actual content.

There’s one ingredient that may not be as obvious: you have to ensure the existing UIPanGestureRecognizer — which lets you swipe to show the delete button — is disabled. Otherwise that gesture recognizer will collide with the custom one you’re adding to your project.

The good news is that disabling the default swipe is pretty simple.

Open MasterViewController.m. Modify tableView:canEditRowAtIndexPath: to always return NO as follows:

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
  return NO;
}

Build and run your application; swipe one of the items and you’ll find that you can no longer swipe to delete.

To keep it simple, you'll walk through this example with two buttons, but these same techniques will work with one button, or more than two buttons — though be warned you may need to add a few tweaks not covered in this article if you add so many buttons that you’d have to slide the entire cell out of view to see them all.

Creating the Custom Cell

You can see from the basic list of views and gesture recognizers that there’s an awful lot going on in the table view cell. You’ll want to create your own custom UITableViewCell subclass to keep all the logic in one place.

Go to File\New\ File… and select iOS\Cocoa Touch\Objective-C class. Name the new class SwipeableCell and make it a subclass of UITableViewCell, like so:

Creating custom cell

Set up the following class extension and IBOutlets in SwipeableCell.m, just below the #import statement and above the @implementation statement:

@interface SwipeableCell()

@property (nonatomic, weak) IBOutlet UIButton *button1;
@property (nonatomic, weak) IBOutlet UIButton *button2;
@property (nonatomic, weak) IBOutlet UIView *myContentView;
@property (nonatomic, weak) IBOutlet UILabel *myTextLabel;

@end

Next, go into your storyboard and select the UITableViewCell prototype, as shown below:

Select Table View Cell

Open the Identity Inspector, then change the Custom Class to SwipeableCell, like so:

Change Custom Class

The name of the UITableViewCell prototype now appears as “Swipeable Cell” in the Document Outline on the left. Right-click on the item that says Swipeable Cell - Cell, you'll see the list of IBOutlets you set up above:

New Name and Outlets

First, you’ll need to change a couple things in the Attributes Inspector to customize the view. Set the Style to Custom, the Selection to None, and the Accessory to None, as shown in the screenshot below:

Reset Cell Items

Next, drag two Buttons into the cell's content view. Set each button's background color in the View section of the Attributes Inspector to some distinctive color and set each button's text color to something legible so you can see the buttons clearly.

Pin the first button to the right side, top, and bottom of the contentView. Pin the second button to the left edge of the first button, and to the top and bottom of the contentView. When you’re done, the cell should look something like this, although your colors may differ:

Buttons Added to Prototype Cell

Next, hook up each of your buttons to the appropriate outlets. Right-click the swipeable cell to open up its outlets, then drag from the button1 outlet to the right button, and button2 to the left button, as such:

swipeable-button1

You need to create a method to handle taps on each of these buttons.

Open SwipeableCell.m and add the following method:

- (IBAction)buttonClicked:(id)sender {
  if (sender == self.button1) {
    NSLog(@"Clicked button 1!");
  } else if (sender == self.button2) {
    NSLog(@"Clicked button 2!");
  } else {
    NSLog(@"Clicked unknown button!");
  }
}

This handles button taps from either of the buttons and logs it to the console so you can confirm which button was tapped.

Open the Storyboard again, and hook up the action for both buttons to this new method. Right-click the Swipeable Cell - Cell to bring up its list of outlets and actions. Drag from the buttonClicked: action to your button, like so:

swipeable-buttonClicked

Select Touch Up Inside from the list of events, as shown below:

swipeable-touchupinside

Repeat the above steps for the second button. Now tapping on either button calls buttonClicked:.

Since you're customizing the cell's content view, you can't rely on the built-in text label. Instead, you’ll need to add your own property and method to set the cell's text.

Open SwipeableCell.h and add the following property:

@property (nonatomic, strong) NSString *itemText;

You’ll be doing more with the itemText property later, but for now, this is all you need.

Open MasterViewController.m and add the following line to the top:

#import "SwipeableCell.h"

This ensures the class knows about your custom cell subclass.

Replace the contents of tableView:cellForRowAtIndexPath: with the following:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  SwipeableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

  NSString *item = _objects[indexPath.row];
  cell.itemText = item;

  return cell;
}

It's now your new cell class being used instead of the standard UITableViewCell.

Build and run your application; you’ll see something like the following:

ALL THE BUTTONS!

Adding a delegate

Hooray — your buttons are there! If you tap on each button, you’ll see the appropriate log messages in your console. However, you don't want to have the cell itself take any direct action.

For instance, a cell can’t present another view controller or push directly onto the navigation stack. You’ll have to set up a delegate to pass the button tap event back to the view controller to handle that event.

Open SwipeableCell.h and add the following delegate protocol declaration above the @interface statement:

@protocol SwipeableCellDelegate <NSObject>
- (void)buttonOneActionForItemText:(NSString *)itemText;
- (void)buttonTwoActionForItemText:(NSString *)itemText;
@end

Add the following delegate property to SwipeableCell.h, just below your property for itemText:

@property (nonatomic, weak) id <SwipeableCellDelegate> delegate;

Update buttonClicked: in SwipeableCell.m as shown below:

- (IBAction)buttonClicked:(id)sender {
  if (sender == self.button1) {
    [self.delegate buttonOneActionForItemText:self.itemText];
  } else if (sender == self.button2) {
    [self.delegate buttonTwoActionForItemText:self.itemText];
  } else {
    NSLog(@"Clicked unknown button!");
  }
}

This updates the method to call the appropriate delegate methods instead of simply creating an entry in the log.

Now, open MasterViewController.m and add the following delegate methods to the implementation:

#pragma mark - SwipeableCellDelegate
- (void)buttonOneActionForItemText:(NSString *)itemText {
  NSLog(@"In the delegate, Clicked button one for %@", itemText);
}

- (void)buttonTwoActionForItemText:(NSString *)itemText {
  NSLog(@"In the delegate, Clicked button two for %@", itemText);
}

These methods will simply log to the console to ensure everything is passing through properly.

Next, add the following protocol conformance declaration to the class extension at the top of MasterViewController.m:

@interface MasterViewController () <SwipeableCellDelegate> {
  NSMutableArray *_objects;
}
@end

This simply indicates that this class conforms to the SwipeableCellDelegate protocol.

Finally, you need to set this view controller as the cell's delegate.

Add the following line to tableView:cellForRowAtIndexPath: just before the final return statement:

cell.delegate = self;

Build and run your application; you’ll see the appropriate “in the delegate” messages firing off when you tap on the buttons.

Contributors

Over 300 content creators. Join our team.