How to use a UIImage as a mask over a color in Objective-C

有些话、适合烂在心里 提交于 2019-11-30 01:50:34
Jano

In iOS 7+ you should use UIImageRenderingModeAlwaysTemplate instead. See https://stackoverflow.com/a/26965557/870313


Creating arbitrarily-colored icons from a black-with-alpha master image (iOS).

// Usage: UIImage *buttonImage = [UIImage ipMaskedImageNamed:@"UIButtonBarAction.png" color:[UIColor redColor]];

+ (UIImage *)ipMaskedImageNamed:(NSString *)name color:(UIColor *)color
{
    UIImage *image = [UIImage imageNamed:name];
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
    UIGraphicsBeginImageContextWithOptions(rect.size, NO, image.scale);
    CGContextRef c = UIGraphicsGetCurrentContext();
    [image drawInRect:rect];
    CGContextSetFillColorWithColor(c, [color CGColor]);
    CGContextSetBlendMode(c, kCGBlendModeSourceAtop);
    CGContextFillRect(c, rect);
    UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return result;
}

Credits to Ole Zorn: https://gist.github.com/1102091

Translating Jano's answer into Swift:

func ipMaskedImageNamed(name:String, color:UIColor) -> UIImage {
    let image = UIImage(named: name)
    let rect:CGRect = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: image!.size.width, height: image!.size.height))
    UIGraphicsBeginImageContextWithOptions(rect.size, false, image!.scale)
    let c:CGContextRef = UIGraphicsGetCurrentContext()
    image?.drawInRect(rect)
    CGContextSetFillColorWithColor(c, color.CGColor)
    CGContextSetBlendMode(c, kCGBlendModeSourceAtop)
    CGContextFillRect(c, rect)
    let result:UIImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return result
}

Usage:

myButton.setImage(ipMaskedImageNamed("grayScalePNG", color: UIColor.redColor()), forState: .Normal)

Edit: According to this article, you can turn an image into a mask whose opaque areas are represented by the tint color. Within the asset catalog, under the Attributes Inspector of the image, change Render As to Template Image. No code necessary.

Barry Langdon-Lassagne

Here's a variation in Swift 3, written as an extension to UIImage:

extension UIImage {
    func tinted(color:UIColor) -> UIImage? {
        let image = self
        let rect:CGRect = CGRect(origin: CGPoint(x: 0, y: 0), size: image.size)
        UIGraphicsBeginImageContextWithOptions(rect.size, false, image.scale)
        let context = UIGraphicsGetCurrentContext()!
        image.draw(in: rect)
        context.setFillColor(color.cgColor)
        context.setBlendMode(.sourceAtop)
        context.fill(rect)
        if let result:UIImage = UIGraphicsGetImageFromCurrentImageContext() {
            UIGraphicsEndImageContext()
            return result
        }
        else {
            return nil
        }
    }
}

// Usage
let myImage = #imageLiteral(resourceName: "Check.png") // any image
let blueImage = myImage.tinted(color: .blue)

I converted this for OSX here as a Category and copied below. Note this is for non-ARC projects. For ARC projects, the autorelease can be removed.

- (NSImage *)cdsMaskedWithColor:(NSColor *)color
{
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

    NSImage *result = [[NSImage alloc] initWithSize:self.size];
    [result lockFocusFlipped:self.isFlipped];

    NSGraphicsContext *context = [NSGraphicsContext currentContext];
    CGContextRef c = (CGContextRef)[context graphicsPort];

    [self drawInRect:NSRectFromCGRect(rect)];

    CGContextSetFillColorWithColor(c, [color CGColor]);
    CGContextSetBlendMode(c, kCGBlendModeSourceAtop);
    CGContextFillRect(c, rect);

    [result unlockFocus];

    return [result autorelease];
}
+ (NSImage *)cdsMaskedImageNamed:(NSString *)name color:(NSColor *)color
{
    NSImage *image = [NSImage imageNamed:name];
    return [image cdsMaskedWithColor:color];
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!