Cómo teñir una imagen PNG transparente en iPhone?

Sé que es posible teñir una imagen rectangular dibujando un CGContextFillRect sobre ella y configurando el modo de fusión. Sin embargo, no puedo imaginar cómo hacer un tinte en una imagen transparente, como un icono. Debe ser posible ya que el SDK lo hace solo en barras de tabs en tal. ¿Alguien podría proporcionar un fragmento?

ACTUALIZAR:

Se han dado muchas sugerencias excelentes para este problema desde que originalmente lo pregunté. Asegúrese de leer todas las respuestas para descubrir qué es lo que más le conviene.

ACTUALIZACIÓN (30 de abril de 2015):

Con iOS 7.0, ahora puedo hacer lo siguiente, que satisfaría las necesidades de mi pregunta original. Pero si tienes casos más complicados, mira todas las respuestas.

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

Actualización: Aquí hay un Gist para una extensión Swift UIColor usando el siguiente código.


Si tiene una imagen en escala de grises y desea que el blanco se convierta en el color de tinte , kCGBlendModeMultiply es el camino a seguir. Con este método, no puede tener reflections más claros que su color de tinte.

Por el contrario, si tiene una imagen que no es en escala de grises , O tiene reflections y sombras que deben conservarse, el modo de combinación kCGBlendModeColor es el camino a seguir. El blanco permanecerá blanco y el negro permanecerá negro a medida que se preserve la claridad de la imagen. Este modo está hecho para teñir, es lo mismo que el modo de mezcla de capas de Color de Photoshop (descargo de responsabilidad: pueden ocurrir resultados ligeramente diferentes).

Tenga en cuenta que teñir los píxeles alfa no funciona correctamente ni en iOS ni en Photoshop: los píxeles negros medio transparentes no se mantendrían negros. Actualicé la respuesta a continuación para solucionar ese problema, tomó bastante tiempo descubrirlo.

También puede usar uno de los modos de fusión kCGBlendModeSourceIn/DestinationIn lugar de CGContextClipToMask .

Si desea crear un UIImage , cada una de las siguientes secciones de código puede estar rodeada por el siguiente código:

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

Así que aquí está el código para teñir una imagen transparente con 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); 

Si su imagen no tiene píxeles medio transparentes, también puede hacerlo al revés con 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); 

Si no le importa la luminosidad, ya que solo tiene una imagen con un canal alfa que debe estar tintado con un color, puede hacerlo de una manera más eficiente:

 // 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); 

O al revés:

 // 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); 

¡Que te diviertas!

Tuve mucho éxito con este método, porque los otros que probé causaron colores distorsionados para píxeles semitransparentes para ciertas combinaciones de colores. Esto también debería ser un poco mejor en el lado del rendimiento.

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

Después de buscar, la mejor solución que he encontrado hasta ahora es utilizar una combinación de modo de fusión y la máscara de recorte para lograr colorear / teñir un PNG transparente:

 CGContextSetBlendMode (context, kCGBlendModeMultiply); CGContextDrawImage(context, rect, myIconImage.CGImage); CGContextClipToMask(context, rect, myIconImage.CGImage); CGContextSetFillColorWithColor(context, tintColor); CGContextFillRect(context, rect); 

Puedo obtener resultados muy cercanos al tinte en la barra de navegación de Apple usando kCGBlendModeOverlay. Tomando excelente respuesta @fabb y combinando el enfoque @omz en esta publicación https://stackoverflow.com/a/4684876/229019 Vine con esta solución que tiene los resultados que esperaba:

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

Aquí hay un ejemplo que tiñe varias imágenes en escala de grises con transparencia:

Ejemplo rendido :

  • La primera línea es la barra de herramientas de apple teñida [UIColor orangeColor].
  • La segunda línea es el mismo degradado teñido en varios colores comenzando con el color claro (= el gradiente real) y terminando con el mismo color naranja.
  • El tercero es un círculo simple con transparencia (el lino es el color de fondo)
  • La cuarta línea es una compleja textura ruidosa oscura

Puede crear una categoría de UIImage y hacerlo así:

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

En iOS7, han introducido la propiedad tintColor en UIImageView y renderingMode en UIImage. Ver mi ejemplo en https://stackoverflow.com/a/19125120/1570970

Tenga en cuenta que en la respuesta aceptada por fabb, el código “circundante” para hacer un UIImage me dio una resolución incorrecta de las imágenes en la pantalla Retina. Para arreglar, cambiar:

UIGraphicsBeginImageContext (myIconImage.size);

a:

UIGraphicsBeginImageContextWithOptions (myIconImage.size, NO, 0.0);

El último parámetro que se establece en 0.0 es la escala, y según la documentación de Apple: “Si especifica un valor de 0.0, el factor de escala se establece en el factor de escala de la pantalla principal del dispositivo”.

No tengo el permiso para comentar, y la edición parece un poco grosera, así que mencioné esto en una respuesta. En caso de que alguien encuentre el mismo problema.

Con iOS 7.0, también puedes hacer esto para teñir un UIImageView básico:

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

UIImageView (o cualquier vista para ese asunto) tiene un color de fondo que es RGBA. El alfa en el color puede hacer lo que necesita sin inventar algo nuevo.

No es mi trabajo, pero he utilizado con éxito este enfoque:

http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage

Quería sombrear mis vistas de imagen en mi subclase UIButton personalizada y las otras soluciones no hicieron lo que yo quería. Necesitaba oscurecer “tinte” el color de la imagen. Aquí se explica cómo cambiar el brillo con CoreImage.

Botones personalizados con imágenes sombreadas al presionar un botón

  1. Asegúrese de agregar CoreImage.framework a las bibliotecas de su proyecto. (Enlace binario con bibliotecas)

  2. Método de sombra UIImage

     - (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. Querrá almacenar una copia del contexto como un ivar, en lugar de volver a crearlo.

No hay respuestas que me ayuden en el desbordamiento de la stack: nuestros diseñadores dibujan elementos UI con varias formas, varios valores alfa (y “agujeros alfa”). En la mayoría de los casos, este es un archivo PNG de 32 bits con canal alfa, que comprende píxeles en blanco y negro (de todas las intensidades posibles). Después de teñir una imagen así tuve que obtener este resultado teñido: píxeles blancos – tintados más, y píxeles oscuros – menos. Y todo esto a la vista del canal alfa. Y escribí este método para mi categoría UIImage. Tal vez no sea altamente eficiente, pero funciona como reloj 🙂 Aquí está:

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

Primero quiero agradecer a Fabb por su excepcional solución que me ha ayudado a cumplir mi tarea de teñir íconos medio transparentes. Como necesitaba una solución para C # (Monotouch) tuve que traducir su código. Esto es lo que se me ocurrió. Solo copie y pegue esto en su código y agregue su Imagen semi transparente y su finalización.

Así que de nuevo todos los créditos van a fabb. Esto es solo para iniciar a los usuarios de C # 🙂

 //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);