iOS - How To Give A Peek At The "Swipe To Delete" Action On A Table View Cell?
Asked Answered
S

3

5

When a certain Table View Controller displays for the first time, how to briefly show that the red “swipe to delete” functionality exists in a table row?

The goal of programmatically playing peekaboo with this is to show the user that the functionality exists.

Environment: iOS 11+ and iPhone app.

Here's an image showing the cell slid partway with a basic "swipe to delete" red action button. Showing delete action under table cell via partial swipe

A fellow developer kindly mentioned SwipeCellKit, but there’s a lot to SwipeCellKit. All we want to do is briefly simulate a partial swipe to let the user know the "swipe to delete" exists. In other words, we want to provide a sneak peak at the delete action under the cell.

In case it helps, here's the link to the SwipeCellKit's showSwipe code Here is a link with an example of its use.

I looked at the SwipeCellKit source code. It's not clear to me how to do it without SwipeCellKit. Also, Using SwipeCellKit is not currently an option.

Googling hasn't helped. I keep running into how to add swipe actions, but not how to briefly show the Swipe Actions aka UITableViewRowAction items that are under the cell to the user.

How to briefly show this built in "swipe to delete" action to the user?

Shugart answered 9/4, 2019 at 11:45 Comment(0)
B
0

I'm pretty sure this can't be done. The swipe actions are contained in a UISwipeActionPullView, which contains UISwipeStandardAction subviews, both of which are private. They are also part of the table view, not the cell, and they're not added unless a gesture is happening, so you can't just bump the cell to one side and see them there.

Outside of UI automation tests, it isn't possible to simulate user gestures without using private API, so you can't "fake" a swipe and then show the results to the user.

However, why bother doing it "properly" when you can cheat? It shouldn't be too hard to bounce the cell's content view slightly to the left, and bounce in a red box (not so far that you can see text, to avoid localisation issues), then return to normal. Do this on the first load of the table view, and stop doing it after N times or after the user has proven that they know how this particular iOS convention works.

Bullfighter answered 9/4, 2019 at 12:43 Comment(3)
Interesting! 🤔I shall check that out. Thanks!Shugart
Will keep the question open for now in case other ideas are revealed as well. I appreciate it though!Shugart
The "when you can cheat" definitely seems like the answer. Here is a video that explains how: youtube.com/watch?v=oAGoFd_GrxE Credit to jimc of the ios-developers.io slack iOS community for sharing it.Shugart
M
10

I've written this simple extension for UITableView. It searches through visible cells, finds first row that contains edit actions and then "slides" that cell for a bit to reveal action underneath. You can adjust parameters like width and duration of the hint.

It works great because it doesn't hardcode any values, so for example the hint's background color will always match the real action button.

import UIKit

extension UITableView {
    /**
     Shows a hint to the user indicating that cell can be swiped left.
     - Parameters:
        - width: Width of hint.
        - duration: Duration of animation (in seconds)
     */
    func presentTrailingSwipeHint(width: CGFloat = 20, duration: TimeInterval = 0.8) {
        var actionPath: IndexPath?
        var actionColor: UIColor?
        
        guard let visibleIndexPaths = indexPathsForVisibleRows else {
            return
        }
        if #available(iOS 13.0, *) {
            // Use new API, UIContextualAction
            for path in visibleIndexPaths {
                if let config = delegate?.tableView?(self, trailingSwipeActionsConfigurationForRowAt: path), let action = config.actions.first {
                    actionPath = path
                    actionColor = action.backgroundColor
                    break
                }
            }
        } else {
            for path in visibleIndexPaths {
                if let actions = delegate?.tableView?(self, editActionsForRowAt: path), let action = actions.first {
                    actionPath = path
                    actionColor = action.backgroundColor
                    break
                }
            }
        }
        guard let path = actionPath, let cell = cellForRow(at: path) else { return }
        cell.presentTrailingSwipeHint(actionColor: actionColor ?? tintColor)
    }
}

