Color tinting an UIImage - objective-c

I'm really stuck on tinting an UIImage with a certain tint. The UIImage is in a custom TableView cell (if that makes any difference).
Looking at the picture, the UIImage has been replaced by 0.5 opacity yellow and the original image has been discarded. I want a tint on an existing image, not overwrite the image entirely. (p.s: the numbers in the picture are labels that are on top of the image, they are unrelated).
I realize there are similiar threads on stackoverflow and I must have tried out all the code that there is :-) Nothing is working. I don't expect anyone to post any code, just a push in the right direction!
Explanation of the code below:
myImage is the image
color is the color, probably yellow, with an opacity of 0.5
I use [util tintedImageWithColor] with both myImage and color.
It comes out as the picture above.
Does anyone have an idea, a push in the right direction?
Updated
MasterViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
Article *article = self.articles[indexPath.row];
Utils *util = [[Utils alloc] init];
// Populate cell components
UIImage *myImage = [UIImage imageWithData:article.post_image_thumbnail_data];
// Set image tint
UIColor *color = [UIColor colorWithRed:(160/15.0) green:(97/15.0) blue:(5/55.0) alpha:0.5];
myImage = [util tintedImageWithColor:(UIImage *) myImage:(UIColor *) color blendingMode:kCGBlendModeDestinationIn highQuality:YES];
cell.imageView.image = myImage;
return cell;
}
Utils.m
- (UIImage *)tintedImageWithColor:(UIImage*)image : (UIColor *)tintColor blendingMode:(CGBlendMode)blendMode highQuality:(BOOL) yerOrNo;
{
CGSize size = CGSizeMake(70, 70);
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
if (yerOrNo) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShouldAntialias(context, true);
CGContextSetAllowsAntialiasing(context, true);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
}
[tintColor setFill];
CGRect bounds = CGRectMake(0, 0, size.width, size.height);
UIRectFill(bounds);
[image drawInRect:bounds blendMode:blendMode alpha:1.0f];
if (blendMode != kCGBlendModeDestinationIn)
[image drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:0.4];
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
Utils.h
- (UIImage *)tintedImageWithColor:(UIImage*)image : (UIColor *)tintColor blendingMode:(CGBlendMode)blendMode highQuality:(BOOL) yerOrNo;

I've found this piece on code in one of my projects, I don't know the exact source, but it should works, use kCGBlendModeDestinationIn:
- (UIImage *)tintedImageWithColor:(UIColor *)tintColor blendingMode:(CGBlendMode)blendMode highQuality:(BOOL) yerOrNo;
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
if (yerOrNo) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShouldAntialias(context, true);
CGContextSetAllowsAntialiasing(context, true);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
}
[tintColor setFill];
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
UIRectFill(bounds);
[self drawInRect:bounds blendMode:blendMode alpha:1.0f];
if (blendMode != kCGBlendModeDestinationIn)
[self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0];
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}

Related

I can't draw continuous lines on another image using PAN GESTURE

