Como posso alterar a imagem tintColor no iOS e no WatchKit

395

Eu tenho um UIImageView chamado "theImageView", com UIImage em uma única cor (plano de fundo transparente), assim como o coração preto esquerdo abaixo. Como posso alterar a cor da tonalidade desta imagem programaticamente no iOS 7 ou superior, de acordo com o método de tonalidade usado nos ícones da barra de navegação do iOS 7+?

Esse método também pode funcionar no WatchKit para um aplicativo Apple Watch?

insira a descrição da imagem aqui

mastigável
fonte
4
O que quer dizer "o seguinte código está errado", estabelecendo um UIImagecom UIImageRenderingModeAlwaysTemplatee em seguida, definindo UIImageVIew's tintColortrabalho faz. (no meu código ^^)
Vinzzz 9/10
2
Use um png com transparência como este
Alladinian 9/13
4
Você realmente deve mover sua resposta para a seção de respostas, pois eu acho que é a melhor e mais moderna.
Richard Venable
Eu gostaria de poder votar duas vezes mais essa pergunta !!
Jacobo Koenig

Respostas:

763

iOS
Para um aplicativo iOS, no Swift 3, 4 ou 5:

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

Para Swift 2:

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

Enquanto isso, a moderna solução Objective-C é:

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

Watchkit
Nos aplicativos do WatchKit para Apple Watch, você pode definir a cor da tonalidade de uma imagem de modelo .

  1. Você deve adicionar sua imagem a um Catálogo de ativos no seu WatchKit App e definir a imagem definida para ser renderizada como uma Imagem de modelo no Inspetor de atributos. Diferentemente de um aplicativo para iPhone, você não pode definir a renderização do modelo no código na extensão WatchKit no momento.
  2. Defina essa imagem para ser usada no seu WKInterfaceImage no construtor de interfaces do seu aplicativo
  3. Crie um IBOutlet no seu WKInterfaceController para o WKInterfaceImage chamado 'theImage' ...

Para definir a cor da tonalidade no Swift 3 ou 4:

theImage.setTintColor(UIColor.red)

Swift 2:

theImage.setTintColor(UIColor.redColor())

Para definir a cor da tonalidade em Objective-C:

[self.theImage setTintColor:[UIColor redColor]];

Se você usar uma imagem de modelo e não aplicar uma cor de tonalidade, será aplicada a tonalidade global do seu aplicativo WatchKit. Se você não definiu uma tonalidade global, a theImagecor será azul claro por padrão quando usada como imagem de modelo.

Duncan Babbage
fonte
2
Esta é a melhor e mais simples solução.
Ankish Jain #
4
imageWithRenderingMode está muito lento. No storyboard e na imagem Ativos. você pode alterar esse dois também: Atualização do modo de renderização de imagem modelo - isso é uma solução melhor
Katerina
Perfeito, agora eu uso esse método com base no seu código: + (UIImageView ) tintImageView: (UIImageView *) imageView withColor: (UIColor ) cor {imageView.image = [imageView.image imageWithRenderingMode: UIImageRenderingModeAlwaysTemplate]; [imageView setTintColor: color]; retornar imageView; }
Josep Escobar
é melhor com a imagem preta?
Bruno
11
A imagem @Bruno não precisa ser preta, não. Funciona com qualquer cor.
Duncan Babbage
121

Aqui está uma categoria que deve fazer o truque

@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

então você faria:

theImageView.image = [theImageView.image imageWithColor:[UIColor redColor]];
iamamused
fonte
Obrigado por esta resposta muito válida, acho que meu código foi ok desde o início, eu deveria responder a minha própria pergunta e deu-lhe uma +1 independentemente ..
mastigável
104

Eu tive que fazer isso no Swift usando um extension.

Eu pensei em compartilhar como eu fiz isso:

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

Uso:

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

Uso:

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

fulvio
fonte
11
Para sua informação, isso não funcionou para mim até que eu me movi para a color1.setFill()direita abaixo da primeira linha do método UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale).
Aaron
@Aaron Atualizado com base no seu comentário. Obrigado.
Fulvio
7
@CeceXX use CGBlendMode.Normalvez
Adolfo
2
Você é incrível, no entanto, pode ser necessário alterá-lo para Swift 3
SimpuMind
11
@SimpiMind Forneceu o Swift 4.
Fulvio
97

