ฉันจะย้อมสีภาพโดยใช้โปรแกรมบน iOS ได้อย่างไร


87

ฉันต้องการย้อมสีภาพด้วยการอ้างอิงสี ผลลัพธ์ควรมีลักษณะเหมือนโหมดการผสมแบบทวีคูณใน Photoshop โดยที่สีขาวจะถูกแทนที่ด้วยสี :

ข้อความแสดงแทน

ฉันจะเปลี่ยนค่าสีไปเรื่อย ๆ

ติดตามผล:ฉันจะใส่โค้ดเพื่อทำสิ่งนี้ในวิธี drawRect: ImageView ของฉันใช่ไหม

เช่นเคยข้อมูลโค้ดจะช่วยในการทำความเข้าใจของฉันได้มากเมื่อเทียบกับลิงก์

อัปเดต:คลาสย่อยของ UIImageView ด้วยรหัสที่Raminแนะนำ

ฉันใส่สิ่งนี้ไว้ใน viewDidLoad: ของตัวควบคุมมุมมองของฉัน:

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

ฉันเห็นภาพ แต่มันไม่ได้ถูกย้อมสี ฉันยังลองโหลดภาพอื่น ๆ ตั้งค่าภาพใน IB และเรียก setNeedsDisplay: ในตัวควบคุมมุมมองของฉัน

อัปเดต : drawRect: ไม่ถูกเรียก

การอัปเดตครั้งสุดท้าย:ฉันพบโปรเจ็กต์เก่าที่ตั้งค่า imageView อย่างถูกต้องดังนั้นฉันจึงสามารถทดสอบโค้ดของรามินและทำงานได้อย่างมีเสน่ห์!

ขั้นสุดท้ายการอัปเดตขั้นสุดท้าย:

สำหรับผู้ที่เพิ่งเรียนรู้เกี่ยวกับ Core Graphics นี่คือสิ่งที่ง่ายที่สุดที่อาจใช้งานได้

ใน 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
}

ฉันเพิ่มข้อมูลโค้ดก่อนโพสต์ไปยังโค้ด drawRect เก่า ๆ ที่ฉันนั่งเฉยๆและมันก็ใช้ได้ดี อาจต้องการลองโดยไม่ต้องเรียก [super viewDidLoad] (หรือย้ายไปด้านบน) ตรวจสอบอีกครั้งเพื่อให้แน่ใจว่าใครเป็นผู้จัดสรรอ็อบเจ็กต์นี้กำลังจัดสรรเวอร์ชันที่สืบทอดมาไม่ใช่ vanilla UIImageView (เช่นถ้าโหลดใน nib ตัวจัดสรร ฯลฯ )
Ramin

ข้อเสนอแนะอื่นหากข้างต้นไม่ได้ผล: แทนที่จะใช้คลาสย่อย UIImageView เป็น UIView ธรรมดาและเพิ่มคุณสมบัติโอเวอร์เลย์คัลเลอร์และ UIImage ที่เรียกว่า 'รูปภาพ' ที่คุณสามารถตั้งค่าได้ จากนั้นใส่ drawRect ตรงนั้น โค้ด drawRect ไม่สนใจว่าค่า 'image' มาจาก UIImageView หรือจากคุณสมบัติที่คุณกำหนดไว้
Ramin

สิ่งนี้จะไม่ล้มเหลวหากรูปภาพเป็นอัลฟ่า? จะเป็นอย่างไรถ้าฉันต้องการย้อมสีเฉพาะส่วนของภาพ (เช่นเดียวกับ UIButton หรือไม่)
Sunil Chauhan

คำตอบ:


45

ก่อนอื่นคุณจะต้องซับคลาส UIImageView และแทนที่เมธอด drawRect ชั้นเรียนของคุณต้องการคุณสมบัติ UIColor (เรียกว่าโอเวอร์เลย์คัลเลอร์) เพื่อเก็บสีผสมผสานและตัวตั้งค่าแบบกำหนดเองที่บังคับให้วาดใหม่เมื่อสีเปลี่ยนไป สิ่งนี้:

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

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

