Comment puis-je changer la teinte de l'image dans iOS et WatchKit

395

J'ai un UIImageView appelé "theImageView", avec UIImage dans une seule couleur (fond transparent) comme le coeur noir gauche ci-dessous. Comment puis-je changer la couleur de teinte de cette image par programme dans iOS 7 ou supérieur, selon la méthode de teinte utilisée dans les icônes de la barre de navigation iOS 7+?

Cette méthode peut-elle également fonctionner dans WatchKit pour une application Apple Watch?

entrez la description de l'image ici

moelleux
la source
4
Que voulez-vous dire "le code suivant est incorrect", la définition d'un UIImageavec UIImageRenderingModeAlwaysTemplatepuis la définition UIImageVIewde tintColorfonctionne. (dans mon code ^^)
Vinzzz
2
Utilisez un png avec transparence comme celui-ci
Alladinian
4
Vous devriez vraiment déplacer votre réponse dans la section des réponses, car je pense que c'est la meilleure et la plus moderne.
Richard Venable
J'aimerais pouvoir doubler cette question !!
Jacobo Koenig

Réponses:

764

iOS
Pour une application iOS, dans Swift 3, 4 ou 5:

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

Pour Swift 2:

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

Pendant ce temps, la solution Objective-C moderne est:

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

Watchkit
Dans WatchKit pour les applications Apple Watch, vous pouvez définir la couleur de teinte d'une image de modèle .

  1. Vous devez ajouter votre image à un catalogue d'actifs dans votre application WatchKit et définir le jeu d'images à rendre en tant qu'image de modèle dans l'inspecteur d'attributs. Contrairement à une application iPhone, vous ne pouvez pas définir le rendu du modèle dans le code dans l'extension WatchKit pour le moment.
  2. Définissez cette image à utiliser dans votre WKInterfaceImage dans le générateur d'interface pour votre application
  3. Créez un IBOutlet dans votre WKInterfaceController pour le WKInterfaceImage appelé 'theImage' ...

Pour ensuite définir la couleur de teinte dans Swift 3 ou 4:

theImage.setTintColor(UIColor.red)

Swift 2:

theImage.setTintColor(UIColor.redColor())

Pour ensuite définir la couleur de teinte dans Objective-C:

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

Si vous utilisez une image de modèle et n'appliquez pas de couleur de teinte, la teinte globale de votre application WatchKit sera appliquée. Si vous n'avez pas défini de teinte globale, theImagesera teinté en bleu clair par défaut lorsqu'il est utilisé comme image de modèle.

Duncan Babbage
la source
2
c'est la solution la meilleure et la plus simple.
Ankish Jain
4
imageWithRenderingMode est trop lent. Dans les éléments de storyboard et d'image. vous pouvez également modifier ces deux: Mettre à jour le mode de rendu en image modèle - c'est une meilleure solution
Katerina
Parfait, maintenant j'utilise cette méthode basée sur votre code: + (UIImageView ) tintImageView: (UIImageView *) imageView withColor: (UIColor ) color {imageView.image = [imageView.image imageWithRenderingMode: UIImageRenderingModeAlwaysTemplate]; [imageView setTintColor: couleur]; return imageView; }
Josep Escobar
est-ce mieux avec l'image noir?
Bruno
1
L'image @Bruno n'a pas besoin d'être noire, non. Fonctionne avec n'importe quelle couleur.
Duncan Babbage
121

Voici une catégorie qui devrait faire l'affaire

@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

vous feriez donc:

theImageView.image = [theImageView.image imageWithColor:[UIColor redColor]];
iamamused
la source
Merci pour cette réponse très valable, je suppose que mon code était correct depuis le début, je devrais répondre à ma propre question et vous a donné un +1 malgré tout.
Chewy
104

J'ai dû le faire dans Swift en utilisant un extension.

Je pensais partager ma façon de procéder:

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

Usage:

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

Usage:

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

fulvio
la source
1
Pour info, cela n'a pas fonctionné pour moi jusqu'à ce que je déplace la color1.setFill()droite sous la première ligne de la méthode UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale).
Aaron
@Aaron Mis à jour en fonction de votre commentaire. Merci.
fulvio
7
@CeceXX utiliser à la CGBlendMode.Normalplace
Adolfo
2
Vous êtes génial, mais vous devrez peut-être le changer pour Swift 3
SimpuMind
1
@SimpiMind a fourni Swift 4 à la place.
fulvio
97

Dans les éléments de storyboard et d'image. vous pouvez également modifier ces deux:

Mettre à jour le mode de rendu en image modèle

Mettre à jour le mode de rendu en image de modèle dans les ressources d'image

