I'd like to show a loading indicator when the user swipes the cell to the right, which triggers an asynchronous action. While this action is ongoing, I'd like to hide the label inside, keep the cell "inlined", not "re-swipeable" and show the already mentioned indicator.
This is what I got so far, and it is already partially working:
override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(style: .normal, title: "My Action") { (action, view, handler: @escaping (Bool) -> Void) in
// Getting UIButtonLabel, which is a subclass of UILabel
guard let label = view as? UILabel else {
return
}
// Not used right now
guard let superView = label.superview else {
return
}
// Try to hide the existing label
label.text = "" // Doesn't work
label.attributedText = nil // Doesn't work
label.textColor = UIColor.clear // Doesn't work
// Add a loading indicator at the position of the label, works!
let loadingIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.white)
loadingIndicator.translatesAutoresizingMaskIntoConstraints = false
loadingIndicator.startAnimating()
label.addSubview(loadingIndicator)
NSLayoutConstraint.activate([
loadingIndicator.centerXAnchor.constraint(equalTo: label.centerXAnchor),
loadingIndicator.centerYAnchor.constraint(equalTo: label.centerYAnchor),
loadingIndicator.heightAnchor.constraint(equalTo: label.heightAnchor, multiplier: 0.8),
loadingIndicator.widthAnchor.constraint(equalTo: loadingIndicator.heightAnchor)
])
self.viewModel.doAsyncAction(completionHandler: { (success) in
loadingIndicator.stopAnimating()
loadingIndicator.removeFromSuperview()
handler(true)
})))
}
action.backgroundColor = .darkGray
return UISwipeActionsConfiguration(actions:[action])
}
I'm able to add a loading indicator at the right position. I'm still not able to hide the label. Removing it or settings it's alpha or hidden state will not work, as the loading indicator will have to be a subview of it (for UITableView's internal positioning).
Also, I'm able to stop the user from pulling the action again by calling the callback handler late, but I'm not able to make the cell persist in that "one-level inline" swipe position. This is especially ugly when the user does not swipe the cell and click the button, but decides to swipe the cell all over to trigger the action. In this case I'd like the cell to automatically move to the "one level inline" position.
Does someone know a trick for this? I've seen this in some apps already. Is there a common UIKit addition for this kind of stuff?
One way I to go for I found out is disabling the superview, which is a subclass of a UIButton.
You can get it by
guard let superView = label.superview as? UIButton else {
return
}
and then disable it by doing
superView.isEnabled = false
label.isEnabled = false
label.isUserInteractionEnabled = false // Probably not required
This way, the label inside the button appears slightly faded. Also, it prevents the user from clicking the button multiple times.
It will not look so nice when the user "swipes to action", as the action button will remain at the slided position. But maybe this is something that can be tweaked easily, too?!