iPhone - 如何给一张图片上色?

35

我想知道如何给一张图片着色(比如将一个白色的.png文件变成红色)。我看过各种建议,但从未得到任何确认这是真正可行的。我已经尝试了以下方法:

-(UIImage *)colorizeImage:(UIImage *)baseImage color:(UIColor *)theColor {
    UIGraphicsBeginImageContext(baseImage.size);

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGRect area = CGRectMake(0, 0, baseImage.size.width, baseImage.size.height);

    CGContextScaleCTM(ctx, 1, -1);
    CGContextTranslateCTM(ctx, 0, -area.size.height);
    CGContextSaveGState(ctx);
    CGContextClipToMask(ctx, area, baseImage.CGImage);
    [theColor set];
    CGContextFillRect(ctx, area);
    CGContextRestoreGState(ctx);
    CGContextSetBlendMode(ctx, kCGBlendModeNormal);
    CGContextDrawImage(ctx, area, baseImage.CGImage);
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

myImageView.image = [self colorizeImage:[UIImage imageNamed:@"whiteImage.png"] color:[UIColor redColor]];

但它不起作用 - 图像仍然在屏幕上是白色的。


但是你究竟想做什么?你是想把所有完全白色(RGB(255,255,255))的像素都变成红色吗?你只是想添加一种红色色调吗?你的“whiteImage.png”实际上只是一个完全白色的大矩形吗? - Amagrammer
是的,我想将白色(或灰色)像素变成红色。 png 的透明部分应该保持透明。whiteImage.png 不是一个白色矩形 - 例如它是一张动物的图片。 - sol
你不能只有同一张图片的红色版本,并在需要时进行切换吗?双色图像压缩非常小。 - Amagrammer
可以参考https://dev59.com/GGIk5IYBdhLWcg3wbtyb#41149046。 - Shrawan
10个回答

64

将此代码制作成UIImage类别,并在iOS 4中考虑比例因素。

- (UIImage *)imageWithOverlayColor:(UIColor *)color
{        
    CGRect rect = CGRectMake(0.0f, 0.0f, self.size.width, self.size.height);

    if (UIGraphicsBeginImageContextWithOptions) {
        CGFloat imageScale = 1.0f;
        if ([self respondsToSelector:@selector(scale)])  // The scale property is new with iOS4.
            imageScale = self.scale;
        UIGraphicsBeginImageContextWithOptions(self.size, NO, imageScale);
    }
    else {
        UIGraphicsBeginImageContext(self.size);
    }

    [self drawInRect:rect];

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);

    CGContextSetFillColorWithColor(context, color.CGColor);
    CGContextFillRect(context, rect);

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

    return image;
}

更新 - Swift 版本:

func imageWithColor(color: UIColor) -> UIImage {
    let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
    UIGraphicsBeginImageContextWithOptions(self.size, false, 0.0);
    drawInRect(rect)
    let context = UIGraphicsGetCurrentContext()
    CGContextSetBlendMode(context, .SourceIn)
    CGContextSetFillColorWithColor(context, color.CGColor)
    CGContextFillRect(context, rect);
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}

谢谢这个...正是我需要的,可以根据用户选择的主题来给界面特性着色。 - Jim Rhoades
1
+1 非常感谢,它对我有用。之前我只得到了白色,现在按照willc2的建议{CGContextSetBlendMode(ctx,kCGBlendModeMultiply);} ,它工作得很好。 - HDdeveloper
有一天早上发现了这个……而那是如此简单!我爱你Dave Balton。 - Martin
这太棒了,这是实现UIImage着色的最佳解决方案。我花了整整一天的时间寻找一种重新着色UIImage并能够使用resizableImageWithCapInsets方法的方法。[[[UIImage imageNamed:@"myImage.png"] imageWithOverlayColor:[UIColor blackColor]] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 15)]; - davis
适用于具有清晰颜色背景的图形,您只想着色非透明部分。 - kraftydevil
2
在iOS7+中,这个问题有一个更简单的解决方案,请参见https://dev59.com/K3M_5IYBdhLWcg3wzmgw#27356928。 - Magoo

12

将您的混合模式更改为multiply,您的代码就能正常工作:

CGContextSetBlendMode(ctx, kCGBlendModeMultiply);

1
+1 谢谢,这对我很有用。之前我只能得到白色,现在它正常工作了。 - HDdeveloper

