Wie färbe ich ein transparentes PNG-Bild im iPhone?

74

Ich weiß, dass es möglich ist, ein rechteckiges Bild zu tönen, indem Sie einen CGContextFillRect darüber zeichnen und den Mischmodus einstellen. Ich kann jedoch nicht herausfinden, wie ein transparentes Bild wie ein Symbol getönt wird. Dies muss möglich sein, da das SDK dies selbst auf Registerkarten in solchen tut. Wäre jemand in der Lage, ein Snippet bereitzustellen?

AKTUALISIEREN:

Seit ich ursprünglich gefragt habe, wurden viele großartige Vorschläge für dieses Problem gemacht. Lesen Sie alle Antworten durch, um herauszufinden, was am besten zu Ihnen passt.

UPDATE (30. April 2015):

Mit iOS 7.0 kann ich jetzt nur Folgendes tun, um die Anforderungen meiner ursprünglichen Frage zu erfüllen. Wenn Sie jedoch kompliziertere Fälle haben, lesen Sie alle Antworten.

UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];    
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];
anna
quelle

Antworten:

165

Update: Hier ist ein Gist für eine Swift UIColor Erweiterung mit dem Code unten.


Wenn Sie ein Graustufenbild haben und möchten, dass Weiß zur Tönungsfarbe wird , kCGBlendModeMultiplyist dies der richtige Weg. Mit dieser Methode können Sie keine Glanzlichter erzielen, die heller als Ihre Tönungsfarbe sind.

Im Gegenteil, wenn Sie entweder einen haben Nicht-Graustufenbild , ODER Sie Highlights und Schatten , die erhalten werden sollen, der Mischmodus kCGBlendModeColorist der Weg zu gehen. Weiß bleibt weiß und Schwarz bleibt schwarz, wenn die Helligkeit des Bildes erhalten bleibt. Dieser Modus dient nur zum Abtönen. Er entspricht dem Ebenenüberblendungsmodus von Photoshop Color(Haftungsausschluss: Es können leicht abweichende Ergebnisse auftreten).

Beachten Sie, dass das Abtönen von Alpha-Pixeln weder in iOS noch in Photoshop ordnungsgemäß funktioniert - halbtransparente schwarze Pixel bleiben nicht schwarz. Ich habe die Antwort unten aktualisiert, um dieses Problem zu umgehen. Es hat ziemlich lange gedauert, es herauszufinden.

Sie können kCGBlendModeSourceIn/DestinationInstattdessen auch einen der Mischmodi verwenden CGContextClipToMask.

Wenn Sie eine erstellen möchten, UIImagekann jeder der folgenden Codeabschnitte von folgendem Code umgeben sein:

UIGraphicsBeginImageContextWithOptions (myIconImage.size, NO, myIconImage.scale); // for correct resolution on retina, thanks @MobileVet
CGContextRef context = UIGraphicsGetCurrentContext();

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

CGRect rect = CGRectMake(0, 0, myIconImage.size.width, myIconImage.size.height);

// image drawing code here

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

Hier ist der Code zum Abtönen eines transparenten Bildes mit kCGBlendModeColor:

// draw black background to preserve color of transparent pixels
CGContextSetBlendMode(context, kCGBlendModeNormal);
[[UIColor blackColor] setFill];
CGContextFillRect(context, rect);

// draw original image
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);

// tint image (loosing alpha) - the luminosity of the original image is preserved
CGContextSetBlendMode(context, kCGBlendModeColor);
[tintColor setFill];
CGContextFillRect(context, rect);

// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

Wenn Ihr Bild keine halbtransparenten Pixel enthält, können Sie dies auch umgekehrt tun mit kCGBlendModeLuminosity:

// draw tint color
CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);

// replace luminosity of background (ignoring alpha)
CGContextSetBlendMode(context, kCGBlendModeLuminosity);
CGContextDrawImage(context, rect, myIconImage.CGImage);

// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

Wenn Sie sich nicht für die Leuchtkraft interessieren, da Sie nur ein Bild mit einem Alphakanal haben, das mit einer Farbe getönt werden sollte, können Sie dies effizienter tun:

// draw tint color
CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);

// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

oder umgekehrt:

// draw alpha-mask
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);

// draw tint color, preserving alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeSourceIn);
[tintColor setFill];
CGContextFillRect(context, rect);

Habe Spaß!

fabb
quelle
Das ist fantastisch und hat mir jede Menge Zeit gespart! Ich mag die Technik, mit kCGBlendModeDestinationIn das ursprüngliche Alpha des Bildes wiederherzustellen, das Sie bearbeiten. Viel einfacher als mit Schnittmasken umzugehen.
Ziconic
8
Dies ist brillant und gründlich ... es fehlt jedoch eine winzige Option, die eine qualitativ hochwertige Ausgabe auf Retina-Displays gewährleistet ---- UIGraphicsBeginImageContextWithOptions (myIconImage.size, NO, [[UIScreen mainScreen] -Skala]);
MobileVet
Oder stellen Sie die Retina-Skala wie folgt ein: UIGraphicsBeginImageContextWithOptions (self.size, 0, self.scale) in der Kategorie
HotJard
21

Ich hatte den größten Erfolg mit dieser Methode, weil die anderen, die ich ausprobierte, bei bestimmten Farbkombinationen verzerrte Farben für halbtransparente Pixel verursachten. Dies sollte auch auf der Leistungsseite etwas besser sein.

+ (UIImage *) imageNamed:(NSString *) name withTintColor: (UIColor *) tintColor {

    UIImage *baseImage = [UIImage imageNamed:name];

    CGRect drawRect = CGRectMake(0, 0, baseImage.size.width, baseImage.size.height);

    UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

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

    // draw original image
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGContextDrawImage(context, drawRect, baseImage.CGImage);

    // draw color atop
    CGContextSetFillColorWithColor(context, tintColor.CGColor);
    CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
    CGContextFillRect(context, drawRect);

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

    return tintedImage;
}
Kanada
quelle
Dies scheint nicht mit Farben zu funktionieren, deren Alpha-Komponente kleiner als 1 ist. Kennen Sie einen Weg, dies zu beheben?
Zev Eisenberg
Ich habe es mit diesem Code von github.com/vilanovi/UIImage-Additions/blob/master/… zum Laufen gebracht :CGContextSetBlendMode(context, kCGBlendModeNormal); CGContextDrawImage(context, rect, self.CGImage); CGContextSetBlendMode(context, kCGBlendModeSourceIn); [color setFill]; CGContextFillRect(context, rect);
Zev Eisenberg
18

Nach dem Durchsuchen besteht die beste Lösung, die ich bisher gefunden habe, darin, eine Kombination aus Mischmodus und Schnittmaske zu verwenden, um ein transparentes PNG einzufärben / zu färben:

CGContextSetBlendMode (context, kCGBlendModeMultiply);

CGContextDrawImage(context, rect, myIconImage.CGImage);

CGContextClipToMask(context, rect, myIconImage.CGImage);

CGContextSetFillColorWithColor(context, tintColor);

CGContextFillRect(context, rect);
anna
quelle
Ich habe alles versucht, was mir sonst noch einfiel, bevor ich das gefunden habe, und das war das einzige, was funktioniert hat. Danke, @anna!
Todd Lehman
Dies funktionierte, drehte aber mein Bild um und ich benutzte diese Antwort von Cliff Viegas, um das zu beheben: stackoverflow.com/questions/506622/…
C. Lee
hat super funktioniert. Die obige Lösung hat mir aus irgendeinem seltsamen Grund einen Rand von 1 Pixel hinterlassen.
Gottfried
16

Mit kCGBlendModeOverlay kann ich Ergebnisse erzielen, die dem Farbton in der Apple-Navigationsleiste sehr nahe kommen. Ich habe eine exzellente @ fabb-Antwort genommen und den @ omz-Ansatz in diesem Beitrag kombiniert. Https://stackoverflow.com/a/4684876/229019 Ich bin mit dieser Lösung gekommen, die die erwarteten Ergebnisse enthält:

- (UIImage *)tintedImageUsingColor:(UIColor *)tintColor;
{
    UIGraphicsBeginImageContextWithOptions (self.size, NO, [[UIScreen mainScreen] scale]);

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

    // draw original image
    [self drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0f];

    // tint image (loosing alpha). 
    // kCGBlendModeOverlay is the closest I was able to match the 
    // actual process used by apple in navigation bar 
    CGContextSetBlendMode(context, kCGBlendModeOverlay);
    [tintColor setFill];
    CGContextFillRect(context, rect);

    // mask by alpha values of original image
    [self drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0f];

    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return tintedImage;
}

Hier ist ein Beispiel für das Abtönen mehrerer Graustufenbilder mit Transparenz:

Beispiel gerendert::

  • Die erste Zeile ist die getönte Apfelsymbolleiste [UIColor orangeColor].
  • Die zweite Linie ist der gleiche Farbverlauf, der in mehreren Farben getönt ist, beginnend mit einer klaren Farbe (= dem tatsächlichen Farbverlauf) und endend mit demselben Orange.
  • Der dritte ist ein einfacher Kreis mit Transparenz (das Leinen ist die Hintergrundfarbe)
  • Die vierte Linie ist eine komplexe dunkle verrauschte Textur
tozevv
quelle
4
Das hat bei mir super funktioniert. Ich würde nur hinzufügen, dass das Ändern der ersten Zeile in: UIGraphicsBeginImageContextWithOptions (self.size, NO, [[UIScreen mainScreen] scale]); erlaubte dem Bild, gegebenenfalls eine Netzhautformatierung beizubehalten.
jwarrent
1
Dies wäre angemessener: UIGraphicsBeginImageContextWithOptions (self.size, NO, self.scale);
Malhal
8

Sie können eine UIImage-Kategorie erstellen und dies folgendermaßen tun:

- (instancetype)tintedImageWithColor:(UIColor *)tintColor {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGRect rect = (CGRect){ CGPointZero, self.size };
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    [self drawInRect:rect];

    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    [tintColor setFill];
    CGContextFillRect(context, rect);

    UIImage *image  = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
kkodev
quelle
3

Mit iOS 7.0 können Sie dies auch einfach tun, um eine grundlegende UIImageView zu tönen:

UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];    
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];
anna
quelle
3

Beachten Sie, dass in der akzeptierten Antwort von fabb der "umgebende" Code zum Erstellen eines UIImage die falsche Auflösung von Bildern auf dem Retina-Bildschirm ergab. Um dies zu beheben, ändern Sie:

UIGraphicsBeginImageContext(myIconImage.size);

zu:

UIGraphicsBeginImageContextWithOptions(myIconImage.size, NO, 0.0);

Der letzte Parameter, der auf 0.0 gesetzt ist, ist scale und laut Apple-Dokumentation:

"Wenn Sie einen Wert von 0,0 angeben, wird der Skalierungsfaktor auf den Skalierungsfaktor des Hauptbildschirms des Geräts eingestellt."

Ich habe keine Erlaubnis zum Kommentieren und die Bearbeitung scheint etwas unhöflich zu sein, daher erwähne ich dies in einer Antwort. Nur für den Fall, dass jemand auf dasselbe Problem stößt.

lxl
quelle
2

UIImageView (oder eine beliebige Ansicht) hat eine Hintergrundfarbe, die RGBA ist. Das Alpha in der Farbe kann das tun, was Sie brauchen, ohne etwas Neues zu erfinden.

NWCoder
quelle
1

Ich wollte meine Bildansichten in meiner benutzerdefinierten UIButton-Unterklasse schattieren, und die anderen Lösungen haben nicht das getan, was ich wollte. Ich musste die Bildfarbe abdunkeln. So ändern Sie die Helligkeit mit CoreImage.

