Je voudrais teinter une image avec une référence de couleur. Les résultats devraient ressembler au mode de fusion Multiplier de Photoshop, où blancs seraient remplacés par tint:
Je changerai continuellement la valeur de la couleur.
Suivi: Je mettrais le code pour le faire dans la méthode drawRect: de mon ImageView, non?
Comme toujours, un extrait de code faciliterait grandement ma compréhension, par opposition à un lien.
Mise à jour: Sous-classer un UIImageView avec le code Ramin suggéré.
Je mets ceci dans viewDidLoad: de mon contrôleur de vue:
[self.lena setImage:[UIImage imageNamed:kImageName]];
[self.lena setOverlayColor:[UIColor blueColor]];
[super viewDidLoad];
Je vois l'image, mais elle n'est pas teintée. J'ai également essayé de charger d'autres images, de définir l'image dans IB et d'appeler setNeedsDisplay: dans mon contrôleur de vue.
Update : drawRect: n'est pas appelé.
Dernière mise à jour: J'ai trouvé un vieux projet avec une imageView correctement configurée pour pouvoir tester le code de Ramin et cela fonctionne à merveille!
Dernière mise à jour:
Pour ceux qui viennent d’apprendre à connaître Core Graphics, voici la chose la plus simple qui puisse fonctionner.
Dans votre sous-classe UIView:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColor(context, CGColorGetComponents([UIColor colorWithRed:0.5 green:0.5 blue:0 alpha:1].CGColor)); // don't make color too saturated
CGContextFillRect(context, rect); // draw base
[[UIImage imageNamed:@"someImage.png"] drawInRect: rect blendMode:kCGBlendModeOverlay alpha:1.0]; // draw image
}
Tout d'abord, vous voudrez sous-classer UIImageView et substituer la méthode drawRect. Votre classe a besoin d'une propriété UIColor (appelons-la overlayColor) pour contenir la couleur de fusion et d'un jeu de paramètres personnalisé qui force le rafraîchissement lorsque la couleur change. Quelque chose comme ça:
- (void) setOverlayColor:(UIColor *)newColor {
if (overlayColor)
[overlayColor release];
overlayColor = [newColor retain];
[self setNeedsDisplay]; // fires off drawRect each time color changes
}
Dans la méthode drawRect, vous voudrez d'abord dessiner l'image, puis la superposer avec un rectangle rempli de la couleur souhaitée ainsi que du mode de fusion approprié, comme ceci:
- (void) drawRect:(CGRect)area
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
// Draw picture first
//
CGContextDrawImage(context, self.frame, self.image.CGImage);
// Blend mode could be any of CGBlendMode values. Now draw filled rectangle
// over top of image.
//
CGContextSetBlendMode (context, kCGBlendModeMultiply);
CGContextSetFillColor(context, CGColorGetComponents(self.overlayColor.CGColor));
CGContextFillRect (context, self.bounds);
CGContextRestoreGState(context);
}
Normalement, pour optimiser le dessin, vous devez limiter le dessin réel à la seule zone transmise à drawRect. Toutefois, comme l'image d'arrière-plan doit être redessinée à chaque changement de couleur, il est probable que tout sera à rafraîchir.
Pour l'utiliser, créez une instance de l'objet, puis définissez la propriété image
(héritée de UIImageView) sur l'image et overlayColor
sur une valeur UIColor (les niveaux de fusion peuvent être ajustés en modifiant la valeur alpha de la couleur transmise).
Dans iOS7, ils ont introduit la propriété tintColor sur UIImageView et renderingMode sur UIImage. Pour teinter un UIImage sur iOS7, il suffit de:
UIImageView* imageView = …
UIImage* originalImage = …
UIImage* imageForRendering = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
imageView.image = imageForRendering;
imageView.tintColor = [UIColor redColor]; // or any color you want to tint it with
Juste une petite clarification (après quelques recherches sur ce sujet). Le document Apple ici indique clairement que:
La classe
UIImageView
est optimisée pour afficher ses images sur l’affichage.UIImageView
n'appelle pas la méthodedrawRect:
de ses sous-classes. Si votre sous-classe doit inclure du code de dessin personnalisé, vous devez sous-classer la classeUIView
.
alors ne perdez même pas de temps à essayer de remplacer cette méthode dans une sous-classe UIImageView
. Commencez par UIView
à la place.
Je voulais teinter une image avec alpha et j'ai créé la classe suivante. S'il vous plaît laissez-moi savoir si vous trouvez des problèmes avec cela.
J'ai nommé ma classe CSTintedImageView
et elle hérite de UIView
puisque UIImageView
n'appelle pas la méthode drawRect:
, comme indiqué dans les réponses précédentes . J'ai défini un initialiseur désigné similaire à celui de la classe UIImageView
.
Usage:
CSTintedImageView * imageView = [[CSTintedImageView alloc] initWithImage:[UIImage imageNamed:@"image"]];
imageView.tintColor = [UIColor redColor];
CSTintedImageView.h
@interface CSTintedImageView : UIView
@property (strong, nonatomic) UIImage * image;
@property (strong, nonatomic) UIColor * tintColor;
- (id)initWithImage:(UIImage *)image;
@end
CSTintedImageView.m
#import "CSTintedImageView.h"
@implementation CSTintedImageView
@synthesize image=_image;
@synthesize tintColor=_tintColor;
- (id)initWithImage:(UIImage *)image
{
self = [super initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
if(self)
{
self.image = image;
//set the view to opaque
self.opaque = NO;
}
return self;
}
- (void)setTintColor:(UIColor *)color
{
_tintColor = color;
//update every time the tint color is set
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
//resolve CG/iOS coordinate mismatch
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -rect.size.height);
//set the clipping area to the image
CGContextClipToMask(context, rect, _image.CGImage);
//set the fill color
CGContextSetFillColor(context, CGColorGetComponents(_tintColor.CGColor));
CGContextFillRect(context, rect);
//blend mode overlay
CGContextSetBlendMode(context, kCGBlendModeOverlay);
//draw the image
CGContextDrawImage(context, rect, _image.CGImage);
}
@end
Cela pourrait être très utile: PhotoshopFramework est une bibliothèque puissante pour manipuler des images sur Objective-C. Cela a été développé pour apporter les mêmes fonctionnalités que les utilisateurs d'Adobe Photoshop sont familiers. Exemples: définition des couleurs à l'aide de RVB 0-255, application de filtres de fondu, de transformations ...
Est open source, voici le lien du projet: https://sourceforge.net/projects/photoshopframew/
UIImage * image = mySourceImage;
UIColor * color = [UIColor yellowColor];
UIGraphicsBeginImageContext(image.size);
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height) blendMode:kCGBlendModeNormal alpha:1];
UIBezierPath * path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, image.size.width, image.size.height)];
[color setFill];
[path fillWithBlendMode:kCGBlendModeMultiply alpha:1]; //look up blending modes for your needs
UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//use newImage for something
Voici un autre moyen d'implémenter la teinte des images, surtout si vous utilisez déjà QuartzCore pour autre chose. C'était ma réponse pour une question similaire .
Importer QuartzCore:
#import <QuartzCore/QuartzCore.h>
Créez un CALayer transparent et ajoutez-le en tant que sous-couche à l'image que vous souhaitez teinter:
CALayer *sublayer = [CALayer layer];
[sublayer setBackgroundColor:[UIColor whiteColor].CGColor];
[sublayer setOpacity:0.3];
[sublayer setFrame:toBeTintedImage.frame];
[toBeTintedImage.layer addSublayer:sublayer];
Ajoutez QuartzCore à votre liste de projets (si ce n’est pas déjà fait), sinon vous obtiendrez des erreurs de compilation telles que
Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer"
Pour ceux d'entre vous qui tentent de sous-classer une classe UIImageView et restent bloqués sous "drawRect: n'est pas appelé", notez que vous devez sous-classer une classe UIView à la place, car pour les classes UIImageView, la méthode "drawRect:" n'est pas appelée. Lisez plus ici: drawRect n'étant pas appelé dans ma sous-classe de UIImageView
Pour Swift 2.0,
let image: UIImage! = UIGraphicsGetImageFromCurrentImageContext()
imgView.image = imgView.image!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
imgView.tintColor = UIColor(red: 51/255.0, green: 51/255.0, blue:
51/255.0, alpha: 1.0)
Vous pouvez également envisager de mettre en cache l'image composée pour la performance et de la restituer dans drawRect :, puis de la mettre à jour si un indicateur incorrect est effectivement endommagé. Même si vous le modifiez souvent, il peut arriver que des tirages arrivent et que vous ne soyez pas sale, vous pouvez donc simplement actualiser à partir du cache. Si la mémoire est plus un problème que la performance, vous pouvez l'ignorer :)
La seule chose à laquelle je puisse penser serait de créer une vue rectangulaire essentiellement transparente avec la couleur désirée et de la superposer à la vue de votre image en l’ajoutant en tant que sous-vue. Je ne sais pas si cela va vraiment ternir l'image de la façon dont vous l'imaginez, je ne sais pas comment vous pourriez pirater une image et remplacer sélectivement certaines couleurs par d'autres ... Cela me semble plutôt ambitieux.
Par exemple:
UIImageView *yourPicture = (however you grab the image);
UIView *colorBlock = [[UIView alloc] initWithFrame:yourPicture.frame];
//Replace R G B and A with values from 0 - 1 based on your color and transparency
colorBlock.backgroundColor = [UIColor colorWithRed:R green:G blue:B alpha:A];
[yourPicture addSubView:colorBlock];
Documentation pour UIColor:
colorWithRed:green:blue:alpha:
Creates and returns a color object using the specified opacity and RGB component values.
+ (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha
Parameters
red - The red component of the color object, specified as a value from 0.0 to 1.0.
green - The green component of the color object, specified as a value from 0.0 to 1.0.
blue - The blue component of the color object, specified as a value from 0.0 to 1.0.
alpha - The opacity value of the color object, specified as a value from 0.0 to 1.0.
Return Value
The color object. The color information represented by this object is in the device RGB colorspace.
J'ai une bibliothèque ouverte pour cela: ios-image-filters
J'ai fait des macros à cet effet:
#define removeTint(view) \
if ([((NSNumber *)[view.layer valueForKey:@"__hasTint"]) boolValue]) {\
for (CALayer *layer in [view.layer sublayers]) {\
if ([((NSNumber *)[layer valueForKey:@"__isTintLayer"]) boolValue]) {\
[layer removeFromSuperlayer];\
break;\
}\
}\
}
#define setTint(view, tintColor) \
{\
if ([((NSNumber *)[view.layer valueForKey:@"__hasTint"]) boolValue]) {\
removeTint(view);\
}\
[view.layer setValue:@(YES) forKey:@"__hasTint"];\
CALayer *tintLayer = [CALayer new];\
tintLayer.frame = view.bounds;\
tintLayer.backgroundColor = [tintColor CGColor];\
[tintLayer setValue:@(YES) forKey:@"__isTintLayer"];\
[view.layer addSublayer:tintLayer];\
}
Pour l'utiliser, il suffit d'appeler:
setTint(yourView, yourUIColor);
//Note: include opacity of tint in your UIColor using the alpha channel (RGBA), e.g. [UIColor colorWithRed:0.5f green:0.0 blue:0.0 alpha:0.25f];
Lorsque vous supprimez la teinte, appelez simplement:
removeTint(yourView);