【问题标题】:How to set color of templated image in NSTextAttachment如何在 NSTextAttachment 中设置模板图像的颜色
【发布时间】:2015-05-16 11:30:44
【问题描述】:

如何设置作为属性字符串附件的模板图像的颜色?

背景:

我有一个 UILabel 并将其属性文本设置为 NSAttributedString。 NSAttributedString 包含一个带有小图像的 NSTextAttachment。现在我想让我的图像颜色与文本颜色匹配,但我不知道如何使它工作。

我通常希望通过将其渲染模式设置为 UIImageRenderingModeAlwaysTemplate 来为图像着色,然后在包含的 UIView 上设置 tintColor。我尝试在我的 UILabel 上设置 tintColor,但没有效果。

这是我的代码。它在 Ruby (RubyMotion) 中,因此语法可能看起来有点滑稽,但它与 Objective C 1:1 映射。

attachment = NSTextAttachment.alloc.initWithData(nil, ofType: nil)
attachment.image = UIImage.imageNamed(icon_name).imageWithRenderingMode(UIImageRenderingModeAlwaysTemplate)

label_string = NSMutableAttributedString.attributedStringWithAttachment(attachment)
label_string.appendAttributedString(NSMutableAttributedString.alloc.initWithString('my text', attributes: { NSFontAttributeName => UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote), NSForegroundColorAttributeName => foreground_color }))

label = UILabel.alloc.initWithFrame(CGRectZero)
label.tintColor = foreground_color
label.attributedText = label_string
label.textAlignment = NSTextAlignmentCenter
label.numberOfLines = 0

【问题讨论】:

  • 您是否找到了任何不涉及在将图像添加为文本附件之前绘制图像的答案?我很想有一个简单的方法来做到这一点。
  • 很遗憾没有。就我而言,我的字符串有时在深色背景上为白色,有时在浅色背景上为黑色......我最终只是将附件图像更改为适用于两种背景的中性灰色。

标签: ios nsattributedstring nstextattachment


【解决方案1】:
let imageAttachment = NSTextAttachment()
imageAttachment.image = UIImage(systemName: "magnifyingglass")
imageAttachment.image = imageAttachment.image?.withTintColor(UIColor(red: 1.00, green: 1.00, blue: 0.00, alpha: 1.00))

这应该可以工作(ios 13 及更高版本)

【讨论】:

  • 它确实有效!!!
【解决方案2】:

我在为UIImage 实例着色时使用the library UIImage+Additions 有很好的经验。特别检查第四节。

如果无法选择添加第三方库,以下是一些帮助您入门的方法:

- (UIImage *)colorImage:(UIImage *)image color:(UIColor *)color
{
    UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, 0, image.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);

    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGContextDrawImage(context, rect, image.CGImage);
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    [color setFill];
    CGContextFillRect(context, rect);


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

    return coloredImage;
}

这将使UIImage 来自:

更新:Swift 版本:

extension UIImage {
    func colorImage(with color: UIColor) -> UIImage? {
        guard let cgImage = self.cgImage else { return nil }
        UIGraphicsBeginImageContext(self.size)
        let contextRef = UIGraphicsGetCurrentContext()

        contextRef?.translateBy(x: 0, y: self.size.height)
        contextRef?.scaleBy(x: 1.0, y: -1.0)
        let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)

        contextRef?.setBlendMode(CGBlendMode.normal)
        contextRef?.draw(cgImage, in: rect)
        contextRef?.setBlendMode(CGBlendMode.sourceIn)
        color.setFill()
        contextRef?.fill(rect)

        let coloredImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return coloredImage
    }
}

【讨论】:

  • 感谢您的指点。不过,希望我不必引入一个全新的库来解决这个问题。
  • @Ghazgkull 添加了一个代码示例来帮助您入门。如果这不是您想要的方式,请告诉我更多关于您想如何着色的信息。
  • 我通常会通过简单地调用 UIImage.imageWithRenderingMode(UIImageRenderingModeAlwaysTemplate) 然后设置包含 UIView 的 tintColor 来为图像着色。这只是两行代码。这里唯一的问题是我的 UIImage 在 NSAttributedString 内,所以我无法访问包含它的即时 UIView。
  • 顺便说一下,@Ghazgkull,链接的整个库只有 49 KB!包括它以小尺寸提供了很多功能:)
  • 我注意到应用这些更改后图像变得有点模糊。知道如何解决吗?我也在用pdf。
【解决方案3】:

在 iOS 12 上,我们需要在图像前插入一个字符并设置该字符的前景色。但是,在 iOS 13 上,我们可以直接在包含我们的 NSTextAttachment 的 NSAttributedString 上设置前景色。

我在 iOS 12.3 和 iOS 13.3.1 上测试了以下扩展

extension NSMutableAttributedString {
    @discardableResult
    func sbs_append(_ image: UIImage, color: UIColor? = nil) -> Self {
        let attachment = NSTextAttachment()
        attachment.image = image
        let attachmentString = NSAttributedString(attachment: attachment)
        if let color = color {
            if #available(iOS 13, *) {} else {
                // On iOS 12 we need to add a character with a foreground color before the image,
                // in order for the image to get a color.
                let colorString = NSMutableAttributedString(string: "\0")
                colorString.addAttributes([.foregroundColor: color], range: NSRange(location: 0, length: colorString.length))
                append(colorString)
            }
            let attributedString = NSMutableAttributedString(attributedString: attachmentString)
            if #available(iOS 13, *) {
                // On iOS 13 we can set the foreground color of the image.
                attributedString.addAttributes([.foregroundColor: color], range: NSRange(location: 0, length: attributedString.length))
            }
            append(attributedString)
        } else {
            append(attachmentString)
        }
        return self
    }
}

【讨论】:

  • 由于某种原因,在 iOS 12.4 上添加 \0 字符对我不起作用。任何非空字符,例如空格或其他任何东西确实有效。但是它留下了不必要的空间,当附件位于字符串的开头时,这一点尤其明显。我使用了 @6055194 answer 的 \u{200c} 并成功了。
【解决方案4】:

我为 Swift 使用了这个 NSMutableAttributedString 扩展。

extension NSMutableAttributedString {
    func addImageAttachment(image: UIImage, font: UIFont, textColor: UIColor, size: CGSize? = nil) {
        let textAttributes: [NSAttributedString.Key: Any] = [
            .strokeColor: textColor,
            .foregroundColor: textColor,
            .font: font
        ]

        self.append(
            NSAttributedString.init(
                //U+200C (zero-width non-joiner) is a non-printing character. It will not paste unnecessary space.
                string: "\u{200c}",
                attributes: textAttributes
            )
        )

        let attachment = NSTextAttachment()
        attachment.image = image.withRenderingMode(.alwaysTemplate)
        //Uncomment to set size of image. 
        //P.S. font.capHeight sets height of image equal to font size.
        //let imageSize = size ?? CGSize.init(width: font.capHeight, height: font.capHeight)
        //attachment.bounds = CGRect(
        //    x: 0,
        //    y: 0,
        //    width: imageSize.width,
        //    height: imageSize.height
        //)
        let attachmentString = NSMutableAttributedString(attachment: attachment)
        attachmentString.addAttributes(
            textAttributes,
            range: NSMakeRange(
                0,
                attachmentString.length
            )
        )
        self.append(attachmentString)
    }
}

这是如何使用它。

let attributedString = NSMutableAttributedString()
if let image = UIImage.init(named: "image") {
    attributedString.addImageAttachment(image: image, font: .systemFont(ofSize: 14), textColor: .red)
}

您还可以将addImageAttachment的参数image: UIImage更改为image: UIImage?并检查扩展中的可空性。

【讨论】:

    【解决方案5】:

    我找到了更好的解决方案。确保纯文本位于第一项中。如果NSTextAttachment(图片)是第一项,您可以在NSTextAttachment之前插入一个空格字符串。

    // create image attachment
    NSTextAttachment *imageAttachment = [[NSTextAttachment alloc] init];
    imageAttachment.image = [[UIImage imageNamed:@"ImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
    NSAttributedString *imageAttchString = [NSAttributedString attributedStringWithAttachment:attachment];
    
    // create attributedString
    NSString *string = @"Some text ";
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
    
    // insert image 
    [attributedString insertAttributedString:imageAttchString atIndex:0];
    [attributedString insertAttributedString:[[NSAttributedString alloc] initWithString:@" "] atIndex:0];
    
    label.attributedText =  attributedString;
    
    // used
    label.textColor = [UIColor redColor];
    // or
    label.textColor = [UIColor greenColor];
    

    【讨论】:

      【解决方案6】:

      使用 UIImageRenderingModeAlwaysOriginal 作为原始图像颜色。 UIImageRenderingModeAlwaysTemplate + 为自定义颜色设置色调颜色。

      【讨论】:

        【解决方案7】:

        @blazejmar 的解决方案有效,但没有必要。为此,您需要做的就是在属性字符串连接后设置颜色。这是一个例子。

        NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
        attachment.image = [[UIImage imageNamed:@"ImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
        
        NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:attachment];
        
        NSString *string = @"Some text ";
        NSRange range2 = NSMakeRange(string.length - 1, attachmentString.length);
        
        NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
        [attributedString appendAttributedString:attachmentString];
        [attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:range2];
        self.label.attributedText = attributedString;
        

        【讨论】:

        • 它之所以有效,是因为您不是从附件开始字符串。在@blazejmar 解决方案中查看我的评论。
        【解决方案8】:

        UIKit 中似乎有一个错误。有一个解决方法;]

        由于某种原因,您需要在图像附件之前附加空白空间,以使其与UIImageRenderingModeAlwaysTemplate 正常工作。

        所以你的 sn-p 看起来像这样(我的在 ObjC 中):

        - (NSAttributedString *)attributedStringWithValue:(NSString *)string image:(UIImage *)image {
            NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
            attachment.image = image;
        
            NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:attachment];
            NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[[NSAttributedString alloc] initWithString:@" "]];
            [mutableAttributedString appendAttributedString:attachmentString];
            [mutableAttributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:0] range:NSMakeRange(0, mutableAttributedString.length)]; // Put font size 0 to prevent offset
            [mutableAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, mutableAttributedString.length)];
            [mutableAttributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@" "]];
        
            NSAttributedString *ratingText = [[NSAttributedString alloc] initWithString:string];
            [mutableAttributedString appendAttributedString:ratingText];
            return mutableAttributedString;
        }
        

        【讨论】:

        • 看起来确实如此,这是有用的信息。我想我会使用着色图像方法,但很高兴知道。
        • 我发现NSForegroundColorAttributeName 没有对附加图像进行着色。相反,它从UILabeltextColor 属性中获取它,即使它在属性字符串中被覆盖。
        • 您可以使用空字符串代替空格,因为空格会添加到布局中。 [[NSMutableAttributedString alloc] initWithAttributedString:[[NSAttributedString alloc] initWithString:@"\0"]];
        • 主要的是必须从任何字母(但不是那个附件)的附件开始着色属性字符串。 @railwayparade 的解决方案和评论解释了这种解决方法。
        • 以空字符串开头 ("\0") 似乎对我不起作用。我必须将其替换为实际空间 (" ") 才能使图像着色。
        猜你喜欢
        • 2018-07-22
        • 1970-01-01
        • 2012-07-05
        • 1970-01-01
        • 1970-01-01
        • 2021-06-16
        • 2011-02-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多