10
从iOS 7开始,您可以像这样使用单个颜色对图像进行着色
Objective C
UIImage *image = [UIImage imageNamed:@"myImage.png"];
UIImageView *imageView = [[UIImageView alloc]initWithImage:[image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
imageView.tintColor = [UIColor redColor];

Swift 3
let image = UIImage(named:"myImage.png")?.withRenderingMode(.alwaysTemplate)
let imageView = UIImageView(image:image)
imageView.tintColor = .red

这将用单一颜色着色整个图像并保留 alpha 通道。

2

我认为在iOS7中给一张图片上色的最佳方式是在你的图片上指定属性UIImageRenderingModeAlwaysTemplate:

myImageView.image = [[UIImage imageNamed:@"myImage"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];

然后使用imageView的tintColor对其进行着色

myImageView.tintColor = [UIColor purpleColor]; //Maybe purple is not the best choice ;)

我认为这更容易(不需要类别),而且当然也更优化!

注:如果您正在使用xcassets,则可以在XCode中将渲染属性设置为UIImageRenderingModeAlwaysTemplate,就像在此屏幕截图中所示:

UIImageRenderingModeAlwaysTemplate in XCode


1

简短、简洁而更好 :)

- (UIImage *)colorizeImage:(UIImage *)image withColor:(UIColor *)color

定义UIColorFromRGB以使用十六进制颜色代码,然后只需调用colorizeImage:withColor

#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIImage *imgToColor = [UIImage imageNamed:@"myImage.png"];
    imgToColor = [self colorizeImage:imgToColor withColor:UIColorFromRGB(0xff00ff)];
    // use normal UIColor or UIColorFromRGB() for hex
}

 - (UIImage *)colorizeImage:(UIImage *)image withColor:(UIColor *)color
{
    CGRect rect = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
    UIGraphicsBeginImageContextWithOptions(image.size, NO, [[UIScreen mainScreen] scale]);
    [image drawInRect:rect];

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);

    CGContextSetFillColorWithColor(context, color.CGColor);
    CGContextFillRect(context, rect);

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

1

@geek kid,如果这对你有帮助,请告诉我...

// translate/flip the graphics context (for transforming from CG* coords to UI* coords)
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

//用UIGraphicsGetCurrentContext()替换"context",或者如果您有当前上下文的实例。

希望能帮到您。


1

好的,这样怎么样?

在您的资产创建阶段(而不是应用程序运行时动态),反转图像的颜色方案。现在是白色的部分 -> 透明。现在是透明的部分 -> 页面背景。

在每个图像下面,放置一个相同大小的空白白色视图。当您希望将图像变为红色时,请将该空白白色视图的颜色更改为红色。

这可行吗?


0
如果你想要改变色调( 就像Rob Napier所建议的那样),而不仅仅是一个染色的图像,可以使用这个UIImage扩展:
extension UIImage {
    func colorized(color: UIColor) -> UIImage? {
        defer {
            UIGraphicsEndImageContext()
        }

        let frame = CGRect(origin: .zero, size: size)

        UIGraphicsBeginImageContext(size)

        guard let context = UIGraphicsGetCurrentContext() else {
            return nil
        }

        context.setFillColor(color.cgColor)
        context.fill(frame)

        guard let solidColorImage = UIGraphicsGetImageFromCurrentImageContext() else {
            return nil
        }

        context.clear(frame)

        draw(in: frame)
        solidColorImage.draw(in: frame, blendMode: .hue, alpha: 1)

        guard let colorizedImage = UIGraphicsGetImageFromCurrentImageContext() else {
            return nil
        }

        return colorizedImage
    }
}

结果

original images colorized images


0
如果您将UIImageView的backgroundColor设置为[UIColor redColor],则原始PNG中的透明部分将变为红色。或者,您可以尝试添加一个半透明UIView,其背景颜色为红色,作为imageview的子视图。这将在显示图像的视图中添加红色的薄雾。

1
谢谢,但我想要着色只存在的像素,而不是整个矩形。 - sol

0

你先绘制颜色,然后在其上方绘制图像。除非图像部分透明,否则它将完全覆盖背景颜色。我建议先尝试绘制原始图像,然后使用kCGBlendModeHue在其上绘制红色。

我不确定为什么要翻转和平移上下文。你的图片是倒置的吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接