Pregunta 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 pestañas 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];

73
2017-08-18 15:59


origen


Respuestas:


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


Si tienes un imagen en escala de grises y querer blanco convertido en el color de tinte, kCGBlendModeMultiply es el camino a seguir. Con este método, no puede tener reflejos más claros que su color de tinte.

Por el contrario, si tienes un imagen no en escala de grises, O tienes reflejos y oscuridad eso debe ser preservado, el modo de mezcla 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 Photoshop Color modo de mezcla de capas (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 puedes usar uno de los modos de fusión kCGBlendModeSourceIn/DestinationIn en lugar de CGContextClipToMask.

Si quieres 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 podría 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!


163
2017-09-11 11:12



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

20
2018-03-20 09:21



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

18
2017-08-19 18:13



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/229019Vine 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:

Rendered Example:

  • 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

16
2018-03-29 15:15



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

8
2017-09-26 10:46



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


8
2017-10-01 20:21



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.


3
2017-07-11 13:08



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

3
2018-04-30 11:50



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.


2
2017-08-18 18:56