İOS'ta bir görüntüyü programlı olarak nasıl renklendirebilirim?


87

Bir görüntüyü bir renk referansı ile renklendirmek istiyorum. Sonuçlar, beyazların renk tonuyla değiştirileceği Photoshop'taki Çoğalt karıştırma moduna benzemelidir :

alternatif metin

Renk değerini sürekli değiştireceğim.

Takip: Bunu yapmak için kodu ImageView'umun drawRect: yöntemine koyardım, değil mi?

Her zaman olduğu gibi, bir kod parçacığı , bir bağlantıdan ziyade anlamama büyük ölçüde yardımcı olacaktır.

Güncelleme: Ramin'in önerdiği kodla bir UIImageView alt sınıflandırma .

Bunu görünüm denetleyicimin viewDidLoad: bölümüne koydum:

[self.lena setImage:[UIImage imageNamed:kImageName]];
[self.lena setOverlayColor:[UIColor blueColor]];
[super viewDidLoad];

Görüntüyü görüyorum ama renklendirilmiyor. Ayrıca diğer görüntüleri yüklemeyi, görüntüyü IB'ye ayarlamayı ve görünüm denetleyicimde setNeedsDisplay: öğesini çağırmayı denedim.

Güncelleme : drawRect: çağrılmıyor.

Son güncelleme: Ramin'in kodunu test edebilmek için düzgün bir şekilde kurulmuş bir imageView olan eski bir proje buldum ve harika çalışıyor!

Son, son güncelleme:

Core Graphics'i yeni öğrenenler için, işte işe yarayabilecek en basit şey burada.

Alt sınıflandırılmış UIView'unuzda:

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

Snippet'i, oturduğum eski bir drawRect koduna göndermeden önce ekledim ve iyi çalıştı. Bunu [super viewDidLoad] çağrısı olmadan denemek (veya yukarıya taşımak) isteyebilirsiniz. Ayrıca, bu nesneyi tahsis eden kişinin türetilmiş sürümü vanilya UIImageView olarak ayırmadığından emin olmak için iki kez kontrol edin (yani, bir uçta yüklüyse, ayırıcı vb.).
Ramin

Yukarıdakilerin işe yaramaması durumunda başka bir öneri: UIImageView alt sınıfına girmek yerine düz bir UIView alt sınıfı oluşturmak ve hem overlayColor hem de ayarlayabileceğiniz 'image' adlı bir UIImage özelliği ekleyin. Ardından drawRect'i oraya koyun. DrawRect kodu, 'image' değerinin UIImageView'dan veya tanımladığınız bir özellikten gelip gelmediğini önemsemez.
Ramin

