如何在 iOS 和 WatchKit 中更改图片的 tintColor?

466

我有一个名为“theImageView”的UIImageView,其中UIImage仅为单色(透明背景),就像下面的黑色左心一样。如何在iOS 7或更高版本中以与iOS 7+导航栏图标使用的着色方法相同的方式通过编程来更改此图像的色调?

这种方法是否也可以在Apple Watch应用程序的WatchKit中工作?

输入图像描述


4
你说 "以下代码有问题",但是我在我的代码中使用 UIImageRenderingModeAlwaysTemplate 设置了 UIImage 的属性,并将 tintColor 设置为 UIImageView 的属性,这样做是可以正常工作的。 - Vinzzz
2
使用带有透明度的 PNG 像这个 - Alladinian
4
你应该把你的答案移到答案部分,因为我认为它是最好的和最现代化的。 - Richard Venable
25个回答

911

iOS
对于一个使用Swift 3、4或5的iOS应用程序:

theImageView.image = theImageView.image?.withRenderingMode(.alwaysTemplate)
theImageView.tintColor = UIColor.red

对于 Swift 2:

theImageView.image = theImageView.image?.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
theImageView.tintColor = UIColor.redColor()

同时,现代化的 Objective-C 解决方案是:

theImageView.image = [theImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[theImageView setTintColor:[UIColor redColor]];

WatchKit
在苹果手表应用的 WatchKit 中,您可以为模板图像设置色调颜色(tint color)

  1. 您必须将图像添加到 WatchKit 应用的 Asset Catalog 中,并在属性检查器中将图像集设置为模板图像,与 iPhone 应用不同,目前无法在 WatchKit Extension 中通过代码设置模板渲染。
  2. 在界面构建器中为您的 WKInterfaceImage 设置该图像以供使用。
  3. 在您的 WKInterfaceController 中创建一个名为“theImage”的 WKInterfaceImage 的 IBOutlet...

要在 Swift 3 或 4 中设置色调颜色:

theImage.setTintColor(UIColor.red)

Swift 2:

theImage.setTintColor(UIColor.redColor())

在Objective-C中设置色调颜色的方法如下:

[self.theImage setTintColor:[UIColor redColor]];
如果您使用模板图像并未应用色调颜色,则会应用WatchKit应用程序的全局着色。如果您没有设置全局着色,则在将其用作模板图像时,默认情况下会将theImage着色为浅蓝色。

2
这是最好的且简单的解决方案。 - Ankish Jain
6
imageWithRenderingMode速度太慢了。在Storyboard和图像资源中,您还可以更改这两个:将渲染模式更新为模板图像-这是一个更好的解决方案。 - Katerina
完美,现在我使用基于你的代码的这个方法:
  • (UIImageView *)tintImageView:(UIImageView )imageView withColor:(UIColor)color{ imageView.image = [imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [imageView setTintColor:color]; return imageView; }
- Josep Escobar
2
@Bruno 图像并不一定需要是黑色的。可以使用任何颜色。 - Duncan Babbage
似乎这个在iOS 16上不再起作用了 - 有人在那里成功运行过吗? - Brandon
显示剩余6条评论

126
在Storyboard和Image Assets中,您也可以修改这两个方面:
将渲染模式更新为Template Image 在Views中更新tintColor。
请看以下内容:

在Storyboard和Image Assets中,您也可以修改这两个方面:

将渲染模式更新为Template Image

在Image Assets中将渲染模式更新为模板图像

在Views中更新tintColor。

在Views中更新tintColor


25
这真是最牛逼的回答! - brandonscript
1
从Storyboard设置这个值对我来说从来没有起作用。我总是不得不在代码中使用imageView.tintColor - Kamil Powałowski
3
@KamilPowałowski,这对我有时有效...但我不确定为什么。我希望我知道它为什么并不总是起作用。所以最终我会通过代码来完成它。 - Jesus Rodriguez
2
对我来说,这个故事板方法适用于按钮,但不适用于图像视图。我仍然需要在代码中设置图像视图的tintColor。 - Derek Soike
2
如果有人仍然在想为什么在IB中它不起作用,请尝试将imageView的Opaque设置为No。 - Bonan
显示剩余9条评论

125

这是一个可以解决问题的分类

@interface UIImage(Overlay)
@end

@implementation UIImage(Overlay)

- (UIImage *)imageWithColor:(UIColor *)color1
{
        UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextTranslateCTM(context, 0, self.size.height);
        CGContextScaleCTM(context, 1.0, -1.0);
        CGContextSetBlendMode(context, kCGBlendModeNormal);
        CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
        CGContextClipToMask(context, rect, self.CGImage);
        [color1 setFill];
        CGContextFillRect(context, rect);
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
}
@end

那么您需要这样做:

theImageView.image = [theImageView.image imageWithColor:[UIColor redColor]];

感谢您提供这个非常有价值的答案,我想我的代码一开始就是正确的,我应该回答自己的问题并给您一个+1。 - chewy

125

我在Swift中使用extension实现了这个功能。

我想分享一下我是怎么做的:

extension UIImage {
    func imageWithColor(color1: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        color1.setFill()

        let context = UIGraphicsGetCurrentContext() as CGContextRef
        CGContextTranslateCTM(context, 0, self.size.height)
        CGContextScaleCTM(context, 1.0, -1.0);
        CGContextSetBlendMode(context, CGBlendMode.Normal)

        let rect = CGRectMake(0, 0, self.size.width, self.size.height) as CGRect
        CGContextClipToMask(context, rect, self.CGImage)
        CGContextFillRect(context, rect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext() as UIImage
        UIGraphicsEndImageContext()

        return newImage
    }
}

使用方法:

theImageView.image = theImageView.image.imageWithColor(UIColor.redColor())

适用于Swift 4

extension UIImage {
    func imageWithColor(color1: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        color1.setFill()

        let context = UIGraphicsGetCurrentContext()
        context?.translateBy(x: 0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.setBlendMode(CGBlendMode.normal)

        let rect = CGRect(origin: .zero, size: CGSize(width: self.size.width, height: self.size.height))
        context?.clip(to: rect, mask: self.cgImage!)
        context?.fill(rect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage!
    }
}

用法:

theImageView.image = theImageView.image?.imageWithColor(color1: UIColor.red)


1
жЏђдң›дүҰжЃҮпәЊз›ө到我将color1.setFill()移动到方法UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)的第一иҰЊдё‹йқұпәЊиү™ж‰ҚеҮ№ж€‘иµ·дҢњз”ЁгЂ‚ - Aaron
在Xcode 7.0 beta 2中出现错误:“未解决的标识符"kCGBlendModeNormal"的使用”。 - Cesare
7
请使用CGBlendMode.Normal替代。 - Adolfo
2
你很棒,但是你可能需要将它改为Swift 3。 - SimpuMind
1
@SimpiMind 提供了 Swift 4 版本。 - fulvio
显示剩余4条评论

47

如果有人想要一种不使用UIImageView的解决方案:

// (Swift 3)
extension UIImage {
    func tint(with color: UIColor) -> UIImage {
        var image = withRenderingMode(.alwaysTemplate)
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        color.set()

        image.draw(in: CGRect(origin: .zero, size: size))
        image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return image
    }
}

哇,搜索了一个多小时后就像魔术一样奏效。需要的是:在NSTextAttachment中设置一个图标,并使其颜色与原始颜色不同。标准答案是使用UIImageView并更改其tintColor,但这里不起作用,因为NSTextAttachment不接受UIImageView。 - marco
1
这是我迄今为止找到的最佳解决方案,特别适用于寻找与Swift 3兼容代码的人。非常好的建议! - shashwat

46

Swift 4

更改唯一颜色图像的UIImage SVG / PDF的色调:

输入图像描述 输入图像描述

import Foundation
    
// MARK: - UIImage extensions

public extension UIImage {

    //
    /// Tint Image
    ///
    /// - Parameter fillColor: UIColor
    /// - Returns: Image with tint color
    func tint(with fillColor: UIColor) -> UIImage? {
        let image = withRenderingMode(.alwaysTemplate)
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        fillColor.set()
        image.draw(in: CGRect(origin: .zero, size: size))

        guard let imageColored = UIGraphicsGetImageFromCurrentImageContext() else {
            return nil
        }
        
        UIGraphicsEndImageContext()
        return imageColored
    }
}

如何改变UIImageView中唯一颜色的图片的色调:

enter image description here enter image description here

let imageView = UIImageView(frame: CGRect(x: 50, y: 50, width: 50, height: 50))
imageView.image = UIImage(named: "hello.png")!.withRenderingMode(.alwaysTemplate)
imageView.tintColor = .yellow
改变UIImage图片的色调,可以使用以下方法:

enter image description here enter image description here

import Foundation

// MARK: - Extensions UIImage

public extension UIImage {

    /// Tint, Colorize image with given tint color
    /// This is similar to Photoshop's "Color" layer blend mode
    /// This is perfect for non-greyscale source images, and images that 
    /// have both highlights and shadows that should be preserved<br><br>
    /// white will stay white and black will stay black as the lightness of 
    /// the image is preserved
    ///
    /// - Parameter TintColor: Tint color
    /// - Returns:  Tinted image
    public func tintImage(with fillColor: UIColor) -> UIImage {
        
        return modifiedImage { context, rect in
            // draw black background - workaround to preserve color of partially transparent pixels
            context.setBlendMode(.normal)
            UIColor.black.setFill()
            context.fill(rect)
            
            // draw original image
            context.setBlendMode(.normal)
            context.draw(cgImage!, in: rect)
            
            // tint image (loosing alpha) - the luminosity of the original image is preserved
            context.setBlendMode(.color)
            fillColor.setFill()
            context.fill(rect)
            
            // mask by alpha values of original image
            context.setBlendMode(.destinationIn)
            context.draw(context.makeImage()!, in: rect)
        }
    }
    
    /// Modified Image Context, apply modification on image
    ///
    /// - Parameter draw: (CGContext, CGRect) -> ())
    /// - Returns:        UIImage
    fileprivate func modifiedImage(_ draw: (CGContext, CGRect) -> ()) -> UIImage {
        
        // using scale correctly preserves retina images
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        let context: CGContext! = UIGraphicsGetCurrentContext()
        assert(context != nil)
        
        // correctly rotate image
        context.translateBy(x: 0, y: size.height)
        context.scaleBy(x: 1.0, y: -1.0)
        
        let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
        
        draw(context, rect)
        
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image!
    }
}

2
嘿,我是Swift的新手,但你在这里告诉我它适用于SVG图像,但我找不到将SVG解析为UIImage的方法,你能帮我吗?或者也许我可以正确处理SVG。谢谢! - Dumitru Rogojinaru
1
@DumitruRogojinaru 使用模板图像中的SVG函数在资产中。 - YannSteph
1
Swift 4 更新 - YannSteph

18

使用Swift

let commentImageView = UIImageView(frame: CGRectMake(100, 100, 100, 100))
commentImageView.image = UIImage(named: "myimage.png")!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
commentImageView.tintColor = UIColor.blackColor()
addSubview(commentImageView)

3
你可以简单地添加 .AlwaysTemplate - Rui Peres
是的,它确实可以缩短代码,但似乎可能会降低代码的清晰度。因此,我对点快捷方式不确定。 - Esqarrouth
我理解你的观点,只是提供一个替代方案。 - Rui Peres

12

为了适用于Swift 3

theImageView.image = theImageView.image!.withRenderingMode(.alwaysTemplate)
theImageView.tintColor = UIColor.red

10

这里有一个适用于 Swift 5 的简单扩展:

extension UIImageView {

func setImageTintColor(_ color: UIColor) {
    let tintedImage = self.image?.withRenderingMode(.alwaysTemplate)
    self.image = tintedImage
    self.tintColor = color
  }
}

使用方法:

myImageView.setImageTintColor(.systemBlue)

7

另外,在iOS 13及更高版本中,有一种干净的方法。

let image = UIImage(named: "imageName")?.withTintColor(.white, renderingMode: .alwaysTemplate)

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