UITableViewRowAction image for title

2019-01-10 19:36发布

问题:

I made a custom UITableViewRowAction. Now I'd like to add an image instead of the text. I know that it's possible but don't know how to do it. Does someone of you knows how to do this in Swift and would like to help me? Thanks for your answers!

回答1:

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"]];


回答2:

I made this simple UITableViewRowAction category, in order to set the icon for my actions. You can set the image, the background color, the cell height (to manage dynamic cells) and the icon size in percentage.

extension UITableViewRowAction {

  func setIcon(iconImage: UIImage, backColor: UIColor, cellHeight: CGFloat, iconSizePercentage: CGFloat)
  {
    let iconHeight = cellHeight * iconSizePercentage
    let margin = (cellHeight - iconHeight) / 2 as CGFloat

    UIGraphicsBeginImageContextWithOptions(CGSize(width: cellHeight, height: cellHeight), false, 0)
    let context = UIGraphicsGetCurrentContext()

    backColor.setFill()
    context!.fill(CGRect(x:0, y:0, width:cellHeight, height:cellHeight))

    iconImage.draw(in: CGRect(x: margin, y: margin, width: iconHeight, height: iconHeight))

    let actionImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    self.backgroundColor = UIColor.init(patternImage: actionImage!)
  }
}


回答3:

class TableViewRowAction: UITableViewRowAction 
{
    var image: UIImage?

    func _setButton(button: UIButton) 
    {
        if let image = image, let titleLabel = button.titleLabel
        {
            let labelString = NSString(string: titleLabel.text!)
            let titleSize = labelString.sizeWithAttributes([NSFontAttributeName: titleLabel.font])

            button.tintColor = UIColor.whiteColor()
            button.setImage(image.imageWithRenderingMode(.AlwaysTemplate), forState: .Normal)
            button.imageEdgeInsets.right = -titleSize.width
        }
    }
}


func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? 
{
    let delete = TableViewRowAction(style: UITableViewRowActionStyle.Default, title: "         ") { action, indexPath in }
    delete.image = UIImage(named: "trashImg")

    let sharing = TableViewRowAction(style: UITableViewRowActionStyle.Default, title: "         ") { action, indexPath in }
    sharing.backgroundColor = UIColor.lightGrayColor()
    sharing.image = UIImage(named: "sharingImg")

    return [delete, sharing]
}


回答4:

Swift 4 (iOS 11+):

iOS 11 now supports images (only) to display in action buttons. You simply have to initialize a UISwipeActionsConfiguration object in your table view delegate object:

extension MyTableViewController:UITableViewDelegate {

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

        let deleteAction = UIContextualAction(style: .normal, title:  nil, handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in

                debugPrint("Delete tapped")

                success(true)
            })

        deleteAction.image = UIImage(named: "icon_delete.png")
        deleteAction.backgroundColor = UIColor.red

        return UISwipeActionsConfiguration(actions: [deleteAction])
    }

}



回答5:

I just implement better version of https://stackoverflow.com/a/48122210 1. In majority of cases height of cell and width of custom swipe part are different, you have to calculate this in your function 2. Image can have different size than square, so you have to calculate proportions not just for height. So, code with my fixes

func setIcon(iconImage: UIImage, backColor: UIColor, cellHeight: CGFloat, customSwipPartWidth: CGFloat, iconSizePercentage: CGFloat) {
        let iconWidth = customSwipPartWidth * iconSizePercentage
        let iconHeight = iconImage.size.height / iconImage.size.width * iconWidth
        let marginY = (cellHeight - iconHeight) / 2 as CGFloat
        let marginX = (customSwipPartWidth - iconWidth) / 2 as CGFloat


        UIGraphicsBeginImageContextWithOptions(CGSize(width: customSwipPartWidth, height: cellHeight), false, 0)
        let context = UIGraphicsGetCurrentContext()

        backColor.setFill()
        context!.fill(CGRect(x:0, y:0, width:customSwipPartWidth, height:cellHeight))

        iconImage.draw(in: CGRect(x: marginX, y: marginY, width: iconWidth, height: iconHeight))

        let actionImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

        self.backgroundColor = UIColor.init(patternImage: actionImage!)
    } 


