Ä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?

RomanHouse
quelle
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

Ankish Jain
quelle
"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()
user1270061
quelle
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
    }
}
Anna Dickinson
quelle
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

ColossalChris
quelle
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)
Charlton Provatas
quelle
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)
        }
    }

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

Mario A Guzman
quelle
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!
    }
}
algrid
quelle
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
}
Bill Chan
quelle
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.

Lowell Bahner
quelle
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!
    }
}
Nursultan Askarbekuly
quelle
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!
    }
}
Matthieu
quelle
0

Für iOS 13 und neuer:

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

Beryllium
quelle