1

I'm implementing leading/trailing swipe actions in my app.

The leading swipe action is to join/leave an event in the table. The trailing swipe action is to delete an event. Both of these swipe actions should be conditional, based primarily on if the user is logged in or not.

If the user swipes left or right, and the user is not logged in, I want to display an alert ("Login required...").

If the user is logged in, the leading action will conditionally be titled "Leave" or "Join" depending on if the user has already joined the event or not. The trailing "Delete" action will be created only if the user is the also the creator of the event.

When I test the app and the user is logged in, everything works perfectly. (That was working before I decided to add the conditional element.)

When I test the app, and the user is not logged in, the leading swipe works perfectly: I swipe left (in my case), the alert pops up. No swipe action appears in the TableViewCell.

The trailing swipe also shows the alert and reacts correctly, but for some reason it's also showing a "Delete" action, even though my code uses the title "Blah". After dismissing the alert, the red "Delete" action is still visible and clickable.

I've also completely removed the "trailingSwipe..." method but the "Delete" action still appears, so I need to figure out where the default is so I can turn it off and/or override it.

How do I prevent the default Delete action from appearing and display my action instead?

Here's my code for the leading swipe:

override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {

    if currentUserID == nil {
        showLoginRequiredMessage()
        return nil
    } else {
        var userName = people.randomElement() // for testing

        if event.eventMembers.contains(userName) {
            let index = event.eventMembers.firstIndex(of: userName)!
            let leaveAction = UIContextualAction(style: .normal, title: "Leave") { (action, view, nil) in
                event.eventMembers.remove(at: index)
                tableView.setEditing(false, animated: true)
                tableView.reloadRows(at: [indexPath], with: .automatic)
                self.saveEvents()
            }

            leaveAction.backgroundColor = .red

            return UISwipeActionsConfiguration(actions: [leaveAction])
        } else {
            let joinAction = UIContextualAction(style: .normal, title: "Join") { (action, view, nil) in
                event.eventMembers.append(userName)
                tableView.setEditing(false, animated: true)
                tableView.reloadRows(at: [indexPath], with: .automatic)
                self.saveEvents()
            }

            joinAction.backgroundColor = .green

            return UISwipeActionsConfiguration(actions: [joinAction])
        }
    }
}

Here's my code for the trailing swipe:

override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    if currentUserID == nil {
        showLoginRequiredMessage()
        return nil
    } else {
        let trailingAction = UIContextualAction(style: .destructive, title: "Blah") { (action, view, nil) in
            tableView.setEditing(false, animated: true)
            print ("Delete this event")
        }
        trailingAction.backgroundColor = .red
        return UISwipeActionsConfiguration(actions: [trailingAction])
    }
}

And here's the code for the alert:

private func showLoginRequiredMessage() {
    let ac = UIAlertController(title: "Login Required", message: "To modify an event, you must first login", preferredStyle: .alert)

    ac.addAction(UIAlertAction(title: "Sign In", style: .default, handler: {(action) in
        if let authenticationController = self.storyboard?.instantiateViewController(withIdentifier: "authenticationScreen") {
            self.present(UINavigationController(rootViewController: authenticationController), animated: true)
        }
    }))

    ac.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

    present(ac, animated: true)

}
Zonker.in.Geneva
  • 1,389
  • 11
  • 19

2 Answers2

1

I have solved your issue. I hope that will work for you. In trailingSwipeActions method change action style to normal, you will get "Blah" title.

Remove return nil from your if statement.

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    if currentUserID == nil {
        self.showLoginRequiredMessage()
    }
    let trailingAction = UIContextualAction(style: .normal, title: "Blah") { (action, view, boolval) in
        print ("Custom action event")
        tableView.setEditing(false, animated: true)
    }
    trailingAction.backgroundColor = .gray
    return UISwipeActionsConfiguration(actions: [trailingAction])
}

And, add .setEditing(false, animated: true) in below method

private func showLoginRequiredMessage() {
    let ac = UIAlertController(title: "Login Required", message: "To modify an event, you must first login", preferredStyle: .alert)

    ac.addAction(UIAlertAction(title: "Sign In", style: .default, handler: {(action) in
        self.myTableView.setEditing(false, animated: true)
        if let authenticationController = self.storyboard?.instantiateViewController(withIdentifier: "authenticationScreen") {
            self.present(UINavigationController(rootViewController: authenticationController), animated: true)
        }
    }))

    ac.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: {(action) in
        self.myTableView.setEditing(false, animated: true)
    }))
    present(ac, animated: true)

}
Komal Goyani
  • 779
  • 6
  • 25
  • This doesn't accomplish what I want to do. Your `canEditRowAt:` method prevents *all* swiping when not signed in, so I had to remove that. I then tried your `trailingSwipe...` method, but it still shows the swipe and the swipe action shows "Delete". – Zonker.in.Geneva Aug 07 '19 at 11:00
  • If you want to disable leading and trailing both actions when not signed in than please add above both methods with together @Zonker.in.Geneva . – Komal Goyani Aug 07 '19 at 11:24
  • Please re-read my post. I DON'T want to disable swipe actions. Swipe should ALWAYS work. – Zonker.in.Geneva Aug 07 '19 at 11:35
  • Thanks for trying, but still no. When the alert is displayed, there should be no visible actions on the left or the right side, but I always see the trailing swipe action. – Zonker.in.Geneva Aug 07 '19 at 15:07
  • The `setEditing` statements inside the "Sign In" and "Cancel" alert actions have no effect, as they are only executed *after* the alert has been displayed and the swipe action is already visible. Not to mention, the `isEditing` value is already equal to `false`.... – Zonker.in.Geneva Aug 07 '19 at 15:25
1

Based on ChillY's answer to this question (Why is the leading swipe action also duplicated as a trailing action?), I realized the problem was that I was returning nil instead of UISwipeActionsConfiguration(actions: []).

Now I just have to figure out why the swipes are not disappearing after the action has been executed. Any ideas?

Zonker.in.Geneva
  • 1,389
  • 11
  • 19
  • did you figure out? I have same issue with same requirement you have...... Please let me know How you solved it – A.s.ALI Jul 16 '20 at 04:34
  • What problem are you having? – Zonker.in.Geneva Jul 17 '20 at 07:08
  • I just wanted to show the leading action and that is i am able to do so and in trailing I am adding empty array of actions, but by doing so, there is issue. When i open up the leading action, and then we try to close that, I am unable to swipe it back to original position. – A.s.ALI Jul 17 '20 at 07:16