简体   繁体   English

标题的 UITableViewRowAction 图像

[英]UITableViewRowAction image for title

I made a custom UITableViewRowAction.我做了一个自定义的 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?你们中有人知道如何在 Swift 中执行此操作并愿意帮助我吗? Thanks for your answers!感谢您的回答!

iOS 11.0 iOS 11.0

Swift迅速

Apple introduced flexible way to declare row actions with great benefits. Apple 引入了灵活的方式来声明行操作,具有很大的好处。

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:例子:

在此处输入图片说明

iOS 8.0 iOS 8.0

You need to set UIImage to backgroundColor of row action, concretely by:您需要将 UIImage 设置为 row action 的 backgroundColor,具体通过:

Swift:迅速:

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

Objective-C:目标-C:

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

Swift 4 (iOS 11+):斯威夫特 4(iOS 11+):

iOS 11 now supports images (only) to display in action buttons. iOS 11 现在支持在操作按钮中显示图像(仅)。 You simply have to initialize a UISwipeActionsConfiguration object in your table view delegate object:你只需要在你的表视图委托对象中初始化一个 UISwipeActionsConfiguration 对象:

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])
    }

} }

I made this simple UITableViewRowAction category, in order to set the icon for my actions.我制作了这个简单的 UITableViewRowAction 类别,以便为我的操作设置图标。 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!)
  }
}
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]
}

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.我只是实现了https://stackoverflow.com/a/48122210 的更好版本 1. 在大多数情况下,单元格的高度和自定义滑动部分的宽度不同,您必须在函数中计算它 2. 图像可以有不同的大小比正方形,所以你必须计算比例而不仅仅是高度。 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!)
    } 

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.我的变体尝试使用 UImageView 的 contentMode 行为,当这还不够时,我发现了一些 Core Geometry Rect 方法的很好用处,因为它们将目标框架保持在单元格框架内的中心。 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斯威夫特 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.用法:来自 tableview 委托的 editActions... 方法。

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)
delActions.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"delete.png"]];

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.您仍然必须设置 expectedWidth 以使其与您放置在操作标题中的空标签相匹配。 eg: @" " -> 64.f例如:@" " -> 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.正如您看到的这种方法,您可以在代码中而不是在图稿本身中设置按钮的背景和色调。

For those who want to create this effect;对于那些想要创造这种效果的人;

在此处输入图片说明

I have created an extension for UISwipeActionsConfiguration which you can use without any third-party library.我为UISwipeActionsConfiguration创建了一个扩展,你可以在没有任何第三方库的情况下使用它。 Basically, the idea is to create an Attributed String from the image and the text and set it to label and create an image from that label.基本上,这个想法是从图像和文本创建一个属性字符串,并将其设置为标签并从该标签创建一个图像。 And append it to UIContextualAction 's image property.并将其附加到UIContextualActionimage属性。

extension UISwipeActionsConfiguration {

    public static func makeTitledImage(
        image: UIImage?,
        title: String,
        textColor: UIColor = .white,
        font: UIFont = .systemFont(ofSize: 14),
        size: CGSize = .init(width: 50, height: 50)
    ) -> UIImage? {
        
        /// Create attributed string attachment with image
        let attachment = NSTextAttachment()
        attachment.image = image
        let imageString = NSAttributedString(attachment: attachment)
        
        /// Create attributed string with title
        let text = NSAttributedString(
            string: "\n\(title)",
            attributes: [
                .foregroundColor: textColor,
                .font: font
            ]
        )
        
        /// Merge two attributed strings
        let mergedText = NSMutableAttributedString()
        mergedText.append(imageString)
        mergedText.append(text)
        
        /// Create label and append that merged attributed string
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        label.textAlignment = .center
        label.numberOfLines = 2
        label.attributedText = mergedText
        
        /// Create image from that label
        let renderer = UIGraphicsImageRenderer(bounds: label.bounds)
        let image = renderer.image { rendererContext in
            label.layer.render(in: rendererContext.cgContext)
        }
        
        /// Convert it to UIImage and return
        if let cgImage = image.cgImage {
            return UIImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: .up)
        }
        
        return nil
    }
}

And you can use it like this;你可以这样使用它;

public func tableView(
        _ tableView: UITableView,
        trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath
    ) -> UISwipeActionsConfiguration? 
{
        let deleteAction = UIContextualAction(
            style: .normal,
            title:  nil,
            handler: { [weak self] (_, _, success: (Bool) -> Void) in
                success(true)
                print("Your action in here")
            }
        )
        
        deleteAction.image = UISwipeActionsConfiguration.makeTitledImage(
            image: UIImage(named: "delete_icon"),
            title: "Delete")
        )
        deleteAction.backgroundColor = .orange
        return UISwipeActionsConfiguration(actions: [deleteAction])
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM