How to set color of templated image in NSTextAttachment How to set color of templated image in NSTextAttachment ios ios

How to set color of templated image in NSTextAttachment


It seems that there's a bug in UIKit. There's a workaround for that ;]

For some reason you need to append empty space before image attachment to make it work properly with UIImageRenderingModeAlwaysTemplate.

So your snippet would look like that (mine is in 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;}


I have good experience with using the library UIImage+Additions when tinting UIImage instances. Specially check section IV.

If adding a third-party library is not an option, here is something to get you started:

- (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;}

This will make a UIImage go from:

UIImage before tinting

To

UIImage after tinting

Update: Swift version:

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


I use this NSMutableAttributedString extension for Swift.

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

This is how to use it.

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

You can also change addImageAttachment's parameter image: UIImage to image: UIImage? and check the nullability in extension.