No storyboard e na imagem Ativos. você pode alterar esses dois também:

Atualizar o modo de renderização para a imagem do modelo

Atualizar o modo de renderização para a imagem do modelo em ativos de imagem

Atualize a cor da tonalidade nas vistas.

Atualizar a cor da tonalidade em Views em Views

JerryZhou
fonte
22
Esta é seriamente a resposta mais durona!
brandonscript
11
Definir esse valor no storyboard nunca funciona para mim. Eu sempre tenho que usar imageView.tintColordo código.
Kamil Powałowski
3
@ KamilPowałowski para mim isso funciona às vezes ... Não sei por que. Eu gostaria de saber por que nem sempre funciona. Então eu acabo fazendo isso por código
Jesus Rodriguez
2
Para mim, esse método de storyboard funciona em botões, mas não em imageViews. Eu ainda tenho que definir tintColor no código para imageViews.
Derek Soike
2
No caso de alguém ainda está coçando a cabeça se perguntando por que ele não está funcionando no IB, tente definir Opaque do imageView para No.
Bonan
40

Swift 4

Altere o tom do UIImage SVG / PDF , que funciona para imagens com cores únicas :

insira a descrição da imagem aqui insira a descrição da imagem aqui

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

Altere o tom do UIImageView , que funciona para imagens com cores exclusivas :

insira a descrição da imagem aqui insira a descrição da imagem aqui

let imageView = UIImageView(frame: CGRect(x: 50, y: 50, width: 50, height: 50))
imageView.image = UIImage(named: "hello.png")!.withRenderingMode(.alwaysTemplate)
imageView.tintColor = .yellow

Altere a tonalidade de UIImage para imagem , use o seguinte:

insira a descrição da imagem aqui insira a descrição da imagem aqui

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!
    }
}
YannSteph
fonte
11
Ei, sou novo no Swift, mas você disse aqui que é para imagens SVG, mas não consigo encontrar uma maneira de analisar o SVG para UIImage, você poderia me ajudar? ou talvez de alguma forma eu possa lidar com isso com o SVG corretamente. Obrigado!
Dumitru Rogojinaru
@DumitruRogojinaru fonction uso SVG imagem do modelo em ativos com
YannSteph
Por que é necessário traduzir e escalar em "func modifiedImage"?
Luca Davanzo 16/11
Atualização para Swift 4
YannSteph 2/11
36

Se alguém se importa com uma solução sem 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
    }
}
Puttin
fonte
Uau, funciona como mágica depois de pesquisar por mais de uma hora. Necessário para: Definir um ícone em um NSTextAttachment em uma cor diferente da original do ícone. A resposta padrão para usar um UIImageView e alterar sua tintColor não funciona aqui, porque NSTextAttachment não aceita o UIImageView.
26617 marco Marco
11
Esta é a melhor solução que encontrei até agora, especialmente para quem procura um código que funcione com o Swift 3. Ótima sugestão!
Shashwat
17

Com 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)
Esqarrouth
fonte
3
você pode simplesmente colocar .AlwaysTemplate.
Rui Peres
sim, ele reduz o código, mas parece que isso pode reduzir a clareza do código. não tenho certeza sobre os atalhos de pontos por causa disso
Esqarrouth
Eu vejo seu ponto de vista, apenas uma alternativa.
Rui Peres
4

Tente isto

http://robots.thoughtbot.com/designing-for-ios-blending-modes

ou

- (void)viewDidLoad
{
[super viewDidLoad];

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 30, 300, 50)];
label.numberOfLines = 0;
label.font = [UIFont systemFontOfSize:13];
label.text = @"These checkmarks use the same gray checkmark image with a tintColor applied to the image view";
[self.view addSubview:label];

[self _createImageViewAtY:100 color:[UIColor purpleColor]];
}

