drawRect drawing 'transparent' text? - uilabel

I am looking to draw a UILabel (preferable through subclassing) as a transparent label, but with solid background. I draw up an quick example (sorry, it's ugly, but it gets the points across :)).
Basically I have a UILabel and I would like the background to be a set colour, and the text should be transparent. I do not want to colour the text with the views background, but instead have it be 100% transparent, since I have a texture in the background that I want to make sure lines up inside and outside of the label.
I've been spending the night browsing SO and searching on Google, but I have found no helpful sources. I don't have much experience with CG drawing, so I would appreciate any links, help, tutorial or sample code (maybe Apple has some I need to have a look at?).
Thanks a bunch!

I've rewritten it as a UILabel subclass using barely any code and posted it on GitHub
The gist of it is you override drawRect but call [super drawRect:rect] to let the UILabel render as normal. Using a white label color lets you easily use the label itself as a mask.
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
// let the superclass draw the label normally
[super drawRect:rect];
CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, CGRectGetHeight(rect)));
// create a mask from the normally rendered text
CGImageRef image = CGBitmapContextCreateImage(context);
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerComponent(image), CGImageGetBitsPerPixel(image), CGImageGetBytesPerRow(image), CGImageGetDataProvider(image), CGImageGetDecode(image), CGImageGetShouldInterpolate(image));
CFRelease(image); image = NULL;
// wipe the slate clean
CGContextClearRect(context, rect);
CGContextSaveGState(context);
CGContextClipToMask(context, rect, mask);
CFRelease(mask); mask = NULL;
[self RS_drawBackgroundInRect:rect];
CGContextRestoreGState(context);
}

Solved using CALayer masks. Creating a standard mask (wallpapered text, for example) is simple. To create the knocked-out text, I had to invert the alpha channel of my mask, which involved rendering a label to a CGImageRef and then doing some pixel-pushing.
Sample application is available here: https://github.com/robinsenior/RSMaskedLabel
Relevant code is here to avoid future link-rot:
#import "RSMaskedLabel.h"
#import <QuartzCore/QuartzCore.h>
#interface UIImage (RSAdditions)
+ (UIImage *) imageWithView:(UIView *)view;
- (UIImage *) invertAlpha;
#end
#interface RSMaskedLabel ()
{
CGImageRef invertedAlphaImage;
}
#property (nonatomic, retain) UILabel *knockoutLabel;
#property (nonatomic, retain) CALayer *textLayer;
- (void) RS_commonInit;
#end
#implementation RSMaskedLabel
#synthesize knockoutLabel, textLayer;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self RS_commonInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self RS_commonInit];
}
return self;
}
+ (Class)layerClass
{
return [CAGradientLayer class];
}
- (void) RS_commonInit
{
[self setBackgroundColor:[UIColor clearColor]];
// create the UILabel for the text
knockoutLabel = [[UILabel alloc] initWithFrame:[self frame]];
[knockoutLabel setText:#"booyah"];
[knockoutLabel setTextAlignment:UITextAlignmentCenter];
[knockoutLabel setFont:[UIFont boldSystemFontOfSize:72.0]];
[knockoutLabel setNumberOfLines:1];
[knockoutLabel setBackgroundColor:[UIColor clearColor]];
[knockoutLabel setTextColor:[UIColor whiteColor]];
// create our filled area (in this case a gradient)
NSArray *colors = [[NSArray arrayWithObjects:
(id)[[UIColor colorWithRed:0.349 green:0.365 blue:0.376 alpha:1.000] CGColor],
(id)[[UIColor colorWithRed:0.455 green:0.490 blue:0.518 alpha:1.000] CGColor],
(id)[[UIColor colorWithRed:0.412 green:0.427 blue:0.439 alpha:1.000] CGColor],
(id)[[UIColor colorWithRed:0.208 green:0.224 blue:0.235 alpha:1.000] CGColor],
nil] retain];
NSArray *gradientLocations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:0.54],
[NSNumber numberWithFloat:0.55],
[NSNumber numberWithFloat:1], nil];
// render our label to a UIImage
// if you remove the call to invertAlpha it will mask the text
invertedAlphaImage = [[[UIImage imageWithView:knockoutLabel] invertAlpha] CGImage];
// create a new CALayer to use as the mask
textLayer = [CALayer layer];
// stick the image in the layer
[textLayer setContents:(id)invertedAlphaImage];
// create a nice gradient layer to use as our fill
CAGradientLayer *gradientLayer = (CAGradientLayer *)[self layer];
[gradientLayer setBackgroundColor:[[UIColor clearColor] CGColor]];
[gradientLayer setColors: colors];
[gradientLayer setLocations:gradientLocations];
[gradientLayer setStartPoint:CGPointMake(0.0, 0.0)];
[gradientLayer setEndPoint:CGPointMake(0.0, 1.0)];
[gradientLayer setCornerRadius:10];
// mask the text layer onto our gradient
[gradientLayer setMask:textLayer];
}
- (void)layoutSubviews
{
// resize the text layer
[textLayer setFrame:[self bounds]];
}
- (void)dealloc
{
CGImageRelease(invertedAlphaImage);
[knockoutLabel release];
[textLayer release];
[super dealloc];
}
#end
#implementation UIImage (RSAdditions)
/*
create a UIImage from a UIView
*/
+ (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
/*
get the image to invert its alpha channel
*/
- (UIImage *)invertAlpha
{
// scale is needed for retina devices
CGFloat scale = [self scale];
CGSize size = self.size;
int width = size.width * scale;
int height = size.height * scale;
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *memoryPool = (unsigned char *)calloc(width*height*4, 1);
CGContextRef context = CGBitmapContextCreate(memoryPool, width, height, 8, width * 4, colourSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colourSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), [self CGImage]);
for(int y = 0; y < height; y++)
{
unsigned char *linePointer = &memoryPool[y * width * 4];
for(int x = 0; x < width; x++)
{
linePointer[3] = 255-linePointer[3];
linePointer += 4;
}
}
// get a CG image from the context, wrap that into a
CGImageRef cgImage = CGBitmapContextCreateImage(context);
UIImage *returnImage = [UIImage imageWithCGImage:cgImage scale:scale orientation:UIImageOrientationUp];
// clean up
CGImageRelease(cgImage);
CGContextRelease(context);
free(memoryPool);
// and return
return returnImage;
}
#end