Görüntü alfa ile ise bu başarısız olmaz mı? Ya görüntünün yalnızca çizilmiş kısmını renklendirmek istersem? (Tıpkı UIButton'un yaptığı gibi?)
Sunil Chauhan

Yanıtlar:


45

Öncelikle UIImageView alt sınıfına girmek ve drawRect yöntemini geçersiz kılmak isteyeceksiniz. Sınıfınızın, karışım rengini tutmak için bir UIColor özelliğine (buna overlayColor diyelim) ve renk değiştiğinde yeniden çizmeyi zorlayan özel bir ayarlayıcıya ihtiyacı vardır. Bunun gibi bir şey:

- (void) setOverlayColor:(UIColor *)newColor {
   if (overlayColor)
     [overlayColor release];

   overlayColor = [newColor retain];
   [self setNeedsDisplay]; // fires off drawRect each time color changes
}

DrawRect yönteminde, önce görüntüyü çizmek, ardından uygun karıştırma modunun yanı sıra istediğiniz renkle doldurulmuş bir dikdörtgenle kaplamak isteyeceksiniz, bunun gibi bir şey:

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

Normalde çizimi optimize etmek için gerçek çizimi yalnızca drawRect'e aktarılan alanla sınırlandırırsınız, ancak arka plan görüntüsünün renk her değiştiğinde yeniden çizilmesi gerektiğinden, muhtemelen her şeyin yenilenmesi gerekecektir.

Bunu kullanmak için nesnenin bir örneğini oluşturun ve ardından imageözelliği (UIImageView'dan miras alınan) resme ve overlayColorbir UIColor değerine ayarlayın (karışım seviyeleri, aktardığınız rengin alfa değerini değiştirerek ayarlanabilir).


Orijinal talebe eklenmiş takip önerileri (yukarıda).
Ramin

CGContextSaveGState (bağlam) eklemelidir; harmanlama modunu değiştirmeden önce veya bir gstack alt akışı elde edersiniz.
willc2

D'oh, teşekkürler. Kopyala / yapıştır hatası. Kod parçacığında düzelttim.
Ramin

10
(Burada başka bir yanıtta da belirtildiği gibi) Özel Hususlar UIImageView sınıfı, görüntülerini ekrana çizecek şekilde optimize edilmiştir. UIImageView, bir alt sınıf olan drawRect'i çağırmaz. Alt sınıfınızın özel çizim koduna ihtiyacı varsa, temel sınıf olarak UIView kullanmanız önerilir. Eğer drawRect alt UIImageView ve bekleyemezsiniz Bu araçlar, çağrılacak hiç .
Jonny

Merhaba, maalesef kodu denedim ve işe yaramadı: /. DrawRect'in bir UIImageView'dan çağrılmayacağını ve bunun yerine benim için iyi çalışan bir UIView (UIImage ile) kullanmanız gerektiğini söyleyen @mpstx tarafından aşağıdaki cevaba bakın.
jklp

77

İOS7'de, UIImageView üzerinde tintColor özelliğini ve UIImage üzerinde renderingMode özelliğini tanıttılar. İOS7'de bir UIImage'ı renklendirmek için yapmanız gereken tek şey:

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

18
Aradığınız renk tonu davranışı buysa, bu kesinlikle iOS 7'ye gitmenin en iyi ve en kolay yoludur . Ancak bu kod, orijinal sorunun aradığı "Çarpma" karıştırma modunu değil, Photoshop'ta% 100 opak "Kaplama" karıştırma modunu kullanıyormuşsunuz gibi bir renk tonu uygular.
smileyborg

26

Alfa ile bir görüntüyü renklendirmek istedim ve aşağıdaki sınıfı oluşturdum. Herhangi bir sorun bulursanız lütfen bana bildirin.

Benim sınıf adında var CSTintedImageViewve onu devraldığı UIViewberi UIImageViewçağırmaz drawRect:önceki yanıtlar belirtildiği gibi, yöntemi. UIImageViewSınıfta bulunana benzer bir atanmış başlatıcı ayarladım .

Kullanım:

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

2
Teşekkür ederim. Bu sorudaki görüntü şeffaflığını gerçekten doğru bir şekilde destekleyen ilk çözümdü.
theTRON

Bu cevabı kullanmaya çalıştığımda, arka planım (şeffaf olması gereken alan) bunun yerine siyah. Burada neyi yanlış yapıyor olabilirim biliyor musun?
livingtech

Boşver! (Film şeridinde bir arka plan rengi belirledim! D'oh!) Bu çözüm harika !!!
livingtech

2
Bu çözüm iyi çalışıyor, ancak önce görüntüyü oluşturarak, ardından üstteki rengi kCGBlendModeColor ile doldurarak daha iyi sonuçlar aldım.
Jeremy Fuller

drawRect: yöntem gerçekten bu çözümün "etidir". Gerisi bir zevk / stil meselesidir. Teşekkürler! Benim için çalıştı!
horseshoe7

26

Sadece hızlı bir açıklama (bu konuyla ilgili biraz araştırmadan sonra). Elma doc burada açıkça belirtmektedir:

UIImageViewSınıf ekrana onun görüntüleri çekmek için optimize edilmiştir. alt sınıflarının yöntemini UIImageViewçağırmaz drawRect:. Alt sınıfınızın özel çizim kodu içermesi gerekiyorsa, UIViewbunun yerine sınıfı alt sınıflara ayırmalısınız .

bu nedenle, bir UIImageViewalt sınıfta bu yöntemi geçersiz kılmaya çalışmakla zaman kaybetmeyin . Onun UIViewyerine ile başlayın .


9
Oh, keşke bunu altı ay önce bilseydim. Uyarıyı, kırmızı renkte yanıp sönen etiketle birlikte 72 punto yazmalıdır.
willc2

Demek istediğini biliyorum ... Bununla uğraşarak yarım gün kaybettim.
mattorb

teşekkürler @mpstx ... Ayrıca bu dokümantasyon satırını tırnak içinde cevaba ekleyin ... Apple da aynı şeyi dokümantasyonda
yapmalıydı

5

Bu çok yararlı olabilir: PhotoshopFramework, Objective-C'de görüntüleri işlemek için güçlü bir kitaplıktır. Bu, Adobe Photoshop kullanıcılarının aşina olduğu aynı işlevleri getirmek için geliştirilmiştir. Örnekler: RGB 0-255 kullanarak renkleri ayarlayın, karışım filtreleri uygulayın, dönüştürmeler ...

Açık kaynak, işte proje bağlantısı: https://sourceforge.net/projects/photoshopframew/


2
İlginç görünüyor, ancak Adobe'nin avukatları bunu öğrendiğinde adını neye değiştireceksiniz?
Kristopher Johnson

1
Daha sonra çözeceğiz. Zaten ticari bir ürün değil, bir tür "kod adı". Ayrıca bir iç kitaplıktır.
SEQOY Geliştirme Ekibi

Bir kütüphane önermek (biraz kendini tanıtmak olsa bile) ve tekerleği yeniden icat etmek için adımlar atmamak için +1
Tim

4
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

Bir UIImage'ı renklendirmem gerekiyordu, ancak onu bir UIImageView içinde kullanmamalıydım, ancak bir UINavigationBar için bir arka plan görüntüsü olarak kullanmam gerekiyordu ve kodunuz hile yaptı. Teşekkürler.
ncerezo

3

İşte, özellikle de QuartzCore'u başka bir şey için kullanıyorsanız, görüntü renklendirmeyi uygulamanın başka bir yolu. Bu, benzer bir soruya cevabımdı .

QuartzCore'u İçe Aktar:

#import <QuartzCore/QuartzCore.h>

Şeffaf CALayer oluşturun ve onu renklendirmek istediğiniz görüntü için bir alt katman olarak ekleyin:

CALayer *sublayer = [CALayer layer];
[sublayer setBackgroundColor:[UIColor whiteColor].CGColor];
[sublayer setOpacity:0.3];
[sublayer setFrame:toBeTintedImage.frame];
[toBeTintedImage.layer addSublayer:sublayer];

QuartzCore'u projelerinizin Çerçeve listenize ekleyin (zaten orada değilse), aksi takdirde aşağıdaki gibi derleyici hataları alırsınız:

Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer"

2

Bir UIImageView sınıfını alt sınıfa ayırmaya çalışanlarınız ve "drawRect: çağrılmıyor" durumunda kalanlar için, bunun yerine bir UIView sınıfını alt sınıfa ayırmanız gerektiğini unutmayın, çünkü UIImageView sınıfları için "drawRect:" yöntemi çağrılmaz. Daha fazlasını buradan okuyun: drawRect, UIImageView alt sınıfımda çağrılmıyor


0

Aklıma gelen tek şey, istenen renkte dikdörtgen çoğunlukla saydam bir görünüm oluşturmak ve onu bir alt görünüm olarak ekleyerek görüntü görünümünüzün üzerine yerleştirmek olacaktır. Bunun görüntüyü gerçekten hayal ettiğiniz şekilde renklendirip renklendirmeyeceğinden emin değilim, yine de bir görüntüyü nasıl kıracağınızdan ve belirli renkleri diğerleriyle nasıl değiştireceğinizden emin değilim ... kulağa oldukça iddialı geliyor.

Örneğin:

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

UIColor için belgeler:

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. 

Core Graphics, bükme modlarını uygulayabilecek gibi görünüyor. Soru nasıl?
willc2

0

Ayrıca, performans için birleştirilmiş görüntüyü önbelleğe almayı ve onu sadece drawRect: 'de oluşturmayı düşünebilir, ardından kirli bir bayrak gerçekten kirliyse güncelleyebilirsiniz. Sık sık değiştiriyor olsanız da, çekilişlerin geldiği ve kirli olmadığınız durumlar olabilir, bu nedenle önbellekten kolayca yenileyebilirsiniz. Bellek performanstan çok bir sorunsa, bunu göz ardı edebilirsiniz :)


Temel CALayer'ın sizin için otomatik olarak yaptığı şey budur. Görünüm çizimini manuel olarak önbelleğe almanıza gerek yoktur.
Dennis Munsie


0

Swift 2.0 için,

    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)

0

Bu amaçla makrolar yaptım:

#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];\
}

Kullanmak için aramanız yeterlidir:

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

Renk tonunu kaldırırken şunu arayın:

removeTint(yourView);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.