Uitableviewrowaction with Icon and Text

UITableViewRowAction with icon and text

What I ended up doing was generating an image on the fly as the background. This required the use of a hack/clever-trick or two.

The first part is the standard delegate method:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

let stockWidth = String(repeating: " ", count: 8)
let rename = UITableViewRowAction(style: .normal, title: stockWidth) { (_, indexPath) in
self.renameEntry(indexPath)
}
self.fixAction(rename, text: "Rename", image: UIImage(named: "pencilEdit")!, color: UIColor(52, 61, 70))
let locate = UITableViewRowAction(style: .normal, title: stockWidth) { (_, indexPath) in
self.locateEntry(indexPath)
}
self.fixAction(locate, text: "Locate", image: UIImage(named: "locatePin")!, color: UIColor(38, 107, 215))
let delete = UITableViewRowAction(style: .normal, title: stockWidth) { (_, indexPath) in
self.deleteEntry(indexPath)
}
self.fixAction(delete, text: "Forget", image: UIImage(named: "triggerDeleteSelector")!, color: UIColor(227, 34, 60))
let gap = UITableViewRowAction(style: .normal, title: "") { (_, _) in
// pass
}
gap.backgroundColor = UIColor.clear
return [gap, delete, locate, rename]
}

There's two not obvious details there. First, the action derives its width from the text string passed in. If you didn't give it some width via some non-visible space characters, the background image wouldn't have any area to be drawn in. That's the reason for the stockWidth string used in the first 3 actions. It's 8 character width is shared by the fixAction method that generates the background image.

The second detail is the inclusion of the fourth "gap" action at the bottom. For some reason, if the first action has a background paint that is a tile pattern, it will stretch left under the other actions. I found that I had to insert this zero width no op action at the front to avoid that.

func fixAction(_ action:UITableViewRowAction, text:String, image:UIImage, color:UIColor) {
// make sure the image is a mask that we can color with the passed color
let mask = image.withRenderingMode(.alwaysTemplate)
// compute the anticipated width of that non empty string
let stockSize = action.title!.sizeWithAttributes([NSFontAttributeName: UIFont.systemFont(ofSize: 18)])
// I know my row height
let height:CGFloat = 70
// Standard action width computation seems to add 15px on either side of the text
let width = (stockSize.width + 30).ceiling
let actionSize = CGSize(width: width, height: height)
// lets draw an image of actionSize
UIGraphicsBeginImageContextWithOptions(actionSize, false, 0.0)
if let context = UIGraphicsGetCurrentContext() {
context.clear(CGRect(origin: .zero, size: actionSize))
}
color.set()
let attributes = [NSForegroundColorAttributeName: color, NSFontAttributeName: UIFont(name: "Avenir-Book", size: 13)]
let textSize = text.size(attributes: attributes)
// implementation of `half` extension left up to the student
let textPoint = CGPoint(x: (width - textSize.width).half, y: (height - (textSize.height * 3)).half + (textSize.height * 2))
text.draw(at: textPoint, withAttributes: attributes)
let maskHeight = textSize.height * 2
let maskRect = CGRect(x: (width - maskHeight).half, y: textPoint.y - maskHeight, width: maskHeight, height: maskHeight)
mask.draw(in: maskRect)
if let result = UIGraphicsGetImageFromCurrentImageContext() {
// adjust the passed in action's backgroundColor to a patternImage
action.backgroundColor = UIColor(patternImage: result)
}
else {
"WTH!!!".logError()
}
UIGraphicsEndImageContext()
}

UITableViewRowAction image for title

iOS 11.0

Swift

Apple introduced flexible way to declare row actions with great benefits.

extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let askAction = UIContextualAction(style: .normal, title: nil) { action, view, complete in
print("Ask!")
complete(true)
}

// here set your image and background color
askAction.image = IMAGE
askAction.backgroundColor = .darkGray

let blockAction = UIContextualAction(style: .destructive, title: "Block") { action, view, complete in
print("Block")
complete(true)
}

return UISwipeActionsConfiguration(actions: [blockAction, askAction])
}

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.textLabel?.text = "row: \(indexPath.row)"
}
}

Example:

Sample Image

iOS 8.0

You need to set UIImage to backgroundColor of row action, concretely by:

Swift:

UIColor(patternImage: UIImage(named: "IMAGE_NAME"))

Objective-C:

[UIColor colorWithPatternImage:[UIImage imageNamed:@"IMAGE_NAME"]];

UISwipeActionsConfiguration/UIContextualAction with icon AND text AND clear background

Use patternImage to set the background color and don't set the title or image. This is a hacky trick but it should work for your situation.

let rename = UIContextualAction(style: .normal, title: nil) { (_, view, _) in 
self.renameEntry(indexPath)
}
rename.backgroundColor = UIColor(patternImage: UIImage(named: "pencilEditWideFrame")!)

You have to save your images with wide frames like this with the icon at the far left so they don't actually repeat:

Sample Image

Note: I tested this and it works when using two or more contextual actions but doesn't work with just one. The pattern ends up repeating on a full swipe.

This also assumes that you have a plain background behind your tableview that you can use for the background color of the image as transparency is still a no-go.

How to add image in UITableViewRowAction?

Finally in iOS 11, SWIFT 4 We can add add image in UITableView's swipe action with help of UISwipeActionsConfiguration

@available(iOS 11.0, *)
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {

let action = UIContextualAction(style: .normal, title: "Files", handler: { (action,view,completionHandler ) in
//do stuff
completionHandler(true)
})
action.image = UIImage(named: "apple.png")
action.backgroundColor = .red
let configuration = UISwipeActionsConfiguration(actions: [action])

return configuration
}

Sample Image

WWDC video at 28.34

Apple Doc

Note: I have used 50*50 points apple.png image with 50 tableview row height

UIContextualAction icon and text alignment

It doesn't appear there is an answer to why the text appears, and appears mis-aligned.

For anyone who comes across this question, we ended up setting the titles to nil for these buttons to keep them "icon only".

Add Image To Button In UITableView's editActionsForRowAt

One of the comments lead me to using unicode characters! Super helpful as they can be added to your normal string. Unicode characters include things like:

Useful unicode character examples
Useful unicode character examples 2
Useful unicode character examples 3

So all you do is pass a string with a unicode character, then a line break (\n), then your button title/text. So the basic syntax looks like:

title: "\u{1234}\n YourText "

For easy reuse (because I have multiple tables) I put the characters I wanted into a struct:

struct cellEditActionIcons {
static var editAction_Arrow = "\u{2191}"
static var editAction_Check = "\u{2713}"
static var editAction_Delete = "\u{2715}"
}

Then you can create the buttons like this:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
var doneBut = UITableViewRowAction (style: .normal, title: "\(cellEditActionIcons.editAction_Check)\n YourText" { action, index in
//Do stuff
}
doneBut.backgroundColor = myColors.someColor
}


Related Topics



Leave a reply



Submit