回答6:

delActions.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"delete.png"]];


回答7:

The problem with the basic pattern-color approach is that your image need to be of the same size than the action button, or at least to have some more flat background at the bottom to prevent the image repetition (even though it will be anchored on top, which is not really nice).

I had to deal with dynamic-height cells so I implemented the following:

- (UIColor *)backgroundImageForActionAtIndexPath:(NSIndexPath *)indexPath withImage:(UIImage *)image tintColor:(UIColor *)tintColor backgroundColor:(UIColor *)backgrounfColor expectedWith:(CGFloat)width {
//
CGRect cellFrame = [self.tableView rectForRowAtIndexPath:indexPath];
CGSize expectedSize = CGSizeMake(width, cellFrame.size.height);

UIGraphicsBeginImageContextWithOptions(expectedSize, NO, 0.0);

CGContextRef ctx = UIGraphicsGetCurrentContext ();
if (ctx) {
    // set the background
    CGContextSetFillColorWithColor(ctx, backgrounfColor.CGColor);
    CGRect fillRect = CGRectZero;
    fillRect.size = expectedSize;
    CGContextFillRect(ctx, fillRect);
    // put the image
    CGContextSetFillColorWithColor(ctx, tintColor.CGColor);
    CGRect rect = CGRectMake((expectedSize.width - image.size.width) / 2., (expectedSize.height - image.size.height) / 2., image.size.width, image.size.height);
    [image drawInRect:rect];
}

UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return [UIColor colorWithPatternImage:newImage];

}

You still have to set the expectedWidth so it matches the empty label you put in the action's title. e.g: @" " -> 64.f

As you see with this approach you can set the button's background and tint color in code and not in the artwork itself.



回答8:

My variation tries to use UImageView's contentMode behavior and when that was not enough, I found some good use for a couple of the Core Geometry Rect methods because they keep the target frame centered within the cell frame. Using an eye test, It appears that swiping was removing half of my normal cell width so that's where the magic numbers show up.

Swift 3.0

extension UITableViewRowAction {

    func setIcon(iconImage: UIImage, backColor: UIColor, cellHeight: CGFloat, cellWidth:CGFloat) ///, iconSizePercentage: CGFloat)
    {
        let cellFrame = CGRect(origin: .zero, size: CGSize(width: cellWidth*0.5, height: cellHeight))
        let imageFrame = CGRect(x:0, y:0,width:iconImage.size.width, height: iconImage.size.height)
        let insetFrame = cellFrame.insetBy(dx: ((cellFrame.size.width - imageFrame.size.width) / 2), dy: ((cellFrame.size.height - imageFrame.size.height) / 2))
        let targetFrame = insetFrame.offsetBy(dx: -(insetFrame.width / 2.0), dy: 0.0)
        let imageView = UIImageView(frame: imageFrame)
        imageView.image = iconImage
        imageView.contentMode = .left
        guard let resizedImage = imageView.image else { return }
        UIGraphicsBeginImageContextWithOptions(CGSize(width: cellWidth, height: cellHeight), false, 0)
        guard let context = UIGraphicsGetCurrentContext() else { return }
        backColor.setFill()
        context.fill(CGRect(x:0, y:0, width:cellWidth, height:cellHeight))
        resizedImage.draw(in: CGRect(x:(targetFrame.origin.x / 2), y: targetFrame.origin.y, width:targetFrame.width, height:targetFrame.height))
        guard let actionImage = UIGraphicsGetImageFromCurrentImageContext() else { return }
        UIGraphicsEndImageContext()
        self.backgroundColor = UIColor.init(patternImage: actionImage)
    }
}

Usage: from the editActions... method of the tableview delegate.

let cellHeight = (self.tableView(tableView, cellForRowAt: indexPath)).frame.size.height
let cellWidth = (self.tableView(tableView, cellForRowAt: indexPath)).frame.size.width
let favorite = UITableViewRowAction(style: .normal, title: nil) { action, index in
                //perform action
                debugPrint("Test")

            }
favorite.setIcon(iconImage: #imageLiteral(resourceName: "favorite"), backColor: .green, cellHeight: cellHeight, cellWidth:cellWidth)