- (void)_createImageViewAtY:(int)y color:(UIColor *)color {
UIImage *image = [[UIImage imageNamed:@"gray checkmark.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
CGRect frame = imageView.frame;
frame.origin.x = 100;
frame.origin.y = y;
imageView.frame = frame;

if (color)
    imageView.tintColor = color;

[self.view addSubview:imageView];
}
yazh
fonte
4

Para 3 propósitos rápidos

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

DairySeeker
fonte
2

Para colorir a imagem de um UIButton

let image1 = "ic_shopping_cart_empty"
btn_Basket.setImage(UIImage(named: image1)?.withRenderingMode(.alwaysTemplate), for: .normal)
btn_Basket.setImage(UIImage(named: image1)?.withRenderingMode(.alwaysTemplate), for: .selected)
btn_Basket.imageView?.tintColor = UIColor(UIColor.Red)
Firas Shrourou
fonte
2

iOS

Solução para fazer isso no Interface Builder, defina o parâmetro templateImage no keyPath e escolha a cor da tonalidade no IB

extension UIImageView {

// make template image with tint color
var templateImage: Bool {
    set {
        if newValue, let image = self.image {
            let newImage = image.withRenderingMode(.alwaysTemplate)
            self.image = newImage
        }
    } get {
        return false
    }
}

}

Hakob
fonte
2

Com o iOS 13 e superior, você pode simplesmente usar

let image = UIImage(named: "Heart")?.withRenderingMode(.alwaysTemplate)
if #available(iOS 13.0, *) {
   imageView.image = image?.withTintColor(UIColor.white)
}
KingofBliss
fonte
1

Beneficie da extensão em Swift: -

extension UIImageView {
    func changeImageColor( color:UIColor) -> UIImage
    {
        image = image!.withRenderingMode(.alwaysTemplate)
        tintColor = color
        return image!
    }
}

   //Change color of logo 
   logoImage.image =  logoImage.changeImageColor(color: .red)

insira a descrição da imagem aqui

Shrawan
fonte
1

Versão Swift 3 da resposta de extensão do fuzz

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

    let context = UIGraphicsGetCurrentContext()! as CGContext
    context.translateBy(x: 0, y: self.size.height)
    context.scaleBy(x: 1.0, y: -1.0);
    context.setBlendMode(.normal)

    let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height) as CGRect
    context.clip(to: rect, mask: self.cgImage!)
    context.fill(rect)

    let newImage = UIGraphicsGetImageFromCurrentImageContext()! as UIImage
    UIGraphicsEndImageContext()

    return newImage
}
Bawpotter
fonte
0

Agora eu uso esse método com base na resposta de Duncan Babbage:

+ (UIImageView *) tintImageView: (UIImageView *)imageView withColor: (UIColor*) color{
    imageView.image = [imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
    [imageView setTintColor:color];
    return imageView;
}
Josep Escobar
fonte
0

Você pode usar isso no Swift 3 se tiver uma imagem para substituir o botão Limpar

func addTextfieldRightView(){

    let rightViewWidth:CGFloat = 30

    let viewMax = self.searchTxt.frame.height
    let buttonMax = self.searchTxt.frame.height - 16

    let buttonView = UIView(frame: CGRect(
        x: self.searchTxt.frame.width - rightViewWidth,
        y: 0,
        width: viewMax,
        height: viewMax))

    let myButton = UIButton(frame: CGRect(
        x: (viewMax - buttonMax) / 2,
        y: (viewMax - buttonMax) / 2,
        width: buttonMax,
        height: buttonMax))

    myButton.setImage(UIImage(named: "BlueClear")!, for: .normal)

    buttonView.addSubview(myButton)

    let clearPressed = UITapGestureRecognizer(target: self, action: #selector(SearchVC.clearPressed(sender:)))
    buttonView.isUserInteractionEnabled = true
    buttonView.addGestureRecognizer(clearPressed)

    myButton.addTarget(self, action: #selector(SearchVC.clearPressed(sender:)), for: .touchUpInside)

    self.searchTxt.rightView = buttonView
    self.searchTxt.rightViewMode = .whileEditing
}
Chester Schofield
fonte
0

Subclasse que também pode ser usada no código e no Interface Builder:

@implementation TintedImageView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self setup];
    }
    return self;
}

