Làm cách nào để tô màu hình ảnh theo chương trình trên iOS?


87

Tôi muốn tô màu một hình ảnh với tham chiếu màu. Kết quả sẽ giống như chế độ hòa trộn Multiply trong Photoshop, trong đó màu trắng sẽ được thay thế bằng màu :

văn bản thay thế

Tôi sẽ thay đổi giá trị màu liên tục.

Tiếp theo: Tôi sẽ đặt mã để thực hiện việc này trong phương thức drawRect: của ImageView, phải không?

Như mọi khi, một đoạn mã sẽ hỗ trợ rất nhiều cho sự hiểu biết của tôi, trái ngược với một liên kết.

Cập nhật: Phân lớp một UIImageView với mã Ramin đề xuất.

Tôi đặt cái này trong viewDidLoad: của bộ điều khiển chế độ xem của tôi:

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

Tôi nhìn thấy hình ảnh, nhưng nó không được nhuộm màu. Tôi cũng đã thử tải các hình ảnh khác, đặt hình ảnh trong IB và gọi setNeedsDisplay: trong bộ điều khiển chế độ xem của tôi.

Cập nhật : drawRect: không được gọi.

Cập nhật cuối cùng: Tôi đã tìm thấy một dự án cũ có imageView được thiết lập đúng cách để tôi có thể kiểm tra mã của Ramin và nó hoạt động như một sự quyến rũ!

Cập nhật cuối cùng, cuối cùng:

Đối với những bạn mới học về Đồ họa lõi, đây là điều đơn giản nhất có thể hoạt động.

Trong UIView phân lớp của bạn:

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

Tôi đã thêm đoạn mã trước khi đăng lên một số mã drawRect cũ mà tôi đã ngồi xung quanh và nó hoạt động tốt. Có thể muốn thử nó mà không cần gọi [super viewDidLoad] (hoặc di chuyển nó lên trên). Ngoài ra, hãy kiểm tra kỹ để đảm bảo rằng ai đang cấp phát đối tượng này đang cấp phát phiên bản có nguồn gốc không phải là vanilla UIImageView (tức là nếu nó được tải trong một nib, thì trình cấp phát, v.v.).
Ramin

Một gợi ý khác nếu cách trên không hoạt động: thay vì phân lớp con UIImageView một lớp con UIView thuần túy và thêm cả overlayColor và thuộc tính UIImage có tên là 'hình ảnh' mà bạn có thể đặt. Sau đó đặt drawRect vào đó. Mã drawRect không quan tâm liệu giá trị 'hình ảnh' đến từ UIImageView hay từ thuộc tính bạn đã xác định.
Ramin

Điều này sẽ không thành công nếu hình ảnh có alpha? Điều gì sẽ xảy ra nếu tôi chỉ muốn tô màu phần đã vẽ của hình ảnh? (Cũng giống như UIButton?)
Sunil Chauhan

Câu trả lời:


45

Đầu tiên, bạn sẽ muốn phân lớp UIImageView và ghi đè phương thức drawRect. Lớp của bạn cần thuộc tính UIColor (chúng ta hãy gọi nó là overlayColor) để giữ màu pha trộn và một bộ thiết lập tùy chỉnh buộc vẽ lại khi màu thay đổi. Một cái gì đó như thế này:

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

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

Trong phương thức drawRect, trước tiên bạn sẽ muốn vẽ hình ảnh, sau đó phủ lên nó một hình chữ nhật có màu bạn muốn cùng với chế độ hòa trộn thích hợp, giống như sau:

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

Thông thường để tối ưu hóa bản vẽ, bạn sẽ giới hạn bản vẽ thực tế chỉ ở khu vực được truyền vào drawRect, nhưng vì hình nền phải được vẽ lại mỗi lần thay đổi màu sắc nên có thể toàn bộ sẽ cần được làm mới.

Để sử dụng nó, hãy tạo một phiên bản của đối tượng, sau đó đặt thuộc imagetính (kế thừa từ UIImageView) thành hình ảnh và overlayColorthành giá trị UIColor (các mức hòa trộn có thể được điều chỉnh bằng cách thay đổi giá trị alpha của màu bạn truyền xuống).