ในเมธอด drawRect คุณจะต้องวาดภาพก่อนจากนั้นซ้อนทับด้วยสี่เหลี่ยมที่เต็มไปด้วยสีที่คุณต้องการพร้อมกับโหมดการผสมที่เหมาะสมดังนี้:

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

โดยปกติแล้วในการเพิ่มประสิทธิภาพการวาดภาพคุณจะ จำกัด การวาดจริงไว้เฉพาะพื้นที่ที่ส่งผ่านไปยัง drawRect แต่เนื่องจากภาพพื้นหลังจะต้องมีการวาดใหม่ทุกครั้งที่สีเปลี่ยนไปจึงเป็นไปได้ว่าทุกอย่างจะต้องมีการรีเฟรช

หากต้องการใช้สร้างอินสแตนซ์ของวัตถุจากนั้นตั้งค่าimageคุณสมบัติ (สืบทอดมาจาก UIImageView) เป็นรูปภาพและoverlayColorเป็นค่า UIColor (ระดับการผสมผสานสามารถปรับได้โดยการเปลี่ยนค่าอัลฟาของสีที่คุณส่งผ่าน)


คำแนะนำการติดตามที่แนบมากับคำขอเดิม (ด้านบน)
Ramin

ควรเพิ่ม CGContextSaveGState (บริบท); ก่อนที่จะเปลี่ยนโหมดผสมผสานหรือคุณมี gstack underflow
willc2

ขอบคุณครับ ข้อผิดพลาดในการคัดลอก / วาง ฉันได้แก้ไขแล้วในข้อมูลโค้ด
Ramin

10
(ตามที่ระบุไว้ในคำตอบอื่นที่นี่) ข้อควรพิจารณาพิเศษคลาส UIImageView ได้รับการปรับให้เหมาะสมเพื่อวาดภาพไปยังจอแสดงผล UIImageView จะไม่เรียก drawRect: คลาสย่อย หากคลาสย่อยของคุณต้องการโค้ดการวาดแบบกำหนดเองขอแนะนำให้คุณใช้ UIView เป็นคลาสพื้นฐาน ซึ่งหมายความว่าคุณไม่สามารถซับคลาส UIImageView และคาดว่า drawRect จะเรียกว่าที่ทุกคน
จอนนี่

สวัสดีน่าเสียดายที่ฉันลองใช้รหัสแล้วไม่ได้ผล: /. ดูคำตอบด้านล่างโดย @mpstx ซึ่งระบุว่า drawRect จะไม่ถูกเรียกจาก UIImageView และคุณควรใช้ UIView (พร้อม UIImage) ซึ่งทำงานได้ดีสำหรับฉัน
jklp

77

ใน iOS7 พวกเขาได้นำเสนอคุณสมบัติ tintColor บน UIImageView และ renderingMode บน UIImage ในการย้อมสี UIImage บน iOS7 สิ่งที่คุณต้องทำคือ:

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
นี่เป็นวิธีที่ดีที่สุดและง่ายที่สุดในการใช้งาน iOS 7 หากนี่เป็นพฤติกรรมการย้อมสีที่คุณกำลังมองหา แต่โค้ดนี้ใช้โทนสีราวกับว่าคุณกำลังใช้โหมดผสม "โอเวอร์เลย์" แบบทึบ 100% ใน Photoshop ไม่ใช่โหมดผสม "คูณ" ที่คำถามเดิมกำลังมองหา
smileyborg

26

ฉันต้องการย้อมสีภาพด้วยอัลฟาและฉันสร้างคลาสต่อไปนี้ โปรดแจ้งให้เราทราบหากคุณพบปัญหาใด ๆ

