Je sais qu'il est possible de teinter une image rectangulaire en dessinant un CGContextFillRect dessus et en définissant le mode de fusion. Cependant, je ne sais pas comment faire une teinte sur une image transparente telle qu'une icône. Cela doit être possible puisque le SDK le fait lui-même sur des tab-bars dans un tel. Quelqu'un pourrait-il fournir un extrait?
MISE À JOUR:
Beaucoup de bonnes suggestions ont été faites pour ce problème depuis que j'ai demandé à l'origine. Assurez-vous de lire toutes les réponses pour déterminer ce qui vous convient le mieux.
MISE À JOUR (30 avril 2015):
Avec iOS 7.0, je peux maintenant simplement faire ce qui suit, ce qui satisferait les besoins de ma question d'origine. Mais si vous avez des cas plus compliqués, consultez toutes les réponses.
UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];
Mise à jour: Voici un Gist pour une extension Swift UIColor en utilisant le code ci-dessous.
Si vous avez une image en niveaux de gris et que vous voulez le blanc devient la couleur de teinte, kCGBlendModeMultiply
est la voie à suivre. Avec cette méthode, vous ne pouvez pas avoir de reflets plus clairs que votre couleur de teinture.
Au contraire, si vous avez soit une image non en niveaux de gris, [~ # ~] ou [~ # ~] vous avez surlignages et ombres qui doivent être préservés, le mode de fusion kCGBlendModeColor
est la voie à suivre. Le blanc restera blanc et le noir restera noir car la clarté de l'image est préservée. Ce mode est juste conçu pour la teinture - il est identique au mode de fusion des calques Color
de Photoshop (avertissement: des résultats légèrement différents peuvent se produire).
Notez que la coloration des pixels alpha ne fonctionne pas correctement ni dans iOS ni dans Photoshop - les pixels noirs semi-transparents ne resteraient pas noirs. J'ai mis à jour la réponse ci-dessous pour contourner ce problème, il a fallu beaucoup de temps pour le découvrir.
Vous pouvez également utiliser l'un des modes de fusion kCGBlendModeSourceIn/DestinationIn
au lieu de CGContextClipToMask
.
Si vous souhaitez créer un UIImage
, chacune des sections de code suivantes peut être entourée du code suivant:
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();
Voici donc le code pour teinter une image transparente avec 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 votre image n'a pas de pixels semi-transparents, vous pouvez également le faire dans l'autre sens avec 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 vous ne vous souciez pas de la luminosité, car vous venez d'avoir une image avec un canal alpha qui devrait être teintée d'une couleur, vous pouvez le faire de manière plus efficace:
// 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);
ou l'inverse:
// 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);
S'amuser!
J'ai eu le plus de succès avec cette méthode, car les autres que j'ai essayées ont provoqué des couleurs déformées pour les pixels semi-transparents pour certaines combinaisons de couleurs. Cela devrait également être un peu mieux du côté des performances.
+ (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;
}
Après avoir cherché, la meilleure solution que j'ai trouvée jusqu'à présent est d'utiliser une combinaison du mode de fusion et du masque d'écrêtage pour réaliser la colorisation/teinte d'un PNG transparent:
CGContextSetBlendMode (context, kCGBlendModeMultiply);
CGContextDrawImage(context, rect, myIconImage.CGImage);
CGContextClipToMask(context, rect, myIconImage.CGImage);
CGContextSetFillColorWithColor(context, tintColor);
CGContextFillRect(context, rect);
Je peux obtenir des résultats très proches de la teinte dans la barre de navigation Apple en utilisant kCGBlendModeOverlay. Prendre une excellente réponse @fabb et combiner l'approche @omz dans cet article https: // stackoverflow. com/a/4684876/229019 Je suis venu avec cette solution qui détient les résultats que j'attendais:
- (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;
}
Voici un exemple teintant plusieurs images en niveaux de gris avec transparence:
:
Vous pouvez créer une catégorie UIImage et le faire comme ceci:
- (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;
}
Dans iOS7, ils ont introduit la propriété tintColor sur UIImageView et le renduMode sur UIImage. Voir mon exemple sur https://stackoverflow.com/a/19125120/157097
Notez que dans la réponse acceptée par fabb, le code "environnant" pour faire un UIImage m'a donné la mauvaise résolution des images sur l'écran de la rétine. Pour corriger, changez:
UIGraphicsBeginImageContext(myIconImage.size);
à:
UIGraphicsBeginImageContextWithOptions(myIconImage.size, NO, 0.0);
Le dernier paramètre défini sur 0,0 est l'échelle et, selon Apple:
"Si vous spécifiez une valeur de 0,0, le facteur d'échelle est défini sur le facteur d'échelle de l'écran principal de l'appareil".
Je n'ai pas la permission de commenter, et l'édition semble un peu grossière, donc je mentionne cela dans une réponse. Juste au cas où quelqu'un rencontre ce même problème.
Avec iOS 7.0, vous pouvez également le faire pour teinter une UIImageView de base:
UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];
UIImageView (ou toute autre vue d'ailleurs) a une couleur d'arrière-plan qui est RGBA. L'alpha dans la couleur peut faire ce dont vous avez besoin sans inventer quelque chose de nouveau.
Je voulais ombrer mes vues d'image dans ma sous-classe UIButton personnalisée et les autres solutions n'ont pas fait ce que je voulais. J'avais besoin d'assombrir la "teinte" de la couleur de l'image. Voici comment modifier la luminosité à l'aide de CoreImage.
Assurez-vous d'ajouter CoreImage.framework aux bibliothèques de votre projet. (Lier le binaire aux bibliothèques)
Méthode de teinte 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;
}
Vous aurez envie de stocker une copie du contexte comme un ivar, plutôt que de le recréer.
Aucune réponse ne m'aidera sur le débordement de pile: nos concepteurs dessinent des éléments d'interface utilisateur avec différentes formes, différentes valeurs alpha (et "trous alpha"). Dans la plupart des cas, il s'agit d'un fichier PNG 32 bits avec canal alpha, qui comprend des pixels noir et blanc (de toutes les intensités possibles). Après avoir teinté une telle image, j'ai dû obtenir ce résultat teinté: pixels blancs - plus teintés et pixels sombres - moins. Et tout cela en vue du canal alpha. Et j'ai écrit cette méthode pour ma catégorie UIImage. Peut-être que ce n'est pas très efficace, mais cela fonctionne comme une horloge :) La voici:
- (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;
}
Ce n'est pas mon travail, mais j'ai utilisé avec succès cette approche:
http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage
Je veux d'abord remercier fabb pour sa solution exceptionnelle qui m'a aidé à accomplir ma tâche de teinter des icônes à moitié transparentes. Parce que j'avais besoin d'une solution pour C # (Monotouch) j'ai dû traduire son code. Voici ce que j'ai trouvé. Copiez-collez simplement cela dans votre code et ajoutez votre image à moitié transparente et votre résultat.
Encore une fois, tous les crédits vont à fabb. C'est juste pour lancer les utilisateurs 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);