Modifica del colore di UIImage


100

Sto cercando di cambiare il colore di UIImage. Il mio codice:

-(UIImage *)coloredImage:(UIImage *)firstImage withColor:(UIColor *)color {
    UIGraphicsBeginImageContext(firstImage.size);

    CGContextRef context = UIGraphicsGetCurrentContext();
    [color setFill];

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

    CGContextSetBlendMode(context, kCGBlendModeCopy);
    CGRect rect = CGRectMake(0, 0, firstImage.size.width, firstImage.size.height);
    CGContextDrawImage(context, rect, firstImage.CGImage);

    CGContextClipToMask(context, rect, firstImage.CGImage);
    CGContextAddRect(context, rect);
    CGContextDrawPath(context,kCGPathElementMoveToPoint);

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

    return coloredImg;
}

Questo codice funziona, ma l'immagine ottenuta non è così buona come dovrebbe essere: i pixel dei bordi dell'immagine restituita sono intermittenti e non così fluidi come nella mia prima immagine. Come posso risolvere questo problema?


1
possibile duplicato del cambio colore
idmean

Risposte:


263

Da iOS 7, questo è il modo più semplice per farlo.

Obiettivo-C:

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

Swift 2.0:

theImageView.image = theImageView.image?.imageWithRenderingMode(.AlwaysTemplate) 
theImageView.tintColor = UIColor.magentaColor()

Swift 4.0:

theImageView.image = theImageView.image?.withRenderingMode(.alwaysTemplate) 
theImageView.tintColor = .magenta

Storyboard:

Per prima cosa configura l'immagine come modello (sulla barra di destra - Rendering come) nelle tue risorse. Quindi il colore dell'immagine sarebbe il colore della tinta applicato. inserisci qui la descrizione dell'immagine


"UIImageRenderingModeAlwaysTemplate: disegna sempre l'immagine come immagine modello, ignorando le informazioni sul colore." Bello!
Tieme

3
In Swift 2.0+theImageView.image? = (theImageView.image?.imageWithRenderingMode(.AlwaysTemplate))! theImageView.tintColor = UIColor.magentaColor()
ColossalChris

@AnkishJain ci sono problemi di prestazioni in merito a questo approccio?
Ríomhaire

3
Questo non cambia il colore dell'immagine, ma piuttosto istruisce la vista a renderla con una tinta (colore) diversa.
Steve Kuo

