如何通过编程动态地为UIButton着色背景图片?

7
我正在开发一个应用程序——或者更准确地说是一些可重复使用的“框架”,一旦它可以使用,我很高兴分享。在这个应用程序中,用户应该能够从一个颜色主题列表中选择。因此,应用程序必须以某种相当动态的方式对其UI元素进行着色。
对于按钮,所有涂色都不起作用。这里必须提供正确着色的背景图像。但是为每个主题准备一组背景图像只是次优解决方案。它不够动态和灵活。
最终的解决方案可能是:为所选状态和正常状态提供一个单色(灰色)渐变的图像,并使用CoreGraphics或OpenGL编程实现了着色。但是,坦白地说,我不知道该从哪里开始。渐变应该是什么样子的,我又该如何对其进行任意给定颜色的编程着色呢?
对于UISegmentedControls的情况也基本适用,只是要稍微麻烦一点:)
非常感谢任何通用解决方案,它也适用于UISegementedControls。

请查看GMButton。它允许您为所有按钮状态设置颜色,并提供斜角和光泽渐变。 - progrmr
看起来你做得很好。我觉得我只需要将它适应ARC,然后它就能做我想要的一切了。谢谢。 - Hermann Klecker
6个回答

42

iOS7中,您可以使用imagedWithRenderingMode:方法与setTintColor:方法一起使用。

但是请确保使用UIImageRenderingModeAlwaysTemplate,因为它会将UIImage上的所有非透明颜色替换为色调颜色。默认值为UIImageRenderingModeAutomatic

UIImageRenderingModeAlwaysTemplate

Always draw the image as a template image, ignoring its color information.

Objective-C:

UIImage * image = [myButton.currentImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[myButton setImage:image forState:UIControlStateNormal];

Swift:

let image = myButton.currentImage?.withRenderingMode(.alwaysTemplate)
myButton.setImage(image, for: .normal)

1
小心 - 在iOS 9发布模式下构建时,使用UIImage __weak会导致按钮上没有显示图像。 - Alexandre G
工作于SetImage而非SetBackgroudIamge。 - Raj Aggrawal

12

我制作了一个UIImage类别,遵循另一篇我找不到的帖子,它会对图像进行染色,如下所示:

- (UIImage *)tintedImageWithColor:(UIColor *)tintColor {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
    CGContextRef context = UIGraphicsGetCurrentContext();


    CGContextTranslateCTM(context, 0, self.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

    // draw alpha-mask
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGContextDrawImage(context, rect, self.CGImage);

    // draw tint color, preserving alpha values of original image
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    [tintColor setFill];
    CGContextFillRect(context, rect);

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

这将获取图像并使用给定的颜色填充所有 alpha 值区域。


像魔法般工作! - Desmond DAI

4
我使用horsejockey的代码创建了一个UIButton类别: https://github.com/filipstefansson/UITintedButton 以下是新方法:
-(void)setImageTintColor:(UIColor *)color;
-(void)setBackgroundTintColor:(UIColor *)color forState:(UIControlState)state;
+(void)tintButtonImages:(NSArray *)buttons withColor:(UIColor *)color;
+(void)tintButtonBackgrounds:(NSArray *)buttons withColor:(UIColor *)color forState:(UIControlState)state;

1
这是您问题的部分答案。这是我编写的一个辅助方法,用于给灰度图像着色:
// baseImage is the grey scale image. color is the desired tint color
+ (UIImage *)tintImage:(UIImage *)baseImage withColor:(UIColor *)color {
    UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, baseImage.scale);

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

    [color set];
    CGContextFillRect(ctx, area);
    CGContextRestoreGState(ctx);

    CGContextSetBlendMode(ctx, kCGBlendModeOverlay);

    CGContextDrawImage(ctx, area, baseImage.CGImage);

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    // If the original image was stretchable, make the new image stretchable
    if (baseImage.leftCapWidth || baseImage.topCapHeight) {
        newImage = [newImage stretchableImageWithLeftCapWidth:baseImage.leftCapWidth topCapHeight:baseImage.topCapHeight];
    }

    return newImage;
}

到目前为止,谢谢。我会在周末或下周初尝试一下。它看起来与@OnlyYou链接的核心非常相似。虽然我对CG Graphics不是很熟悉,但它相当容易理解。只有一个问题。这个CGContextScalTCM和ContextTranslateTCM是用于坐标转换的,因为UIKit和CGGraphics在y轴上具有不同的原点和不同的方向吗? - Hermann Klecker

0

我对“horsejockey”的一些东西进行了修改:

+(UIImage *)getTintedImage:(UIImage*)image withColor:(UIColor *)tintColor
{
    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);

    // draw alpha-mask
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGContextDrawImage(context, rect, image.CGImage);

    // draw tint color, preserving alpha values of original image
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    [tintColor setFill];
    CGContextFillRect(context, rect);

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

0

1
这不是我想要的答案,但似乎可行。谢谢。我会尝试一下,可能在周末的时候。 - Hermann Klecker

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