The code which I used to draw on images is as given below, I am using panGesture to find where the user touches. Now when I use this code the lines the user draws comes as points when i am moving my hands over the image very fast.
UIGraphicsBeginImageContextWithOptions((self.thatsMyImage.frame.size), NO, 0.0);
[self.selfieImage.image drawInRect:CGRectMake(0,0,self.thatsMyImage.frame.size.width, self.thatsMyImage.frame.size.height)];
[[UIColor whiteColor] set];
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), from.x, from.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), to.x , to.y);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 10.0f);
CGContextStrokeEllipseInRect(UIGraphicsGetCurrentContext(), CGRectMake(to.x, to.y,10,10));
CGContextSetFillColor(UIGraphicsGetCurrentContext(), CGColorGetComponents([[UIColor blueColor] CGColor]));
CGContextFillPath(UIGraphicsGetCurrentContext());
CGContextStrokePath(UIGraphicsGetCurrentContext());
self.thatsMyImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
This is the method which is being called when panGesture is detected.
-(void)freeFormDrawing:(UIPanGestureRecognizer *)gesture
{
if(gesture.state == UIGestureRecognizerStateChanged)
{
CGPoint p = [gesture locationInView:self.selfieImage];
CGPoint startPoint = lastPoint;
lastPoint = [gesture locationInView:self.selfieImage];
[self drawLineFrom:startPoint endPoint:p];
}
if(gesture.state == UIGestureRecognizerStateEnded)
{
// lastPoint = [gesture locationInView:self.selfieImage];
}
}
Can anybody please tell me how can i do free-form (Doodle) on images with smooth lines and curves? Thanks in advance and Happy Coding!
I had implemented the same what I had was that I was adding a transparent UIImageView above the UIImageView that had my UIImage I wanted to draw on.
UIImageView *drawableView = [[UIImageView alloc]initWithFrame:self.drawImageView.bounds];
drawableView.userInteractionEnabled = YES;
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(drawingViewDidPan:)];
[drawableView addGestureRecognizer:panGesture];
Then when the user panned on the UIImageView I would call this function
- (void)drawingViewDidPan:(UIPanGestureRecognizer*)sender
{
CGPoint currentDraggingPosition = [sender locationInView:drawableView];
if(sender.state == UIGestureRecognizerStateBegan) {
prevDraggingPosition = currentDraggingPosition;
}
if(sender.state != UIGestureRecognizerStateEnded){
[self drawLine:prevDraggingPosition to:currentDraggingPosition];
}
prevDraggingPosition = currentDraggingPosition;
}
Both prevDraggingPosition and currentDraggingPosition are CGPoint.
Then I used the following function to draw line from the prevDraggingPosition to currentDraggingPosition
-(void)drawLine:(CGPoint)from to:(CGPoint)to
{
#autoreleasepool {
CGSize size = drawableView.frame.size;
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[drawableView.image drawAtPoint:CGPointZero];
CGFloat strokeWidth = 4.0;
strokeColor = colorChangeView.backgroundColor;
CGContextSetLineWidth(context, strokeWidth);
CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextMoveToPoint(context, from.x, from.y);
CGContextAddLineToPoint(context, to.x, to.y);
CGContextStrokePath(context);
drawableView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
}
Then finally to get the image with the drawing on it you can build the image by drawing the drawableView image onto your UIImageView that has your image like this.
- (UIImage*)buildImage
{
#autoreleasepool {
UIGraphicsBeginImageContextWithOptions(originalImageSize, NO, self.drawImageView.image.scale);
[self.drawImageView.image drawAtPoint:CGPointZero];
[drawableView.image drawInRect:CGRectMake(0,0, originalImageSize.width, originalImageSize.height)];
UIImage *tmp = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tmp;
}
}
Where originalImageSize is the size of your image.
Hope this helps!

Xcode - UIImage Preset Apple Textures