Here's a technique that's similar to Matt Gallagher's, which will generate an inverted text mask with an image.
Allocate a (mutable) data buffer. Create a bitmap context with an 8-bit alpha channel. Configure settings for text drawing. Fill the whole buffer in copy mode (default colour assumed to have alpha value of 1). Write the text in clear mode (alpha value of 0). Create an image from the bitmap context. Use the bitmap as a mask to make a new image from the source image. Create a new UIImage and clean up.
Every time the textString or sourceImage or size values change, re-generate the final image.
CGSize size = /* assume this exists */;
UIImage *sourceImage = /* assume this exists */;
NSString *textString = /* assume this exists */;
char *text = [textString cStringUsingEncoding:NSMacOSRomanStringEncoding];
NSUInteger len = [textString lengthOfBytesUsingEncoding:cStringUsingEncoding:NSMacOSRomanStringEncoding];
NSMutableData *data = [NSMutableData dataWithLength:size.width*size.height*1];
CGContextRef context = CGBitmapContextCreate([data mutableBytes], size.width, size.height, 8, size.width, NULL, kCGImageAlphaOnly);
CGContextSelectFont(context, "Gill Sans Bold", 64.0f, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextFillRect(context, overlay.bounds);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextShowTextAtPoint(context, 16.0f, 16.0f, text, len);
CGImageRef textImage = CGBitmapContextCreateImage(context);
CGImageRef newImage = CGImageCreateWithMask(sourceImage.CGImage, textImage);
UIImage *finalImage = [UIImage imageWithCGImage:newImage];
CGContextRelease(context);
CFRelease(newImage);
CFRelease(textImage);
Another way to do this involves putting the textImage into a new layer and setting that layer on your view's layer. (Remove the lines that create "newImage" and "finalImage".) Assuming this happens inside your view's code somewhere:
CALayer *maskLayer = [[CALayer alloc] init];
CGPoint position = CGPointZero;
// layout the new layer
position = overlay.layer.position;
position.y *= 0.5f;
maskLayer.bounds = overlay.layer.bounds;
maskLayer.position = position;
maskLayer.contents = (__bridge id)textImage;
self.layer.mask = maskLayer;
There are more alternatives, some might be better (subclass UIImage and draw the text directly in clear mode after the superclass has done its drawing?).

Swift 5 solution (Xcode: 12.5):
class MaskedLabel: UILabel {
var maskColor : UIColor?
override init(frame: CGRect) {
super.init(frame: frame)
customInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
customInit()
}
func customInit() {
maskColor = self.backgroundColor
self.textColor = UIColor.white
backgroundColor = UIColor.clear
self.isOpaque = false
}
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()!
super.draw(rect)
context.concatenate(__CGAffineTransformMake(1, 0, 0, -1, 0, rect.height))
let image: CGImage = context.makeImage()!
let mask: CGImage = CGImage(maskWidth: image.width, height: image.height, bitsPerComponent: image.bitsPerComponent, bitsPerPixel: image.bitsPerPixel, bytesPerRow: image.bytesPerRow, provider: image.dataProvider!, decode: image.decode, shouldInterpolate: image.shouldInterpolate)!
context.clear(rect)
context.saveGState()
context.clip(to: rect, mask: mask)
if (self.layer.cornerRadius != 0.0) {
context.addPath(CGPath(roundedRect: rect, cornerWidth: self.layer.cornerRadius, cornerHeight: self.layer.cornerRadius, transform: nil))
context.clip()
}
drawBackgroundInRect(rect: rect)
context.restoreGState()
}
func drawBackgroundInRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
if let _ = maskColor {
maskColor!.set()
}
context!.fill(rect)
}
}