-(void)setup {
    self.image = [self.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}

@end
Łukasz Gierałtowski
fonte
0

Esta é a minha extensão UIImage e você pode usar diretamente a função changeTintColor para uma imagem.

extension UIImage {

    func changeTintColor(color: UIColor) -> UIImage {
        var newImage = self.withRenderingMode(.alwaysTemplate)
        UIGraphicsBeginImageContextWithOptions(self.size, false, newImage.scale)
        color.set()
        newImage.draw(in: CGRect(x: 0.0, y: 0.0, width: self.size.width, height: self.size.height))
        newImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return newImage
    }

    func changeColor(color: UIColor) -> UIImage {
        let backgroundSize = self.size
        UIGraphicsBeginImageContext(backgroundSize)
        guard let context = UIGraphicsGetCurrentContext() else {
            return self
        }
        var backgroundRect = CGRect()
        backgroundRect.size = backgroundSize
        backgroundRect.origin.x = 0
        backgroundRect.origin.y = 0

        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var alpha: CGFloat = 0
        color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        context.setFillColor(red: red, green: green, blue: blue, alpha: alpha)
        context.translateBy(x: 0, y: backgroundSize.height)
        context.scaleBy(x: 1.0, y: -1.0)
        context.clip(to: CGRect(x: 0.0, y: 0.0, width: self.size.width, height: self.size.height),
                 mask: self.cgImage!)
        context.fill(backgroundRect)

        var imageRect = CGRect()
        imageRect.size = self.size
        imageRect.origin.x = (backgroundSize.width - self.size.width) / 2
        imageRect.origin.y = (backgroundSize.height - self.size.height) / 2

        context.setBlendMode(.multiply)
        context.draw(self.cgImage!, in: imageRect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }

}

Exemplo de uso como este

let image = UIImage(named: "sample_image")
imageView.image = image.changeTintColor(color: UIColor.red)

E você pode usar a changeColorfunção de alteração para alterar a cor da imagem

abdullahselek
fonte
0

profileImageView.image = theImageView.image! .withRenderingMode (.alwaysTemplate)
profileImageView.tintColor = UIColor.green

OU

Primeiro, selecione Imagem particular no ativo da imagem e depois selecione vermelho como modelo em vez de padrão e depois a linha de gravação. profileImageView.tintColor = UIColor.green

Priyanka Jadhav
fonte
0

se você tiver algum ID para a imagem SVG, poderá preencher as cores com relação ao ID.

    let image = SVGKImage(named: "iconName")
    let svgIMGV = SVGKFastImageView(frame: self.imgView.frame)
         svgIMGV.image = image
          svgIMGV.fillTintColor(colorImage: UIColor.red, iconID: "Bank")
// Add in extension SVGKImageView
extension SVGKImageView {
 func fillTintColor(colorImage: UIColor, iconID: String) {
        if self.image != nil && self.image.caLayerTree != nil {
            print(self.image.caLayerTree.sublayers)
            guard let sublayers = self.image.caLayerTree.sublayers else { return }
            fillRecursively(sublayers: sublayers, color: colorImage, iconID: iconID)
        }
    }

     private func fillRecursively(sublayers: [CALayer], color: UIColor, iconID: String, hasFoundLayer: Bool) {
        var isLayerFound = false
        for layer in sublayers {
            if let l = layer as? CAShapeLayer {

                print(l.name)                
                //IF you want to color the specific shapelayer by id else remove the l.name  == "myID"  validation
                if let name =  l.name,  hasFoundLayer == true && name == "myID" {
                    self.colorThatImageWIthColor(color: color, layer: l)
                    print("Colouring FInished")
                }
            } else {
                if layer.name == iconID {
                    if let innerSublayer = layer.sublayers as? [CAShapeLayer] {
                        fillRecursively(sublayers: innerSublayer, color: color, iconID: iconID, hasFoundLayer: true )
                        print("FOund")
                    }
                } else {
                    if let l = layer as? CALayer, let sub = l.sublayers {
                        fillRecursively(sublayers: sub, color: color, iconID: iconID, hasFoundLayer: false)
                    }
                }
            }

        }
    }

    func colorThatImageWIthColor(color: UIColor, layer: CAShapeLayer) {
        if layer.strokeColor != nil {
            layer.strokeColor = color.cgColor
        }
        if layer.fillColor != nil {
            layer.fillColor = color.cgColor
        }
    }

}

OU Confira este exemplo.

https://github.com/ravisfortune/SVGDEMO

K Ravi Kumar
fonte
0
let navHeight = self.navigationController?.navigationBar.frame.height;
let menuBtn = UIButton(type: .custom)
menuBtn.frame = CGRect(x: 0, y: 0, width: 45, height: navHeight!)     
menuBtn.setImage(UIImage(named:"image_name")!.withRenderingMode(.alwaysTemplate), for: .normal)        
menuBtn.tintColor = .black
delabahan
fonte