Is there any way of accessing the preset UIColor Textures, then setting them as a background? Sort of like this:
UIColor *mytexture = [UIColor scrollViewTexturedBackgroundColor];
UIImage *myimage = [UIImage imageWithData:mytexture];
[outletWallpaper setImage:myimage];
Thanks in advance,
Declan
Just set the backgroundColor of the view to the texture colour you require.
outletWallpaper.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
In addition you can load your own images and use them as a UIColour by using the [UIColour -colorWithPatternImage:] API:
UIImage *myImage = [UIImage imageNamed:#"anImageFile"];
UIColour colourPattern = [UIColor colorWithPatternImage: myImage];
outletWallpaper.backgroundColor = colourPattern;
You can create category of UIImage with method like this:
+ (UIImage *)imageWithColor:(UIColor *)color
{
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

Glowing effect in a UILabel using inner shadow

I am trying to achieve this glowing effect for a UILabel as shown below :
I have subclassed UILabel , and created a custom label class that adds an outer shadow.
Edit : Here's the code i have used for outer shadow/glow in my custom Label class :
- (void)drawTextInRect:(CGRect)rect {
UIColor *insideColor;
UIColor *blurColor;
CGContextRef ctx = UIGraphicsGetCurrentContext();
insideColor =[UIColor colorWithRed:255/255.0 green:255/255.0 blue:191/255.0 alpha:1];
blurColor =[UIColor orangeColor];
CGContextSetFillColorWithColor(ctx, insideColor.CGColor);
CGContextSetShadowWithColor(ctx, CGSizeMake(0, 0), self.glowAmount, blurColor.CGColor);
CGContextSetTextDrawingMode(ctx, kCGTextFillStroke);
[self.text drawInRect:self.bounds withFont:self.font lineBreakMode:self.lineBreakMode alignment:self.textAlignment];
}
But this gives me the following result
As you can see this lacks the desired effect because of the missing inner shadow. Can anyone suggest how to achieve this?
Thanks!
I referred the answer by Steven XM for Inner Shadow in UILabel . It was a great help.
Here's what i have done to achieve the result but i want to know if this can be more optimized?
-(void)setInnerGlowWithColor:(UIColor *)shadowColor fillColor:(UIColor *)insideColor inRect:(CGRect)rect
{
UIFont *font = self.font;
// UIFont *font = [UIFont fontWithName:#"HelveticaNeue-Bold" size:17];
CGSize fontSize = [self.text sizeWithFont:font];
/**** Following are the steps to create an inside shadow ****/
// STEP 1 : Create a image mask of your text.
CGImageRef mask = [self createMaskWithSize:rect.size shape:^{
[[UIColor blackColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
[[UIColor whiteColor] setFill];
// custom shape goes here
[self.text drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), 0) withFont:font];
}];
// STEP 2 : Invert that mask.
CGImageRef cutoutRef = CGImageCreateWithMask([self blackSquareOfSize:rect.size].CGImage, mask);
CGImageRelease(mask);
UIImage *cutout = [UIImage imageWithCGImage:cutoutRef scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
CGImageRelease(cutoutRef);
// STEP 3 : Use this inverted mask to draw a shadow around the inside edges of the text.
CGImageRef shadedMask = [self createMaskWithSize:rect.size shape:^{
[[UIColor whiteColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
//****************For inner shadow/glow
NSLog(#"in custom label----> %f",self.glowAmount);
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, 0), self.glowAmount, shadowColor.CGColor);
[cutout drawAtPoint:CGPointZero];
}];
// STEP 4 : Create negative image
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
[shadowColor setFill];
// custom shape goes here
[self.text drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), 0) withFont:font];
UIImage *negative = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// STEP 5 : Create the shadow image
CGImageRef innerShadowRef = CGImageCreateWithMask(negative.CGImage, shadedMask);
CGImageRelease(shadedMask);
UIImage *innerShadow = [UIImage imageWithCGImage:innerShadowRef scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
CGImageRelease(innerShadowRef);
// STEP 6 : Draw actual text
[insideColor setFill];
[self.text drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), 0) withFont:font];
// STEP 7 : Finally apply the shadow image
[innerShadow drawAtPoint:CGPointZero];
}
- (UIImage*)blackSquareOfSize:(CGSize)size {
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
[[UIColor blackColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, size.width, size.height));
UIImage *blackSquare = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return blackSquare;
}
- (CGImageRef)createMaskWithSize:(CGSize)size shape:(void (^)(void))block {
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
block();
CGImageRef shape = [UIGraphicsGetImageFromCurrentImageContext() CGImage];
UIGraphicsEndImageContext();
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(shape),
CGImageGetHeight(shape),
CGImageGetBitsPerComponent(shape),
CGImageGetBitsPerPixel(shape),
CGImageGetBytesPerRow(shape),
CGImageGetDataProvider(shape), NULL, false);
return mask;
}

Retina Support for custom UITabBarController-like highlighting of UIImage?