Benutzerdefinierte Schaltflächen mit schattierten Bildern beim Drücken der Taste

  1. Stellen Sie sicher, dass Sie CoreImage.framework zu den Bibliotheken Ihres Projekts hinzufügen. (Binär mit Bibliotheken verknüpfen)

  2. UIImage Schattenmethode

    - (UIImage *)shadeImage:(UIImage *)image {
     CIImage *inputImage = [CIImage imageWithCGImage:image.CGImage];
    
     CIContext *context = [CIContext contextWithOptions:nil];
    
     CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"
                                  keysAndValues:kCIInputImageKey, inputImage,
                        @"inputBrightness", [NSNumber numberWithFloat:-.5], nil];
     CIImage *outputImage = [filter outputImage];
    
     CGImageRef cgImage = [context createCGImage:outputImage fromRect:[outputImage extent]];
     UIImage *newImage = [UIImage imageWithCGImage:cgImage scale:image.scale orientation:image.imageOrientation];
     CGImageRelease(cgImage);
    
     return newImage;
    }
    
  3. Sie möchten eine Kopie des Kontexts als ivar speichern, anstatt ihn neu zu erstellen.

Paul Solt
quelle
1

Keine Antworten helfen mir beim Stapelüberlauf: Unsere Designer zeichnen UI-Elemente mit verschiedenen Formen, verschiedenen Alpha-Werten (und "Alpha-Löchern"). In den meisten Fällen handelt es sich um eine 32-Bit-PNG-Datei mit Alphakanal, die Schwarz-Weiß-Pixel (aller möglichen Intensitäten) enthält. Nachdem ich ein solches Bild getönt hatte, musste ich dieses getönte Ergebnis erzielen: weiße Pixel - über mehr getönt und dunkle Pixel - weniger. Und das alles im Hinblick auf den Alpha-Kanal. Und ich habe diese Methode für meine UIImage-Kategorie geschrieben. Vielleicht ist es nicht hocheffizient, aber es funktioniert als Uhr :) Hier ist es:

- (UIImage *)imageTintedWithColor:(UIColor *)color {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

    CGContextSetBlendMode(context, kCGBlendModeCopy);
    [color setFill];
    CGContextFillRect(context, rect);

    [self drawInRect:rect blendMode:kCGBlendModeXOR alpha:1.0];

    CGContextSetBlendMode(context, kCGBlendModeXOR);
    CGContextFillRect(context, rect);

    [self drawInRect:rect blendMode:kCGBlendModeMultiply alpha:1.0];

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

    return coloredImage;
}
Dmitry Evglevsky
quelle
0

Zunächst möchte ich mich bei fabb für seine außergewöhnliche Lösung bedanken, die mir geholfen hat, meine Aufgabe zu erfüllen, halbtransparente Symbole zu tönen. Da ich eine Lösung für C # (Monotouch) brauchte, musste ich seinen Code übersetzen. Folgendes habe ich mir ausgedacht. Kopieren Sie dies einfach, fügen Sie es in Ihren Code ein und fügen Sie Ihr halbtransparentes Bild hinzu.

Also gehen wieder alle Credits an fabb. Dies ist nur, um C # -Nutzer zu starten :)

//TINT COLOR IMAGE
UIImageView iImage = new UIImageView(new RectangleF(12, 14, 24,24));
iImage.ContentMode = UIViewContentMode.ScaleAspectFit;
iImage.Image = _dataItem.Image[0] as UIImage;

UIGraphics.BeginImageContextWithOptions(iImage.Bounds.Size, false, UIScreen.MainScreen.Scale);
CGContext context = UIGraphics.GetCurrentContext();

context.TranslateCTM(0, iImage.Bounds.Size.Height);
context.ScaleCTM(1.0f, -1.0f);

RectangleF rect = new RectangleF(0,0, iImage.Bounds.Width, iImage.Bounds.Height);

// draw black background to preserve color of transparent pixels
context.SetBlendMode(CGBlendMode.Normal);
UIColor.Black.SetFill();
context.FillRect(rect);

// draw original image
context.SetBlendMode(CGBlendMode.Normal);
context.DrawImage(rect, iImage.Image.CGImage);

// tint image (loosing alpha) - the luminosity of the original image is preserved
context.SetBlendMode(CGBlendMode.Color);
UIColor.Orange.SetFill();
context.FillRect(rect);

// mask by alpha values of original image
context.SetBlendMode(CGBlendMode.DestinationIn);
context.DrawImage(rect, iImage.Image.CGImage);

UIImage coloredImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();

iImage = new UIImageView(coloredImage);
iImage.Frame = new RectangleF(12, 14, 24,24);
//END TINT COLOR IMAGE

cell.Add(iImage);
Henning Schulz
quelle