ฉันตั้งชื่อชั้นเรียนของฉันCSTintedImageViewและสืบทอดมาจากUIViewตั้งแต่UIImageViewนั้นไม่ได้เรียกdrawRect:เมธอดดังที่กล่าวไว้ในคำตอบก่อนหน้า ฉันได้ตั้งค่าเริ่มต้นที่กำหนดไว้คล้ายกับที่พบในUIImageViewชั้นเรียน

การใช้งาน:

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
ขอขอบคุณ. นี่เป็นคำตอบแรกสำหรับคำถามนี้ที่สนับสนุนความโปร่งใสของภาพอย่างเหมาะสม
theTRON

เมื่อฉันพยายามใช้คำตอบนี้พื้นหลังของฉัน (บริเวณที่ควรโปร่งใส) จะเป็นสีดำแทน คุณรู้ไหมว่าฉันทำอะไรผิดที่นี่?
livingtech

ไม่เป็นไร! (ฉันมีชุดสีพื้นหลังในกระดานเรื่องราว!) วิธีนี้ยอดเยี่ยมมาก !!!
livingtech

2
วิธีนี้ใช้งานได้ดีแม้ว่าฉันจะได้ผลลัพธ์ที่ดีกว่าโดยการเรนเดอร์ภาพก่อนจากนั้นเติมสีที่ด้านบนด้วย kCGBlendModeColor
Jeremy Fuller

drawRect: วิธีการเป็น "เนื้อ" ของการแก้ปัญหานี้จริงๆ ส่วนที่เหลือเป็นเรื่องของรสนิยม / รูปแบบ ขอบคุณ! ทำงานให้ฉัน!
เกือกม้า 7

26

เพียงแค่ชี้แจงอย่างรวดเร็ว (หลังจากการวิจัยในหัวข้อนี้) แอปเปิ้ล doc ที่นี่อย่างชัดเจนระบุว่า:

UIImageViewระดับมีการเพิ่มประสิทธิภาพในการวาดภาพในการแสดงผล UIImageViewไม่เรียกdrawRect:เมธอดของคลาสย่อย หากคลาสย่อยของคุณต้องการรวมโค้ดการวาดแบบกำหนดเองคุณควรซับคลาสนั้นUIViewแทน

ดังนั้นอย่าเสียเวลากับการพยายามลบล้างเมธอดนั้นในUIImageViewคลาสย่อย เริ่มต้นด้วยUIViewแทน


9
โอ้ฉันหวังว่าฉันจะรู้เรื่องนี้ได้อย่างไรเมื่อหกเดือนที่แล้ว ควรระบุคำเตือนเป็นประเภท 72 จุดสีแดงพร้อมกับป้ายกะพริบ
willc2

ฉันรู้ว่าคุณหมายถึง ... ฉันหายไปครึ่งวันเพื่อยุ่งกับมัน
mattorb

ขอบคุณ @mpstx ... ใส่บรรทัดเอกสารนั้นไว้ในคำตอบด้วย ... Apple น่าจะทำเช่นเดียวกันในเอกสาร ... :-)
Krishnabhadra

5

สิ่งนี้มีประโยชน์มาก: PhotoshopFramework เป็นไลบรารีที่ทรงพลังอย่างหนึ่งในการจัดการภาพบน Objective-C สิ่งนี้ได้รับการพัฒนาเพื่อนำฟังก์ชันเดียวกับที่ผู้ใช้ Adobe Photoshop คุ้นเคย ตัวอย่าง: ตั้งค่าสีโดยใช้ RGB 0-255, ใช้ Blend filers, transformations ...

เป็นโอเพ่นซอร์สนี่คือลิงค์โครงการ: https://sourceforge.net/projects/photoshopframew/


2
ดูน่าสนใจ แต่คุณจะเปลี่ยนชื่อเป็นอะไรเมื่อทนายความของ Adobe ทราบข้อมูลเกี่ยวกับเรื่องนี้
Kristopher Johnson

1
เราจะคิดออกในภายหลัง ไม่ใช่ผลิตภัณฑ์เชิงพาณิชย์ แต่เป็น "สมญานาม" บางประเภท นอกจากนี้ยังเป็นห้องสมุดภายใน
SEQOY Development Team