I'm using BCTabBarController in my app, and I'm trying to customize it so that it uses Core Graphics to highlight the images automatically, so that I don't need four copies of each image. (Retina, Retina-selected, Legacy, Legacy-selected)
User Ephraim has posted a great starting point for this, but it returns legacy sized images. I've played with some of the settings, but I'm not very familiar with Core Graphics, so I'm shooting in the dark.
Ephraim's Code:
- (UIImage *) imageWithBackgroundColor:(UIColor *)bgColor
shadeAlpha1:(CGFloat)alpha1
shadeAlpha2:(CGFloat)alpha2
shadeAlpha3:(CGFloat)alpha3
shadowColor:(UIColor *)shadowColor
shadowOffset:(CGSize)shadowOffset
shadowBlur:(CGFloat)shadowBlur {
UIImage *image = self;
CGColorRef cgColor = [bgColor CGColor];
CGColorRef cgShadowColor = [shadowColor CGColor];
CGFloat components[16] = {1,1,1,alpha1,1,1,1,alpha1,1,1,1,alpha2,1,1,1,alpha3};
CGFloat locations[4] = {0,0.5,0.6,1};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef colorGradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, (size_t)4);
CGRect contextRect;
contextRect.origin.x = 0.0f;
contextRect.origin.y = 0.0f;
contextRect.size = [image size];
//contextRect.size = CGSizeMake([image size].width+5,[image size].height+5);
// Retrieve source image and begin image context
UIImage *itemImage = image;
CGSize itemImageSize = [itemImage size];
CGPoint itemImagePosition;
itemImagePosition.x = ceilf((contextRect.size.width - itemImageSize.width) / 2);
itemImagePosition.y = ceilf((contextRect.size.height - itemImageSize.height) / 2);
UIGraphicsBeginImageContext(contextRect.size);
CGContextRef c = UIGraphicsGetCurrentContext();
// Setup shadow
CGContextSetShadowWithColor(c, shadowOffset, shadowBlur, cgShadowColor);
// Setup transparency layer and clip to mask
CGContextBeginTransparencyLayer(c, NULL);
CGContextScaleCTM(c, 1.0, -1.0);
CGContextClipToMask(c, CGRectMake(itemImagePosition.x, -itemImagePosition.y, itemImageSize.width, -itemImageSize.height), [itemImage CGImage]);
// Fill and end the transparency layer
CGContextSetFillColorWithColor(c, cgColor);
contextRect.size.height = -contextRect.size.height;
CGContextFillRect(c, contextRect);
CGContextDrawLinearGradient(c, colorGradient,CGPointZero,CGPointMake(contextRect.size.width*1.0/4.0,contextRect.size.height),0);
CGContextEndTransparencyLayer(c);
//CGPointMake(contextRect.size.width*3.0/4.0, 0)
// Set selected image and end context
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGColorSpaceRelease(colorSpace);
CGGradientRelease(colorGradient);
return resultImage;
}
To implement this code, I've added a category to UIImage in my project, and then made the following changes to BCTab.h:
- (id)initWithIconImageName:(NSString *)imageName {
if (self = [super init]) {
self.adjustsImageWhenHighlighted = NO;
self.background = [UIImage imageNamed:#"BCTabBarController.bundle/tab-background.png"];
self.rightBorder = [UIImage imageNamed:#"BCTabBarController.bundle/tab-right-border.png"];
self.backgroundColor = [UIColor clearColor];
// NSString *selectedName = [NSString stringWithFormat:#"%#-selected.%#",
// [imageName stringByDeletingPathExtension],
// [imageName pathExtension]];
UIImage *defImage = [UIImage imageNamed:imageName];
[self setImage:[defImage imageWithBackgroundColor:[UIColor lightGrayColor] shadeAlpha1:0.4 shadeAlpha2:0.0 shadeAlpha3:0.6 shadowColor:[UIColor blackColor] shadowOffset:CGSizeMake(0.0, -1.0f) shadowBlur:3.0] forState:UIControlStateNormal];
[self setImage:[defImage imageWithBackgroundColor:[UIColor redColor] shadeAlpha1:0.4 shadeAlpha2:0.0 shadeAlpha3:0.6 shadowColor:[UIColor blackColor] shadowOffset:CGSizeMake(0.0, -1.0f) shadowBlur:3.0] forState:UIControlStateSelected];
}
return self;
}
How can I use Ephraim's code to work correctly with Retina display?
After digging around the internet, a Google search lead me back to StackOverflow. I found this answer to this question which discusses a different method which should be used to set the scale factor of the UIImageGraphicsContext when it is initialized.
UIGraphicsBeginImageContext(contextRect.size); needs to be changed to UIGraphicsBeginImageContextWithOptions(contextRect.size, NO, scale);, where "scale" is the
value of the scale you want to use. I grabbed it from [[UIScreen mainScreen] scale].

How to use a UIImage as a mask over a color in Objective-C

I have a UIImage that is all black with an alpha channel so some parts are grayish and some parts are completely see-through. I want to use that images as a mask over some other color (let's say white to make it easy), so the final product is now a white image with parts of it transparent.
I've been looking around on the Apple documentation site here:
https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_images/dq_images.html#//apple_ref/doc/uid/TP30001066-CH212-CJBHIJEB
But I don't can't really make sense of those examples.
In iOS 7+ you should use UIImageRenderingModeAlwaysTemplate instead. See https://stackoverflow.com/a/26965557/870313
Creating arbitrarily-colored icons from a black-with-alpha master image (iOS).
// Usage: UIImage *buttonImage = [UIImage ipMaskedImageNamed:#"UIButtonBarAction.png" color:[UIColor redColor]];
+ (UIImage *)ipMaskedImageNamed:(NSString *)name color:(UIColor *)color
{
UIImage *image = [UIImage imageNamed:name];
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, image.scale);
CGContextRef c = UIGraphicsGetCurrentContext();
[image drawInRect:rect];
CGContextSetFillColorWithColor(c, [color CGColor]);
CGContextSetBlendMode(c, kCGBlendModeSourceAtop);
CGContextFillRect(c, rect);
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
Credits to Ole Zorn: https://gist.github.com/1102091
Translating Jano's answer into Swift:
func ipMaskedImageNamed(name:String, color:UIColor) -> UIImage {
let image = UIImage(named: name)
let rect:CGRect = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: image!.size.width, height: image!.size.height))
UIGraphicsBeginImageContextWithOptions(rect.size, false, image!.scale)
let c:CGContextRef = UIGraphicsGetCurrentContext()
image?.drawInRect(rect)
CGContextSetFillColorWithColor(c, color.CGColor)
CGContextSetBlendMode(c, kCGBlendModeSourceAtop)
CGContextFillRect(c, rect)
let result:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
Usage:
myButton.setImage(ipMaskedImageNamed("grayScalePNG", color: UIColor.redColor()), forState: .Normal)
Edit: According to this article, you can turn an image into a mask whose opaque areas are represented by the tint color. Within the asset catalog, under the Attributes Inspector of the image, change Render As to Template Image. No code necessary.
Here's a variation in Swift 3, written as an extension to UIImage:
extension UIImage {
func tinted(color:UIColor) -> UIImage? {
let image = self
let rect:CGRect = CGRect(origin: CGPoint(x: 0, y: 0), size: image.size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, image.scale)
let context = UIGraphicsGetCurrentContext()!
image.draw(in: rect)
context.setFillColor(color.cgColor)
context.setBlendMode(.sourceAtop)
context.fill(rect)
if let result:UIImage = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
return result
}
else {
return nil
}
}
}
// Usage
let myImage = #imageLiteral(resourceName: "Check.png") // any image
let blueImage = myImage.tinted(color: .blue)
I converted this for OSX here as a Category and copied below. Note this is for non-ARC projects. For ARC projects, the autorelease can be removed.
- (NSImage *)cdsMaskedWithColor:(NSColor *)color
{
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
NSImage *result = [[NSImage alloc] initWithSize:self.size];
[result lockFocusFlipped:self.isFlipped];
NSGraphicsContext *context = [NSGraphicsContext currentContext];
CGContextRef c = (CGContextRef)[context graphicsPort];
[self drawInRect:NSRectFromCGRect(rect)];
CGContextSetFillColorWithColor(c, [color CGColor]);
CGContextSetBlendMode(c, kCGBlendModeSourceAtop);
CGContextFillRect(c, rect);
[result unlockFocus];
return [result autorelease];
}
+ (NSImage *)cdsMaskedImageNamed:(NSString *)name color:(NSColor *)color
{
NSImage *image = [NSImage imageNamed:name];
return [image cdsMaskedWithColor:color];
}