Ändern der UIImage-Farbe


100

Ich versuche, die Farbe von UIImage zu ändern. Mein Code:

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

Dieser Code funktioniert, aber das erhaltene Bild ist nicht so gut wie es sein sollte: Begrenzte Pixel des zurückgegebenen Bildes sind intermittierend und nicht so glatt wie in meinem ersten Bild. Wie kann ich dieses Problem lösen?


1
Mögliches Duplikat der UIImage-Farbänderung?
idmean

Antworten:


262

Seit iOS 7 ist dies der einfachste Weg, dies zu tun.

Ziel 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:

Konfigurieren Sie zuerst das Bild als Vorlage (in der rechten Leiste - Rendern als) in Ihren Assets. Dann wäre die Farbe des Bildes die angewendete Tönungsfarbe. Geben Sie hier die Bildbeschreibung ein


"UIImageRenderingModeAlwaysTemplate: Zeichnen Sie das Bild immer als Vorlagenbild, ohne die Farbinformationen zu beachten." Nett!
Tieme

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

@AnkishJain Gibt es Leistungsprobleme bei diesem Ansatz?
Ríomhaire

3
Dadurch wird die Farbe des Bildes nicht geändert, sondern die Ansicht wird angewiesen, es mit einem anderen Farbton zu rendern.
Steve Kuo

@ Womble nicht wirklich. Sie können dies wirklich für jedes UIImage verwenden. img = [img imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [button setTintColor:[UIColor redColor]]; [button setImage:img forState:UIControlStateNormal];@ Ankish danke!
Motasim

31

Dies ist so ziemlich die Antwort oben, aber leicht verkürzt. Dies nimmt das Bild nur als Maske und "multipliziert" oder färbt das Bild nicht.

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

Schnell:

    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
Sie sollten verwenden, UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);da Ihre Version nur Nicht-Retina-Grafiken erstellt.
Nikola Lajic

user1270061 ist meiner Erfahrung nach der richtige Weg. Die andere Antwort mit "Brennen" benötigt anscheinend ein Quellbild einer bestimmten Farbe. Dieser verwendet nur die Alpha-Werte in den Quellpixeln und kombiniert diese mit der gewünschten Farbe - perfekt.
Jason

Perfekt - nur eine Antwort, die für mich gut funktioniert hat. (16. Mai ')
Trdavidson

10

Eine andere Möglichkeit, ein Bild zu tönen, besteht darin, es einfach mit einer konstanten Farbe zu multiplizieren. Manchmal ist dies vorzuziehen, da dadurch die Farbwerte in schwarzen Bereichen nicht "angehoben" werden. Dadurch bleiben die relativen Intensitäten im Bild gleich. Wenn Sie eine Überlagerung als Farbton verwenden, wird der Kontrast tendenziell abgeflacht.

Dies ist der Code, den ich benutze:

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

Schnelle Version

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

Genau die Lösung, nach der ich gesucht habe! Vielen Dank!
Vir uns

Was ist, wenn ich eine virtuelle Wandmalerei-App erstellen möchte? Dies funktioniert, wenn Sie nur die Wände im Bild einfärben möchten. Schauen Sie sich bitte diesen Link an: - stackoverflow.com/questions/27482508/…
Shailesh

Dies funktioniert viel besser als einige der anderen Lösungen da draußen.
Matt Hudson

3
Dies färbte für mich nur den Objekthintergrund, nicht das Objekt selbst.
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()

Viel Spaß Sie Swift Pioniere


5
Code ist in Ordnung, aber in der Post geht es um UIImage. Wir haben es nicht immer mit UIImageViews zu tun.
HenryRootTwo

5

Swift 4.2 Lösung

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

Ab iOS 10 können Sie Folgendes verwenden 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

Wenn Sie es nicht programmgesteuert ausführen müssen, können Sie es einfach über die Xcode-Benutzeroberfläche ausführen.

Wenn Sie zu dem Bild in Ihrem Bildassets-Ordner wechseln, öffnen Sie den Inspektor auf der rechten Seite und es gibt eine Dropdown-Liste "Rendern als" mit den folgenden Optionen:

  1. Standard
  2. Original
  3. Vorlage

Sobald Sie die Vorlagenauswahl getroffen haben, können Sie die Farbtonfarbe des Bildes nach Belieben ändern - unabhängig davon, ob es die Xcode-Storyboard-Benutzeroberfläche verwendet oder programmgesteuert.

Geben Sie hier die Bildbeschreibung ein

Siehe dieses Bild:

Geben Sie hier die Bildbeschreibung ein


Verwenden Sie XCODE 8?
Doxsi

2

Hier ist meine Anpassung von @ Annas Antwort. Zwei wichtige Punkte hier:

  • Verwenden Sie den destinationInMischmodus
  • Rufen Sie UIGraphicsBeginImageContextWithOptions(backgroundSize, false, UIScreen.main.scale)an, um ein flüssiges Bild zu erhalten

Code 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
Warum übergeben Sie das Bild, wenn Sie UIImage erweitern? Sie sollten das statische Schlüsselwort Ihrer Methode entfernen und es einfach selfinnerhalb Ihrer Methode verwenden und den unnötigen Bildparameter entfernen
Leo Dabus

1

Basierend auf @ Annas Antwort schreibe ich für Swift 2.2 um und behandle das Bild mit dem Alphakanal:

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

Annas Code eignet sich gut zum Kopieren eines UIImage.image über einen farbigen .image-Hintergrund, indem kCGBlendModeNormal anstelle von kCGBlendModeMultiply verwendet wird. Zum Beispiel self.mainImage.image = [self NormalImageByConstantColor: self.mainImage.image withColor: yourColor];wird der Inhalt von mainImage.image über den Farbton yourColor platzieren , während die Opazität yourColor erhalten. Dies löste mein Problem, eine Hintergrundfarbe mit Deckkraft hinter einem Bild zu platzieren, das auf der Kamerarolle gespeichert werden soll.


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

Schnelle 3.0-Version von Annas wunderbarem Code:

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

Für iOS 13 und neuer:

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.