Related

Exporting a QR to vector NSImage & CGPDFContextCreate - macOS ObjC

I've been trying to figure out how to export my QR codes on my little project as a vector.
I start from qrImageForString function which generates the image initially, and does so as a bitmap.
The main two lines in question are:
CGContextRef ctx = CGBitmapContextCreate(0, size, size, 8, size * 4, colorSpace,kCGImageAlphaPremultipliedLast);
CGImageRef qrCGImage = CGBitmapContextCreateImage(ctx);
For the first one I think this may be a replacement:
NSRect rect = CGRectMake(0.0, 0.0, 750.0, 750.0);
CGContextRef ctx = CGPDFContextCreate(dataConsumer, &rect, NULL);
However I'm not sure how to replace the second line to make it a vector.
Full function is:
+ (NSImage *)qrImageForString:(NSString *)string imageSize:(CGFloat)size {
if (![string length]) {
return nil;
}
int str1;
QRcode *code = QRcode_encodeString([string UTF8String], 0, str1, QR_MODE_8, 1);
// create context
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(0, size, size, 8, size * 4, colorSpace, kCGImageAlphaPremultipliedLast);
NSRect rect = CGRectMake(0.0, 0.0, 750.0, 750.0);
NSMutableData* outputData = [[NSMutableData alloc] init];
CGDataConsumerRef dataConsumer = CGDataConsumerCreateWithCFData((CFMutableDataRef)outputData);
CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(0, -size);
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(1, -1);
CGContextConcatCTM(ctx, CGAffineTransformConcat(translateTransform, scaleTransform));
// draw QR on this context
[MainClass drawQRCode:code context:ctx size:size];
// get image
CGImageRef qrCGImage = CGBitmapContextCreateImage(ctx);
NSImage *qrImage = [[NSImage alloc] initWithCGImage:qrCGImage size:NSZeroSize];
// some releases
CGContextRelease(ctx);
CGImageRelease(qrCGImage);
CGColorSpaceRelease(colorSpace);
QRcode_free(code);
return qrImage;
}
Copied from How to make dataWithEPSInsideRect vector rather than bitmap in vector format?: Create an offscreen view, set up the draw method of the view to include your graphic and then use NSView's dataWithEPSInsideRect: method.
I made mdDrawQRCode:context:size:fillColor: an instance method of the view but it can be a method of different class. The view:
#interface MyView : NSView
#property (strong) NSString *qrString;
#end
#implementation MyView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
if (!self.qrString)
return;
CGContextRef cgContext = [[NSGraphicsContext currentContext] CGContext];
CGContextSaveGState(cgContext);
QRcode *code = QRcode_encodeString([self.qrString UTF8String], 0, QR_ECLEVEL_L, QR_MODE_8, 1);
[self mdDrawQRCode:code context:cgContext size:self.bounds.size.width fillColor:[NSColor blackColor]];
QRcode_free(code);
CGContextRestoreGState(cgContext);
}
#end
Use the view like:
MyView *myView = [[MyView alloc] initWithFrame:NSMakeRect(0, 0, 100.0, 100.0)];
myView.qrString = #"Hello World!";
NSData *data = [myView dataWithEPSInsideRect:myView.bounds];

How to display animated GIF in Objective C on top of the layered View?