@ Womble non proprio. Puoi usarlo per qualsiasi UIImage davvero. img = [img imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [button setTintColor:[UIColor redColor]]; [button setImage:img forState:UIControlStateNormal];@Ankish grazie!
Motasim

31

Questa è praticamente la risposta sopra, ma leggermente accorciata. Questo prende l'immagine solo come una maschera e in realtà non "moltiplica" o colora l'immagine.

Obiettivo C:

    UIColor *color = <# UIColor #>;
    UIImage *image = <# UIImage #>;// Image to mask with
    UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [color setFill];
    CGContextTranslateCTM(context, 0, image.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextClipToMask(context, CGRectMake(0, 0, image.size.width, image.size.height), [image CGImage]);
    CGContextFillRect(context, CGRectMake(0, 0, image.size.width, image.size.height));

    UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

Swift:

    let color: UIColor = <# UIColor #>
    let image: UIImage = <# UIImage #> // Image to mask with
    UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
    let context = UIGraphicsGetCurrentContext()
    color.setFill()
    context?.translateBy(x: 0, y: image.size.height)
    context?.scaleBy(x: 1.0, y: -1.0)
    context?.clip(to: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height), mask: image.cgImage!)
    context?.fill(CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
    let coloredImg = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

2
Dovresti usare UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);perché la tua versione creerà solo grafica non retina.
Nikola Lajic

user1270061 è il modo per farlo, nella mia esperienza. L'altra risposta con "masterizzazione" richiede un'immagine di origine di un certo colore apparentemente. Questo utilizza solo i valori alfa nei pixel di origine e li combina con il colore desiderato: perfetto.
Jason

Perfetto - l'unica risposta che ha funzionato bene per me. (16 maggio)
trdavidson

10

Un altro modo per colorare un'immagine è semplicemente moltiplicarla per un colore costante. A volte, questo è preferibile perché non "alza" i valori di colore nelle aree nere; mantiene le stesse intensità relative nell'immagine. L'uso di una sovrapposizione come tinta tende ad appiattire il contrasto.

Questo è il codice che utilizzo:

UIImage *MultiplyImageByConstantColor( UIImage *image, UIColor *color ) {

    CGSize backgroundSize = image.size;
    UIGraphicsBeginImageContext(backgroundSize);

    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGRect backgroundRect;
    backgroundRect.size = backgroundSize;
    backgroundRect.origin.x = 0;
    backgroundRect.origin.y = 0;

    CGFloat r,g,b,a;
    [color getRed:&r green:&g blue:&b alpha:&a];
    CGContextSetRGBFillColor(ctx, r, g, b, a);
    CGContextFillRect(ctx, backgroundRect);

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

    // Unflip the image
    CGContextTranslateCTM(ctx, 0, backgroundSize.height);
    CGContextScaleCTM(ctx, 1.0, -1.0);

    CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
    CGContextDrawImage(ctx, imageRect, image.CGImage);

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return newImage;
}

Versione rapida

extension UIImage{

    static func multiplyImageByConstantColor(image:UIImage,color:UIColor)->UIImage{
        let backgroundSize = image.size
        UIGraphicsBeginImageContext(backgroundSize)

        let ctx = UIGraphicsGetCurrentContext()

        var backgroundRect=CGRect()
        backgroundRect.size = backgroundSize
        backgroundRect.origin.x = 0
        backgroundRect.origin.y = 0

        var r:CGFloat
        var g:CGFloat
        var b:CGFloat
        var a:CGFloat
        color.getRed(&r, green: &g, blue: &b, alpha: &a)
        CGContextSetRGBFillColor(ctx, r, g, b, a)
        CGContextFillRect(ctx, backgroundRect)

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

        // Unflip the image
        CGContextTranslateCTM(ctx, 0, backgroundSize.height)
        CGContextScaleCTM(ctx, 1.0, -1.0)

        CGContextSetBlendMode(ctx, .Multiply)
        CGContextDrawImage(ctx, imageRect, image.CGImage)

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

Esattamente la soluzione che stavo cercando! Grazie!
vir us

E se volessi creare un'app di pittura murale virtuale? funziona, e se si desidera colorare solo le pareti dell'immagine. Dai un'occhiata a questo link per favore: - stackoverflow.com/questions/27482508/…
Shailesh

Funziona molto meglio di alcune delle altre soluzioni disponibili.
Matt Hudson

3
Questo ha colorato solo lo sfondo dell'oggetto per me, non l'oggetto stesso.
user3562927

7

In Swift 3.0

imageView.image? = (imageView.image?.withRenderingMode(.alwaysTemplate))!
imageView.tintColor = UIColor.magenta

In Swift 2.0

yourImage.image? = (yourImage.image?.imageWithRenderingMode(.AlwaysTemplate))!
yourImage.tintColor = UIColor.magentaColor()

Buon divertimento, pionieri di Swift


5
Il codice va bene, ma il post riguarda UIImage. Non abbiamo sempre a che fare con UIImageViews.
HenryRootTwo

5

Soluzione Swift 4.2

extension UIImage {
    func withColor(_ color: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        guard let ctx = UIGraphicsGetCurrentContext(), let cgImage = cgImage else { return self }
        color.setFill()
        ctx.translateBy(x: 0, y: size.height)
        ctx.scaleBy(x: 1.0, y: -1.0)
        ctx.clip(to: CGRect(x: 0, y: 0, width: size.width, height: size.height), mask: cgImage)
        ctx.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
        guard let colored = UIGraphicsGetImageFromCurrentImageContext() else { return self }
        UIGraphicsEndImageContext()
        return colored
    }
}

// Usage:
// let redImage = UIImage().withColor(.red)

5

A partire da iOS 10 puoi utilizzare UIGraphicsImageRenderer:

extension UIImage {

    func colored(_ color: UIColor) -> UIImage {
        let renderer = UIGraphicsImageRenderer(size: size)
        return renderer.image { context in
            color.setFill()
            self.draw(at: .zero)
            context.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height), blendMode: .sourceAtop)
        }
    }

}

3

Se non devi farlo in modo programmatico, puoi farlo semplicemente usando Xcode UI.

Se vai all'immagine nella cartella degli asset immagine, apri l'ispettore sul lato destro e c'è un menu a discesa "Rendering come" con le seguenti opzioni:

  1. Predefinito
  2. Originale
  3. Modello

Dopo aver effettuato la selezione del modello, puoi modificare il colore tinta dell'immagine come preferisci, indipendentemente dal fatto che utilizzi l'interfaccia utente dello storyboard di Xcode o in modo programmatico.

inserisci qui la descrizione dell'immagine

Vedi questa immagine:

inserisci qui la descrizione dell'immagine


Stai usando XCODE 8?
doxsi

2

Ecco il mio adattamento della risposta di @ Anna. Due punti chiave qui:

  • Usa la destinationInmodalità di fusione
  • Chiama UIGraphicsBeginImageContextWithOptions(backgroundSize, false, UIScreen.main.scale)per ottenere un'immagine fluida

Codice in Swift 3 :

extension UIImage {

    static func coloredImage(image: UIImage?, color: UIColor) -> UIImage? {

        guard let image = image else {
            return nil
        }

        let backgroundSize = image.size
        UIGraphicsBeginImageContextWithOptions(backgroundSize, false, UIScreen.main.scale)

        let ctx = UIGraphicsGetCurrentContext()!

        var backgroundRect=CGRect()
        backgroundRect.size = backgroundSize
        backgroundRect.origin.x = 0
        backgroundRect.origin.y = 0

        var r:CGFloat = 0
        var g:CGFloat = 0
        var b:CGFloat = 0
        var a:CGFloat = 0
        color.getRed(&r, green: &g, blue: &b, alpha: &a)
        ctx.setFillColor(red: r, green: g, blue: b, alpha: a)
        ctx.fill(backgroundRect)

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

        // Unflip the image
        ctx.translateBy(x: 0, y: backgroundSize.height)
        ctx.scaleBy(x: 1.0, y: -1.0)

        ctx.setBlendMode(.destinationIn)
        ctx.draw(image.cgImage!, in: imageRect)

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

2
perché passi l'immagine quando estendi UIImage? dovresti rimuovere la parola chiave statica del tuo metodo e usarla semplicemente selfall'interno del tuo metodo e rimuovere il parametro dell'immagine non necessario
Leo Dabus,

1

Basandomi sulla risposta di @ Anna e riscrivo per swift 2.2 e gestisce l'immagine con il canale alfa:

static func multiplyImageByConstantColor(image:UIImage,color:UIColor)->UIImage{
    let backgroundSize = image.size
    UIGraphicsBeginImageContext(backgroundSize)

    let ctx = UIGraphicsGetCurrentContext()

    var backgroundRect=CGRect()
    backgroundRect.size = backgroundSize
    backgroundRect.origin.x = 0
    backgroundRect.origin.y = 0

    var r:CGFloat = 0
    var g:CGFloat = 0
    var b:CGFloat = 0
    var a:CGFloat = 0
    color.getRed(&r, green: &g, blue: &b, alpha: &a)
    CGContextSetRGBFillColor(ctx, r, g, b, a)

    // Unflip the image
    CGContextTranslateCTM(ctx, 0, backgroundSize.height)
    CGContextScaleCTM(ctx, 1.0, -1.0)
    CGContextClipToMask(ctx, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
    CGContextFillRect(ctx, backgroundRect)

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


    CGContextSetBlendMode(ctx, .Multiply)
    CGContextDrawImage(ctx, imageRect, image.CGImage)

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

0

Il codice di Anna funziona bene per copiare un UIImage.image su uno sfondo colorato .image utilizzando kCGBlendModeNormal anziché kCGBlendModeMultiply. Ad esempio, self.mainImage.image = [self NormalImageByConstantColor: self.mainImage.image withColor: yourColor];posizionerà il contenuto di mainImage.image sulla tinta yourColor preservando l'opacità di yourColor. Questo ha risolto il mio problema di posizionare un colore di sfondo con opacità dietro un'immagine da salvare nel rullino fotografico.


0

Swift 3:

extension UIImage{

    static func multiplyImageByConstantColor(image:UIImage,color:UIColor) -> UIImage{

        let backgroundSize = image.size
        UIGraphicsBeginImageContext(backgroundSize)

        guard let ctx = UIGraphicsGetCurrentContext() else {return image}

        var backgroundRect=CGRect()
        backgroundRect.size = backgroundSize
        backgroundRect.origin.x = 0
        backgroundRect.origin.y = 0

        var r:CGFloat = 0
        var g:CGFloat = 0
        var b:CGFloat = 0
        var a:CGFloat = 0
        color.getRed(&r, green: &g, blue: &b, alpha: &a)
        ctx.setFillColor(red: r, green: g, blue: b, alpha: a)

        // Unflip the image
        ctx.translateBy(x: 0, y: backgroundSize.height)
        ctx.scaleBy(x: 1.0, y: -1.0)
        ctx.clip(to: CGRect(0, 0, image.size.width, image.size.height), mask: image.cgImage!)
        ctx.fill(backgroundRect)


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


        ctx.setBlendMode(.multiply)
        ctx.draw(image.cgImage!, in: imageRect)

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

0

Versione Swift 3.0 del meraviglioso codice di Anna:

extension UIImage{

    static func multiplyImageByConstantColor(image:UIImage,color:UIColor)-> UIImage {
        let backgroundSize = image.size
        UIGraphicsBeginImageContext(backgroundSize)

        let ctx = UIGraphicsGetCurrentContext()!

        var backgroundRect=CGRect()
        backgroundRect.size = backgroundSize
        backgroundRect.origin.x = 0
        backgroundRect.origin.y = 0

        let myFloatForR = 0
        var r = CGFloat(myFloatForR)
        let myFloatForG = 0
        var g = CGFloat(myFloatForG)
        let myFloatForB = 0
        var b = CGFloat(myFloatForB)
        let myFloatForA = 0
        var a = CGFloat(myFloatForA)

        color.getRed(&r, green: &g, blue: &b, alpha: &a)
        ctx.setFillColor(red: r, green: g, blue: b, alpha: a)
        ctx.fill(backgroundRect)

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

        // Unflip the image
        ctx.translateBy(x: 0, y: backgroundSize.height)
        ctx.scaleBy(x: 1.0, y: -1.0)

        ctx.setBlendMode(.multiply)
        ctx.draw(image.cgImage!, in: imageRect)

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

0

Per iOS 13 e versioni successive:

let redImage = image.withTintColor(.red, renderingMode: .alwaysTemplate)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.