Since we’ll want to have control over scroll behaviors, we need to specify that our class supports the UIScrollViewDelegate protocol. Do this by adding the protocol in angle brackets after the interface declaration:

Next up, we’ll set up the scroll view. This is done when the cell initializes. Depending on how you create your cells, the cell may initialize in either initWithCoder: or initWithStyle:reuseIdentifier:. I would recommend creating a common method for setting up your cell and calling it from both places.

There’s a few things happening here after we create the scroll view. First, we set its resizing mask. This means how the view will act when its super view changes size. We want the scroll view to fill the entire cell when it changes size at runtime (which is likely to happen with iPhone 5, 6 and 6 Plus all having different screen widths). Then we set the content size to be the same, but also add a left inset with the width of the buttons we intend to add. Setting scrollsToTop to be turned off means this scroll view doesn’t care about the status bar being tapped (which normally scrolls a view to the top). This’ll let our table view take care of that instead of breaking that behavior. We also turn off horizontal and vertical scroll indicators so that the little translucent black bar doesn’t appear when we scroll.

Nothing really surprising going on above, we’re just adding in the necessary parts for presenting the main area of the table cell (which is normally in the contentView).

This creates a view which contains the action buttons. The important things to note here is that the view has the same width as all the buttons together, and it’s inserted as the first view in the hierarchy (which will make it appear under everything else). Then we just add the buttons we want there. I didn’t add tap handlers to keep the example short.

If you run the app now, it should let you scroll to make the button appear. If you’re running this on an iPhone 6 or larger you may notice a glitch that we’ll fix later. Another thing missing is the snapping in place of the button being either visible or not visible when you stop scrolling. Let’s fix that now.

When your class gets notified that the user has stopped swiping (via the UIScrollViewDelegate protocol), you make the scroll view scroll back to either exactly where the buttons end (if they swiped past the buttons), or back to hide the buttons completely (if they didn’t swipe far enough).

That’s pretty much the basics of building a swipeable cell! We’ve got a few loose ends that we’ll tie up now, so keep reading.

The biggest problem with our code right now is handling of bigger screens (such as iPhone 6). We can fix that with this simple “hack” (please let me know if this can be done better!)

Whenever the cell area changes, fix the content size and reset the scroll offset.

That’ll avoid graphical glitches. Next up, we may want to prevent the cell from being scrollable in the other direction (which doesn’t have any buttons to show). That’s simple:

If the user scrolls the other way (right-to-left), reset the scroll offset.

Finally, you may want the buttons to stay in place under the cell instead of moving with the cell content. To make that happen, we will simply move the buttons container view to counteract the scrolling offset. We’ll do this by changing the scrollViewDidScroll: method above to the following:

Where to go from here

Now that we’ve gone through the basics there’s many things that can still be improved. Here’s a few:

Auto-hiding buttons when scrolling the table view

Auto-hiding buttons when swiping open another cell

Allowing buttons on both sides of the cell

Enabling button creation outside the class (e.g., in the view controller)

Dynamically calculating the buttons container size

Making opening/closing of the actions menu behave better

To fast-forward a bit, I’ve implemented the features above and made the code available on GitHub: https://github.com/blixt/SwipeableTableViewCell

Thank you for reading! Please reach out to me with any comments. The easiest way to reach me is on Twitter, where I’m known as @blixt.