(Swift) How to set clear background in cell swipe action?
You can just set the alpha value to 0 for background color of the action Here is the example
let modifyAction = UIContextualAction(style: .normal, title: "", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
print("Update action ...")
success(true)
})
modifyAction.backgroundColor = UIColor.init(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 0.0)
You'll have to access the UIView inside the UIActionStandardButton
inside the UISwipeActionPullView
. and then change its background color.
You can see the view hierarchy of your app swiping on a cell, then going the Debug
menu in Xcode, then View Debugging
, and choose Capture View Hierarchy
.
First of all, let add this useful extension that gets all subviews and their subviews in an array:
extension UIView {
var allSubViews : [UIView] {
var array = [self.subviews].flatMap {$0}
array.forEach { array.append(contentsOf: $0.allSubViews) }
return array
}
}
And then in viewWillLayoutSubviews()
:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let btn = tableView
.allSubViews //get all the subviews
.first(where: {String(describing:type(of: $0)) == "UISwipeActionStandardButton"}) // get the first one that is a UISwipeActionStandardButton
//This UISwipeActionStandardButton has two subviews, I'm getting the one that is not a UILabel, in your case, since you've set the image, you should get the one that is not an imageView
if let view = btn?.subviews.first(where: { !($0 is UILabel)})
{
view.backgroundColor = .clear //Change the background color of the gray uiview
}
}
I am using viewWillLayoutSubviews()
since it's called to notify the view controller that its view is about to layout its subviews. Have a look here for more details.
This solution is optimized for one swipe action button. If you have more than one button, the code would look like this:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let buttons = tableView
.allSubViews //get all the subviews
.filter {String(describing:type(of: $0)) == "UISwipeActionStandardButton"}
buttons.forEach { btn in
if let view = btn.subviews.first(where: { !($0 is UIImageView)}) //If you're sure that other than the uiview there is a UIImageView in the subviews of the UISwipeActionStandardButton
{
view.backgroundColor = .clear //Change the background color of the gray uiview
}
}
}
SOLUTION
Thanks to Carpsen90
You have to set the backgroundColor of that UIImageView to .clear, but it doesn't exist in the time of viewWillLayoutSubviews. It is created after you swipe.
A posible solution is to have a Timer:
var timerCellSwipeButtons: Timer?
Launched when the swipe is done:
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let editAction = UIContextualAction.init(style: .normal, title: nil) { [weak self] (action, view, completion) in
// editAction code
}
let deleteAction = UIContextualAction.init(style: .normal, title: nil) { [weak self] (action, view, completion) in
// deleteAction code
}
// Set the button's images
editAction.image = UIImage.init(named: "editIcon")
deleteAction.image = UIImage.init(named: "deleteIcon")
// You also must set the background color of the actions to .clear
editAction.backgroundColor = .clear
deleteAction.backgroundColor = .clear
// Launch the timer, that will run a function every 10 milliseconds
self.timerCellSwipeButtons = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(timerCellSwipeButtonsFunction), userInfo: nil, repeats: true)
// Return the actions
return UISwipeActionsConfiguration.init(actions: [deleteAction, editAction])
}
Now every 10 milliseconds (you can increase the frequency if you want), this function checks the tableView subviews looking for all the UISwipeActionStandardButton and setting to .clear the backgroundColor of their UIView:
@objc func timerCellSwipeButtonsFunction() {
// Gets all the buttons, maybe we have more than one in a row
let buttons = tableView.allSubViews.filter { (view) -> Bool in
String(describing: type(of: view)) == "UISwipeActionStandardButton"
}
// Loops through all the buttons
for button in buttons {
if let view = button.subviews.first(where: { !($0 is UIImageView)})
{
// We are interested in the UIView that isn't a UIImageView
view.backgroundColor = .clear
}
}
// When finish, timer is invalidated because we don't need it anymore.
// A new one will be launched with every swipe
self.timerCellSwipeButtons?.invalidate()
}
To get all the subviews of a UIView, I used the function given by Carpsen90:
extension UIView {
var allSubViews : [UIView] {
var array = [self.subviews].flatMap {$0}
array.forEach { array.append(contentsOf: $0.allSubViews) }
return array
}
}
For safety reasons, you should also invalidate the timer in the viewWillDisappear method:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.timerCellSwipeButtons?.invalidate()
}
And this is the result:
But as you can see, when you have more than one action in the same side of the cell and you swipe completely, doesn't look very nice:
To avoid icons overlapping I put only one action in each side:
// Remember to launch the timer in both swipe functions, like in the example above
// Function to add actions to the leading side of the cell
tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
// Function to add actions to the trailing side of the cell
tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?