+1 สำหรับการแนะนำห้องสมุด (แม้ว่าจะเป็นการโปรโมตตัวเองเพียงเล็กน้อยก็ตาม) และไม่ให้ขั้นตอนในการสร้างวงล้อใหม่
ทิม

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

ฉันต้องการย้อมสี UIImage แต่ไม่ควรใช้ภายใน UIImageView แต่เป็นภาพพื้นหลังสำหรับ UINavigationBar และโค้ดของคุณก็ทำเคล็ดลับ ขอบคุณ.
ncerezo

3

นี่เป็นอีกวิธีหนึ่งในการใช้การย้อมสีภาพโดยเฉพาะอย่างยิ่งหากคุณใช้ QuartzCore สำหรับอย่างอื่นอยู่แล้ว นี่คือคำตอบของฉันสำหรับคำถามที่คล้ายกัน

นำเข้า QuartzCore:

#import <QuartzCore/QuartzCore.h>

สร้าง CALayer แบบโปร่งใสและเพิ่มเป็นเลเยอร์ย่อยสำหรับภาพที่คุณต้องการย้อมสี:

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

เพิ่ม QuartzCore ในรายการกรอบงานโครงการของคุณ (ถ้ายังไม่มี) มิฉะนั้นคุณจะได้รับข้อผิดพลาดของคอมไพเลอร์ดังนี้:

Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer"

2

สำหรับผู้ที่พยายาม subclass คลาส UIImageView และติดขัดที่ "drawRect: ไม่ถูกเรียก" โปรดทราบว่าคุณควร subclass คลาส UIView แทนเนื่องจากสำหรับคลาส UIImageView จะไม่เรียกเมธอด "drawRect:" อ่านเพิ่มเติมที่นี่: drawRect ไม่ถูกเรียกในคลาสย่อยของ UIImageView


0

สิ่งเดียวที่ฉันคิดได้คือการสร้างมุมมองโปร่งใสเป็นรูปสี่เหลี่ยมผืนผ้าส่วนใหญ่ด้วยสีที่ต้องการและวางไว้บนมุมมองภาพของคุณโดยเพิ่มเป็นมุมมองย่อย ฉันไม่แน่ใจว่านี่จะทำให้ภาพเป็นสีตามที่คุณจินตนาการได้จริงหรือไม่ฉันไม่แน่ใจว่าคุณจะแฮ็กภาพและเปลี่ยนสีบางสีด้วยสีอื่นได้อย่างไร ... ฟังดูค่อนข้างทะเยอทะยานสำหรับฉัน

ตัวอย่างเช่น:

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:

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 ดูเหมือนจะใช้โหมดดัดได้ คำถามคืออย่างไร
willc2

0

นอกจากนี้คุณอาจต้องการพิจารณาแคชรูปภาพที่ผสมเพื่อประสิทธิภาพและเพียงแค่แสดงผลใน drawRect: จากนั้นอัปเดตหากแฟล็กสกปรกสกปรก แม้ว่าคุณอาจจะเปลี่ยนบ่อยครั้ง แต่อาจมีบางกรณีที่การดึงเข้ามาและคุณไม่ได้สกปรกดังนั้นคุณสามารถรีเฟรชจากแคชได้ หากหน่วยความจำมีปัญหามากกว่าประสิทธิภาพคุณสามารถเพิกเฉยได้ :)


นั่นคือสิ่งที่ CALayer พื้นฐานทำเพื่อคุณโดยอัตโนมัติ ไม่จำเป็นต้องแคชมุมมองการวาดภาพด้วยตนเอง
Dennis Munsie


0

สำหรับ 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

ฉันสร้างมาโครเพื่อจุดประสงค์นี้:

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

ในการใช้งานเพียงโทร:

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

เมื่อลบสีเพียงโทร:

removeTint(yourView);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.