fileprivate extension UITableViewCell {
    func presentTrailingSwipeHint(actionColor: UIColor, hintWidth: CGFloat = 20, hintDuration: TimeInterval = 0.8) {
        // Create fake action view
        let dummyView = UIView()
        dummyView.backgroundColor = actionColor
        dummyView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(dummyView)
        // Set constraints
        NSLayoutConstraint.activate([
            dummyView.topAnchor.constraint(equalTo: topAnchor),
            dummyView.leadingAnchor.constraint(equalTo: trailingAnchor),
            dummyView.bottomAnchor.constraint(equalTo: bottomAnchor),
            dummyView.widthAnchor.constraint(equalToConstant: hintWidth)
        ])
        // This animator reverses back the transform.
        let secondAnimator = UIViewPropertyAnimator(duration: hintDuration / 2, curve: .easeOut) {
            self.transform = .identity
        }
        // Don't forget to remove the useless view.
        secondAnimator.addCompletion { position in
            dummyView.removeFromSuperview()
        }

        // We're moving the cell and since dummyView
        // is pinned to cell's trailing anchor
        // it will move as well.
        let transform = CGAffineTransform(translationX: -hintWidth, y: 0)
        let firstAnimator = UIViewPropertyAnimator(duration: hintDuration / 2, curve: .easeIn) {
            self.transform = transform
        }
        firstAnimator.addCompletion { position in
            secondAnimator.startAnimation()
        }
        // Do the magic.
        firstAnimator.startAnimation()
    }
}

Example usage:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    tableView.presentSwipeHint()
}

enter image description here

Metasomatism answered 20/7, 2020 at 16:54 Comment(2)
I have implemented only the delete, so it worked for me by getting the cell directly from the tableView let cell = self.cellForRow(at: IndexPath(item: 0, section: 0)) then I have to make set clipsToBounds = false on the cell for the dymmyView to be visible.Folly
Actually (tested on iOS 15.2), you have to set cell.clipsToBounds = false to make the dummyView visible.Radiography
B
0

I'm pretty sure this can't be done. The swipe actions are contained in a UISwipeActionPullView, which contains UISwipeStandardAction subviews, both of which are private. They are also part of the table view, not the cell, and they're not added unless a gesture is happening, so you can't just bump the cell to one side and see them there.

Outside of UI automation tests, it isn't possible to simulate user gestures without using private API, so you can't "fake" a swipe and then show the results to the user.

However, why bother doing it "properly" when you can cheat? It shouldn't be too hard to bounce the cell's content view slightly to the left, and bounce in a red box (not so far that you can see text, to avoid localisation issues), then return to normal. Do this on the first load of the table view, and stop doing it after N times or after the user has proven that they know how this particular iOS convention works.

Bullfighter answered 9/4, 2019 at 12:43 Comment(3)
Interesting! 🤔I shall check that out. Thanks!Shugart
Will keep the question open for now in case other ideas are revealed as well. I appreciate it though!Shugart
The "when you can cheat" definitely seems like the answer. Here is a video that explains how: youtube.com/watch?v=oAGoFd_GrxE Credit to jimc of the ios-developers.io slack iOS community for sharing it.Shugart
W
-1

First : enable the editing

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
   return true
}

Then customise your swipe

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
   let conformeRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: NSLocalizedString("C", comment: ""), handler:{action, indexpath in
        print("C•ACTION")
    })
   conformeRowAction.backgroundColor = UIColor(red: 154.0/255, green: 202.0/255, blue: 136.0/255, alpha: 1.0)
   let notConform = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: NSLocalizedString("NC", comment: ""), handler:{action, indexpath in
        print("NC•ACTION")
    })
   notConform.backgroundColor = UIColor(red: 252.0/255, green: 108.0/255, blue: 107.0/255, alpha: 1.0)
   return [conformeRowAction,notConform]
}
Waistline answered 9/4, 2019 at 12:12 Comment(2)
Won't this only add the possibility to swipe to display actions? It doesn't really answer the question.Irreproachable
Thank you for your time, braham-youssef @andlin is correct. I am looking for how to play peekaboo with this swipe-to-delete feature that already exists so the user knows it exists.Shugart

© 2022 - 2024 — McMap. All rights reserved.