Các đề xuất tiếp theo được đính kèm với yêu cầu ban đầu (ở trên).
Ramin

nên thêm CGContextSaveGState (ngữ cảnh); trước khi thay đổi chế độ hòa trộn hoặc bạn nhận được dòng chảy dưới gstack.
willc2

D'oh, cảm ơn. Sao chép / dán lỗi. Tôi đã sửa nó trong đoạn mã.
Ramin

10
(Như đã nêu trong một câu trả lời khác ở đây) Cân nhắc Đặc biệt Lớp UIImageView được tối ưu hóa để vẽ các hình ảnh của nó lên màn hình. UIImageView sẽ không gọi drawRect: một lớp con. Nếu lớp con của bạn cần mã vẽ tùy chỉnh, bạn nên sử dụng UIView làm lớp cơ sở. Đây có nghĩa là bạn không thể phân lớp UIImageView và mong đợi drawRect được gọi là, ở tất cả .
Jonny

Xin chào, rất tiếc là tôi đã thử mã và nó không hoạt động: /. Xem câu trả lời bên dưới của @mpstx, cho biết drawRect sẽ không được gọi từ UIImageView và thay vào đó, bạn nên sử dụng UIView (với UIImage) phù hợp với tôi.
jklp

77

Trong iOS7, họ đã giới thiệu thuộc tính tintColor trên UIImageView và renderingMode trên UIImage. Để tô màu UIImage trên iOS7, tất cả những gì bạn phải làm là:

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
Đây chắc chắn là cách tốt nhất và dễ dàng nhất để sử dụng iOS 7 nếu đây là hành vi màu bạn đang tìm kiếm. Nhưng mã này áp dụng một tông màu như thể bạn đang sử dụng chế độ hòa trộn "Lớp phủ" 100% mờ đục trong Photoshop, không phải chế độ hòa trộn "Multiply" mà câu hỏi ban đầu đang tìm kiếm.
smileyborg

26

Tôi muốn tô màu một hình ảnh bằng alpha và tôi đã tạo lớp sau. Vui lòng cho tôi biết nếu bạn tìm thấy bất kỳ vấn đề nào với nó.

Tôi đã đặt tên lớp học của tôi CSTintedImageViewvà nó được thừa hưởng từ UIViewtừ UIImageViewkhông gọi drawRect:phương pháp, như đã đề cập trong bài trả lời trước. Tôi đã đặt một bộ khởi tạo được chỉ định tương tự như bộ khởi tạo được tìm thấy trong UIImageViewlớp.

Sử dụng:

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
Cảm ơn bạn. Đây là giải pháp đầu tiên trong câu hỏi này thực sự hỗ trợ độ trong suốt của hình ảnh.
theTRON,

Khi tôi cố gắng sử dụng câu trả lời này, nền của tôi (khu vực phải trong suốt) là màu đen. Bạn có biết tôi có thể làm gì sai ở đây không?
livingtech

Đừng bận tâm! (Tôi đã đặt màu nền trong bảng phân cảnh! D'oh!) Giải pháp này thật tuyệt vời !!!
livingtech

2
Giải pháp này hoạt động tốt, mặc dù tôi đã có kết quả tốt hơn bằng cách kết xuất hình ảnh trước, sau đó tô màu lên trên bằng kCGBlendModeColor.
Jeremy Fuller

method drawRect: thực sự là "thịt" của giải pháp này. Phần còn lại là vấn đề về hương vị / phong cách. Cảm ơn! Đã làm cho tôi!
horsehoe7

26

Chỉ cần làm rõ nhanh (sau một số nghiên cứu về chủ đề này). Tài liệu của Apple ở đây nói rõ rằng:

Các UIImageViewlớp học được tối ưu hóa để vẽ hình ảnh của mình cho màn hình. UIImageViewkhông gọi drawRect:phương thức của các lớp con của nó. Nếu lớp con của bạn cần bao gồm mã vẽ tùy chỉnh, bạn nên phân lớp con UIViewthay thế.

vì vậy, thậm chí không lãng phí bất kỳ thời gian cố gắng ghi đè phương thức đó trong một UIImageViewlớp con. Bắt đầu với UIViewthay vào đó.


9
Ôi, tôi ước gì tôi biết điều này sáu tháng trước. Họ nên đặt cảnh báo ở loại 72 điểm, màu đỏ với thẻ nhấp nháy.
willc2

Tôi biết ý bạn là ... Tôi đã mất nửa ngày để làm việc với nó.
mattorb

cảm ơn @mpstx ... Cũng đặt dòng tài liệu đó trong câu trả lời trong dấu ngoặc kép ... Apple lẽ ra nên làm điều tương tự trong tài liệu ... :-)
Krishnabhadra

