How I set my tableview's action buttons alignment vertical in Swift 3?
This is my tableview's action buttons looks like.
Always this is look like This is horizontal and I want to alignment to vertical. How I do this?
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let RowAction1 = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "action1", handler:{action, indexpath in
print("action1")
})
RowAction1.backgroundColor = UIColor(red: 0.298, green: 0.51, blue: 0.3922, alpha: 1.0)
let RowAction2 = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "action2", handler:{action, indexpath in
print("action2")
})
RowAction2.backgroundColor = UIColor(red: 0.28, green: 0.851, blue: 0.3922, alpha: 1.0)
let RowAction3 = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "action3", handler:{action, indexpath in
print("action3")
})
RowAction3.backgroundColor = UIColor(red: 0.298, green: 0.81, blue: 0.3922, alpha: 1.0)
let RowAction4 = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "action4", handler:{action, indexpath in
print("action4")
})
RowAction4.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0)
return [RowAction1, RowAction2, RowAction3, RowAction4]
}
Why not create a CustomCell and use AutoLayout? Like this example I created below:
So you basically create a custom cell inside your tableView in your Storyboard and then use it:
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomExampleCell", for: indexPath) as! CustomExampleCell
You can download the example project I created for you here.
Update
To use the built in methods in Swift for the Swipe accessibility use the following code:
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let more = UITableViewRowAction(style: .normal, title: "More") { action, index in
print("More")
}
let share = UITableViewRowAction(style: .normal, title: "Share") { action, index in
print("Share")
}
let delete = UITableViewRowAction(style: .default, title: "Delete") { action, index in
print("Delete")
}
more.backgroundColor = .gray
share.backgroundColor = .blue
delete.backgroundColor = .red
return [delete, share, more]
}
Use stackView with Horizontal alignment :)
https://www.raywenderlich.com/114552/uistackview-tutorial-introducing-stack-views
Related
I'm showing the data on tableview with multiple section,
when I open tableView height of section 1 should be 0. If I click a button in section 0 the height of the section 1 will be 150. For example if I click Agent button height of (want to) section cell show 150,I click builder button section hight will 0
I want to make the Section 1 visible on the button click from section 0
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! postaddTableViewCell1
cell.ownerButton.addTarget(self, action: #selector(FisrtButtonClick), for: .touchUpInside)
cell.agentButton.addTarget(self, action: #selector(FisrtButtonClick), for: .touchUpInside)
cell.builderButton.addTarget(self, action: #selector(FisrtButtonClick), for: .touchUpInside)
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as! postaddTableViewCell2
cell.checkbutton.tag = indexPath.row
cell.checkbutton.addTarget(self, action: #selector(checkbuttonClick) , for: .touchUpInside)
return cell
}
}
#objc func FisrtButtonClick(_ sender :UIButton)
{
let indexPath = IndexPath(row: 0, section: 0)
let cell = TableView.cellForRow(at: indexPath) as? postaddTableViewCell1
print(cell?.agentButton.titleLabel as Any)
if sender.tag == 10
{
cell?.ownerButton.backgroundColor = #colorLiteral(red: 0, green: 0.7254901961, blue: 0.4588235294, alpha: 1)
cell?.agentButton.backgroundColor = #colorLiteral(red: 0.768627451, green: 0.768627451, blue: 0.768627451, alpha: 1)
cell?.builderButton.backgroundColor = #colorLiteral(red: 0.768627451, green: 0.768627451, blue: 0.768627451, alpha: 1)
}else if sender.tag == 11
{
cell?.ownerButton.backgroundColor = #colorLiteral(red: 0.768627451, green: 0.768627451, blue: 0.768627451, alpha: 1)
cell?.agentButton.backgroundColor = #colorLiteral(red: 0, green: 0.7254901961, blue: 0.4588235294, alpha: 1)
cell?.builderButton.backgroundColor = #colorLiteral(red: 0.768627451, green: 0.768627451, blue: 0.768627451, alpha: 1)
} else if sender.tag == 12
{
cell?.ownerButton.backgroundColor = #colorLiteral(red: 0.768627451, green: 0.768627451, blue: 0.768627451, alpha: 1)
cell?.agentButton.backgroundColor = #colorLiteral(red: 0.768627451, green: 0.768627451, blue: 0.768627451, alpha: 1)
cell?.builderButton.backgroundColor = #colorLiteral(red: 0, green: 0.7254901961, blue: 0.4588235294, alpha: 1)
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0
{
return 80
}
if indexPath.section == 1
{
return 0
}
return 80
}
Check if the required button is clicked in the FisrtButtonClick method and set a boolean that is accessible to heightForRowAt. After toggling the boolean you can just reload the section you require (also cleaned up your code lil bit).
var selectedAgent = false
#objc func firstButtonClick(_ sender: UIButton) {
if sender.currentTitle == "Agent" {
selectedAgent = true
tableView.reloadSections([1], with: .automatic)
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 1 && !selectedAgent {
return 0
}
return 80
}
I have a trailingSwipeAction in a UITableViewCell, whose background color must be clear.
This is the code where I set the action :
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let myAction = UIContextualAction.init(style: .normal, title: nil) {
// My action code
}
myAction.backgroundColor = .clear
myAction.image = UIImage.init(named: "my-icon")
return UISwipeActionsConfiguration.init(actions: [myAction])
}
But I am getting gray background for the action, when no color was expected:
You can just set the alpha value to 0 for background color of the action:
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
}
}
}
Apparently if you set the background color to white with zero alpha, it will stay clear but without the gray default color.
Try this:
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
{
let deleteAction = UIContextualAction(style: .destructive, title: nil) { [weak self] (action, view, completion) in
// weak self to prevent memory leak if needed
guard let self = self else { return }
// do your nasty stuff here
completion(true)
}
deleteAction.backgroundColor = UIColor(white: 1, alpha: 0)
deleteAction.image = UIImage(systemName: "trash")
return UISwipeActionsConfiguration(actions: [deleteAction])
}
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?
100% working in iOS swift for change swipe button image color and background color change.
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
if isInbox {
let action = UIContextualAction(style: .normal, title: "", handler: { (action,view,completionHandler ) in
self.selectedIndex = indexPath.row
self.deleteNotification()
completionHandler(true)
})
let cgImageX = UIImage(named: "delete-1")?.cgImage
action.image = OriginalImageRender(cgImage: cgImageX!)
action.backgroundColor = UIColor.init(hex: "F7F7F7")
let confrigation = UISwipeActionsConfiguration(actions: [action])
return confrigation
}
return nil
}
Add this Class also for original image color display otherwise its showing only white image
class OriginalImageRender: UIImage {
override func withRenderingMode(_ renderingMode: UIImage.RenderingMode) -> UIImage {
return self
}
}
Following the solution from #Maulik Patel I just added a tint color option to the action.image:
let imageDelete = UIImage(systemName: "trash")?.cgImage
deleteAction.image = OriginalImageRender(cgImage: imageDelete!).withTintColor(UIColor(named: ("colorButton"))!)
deleteAction.backgroundColor = UIColor.init(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 0.0)
let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
return configuration
What I have done in my project is
func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath) {
let subViews = tableView.subviews.filter { (view) -> Bool in
if NSStringFromClass(view.classForCoder) == "UISwipeActionPullView" {
return true
}
return false
}
if subViews.count > 0 {
let bgView = subViews[0]
bgView.backgroundColor = bgColor
}
}
And my project's target is iOS 9.0 and above
When I create a new cell it will automatically mark
or when I marked the first one, I was creating multiple
Cell, there will be duplicates marked
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let itemCellIdentifier = "itemCell"
guard let itemCell = itemCardTableView.dequeueReusableCell(withIdentifier: itemCellIdentifier, for: indexPath) as? ItemCardTableViewCell else {
return UITableViewCell()
}
let itemCard = dataManager.items[indexPath.row]
itemCell.itemTitle.text = itemCard.title
if itemCard.isFinish {
itemCell.itemCellView.backgroundColor = UIColor(red: 0, green: 123, blue: 0)
}
return itemCell
}
Add cell method
let confirm = UIAlertAction(title: "確認", style: .default) { (action: UIAlertAction) in
guard let title = addAlert.textFields?.first?.text else { return }
let newItem = ItemCard(title: title, isFinish: false)
self.dataManager.items.append(newItem)
let indexPath = IndexPath(row: self.itemCardTableView.numberOfRows(inSection: 0), section: 0)
self.itemCardTableView.insertRows(at: [indexPath], with: .left)
When I create new data isFinish = false
How can I fix data duplication?
You should provide else case to set default background color.
if itemCard.isFinish {
itemCell.itemCellView.backgroundColor = UIColor(red: 0, green: 123, blue: 0)
} else {
itemCell.itemCellView.backgroundColor = UIColor.white
}
If I understood your question right, You should hold the isFinish flag somewhere out of the cell, because it's being reused by tableView.
You can create the finished = [Bool]() is your UIViewController and every time check finished[indexPath.row] to see if it's marked or not and pass the boolean to your cell.
I am trying to get the data from the selected rows in my tableView inside an array. I will append the selected row to the array and this does work great. Unfortunately when I deselect the row and select it again it will append it again. I need to write some logic to delete the deselected row from the array.
Does anyone know how I can do this?
Here is the code when I select a row:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath) as? exerciseCell
selectedCell?.contentView.backgroundColor = UIColor(red: 84.0 / 255, green: 199.0 / 255, blue: 252.0 / 255, alpha: 1)
rowSelected+=1
if rowSelected >= 1 {
nextBtn.isEnabled = true
nextBtn.backgroundColor = UIColor(red: 84.0 / 255, green: 199.0 / 255, blue: 252.0 / 255, alpha: 1)
}
let exercisesSelected = selectedCell?.exerciseNameLbl.text
exerisenames.append(exercisesSelected!)
print(exerisenames)
}
Here is the code for deselecting a row:
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath) as? exerciseCell
selectedCell?.textLabel?.textColor = UIColor.darkGray
rowSelected-=1
if rowSelected == 0 {
nextBtn.isEnabled = false
nextBtn.backgroundColor = UIColor.darkGray
nextBtn.setTitle("Choose an exercise", for: UIControlState.disabled)
}
let exercisesDeselected = selectedCell?.exerciseNameLbl.text
if exerisenames.contains(exercisesDeselected!) {
print("exercise already in the row and has to be deleted before going further")
}
print(exerisenames)
}
Both will refer to an array that I created in the top called: exercisenames
From a quick look at the docs, you probably want something like
if let index = exercisenames.index(of: exercisesDeselected) {
exercisenames.remove(at: index)
}
But I also think that #almas's suggestion of an ordered set might be good.
Could you tell me, how to add custom image to delete button when swipe cell on UITableview?
search you need function "editActionsForRowAtIndexPath", where you create scope of actions. You need to set UIImage to backgroundColor of UITableViewRowAction.
let someAction = UITableViewRowAction(style: .Default, title: "") { value in
println("button did tapped!")
}
someAction.backgroundColor = UIColor(patternImage: UIImage(named: "myImage")!)
There's this UITableView delegate function you can make use of:
#available(iOS 11.0, *)
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .normal, title: "", handler: {a,b,c in
// example of your delete function
self.YourArray.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
})
deleteAction.image = UIImage(named: "trash.png")
deleteAction.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [deleteAction])
}
PS: Personally, I think icon size 32 is the best
100 % working Swipable cell with custom image and size of image with background color ios swift #ios #swift #ios13 #ios14
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(style: .normal, title: "", handler: { (action,view,completionHandler ) in
self.selectedIndex = indexPath.row
self.deleteNotification()
completionHandler(true)
})
if #available(iOS 13.0, *) {
action.image = UIGraphicsImageRenderer(size: CGSize(width: 30, height: 30)).image { _ in
UIImage(named: "delete-1")?.draw(in: CGRect(x: 0, y: 0, width: 30, height: 30))
}
action.backgroundColor = UIColor.init(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 0.0)
let confrigation = UISwipeActionsConfiguration(actions: [action])
return confrigation
} else {
// Fallback on earlier versions
let cgImageX = UIImage(named: "delete-1")?.cgImage
action.image = OriginalImageRender(cgImage: cgImageX!)
action.backgroundColor = UIColor.init(hex: "F7F7F7")
let confrigation = UISwipeActionsConfiguration(actions: [action])
return confrigation
}
}