I am trying to draw animated gif on my screen in mac OSX app .
I used this code to insert the gif: I can see the Gif as 1 picture it doesn't animates
only static picture :( what should I add to make it animated ?
#import <Cocoa/Cocoa.h>
#import <Quartz/Quartz.h>//for drawing circle
#import "sharedPrefferences.h"
#interface GenericFanSubView : NSView
{
NSColor * _backgroundColor;
NSImageView* imageView;
}
- (void)setBackgroundColor :(NSColor*)color;
- (void)insertGif1;
- (void)insertGif2;
- (void)insertGif3;
#end
#import "GenericFanSubView.h"
#define PI 3.14285714285714
#implementation GenericFanSubView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
imageView = [[NSImageView alloc]initWithFrame:CGRectMake(0, 0,self.frame.size.width,self.frame.size.height)];
[imageView setAnimates: YES];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
[self drawCircleInRect];
_backgroundColor = [NSColor whiteColor];
[self insertGif1];
}
-(void)drawCircleInRect
{
//draw colored circle here
CGContextRef context = [[NSGraphicsContext // 1
currentContext] graphicsPort];
// ********** Your drawing code here ********** // 2
CGContextSetFillColorWithColor(context,[self NSColorToCGColor:(_backgroundColor)]);
float radius1 = self.frame.size.height/2;
float startAngle = 0;
float endAngle = endAngle = PI*2;
CGPoint position = CGPointMake(self.frame.size.height/2,self.frame.size.height/2);//center of the view
CGContextBeginPath(context);
CGContextAddArc(context, position.x, position.y, radius1, startAngle, endAngle, 1);
CGContextDrawPath(context, kCGPathFill); // Or kCGPathFill
}
- (void)setBackgroundColor :(NSColor*)color
{
_backgroundColor = color;
[self setNeedsDisplay:YES];
}
- (CGColorRef)NSColorToCGColor:(NSColor *)color
{
NSInteger numberOfComponents = [color numberOfComponents];
CGFloat components[numberOfComponents];
CGColorSpaceRef colorSpace = [[color colorSpace] CGColorSpace];
[color getComponents:(CGFloat *)&components];
CGColorRef cgColor = CGColorCreate(colorSpace, components);
return cgColor;
}
//curentlly calling only this 1
- (void)insertGif1
{
[imageView removeFromSuperview];
[imageView setImageScaling:NSImageScaleNone];
[imageView setAnimates: YES];
imageView.image = [NSImage imageNamed:#"FanBlades11.gif"];
[self addSubview:imageView];
}
#end
Edit: I discovered the source of the problem:
I was adding my class (that represents gif inside the circle) on top of RMBlurredView
and the animations doesn't work when I adding it as subview ,However it works on all the other views I added.
Any ideas what could be the reason inside the RMBlurredView to stop my NSImageView from animating ?
Edit:
I think [self setWantsLayer:YES]; is the reason I am not getting animations
how can I still get the animation with this feature enabled?
Edit:
Here is a simple sample with my problem
http://snk.to/f-cdk3wmfn
my gif:This is my gif it is invisible on white background color
"You must disable the autoscaling feature of the NSImageView for the
animation playback to function. After you've done that, no extra
programming required. It works like a charm!"
--http://www.cocoabuilder.com/archive/cocoa/108530-nsimageview-and-animated-gifs.html
imageView.imageScaling = NSImageScaleNone;
imageView.animates = YES;
needed for layer backed views:
if the image view is in a layer backed view or is layer backed itself:
imageView.canDrawSubviewsIntoLayer = YES;
working example using the question's own gif:
NSImageView *view = [[NSImageView alloc] initWithFrame:CGRectMake(10, 10, 50, 50)];
view.imageScaling = NSImageScaleNone;
view.animates = YES;
view.image = [NSImage imageNamed:#"FanBlades2_42x42.gif"];
view.canDrawSubviewsIntoLayer = YES;
NSView *layerview = [[NSView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
layerview.wantsLayer = YES;
[layerview addSubview:view];
[self.window.contentView addSubview:layerview];

ios: change the colors of a UIImage

I am working on an Iphone application.
I have png pictures that represents symbols. symbols are all black with a transparent background.
Is there a way I can turn the black color into another one? What I'm looking for is something that can help me choose the color I want and when using the symbols (in a UIImage) I can make them appear the color of my choice.
I have searched around and found a framework called OpenCV that can manipulate images but I cant find out how to recolor the picture.
Any help and suggestion is greatly appreciated.
Thank you
The easiest and shortest:
Way to do that in case when you dealing with UIImageView:
Obj-C:
theImageView.image = [theImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[theImageView setTintColor:[UIColor redColor]];
Swift:
let theImageView = UIImageView(image: UIImage(named:"foo")!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate))
theImageView.tintColor = UIColor.redColor()
hi you want to change remove/ one specific color means use the below category....
.h file:
#import <UIKit/UIKit.h>
#interface UIImage (Color)
+ (UIImage*)setBackgroundImageByColor:(UIColor *)backgroundColor withFrame:(CGRect )rect;
+ (UIImage*) replaceColor:(UIColor*)color inImage:(UIImage*)image withTolerance:(float)tolerance;
+(UIImage *)changeWhiteColorTransparent: (UIImage *)image;
+(UIImage *)changeColorTo:(NSMutableArray*) array Transparent: (UIImage *)image;
//resizing Stuff...
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize;
#end
.m file
#import <QuartzCore/QuartzCore.h>
#import "UIImage+Color.h"
#implementation UIImage (Color)
+ (UIImage* )setBackgroundImageByColor:(UIColor *)backgroundColor withFrame:(CGRect )rect{
// tcv - temporary colored view
UIView *tcv = [[UIView alloc] initWithFrame:rect];
[tcv setBackgroundColor:backgroundColor];
// set up a graphics context of button's size
CGSize gcSize = tcv.frame.size;
UIGraphicsBeginImageContext(gcSize);
// add tcv's layer to context
[tcv.layer renderInContext:UIGraphicsGetCurrentContext()];
// create background image now
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
// [tcv release];
}
+ (UIImage*) replaceColor:(UIColor*)color inImage:(UIImage*)image withTolerance:(float)tolerance {
CGImageRef imageRef = [image CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
NSUInteger bitmapByteCount = bytesPerRow * height;
unsigned char *rawData = (unsigned char*) calloc(bitmapByteCount, sizeof(unsigned char));
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGColorRef cgColor = [color CGColor];
const CGFloat *components = CGColorGetComponents(cgColor);
float r = components[0];
float g = components[1];
float b = components[2];
//float a = components[3]; // not needed
r = r * 255.0;
g = g * 255.0;
b = b * 255.0;
const float redRange[2] = {
MAX(r - (tolerance / 2.0), 0.0),
MIN(r + (tolerance / 2.0), 255.0)
};
const float greenRange[2] = {
MAX(g - (tolerance / 2.0), 0.0),
MIN(g + (tolerance / 2.0), 255.0)
};
const float blueRange[2] = {
MAX(b - (tolerance / 2.0), 0.0),
MIN(b + (tolerance / 2.0), 255.0)
};
int byteIndex = 0;
while (byteIndex < bitmapByteCount) {
unsigned char red = rawData[byteIndex];
unsigned char green = rawData[byteIndex + 1];
unsigned char blue = rawData[byteIndex + 2];
if (((red >= redRange[0]) && (red <= redRange[1])) &&
((green >= greenRange[0]) && (green <= greenRange[1])) &&
((blue >= blueRange[0]) && (blue <= blueRange[1]))) {
// make the pixel transparent
//
rawData[byteIndex] = 0;
rawData[byteIndex + 1] = 0;
rawData[byteIndex + 2] = 0;
rawData[byteIndex + 3] = 0;
}
byteIndex += 4;
}
UIImage *result = [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)];
CGContextRelease(context);
free(rawData);
return result;
}
+(UIImage *)changeWhiteColorTransparent: (UIImage *)image
{
CGImageRef rawImageRef=image.CGImage;
const float colorMasking[6] = {222, 255, 222, 255, 222, 255};
UIGraphicsBeginImageContext(image.size);
CGImageRef maskedImageRef=CGImageCreateWithMaskingColors(rawImageRef, colorMasking);
{
//if in iphone
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0.0, image.size.height);
CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0);
}
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, image.size.width, image.size.height), maskedImageRef);
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
CGImageRelease(maskedImageRef);
UIGraphicsEndImageContext();
return result;
}
+(UIImage *)changeColorTo:(NSMutableArray*) array Transparent: (UIImage *)image
{
CGImageRef rawImageRef=image.CGImage;
// const float colorMasking[6] = {222, 255, 222, 255, 222, 255};
const float colorMasking[6] = {[[array objectAtIndex:0] floatValue], [[array objectAtIndex:1] floatValue], [[array objectAtIndex:2] floatValue], [[array objectAtIndex:3] floatValue], [[array objectAtIndex:4] floatValue], [[array objectAtIndex:5] floatValue]};
UIGraphicsBeginImageContext(image.size);
CGImageRef maskedImageRef=CGImageCreateWithMaskingColors(rawImageRef, colorMasking);
{
//if in iphone
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0.0, image.size.height);
CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0);
}
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, image.size.width, image.size.height), maskedImageRef);
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
CGImageRelease(maskedImageRef);
UIGraphicsEndImageContext();
return result;
}
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
//UIGraphicsBeginImageContext(newSize);
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
#end
i changed the removed white color to transparent by this code....
the call will be...
self.rawImage.image=[UIImage changeWhiteColorTransparent:originalStateImage];
i hope this idea will help you....
hi use this category file to change the image entire color....
.h file:
#import <UIKit/UIKit.h>
#interface UIImage (AddtionalFunctionalities)
//TintColor...
- (UIImage *)imageWithTint:(UIColor *)tintColor;
//scale and resize...
-(UIImage*)scaleToSize:(CGSize)size;
#end
.m file:
#import "UIImage+AddtionalFunctionalities.h"
#implementation UIImage (AddtionalFunctionalities)
- (UIImage *)imageWithTint:(UIColor *)tintColor
{
// Begin drawing
CGRect aRect = CGRectMake(0.f, 0.f, self.size.width, self.size.height);
CGImageRef alphaMask;
//
// Compute mask flipping image
//
{
UIGraphicsBeginImageContext(aRect.size);
CGContextRef c = UIGraphicsGetCurrentContext();
// draw image
CGContextTranslateCTM(c, 0, aRect.size.height);
CGContextScaleCTM(c, 1.0, -1.0);
[self drawInRect: aRect];
alphaMask = CGBitmapContextCreateImage(c);
UIGraphicsEndImageContext();
}
//
UIGraphicsBeginImageContext(aRect.size);
// Get the graphic context
CGContextRef c = UIGraphicsGetCurrentContext();
// Draw the image
[self drawInRect:aRect];
// Mask
CGContextClipToMask(c, aRect, alphaMask);
// Set the fill color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextSetFillColorSpace(c, colorSpace);
// Set the fill color
CGContextSetFillColorWithColor(c, tintColor.CGColor);
UIRectFillUsingBlendMode(aRect, kCGBlendModeNormal);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Release memory
CGColorSpaceRelease(colorSpace);
CGImageRelease(alphaMask);
return img;
}
-(UIImage*)scaleToSize:(CGSize)size
{
// Create a bitmap graphics context
// This will also set it as the current context
UIGraphicsBeginImageContext(size);
// Draw the scaled image in the current context
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
// Create a new image from current context
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
// Pop the current context from the stack
UIGraphicsEndImageContext();
// Return our new scaled image
return scaledImage;
}
#end
the method call will be :
self.outputImage.image=[sourceImage imageWithTint:[UIColor redColor]];
if u want to use the image means use this:
self.outputImage.image=[sourceImage imageWithTint:[UIColor colorWithPatternImage:[UIImage imageNamed: #"red.jpg"]]];
i hope this will help you...
Set the Image rendering mode then for color purpose use tintcolor property.
yourImageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[yourImageView setTintColor:[UIColor redColor]];
You can create UIView which store UIImageView and change UIview background Color.
UIView *viewForImage = ....;
UIImageView *imageView = .....;
...
[viewForImage addSubview: imageView];
[self.view addSubview:viewForImage];
...
and then use for example
[viewForImage setBackgroundColor: [UIColor redColor]];
For playing with images using objective-c, mainly you have to use Quartz 2D/CoreGraphics framework. I suppose it will be the easiest way for you to accomplish your task.
The link to Quartz 2D guideline is here
, the guideline is pretty comprehensive. Just have a look somewhere in filling the bitmap with color.
Also I've some records about the heavy image processing in my blog, you can have a look, it might be helpfull.
http://levonp.blogspot.de/2012/05/quartz-getting-diff-images-of-two.html
Of course you can do it also using more advanced stuffs like OpenGL, OpenCV, etc.
A very simple solution to change the color of the image. Idea: extensions for UIImage and UIImageView.
Swift 3, Xcode 8.1.
https://stackoverflow.com/a/40884483/4488252
Code sample from my answer (link above):
// use extension UIImage
newImageView = createNewImageView(x: 100)
newImageView.image = UIImage(named: "Apple")?.imageWithColor(newColor: UIColor.blue)
// use extension UIImageView
newImageView = createNewImageView(x: 160)
newImageView.image = UIImage(named: "Apple")
newImageView.imageColor = UIColor.green
For iOS 13.0
Obj-C:
self.yourImageView.image = [self.yourImageView.image imageWithTintColor:[UIColor.redColor]];
Swift:
yourImageView.image = yourImageView.image.withTintColor(UIColor.red);
If the image comes from the Asset Catalog, you can change the rendering in the Attribute Inspector

loading screen not centered on second launch

I have a UIView which is my loading view. All it does is display the circular loading circle(lol to much "circle" for one sentence).
It works fine the first time but after that the circle is not centered. It moves to the left and down some. How can I get it to always be centered, take in mind I have limited the app to only display in the landscape modes (landscape left, landscape right) in all views so the issue is not coming from the device being rotated.
call to load the view:
loadingViewController = [LoadingViewController loadSpinnerIntoView:self.view];
LoadingViewController.h:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "CrestronClient.h"
#interface LoadingViewController : UIView
{
CrestronClient *cClient;
}
+(LoadingViewController *)loadSpinnerIntoView:(UIView *)superView;
-(void)removeLoadingView;
- (UIImage *)addBackground;
#end
LoadingView.m:
#import "LoadingViewController.h"
#import "RootViewController.h"
#implementation LoadingViewController
CGRect priorFrameSettings;
UIView *parentView;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||interfaceOrientation == UIInterfaceOrientationLandscapeRight ) {
return YES;
}else{
return NO;
}
}
-(void)removeLoadingView
{
// [parentView setFrame:priorFrameSettings];
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionFade];
[[[self superview] layer] addAnimation:animation forKey:#"layerAnimation"];
[self removeFromSuperview];
}
+(LoadingViewController *)loadSpinnerIntoView:(UIView *)superView
{
priorFrameSettings = superView.frame;
parentView = superView;
// [superView setFrame:CGRectMake(0, 0, 1024, 1024)];
// Create a new view with the same frame size as the superView
LoadingViewController *loadingViewController = [[LoadingViewController alloc] initWithFrame:superView.frame];
loadingViewController.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
// If something's gone wrong, abort!
if(!loadingViewController){ return nil; }
[superView addSubview:loadingViewController];
if(!loadingViewController){ return nil; }
// This is the new stuff here ;)
UIActivityIndicatorView *indicator =
[[[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge] autorelease];
// Set the resizing mask so it's not stretched
UIImageView *background = [[UIImageView alloc] initWithImage:[loadingViewController addBackground]];
// Make a little bit of the superView show through
background.alpha = 0.7;
[loadingViewController addSubview:background];
indicator.autoresizingMask =
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleBottomMargin |
UIViewAutoresizingFlexibleLeftMargin;
// Place it in the middle of the view
indicator.center = superView.center;
// Add it into the spinnerView
[loadingViewController addSubview:indicator];
// Start it spinning! Don't miss this step
[indicator startAnimating];
// Create a new animation
CATransition *animation = [CATransition animation];
// Set the type to a nice wee fade
[animation setType:kCATransitionFade];
// Add it to the superView
[[superView layer] addAnimation:animation forKey:#"layerAnimation"];
return loadingViewController;
}
- (UIImage *)addBackground{
cClient = [CrestronClient sharedManager];
if (cClient.isConnected == FALSE) {
[cClient connect];
}
// Create an image context (think of this as a canvas for our masterpiece) the same size as the view
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 1);
// Our gradient only has two locations - start and finish. More complex gradients might have more colours
size_t num_locations = 2;
// The location of the colors is at the start and end
CGFloat locations[2] = { 0.0, 1.0 };
// These are the colors! That's two RBGA values
CGFloat components[8] = {
0.4,0.4,0.4, 0.8,
0.1,0.1,0.1, 0.5 };
// Create a color space
CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
// Create a gradient with the values we've set up
CGGradientRef myGradient = CGGradientCreateWithColorComponents (myColorspace, components, locations, num_locations);
// Set the radius to a nice size, 80% of the width. You can adjust this
float myRadius = (self.bounds.size.width*.8)/2;
// Now we draw the gradient into the context. Think painting onto the canvas
CGContextDrawRadialGradient (UIGraphicsGetCurrentContext(), myGradient, self.center, 0, self.center, myRadius, kCGGradientDrawsAfterEndLocation);
// Rip the 'canvas' into a UIImage object
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// And release memory
CGColorSpaceRelease(myColorspace);
CGGradientRelease(myGradient);
UIGraphicsEndImageContext();
// … obvious.
return image;
}
- (void)dealloc {
[super dealloc];
}
#end
Make sure the loading view is set to its parents frame and has the proper autoresizingMask set. This would likely by UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight.
fixed the background by adding
[background setFrame:CGRectMake(0, 0, 1024, 768 )];
and fixed the centering of the circle with:
indicator.center = background.center;

Display NSImage on a CALayer

I've been trying to display a NSImage on a CALayer. Then I realised I need to convert it to a CGImage apparently, then display it...
I have this code which doesn't seem to be working
CALayer *layer = [CALayer layer];
NSImage *finderIcon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFinderIcon)];
[finderIcon setSize:(NSSize){ 128.0f, 128.0f }];
CGImageSourceRef source;
source = CGImageSourceCreateWithData((CFDataRef)finderIcon, NULL);
CGImageRef finalIcon = CGImageSourceCreateImageAtIndex(source, 0, NULL);
layer.bounds = CGRectMake(128.0f, 128.0f, 4, 4);
layer.position = CGPointMake(128.0f, 128.0f);
layer.contents = finalIcon;
// Insert the layer into the root layer
[mainLayer addSublayer:layer];
Why? How can I get this to work?
From the comments: Actually, if you're on 10.6, you can also just set the CALayer's contents to an NSImage rather than a CGImageRef...
If you're on OS X 10.6 or later, take a look at NSImage's CGImageForProposedRect:context:hints: method.
If you're not, I've got this in a category on NSImage:
-(CGImageRef)CGImage
{
CGContextRef bitmapCtx = CGBitmapContextCreate(NULL/*data - pass NULL to let CG allocate the memory*/,
[self size].width,
[self size].height,
8 /*bitsPerComponent*/,
0 /*bytesPerRow - CG will calculate it for you if it's allocating the data. This might get padded out a bit for better alignment*/,
[[NSColorSpace genericRGBColorSpace] CGColorSpace],
kCGBitmapByteOrder32Host|kCGImageAlphaPremultipliedFirst);
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:bitmapCtx flipped:NO]];
[self drawInRect:NSMakeRect(0,0, [self size].width, [self size].height) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
[NSGraphicsContext restoreGraphicsState];
CGImageRef cgImage = CGBitmapContextCreateImage(bitmapCtx);
CGContextRelease(bitmapCtx);
return (CGImageRef)[(id)cgImage autorelease];
}
I think I wrote this myself. But it's entirely possible that I ripped it off from somewhere else like Stack Overflow. It's an older personal project and I don't really remember.
Here's some code which may help you - I sure hope the formatting of this does not get all messed up like it appears is going to happen - all I can offer is that this works for me.
// -------------------------------------------------------------------------------------
- (void)awakeFromNib
{
// setup our main window 'contentWindow' to use layers
[[contentWindow contentView] setWantsLayer:YES]; // NSWindow*
// create a root layer to contain all of our layers
CALayer *root = [[contentWindow contentView] layer];
// use constraint layout to allow sublayers to center themselves
root.layoutManager = [CAConstraintLayoutManager layoutManager];
// create a new layer which will contain ALL our sublayers
// -------------------------------------------------------
mContainer = [CALayer layer];
mContainer.bounds = root.bounds;
mContainer.frame = root.frame;
mContainer.position = CGPointMake(root.bounds.size.width * 0.5,
root.bounds.size.height * 0.5);
// insert layer on the bottom of the stack so it is behind the controls
[root insertSublayer:mContainer atIndex:0];
// make it resize when its superlayer does
root.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
// make it resize when its superlayer does
mContainer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
}
// -------------------------------------------------------------------------------------
- (void) loadMyImage:(NSString*) path
n:(NSInteger) num
x:(NSInteger) xpos
y:(NSInteger) ypos
h:(NSInteger) hgt
w:(NSInteger) wid
b:(NSString*) blendstr
{
#ifdef __DEBUG_LOGGING__
NSLog(#"loadMyImage - ENTER [%#] num[%d] x[%d] y[%d] h[%d] w[%d] b[%#]",
path, num, xpos, ypos, hgt, wid, blendstr);
#endif
NSInteger xoffset = ((wid / 2) + xpos); // use CORNER versus CENTER for location
NSInteger yoffset = ((hgt / 2) + ypos);
CIFilter* filter = nil;
CGRect cgrect = CGRectMake((CGFloat) xoffset, (CGFloat) yoffset,
(CGFloat) wid, (CGFloat) hgt);
if(nil != blendstr) // would be equivalent to #"CIMultiplyBlendMode" or similar
{
filter = [CIFilter filterWithName:blendstr];
}
// read image file via supplied path
NSImage* theimage = [[NSImage alloc] initWithContentsOfFile:path];
if(nil != theimage)
{
[self setMyImageLayer:[CALayer layer]]; // create layer
myImageLayer.frame = cgrect; // locate & size image
myImageLayer.compositingFilter = filter; // nil is OK if no filter
[myImageLayer setContents:(id) theimage]; // deposit image into layer
// add new layer into our main layer [see awakeFromNib above]
[mContainer insertSublayer:myImageLayer atIndex:0];
[theimage release];
}
else
{
NSLog(#"ERROR loadMyImage - no such image [%#]", path);
}
}
+ (CGImageRef) getCachedImage:(NSString *) imageName
{
NSGraphicsContext *context = [[NSGraphicsContext currentContext] graphicsPort];
NSImage *img = [NSImage imageNamed:imageName];
NSRect rect = NSMakeRect(0, 0, [img size].width, [img size].height);
return [img CGImageForProposedRect:&rect context:context hints:NULL];
}
+ (CGImageRef) getImage:(NSString *) imageName withExtension:(NSString *) extension
{
NSGraphicsContext *context = [[NSGraphicsContext currentContext] graphicsPort];
NSString* imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:extension];
NSImage* img = [[NSImage alloc] initWithContentsOfFile:imagePath];
NSRect rect = NSMakeRect(0, 0, [img size].width, [img size].height);
CGImageRef imgRef = [img CGImageForProposedRect:&rect context:context hints:NULL];
[img release];
return imgRef;
}
then you can set it:
yourLayer.contents = (id)[self getCachedImage:#"myImage.png"];
or
yourLayer.contents = (id)[self getImage:#"myImage" withExtension:#"png"];