5

Điều này có thể rất hữu ích: PhotoshopFramework là một thư viện mạnh mẽ để thao tác hình ảnh trên Objective-C. Điều này được phát triển để mang lại các chức năng tương tự như người dùng Adobe Photoshop đã quen thuộc. Ví dụ: Đặt màu bằng RGB 0-255, áp dụng trình tập tin pha trộn, chuyển đổi ...

Là mã nguồn mở, đây là liên kết dự án: https://sourceforge.net/projects/photoshopframew/


2
Trông có vẻ thú vị, nhưng bạn định đổi tên thành gì khi luật sư của Adobe phát hiện ra nó?
Kristopher Johnson

1
Chúng tôi sẽ tìm hiểu sau. Không phải là một sản phẩm thương mại dù sao, là một số loại "mật danh". Cũng là một thư viện nội bộ.
Nhóm phát triển SEQOY 22/10/09

+1 để đề xuất thư viện (ngay cả khi đó chỉ là một chút tự quảng cáo) và không đưa ra các bước để phát minh lại bánh xe.
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

Tôi cần tô màu một UIImage, nhưng không sử dụng nó bên trong UIImageView, mà làm hình nền cho UINavigationBar và mã của bạn đã thực hiện được thủ thuật. Cảm ơn.
ncerezo

3

Đây là một cách khác để thực hiện pha màu hình ảnh, đặc biệt nếu bạn đang sử dụng QuartzCore cho một thứ khác. Đây là câu trả lời của tôi cho một câu hỏi tương tự .

Nhập QuartzCore:

#import <QuartzCore/QuartzCore.h>

Tạo CALayer trong suốt và thêm nó làm lớp con cho hình ảnh bạn muốn tô màu:

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

Thêm QuartzCore vào danh sách Framework dự án của bạn (nếu chưa có), nếu không bạn sẽ gặp lỗi trình biên dịch như sau:

Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer"


0

Điều duy nhất tôi có thể nghĩ đến là tạo một chế độ xem hình chữ nhật gần như trong suốt với màu sắc mong muốn và đặt nó trên chế độ xem hình ảnh của bạn bằng cách thêm nó làm chế độ xem phụ. Tôi không chắc liệu điều này có thực sự tô màu cho hình ảnh theo cách bạn tưởng tượng hay không, tôi không chắc làm thế nào bạn có thể xâm nhập vào một hình ảnh và thay thế có chọn lọc một số màu nhất định bằng những màu khác ... nghe có vẻ khá tham vọng đối với tôi.

Ví dụ:

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

Tài liệu cho 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. 

Core Graphics dường như có thể áp dụng các chế độ uốn cong. Làm thế nào, là câu hỏi.
willc2

0

Ngoài ra, bạn có thể muốn xem xét lưu vào bộ nhớ đệm hình ảnh được tổng hợp để thực hiện và chỉ hiển thị nó trong drawRect :, sau đó cập nhật nó nếu cờ bẩn thực sự là bẩn. Mặc dù bạn có thể thay đổi nó thường xuyên, nhưng có thể có trường hợp các bản vẽ đang đến và bạn không bị bẩn, vì vậy bạn có thể chỉ cần làm mới từ bộ nhớ cache. Nếu bộ nhớ là vấn đề nhiều hơn là hiệu suất, bạn có thể bỏ qua điều này :)


Đó là những gì CALayer cơ bản tự động làm cho bạn. Không cần phải lưu bản vẽ chế độ xem theo cách thủ công.
Dennis Munsie


0

Đối với 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)

0

Tôi đã tạo macro cho mục đích này:

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

Để sử dụng, bạn chỉ cần gọi:

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

Khi loại bỏ màu chỉ cần gọi:

removeTint(yourView);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.