Mettez à jour la couleur de teinte dans les vues.

Mettre à jour la couleur de teinte dans les vues dans les vues

JerryZhou
la source
22
C'est vraiment la réponse la plus badass!
brandonscript
1
Définir cette valeur à partir du storyboard ne fonctionne jamais pour moi. Je dois toujours utiliser imageView.tintColordu code.
Kamil Powałowski
3
@ KamilPowałowski pour moi cela fonctionne parfois ... Je ne sais pas pourquoi. J'aimerais savoir pourquoi cela ne fonctionne pas toujours. Donc je finis par le faire par code
Jesus Rodriguez
2
Pour moi, cette méthode de storyboard fonctionne sur les boutons mais pas sur les images. Je dois encore définir tintColor dans le code pour imageViews.
Derek Soike
2
Au cas où quelqu'un se gratterait encore la tête en se demandant pourquoi cela ne fonctionne pas dans IB, essayez de régler l'Opaque de l'imageView sur Non.
Bonan
40

Swift 4

Changez la teinte de UIImage SVG / PDF , qui fonctionne pour une image avec une couleur unique :

entrez la description de l'image ici entrez la description de l'image ici

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

Changez la teinte de UIImageView , qui fonctionne pour une image avec une couleur unique :

entrez la description de l'image ici entrez la description de l'image ici

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

Changer la teinte de UIImage pour l' image , utilisez cela:

entrez la description de l'image ici entrez la description de l'image ici

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
la source
1
hé, je suis nouveau dans swift, mais vous avez dit ici que c'est pour les images SVG, mais je ne trouve pas de moyen d'analyser le SVG en UIImage, pourriez-vous m'aider? ou peut-être que je peux gérer cela avec le SVG correctement. Merci!
Dumitru Rogojinaru
@DumitruRogojinaru utilise la fonction SVG avec l'image du modèle dans l'actif
YannSteph
Pourquoi est-il nécessaire de traduire et de mettre à l'échelle sur "func modifiedImage"?
Luca Davanzo
Mise à jour pour Swift 4
YannSteph
36

Si quelqu'un se soucie d'une solution sans 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
la source
Wow, fonctionne comme par magie après avoir recherché cela pendant une heure +. Nécessaire pour: définir une icône dans un NSTextAttachment dans une couleur différente de celle d'origine de l'icône. La réponse standard pour utiliser un UIImageView et changer son tintColor ne fonctionne pas ici, car NSTextAttachment ne prend pas le UIImageView.
marco
1
C'est la meilleure solution que j'ai trouvée jusqu'à présent, en particulier pour ceux qui recherchent du code qui fonctionne avec Swift 3. Excellente suggestion!
shashwat
17

Avec 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
la source
3
vous pouvez simplement mettre .AlwaysTemplate.
Rui Peres
oui, cela raccourcit le code, mais il semble que cela pourrait réduire la clarté du code. pas sûr des raccourcis par points à cause de cela
Esqarrouth
Je vois votre POV, juste une alternative.
Rui Peres
4

Essaye ça

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
la source
4

Pour des fins rapides 3

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

DairySeeker
la source
2

Pour teinter l'image d'un bouton UIB

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
la source
2

iOS

Solution pour le faire depuis Interface Builder, définissez le paramètre templateImage dans keyPath et choisissez votre couleur de teinte dans 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
la source
2

Avec iOS 13 et supérieur, vous pouvez simplement utiliser

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

Profitez de l'extension dans 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)

entrez la description de l'image ici

Shrawan
la source
1

Version Swift 3 de la réponse d'extension de 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
la source
0

Maintenant, j'utilise cette méthode basée sur la réponse de Duncan Babbage:

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

Vous pouvez l'utiliser dans Swift 3 si vous avez une image pour remplacer le bouton d'effacement

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
la source
0

Sous-classe qui peut également être utilisée à partir du code et d'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
la source
0

Ceci est mon extension UIImage et vous pouvez directement utiliser la fonction changeTintColor pour une image.

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

}

Exemple d'utilisation comme celui-ci

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

Et vous pouvez utiliser la changeColorfonction de changement pour changer la couleur de l'image

abdullahselek
la source
0

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

OU

Sélectionnez d'abord une image particulière dans l'image, puis sélectionnez rouge comme modèle au lieu de par défaut et après cette ligne d'écriture. profileImageView.tintColor = UIColor.green

Priyanka Jadhav
la source
0

si vous avez un identifiant pour l'image SVG, vous pouvez remplir les couleurs par rapport à l'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 Consultez cet exemple.

https://github.com/ravisfortune/SVGDEMO

K Ravi Kumar
la source
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
la source