How to make a fading effect with Core Graphics? - iphone

I want to make an effect like what you see on the right side of the first cell here:
I'm guessing it's some sort of overlay view with a gradient, but I just can't figure out how it's configured with transparency. I tried making my own overlay view with a gradient and set the alpha of the colors down, but it just shows up as a gray - white gradient.
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor *gradBegin = [UIColor colorWithWhite:1.0 alpha:0.8];
UIColor *gradEnd = [UIColor colorWithWhite:1.0 alpha:0.6];
NSArray* gradientColors = [NSArray arrayWithObjects:
(id)gradBegin.CGColor,
(id)gradEnd.CGColor, nil];
CGFloat gradientLocations[] = {0, 1};
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)gradientColors, gradientLocations);
CGContextDrawLinearGradient(context, gradient, CGPointMake(rect.origin.x, rect.origin.y + rect.size.height/2.0),
CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height/2.0), 0);
What exactly is going on in this screenshot, and how can I replicate it?

I wrote a simple UIView class that will draw itself faded. It is a basic UIView with the drawRect overrided:
- (void)drawRect:(CGRect)rect
{
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor* gradBegin = [UIColor colorWithWhite:1 alpha:0];
UIColor* gradEnd = [UIColor colorWithWhite:1 alpha:1];
NSArray* gradColours = [NSArray arrayWithObjects:
(id)gradBegin.CGColor,
(id)gradBegin.CGColor,
(id)gradEnd.CGColor,
(id)gradEnd.CGColor, nil];
CGFloat gradLocs[] = { 0, 0.5, 0.9, 1 };
CGGradientRef gradient = CGGradientCreateWithColors(colourSpace, (__bridge CFArrayRef)gradColours, gradLocs);
CGContextDrawLinearGradient(context, gradient, CGPointMake(0, 0), CGPointMake(self.frame.size.width, 0), 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(colourSpace);
}
This works if overlayed over your view and the view's background is white.

While this certainly can be done with Core Graphics, it's infinitely easier to add a transparent PNG to your project with the appropriate width and 1 pixel height and then position a UIImageView in your table cell over the content to create this effect.

The gradient drawing code in your question looks ok, although I haven't tested it. I suggest setting gradBegin.alpha = 0 and gradEnd.alpha = 1. Also, the last line could be simplified:
CGContextDrawLinearGradient(context, gradient, rect.origin,
CGPointMake(CGRectGetMaxX(rect), rect.origin.y, 0);
And don't forget to CGGradientRelease(gradient).
Anyway, other than that, you need to set overlayView.opaque = NO.

Related

UILabel text color based on custom linear gradient

So I want to set the text color for a UILabel based on a gradient made in photoshop. I have the rgb values for the gradient, {211,119,95} and {199,86,56}. Is this possible? How can I do it?
Another way if you want to target iOS 6+, with a category to UIColor
You create a UIColor from a gradient:
+ (UIColor*) gradientFromColor:(UIColor*)c1 toColor:(UIColor*)c2 withHeight:(int)height
{
CGSize size = CGSizeMake(1, height);
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
NSArray* colors = [NSArray arrayWithObjects:(id)c1.CGColor, (id)c2.CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorspace, (CFArrayRef)colors, NULL);
CGContextDrawLinearGradient(context, gradient, CGPointMake(0, 0), CGPointMake(0, size.height), 0);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGGradientRelease(gradient);
CGColorSpaceRelease(colorspace);
UIGraphicsEndImageContext();
return [UIColor colorWithPatternImage:image];
}
Then with attrString as your NSMutableAttributeString:
[attrString addAttribute:NSForegroundColorAttributeName value:[UIColor gradientFromColor:[UIColor greenColor] toColor:[UIColor redColor] withHeight:labelView.height] range:defaultRange];
labelView.attributedString = attrString;
or simply set the textColor if you don't also need strokes or other styling effect
labelView.textColor = [UIColor gradientFromColor:[UIColor greenColor] toColor:[UIColor redColor] withHeight:labelView.height];
And voila, it works better with UILabel over one line, otherwise you have to calculate your line height from your font (UIFont.leading) and pass it to the method, the background will repeat vertically.
You might want to use one of these customizable labels:
FXLabel
LEffectLabel
THLabel

Image sharpness goes away with drawing gradient effect on it

I have an image (with X & 2X variants) which look sharp and clean on the retina device. But I wanted a gradient effect on it. Once I draw the gradients on it, image looses the sharpness, I guess because of gradients are not put on exact image boundaries. Any suggestion how to fix this. I am attaching the images (With and without gradient effect) as well as my gradient code:
- (UIImage *)tintedWithLinearGradientColors:(NSArray *)colorsArray forImageNamed:(NSString *)iImageName {
// Load image
UIImage *myIconImage = [UIImage imageNamed:iImageName];
// Create gradient
UIColor *colorOne = [colorsArray objectAtIndex:1]; // top color
UIColor *colorTwo = [colorsArray objectAtIndex:0]; // bottom color
NSArray *colors = [NSArray arrayWithObjects:(id)colorOne.CGColor, (id)colorTwo.CGColor, nil];
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(space, (__bridge CFArrayRef)colors, NULL);
UIGraphicsBeginImageContext(myIconImage.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, myIconImage.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGRect rect = CGRectMake(0, 0, myIconImage.size.width, myIconImage.size.height);
// image drawing code
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);
// draw tint color, preserving alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeMultiply);
CGContextClipToMask(context, rect, myIconImage.CGImage);
CGContextDrawLinearGradient(context, gradient, CGPointMake(0,0), CGPointMake(0, myIconImage.size.height), 0);
UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return coloredImage;
}
You need to add this gradient by code? If don't, add the gradient on photoshop or other software and then add the image to your project already with the gradient.
Replacing the line
UIGraphicsBeginImageContext(myIconImage.size);
with
UIGraphicsBeginImageContextWithOptions(myIconImage.size, NO, myIconImage.scale);
worked for me.

Two-color background for UILabel

I got a requirements to display a UILabel with background split between two colors, like in this image:
(colors here are black at the bottom and 50% gray at the top - but this is not important). I tried setting the label's background colour to 50% grey in the interface builder and then do this in the code:
CALayer *sl1 = [[[CALayer alloc] init] autorelease];
sl1.frame = CGRectMake(0, lbl.frame.size.height / 2, lbl.frame.size.width, score1.frame.size.height/2);
sl1.backgroundColor = [[UIColor blackColor] CGColor];
[lbl.layer insertSublayer:sl1 atIndex:0];
Unfortunately, this resulted in the black part being drawn over the text, so the label looks like this:
which is, needless to say, is not something I need. So how can I get this background without turning to custom images? The issue is I need to have UILabel's like this in several places, different sizes - so I would need to create multiple versions of the background image.
Any ideas? Thanks.
this works:
UILabel* myLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 50)];
myLabel.text = #"Ciao";
myLabel.textColor = [UIColor greenColor];
UIGraphicsBeginImageContext(CGSizeMake(100, 50));
CGContextRef context = UIGraphicsGetCurrentContext();
// drawing with a gray fill color
CGContextSetRGBFillColor(context, 0.4, 0.4, 0.4, 1.0);
// Add Filled Rectangle,
CGContextFillRect(context, CGRectMake(0.0, 0.0, 100, 50));
// drawing with a black fill color
CGContextSetRGBFillColor(context, 0., 0., 0., .9);
// Add Filled Rectangle,
CGContextFillRect(context, CGRectMake(0.0, 25, 100, 25));
UIImage* resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
myLabel.backgroundColor = [UIColor colorWithPatternImage:resultingImage];
[self.view addSubview:myLabel];
Use UIColor's +colorWithPatternImage:. Pass in a 1px by the UILabel's height image and it will be "tiled" across the the width of the view.
myLabel.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"label-background.png"]];
first you have to subclass the UILabel and override it's drawRect: method like this for gradient background
- (void)drawRect:(CGRect)rect
{
//////////////GET REFERENCE TO CURRENT GRAPHICS CONTEXT
CGContextRef context = UIGraphicsGetCurrentContext();
//////////////CREATE BASE SHAPE WITH ROUNDED CORNERS FROM BOUNDS
CGRect activeBounds = self.bounds;
CGFloat cornerRadius = 10.0f;
CGFloat inset = 6.5f;
CGFloat originX = activeBounds.origin.x + inset;
CGFloat originY = activeBounds.origin.y + inset;
CGFloat width = activeBounds.size.width - (inset*2.0f);
CGFloat height = activeBounds.size.height - (inset*2.0f);
CGRect bPathFrame = CGRectMake(originX, originY, width, height);
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:bPathFrame cornerRadius:cornerRadius].CGPath;
//////////////CREATE BASE SHAPE WITH FILL AND SHADOW
CGContextAddPath(context, path);
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:210.0f/255.0f green:210.0f/255.0f blue:210.0f/255.0f alpha:1.0f].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 6.0f, [UIColor colorWithRed:0.0f/255.0f green:0.0f/255.0f blue:0.0f/255.0f alpha:1.0f].CGColor);
CGContextDrawPath(context, kCGPathFill);
//////////////CLIP STATE
CGContextSaveGState(context); //Save Context State Before Clipping To "path"
CGContextAddPath(context, path);
CGContextClip(context);
//////////////DRAW GRADIENT
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
size_t count = 3;
CGFloat locations[3] = {0.0f, 0.57f, 1.0f};
CGFloat components[12] =
{ 0.0f/255.0f, 0.0f/255.0f, 0.0f/255.0f, 1.0f, //1
5.0f/255.0f, 5.0f/255.0f, 5.0f/255.0f, 1.0f, //2
10.0f/255.0f, 10.0f/255.0f, 10.0f/255.0f, 1.0f}; //3
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, count);
CGPoint startPoint = CGPointMake(activeBounds.size.width * 0.5f, 0.0f);
CGPoint endPoint = CGPointMake(activeBounds.size.width * 0.5f, activeBounds.size.height);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGColorSpaceRelease(colorSpace);
CGGradientRelease(gradient);
}
This will draw black to white gradient background
May this will help you
Happy Codding :)
the above code is from this site http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-uialertview-custom-graphics/

Drawing linear gradient in a rect works on iOS 5.x but not 4.x

I have some code that I use to fill a view with a linear gradient, I've refactored this into a helper method. This draws fine on iOS 5.x but when I tried running on 4.3.5 I don't get anything. I've checked all the CG calls and they all are supported on 4.0 or older, it look so simplistic I can't see why it doesn't work on 4.x.
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef startColor = [UIColor lightGrayColor].CGColor;
CGColorRef endColor = [UIColor blackColor].CGColor;
CGRect paperRect = self.frame;
[GraphicsHelpers drawLinearGradientWithContext:context
inRect:paperRect
startColor:startColor
endColor:endColor];
}
Here is that method
+ (void)drawLinearGradientWithContext:(CGContextRef)context inRect:(CGRect)rect startColor:(CGColorRef)startColor endColor:(CGColorRef)endColor {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = [NSArray arrayWithObjects:(__bridge id)startColor, (__bridge id)endColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,
(__bridge CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
I found most of this code here and was working great until I started testing on older OS versions as I'm close to release.
I had the same problem, solved worked around it by creating the gradient using
CGGradientCreateWithColorComponents
.. instead of CGGradientCreateWithColors.
So, for a gradient from light gray to black you need to do the following:
CGFloat colors[8] = {
0.9f, 0.9f, 0.9f, 1.0f, // light gray
0.0f, 0.0f, 0.0f, 1.0f // black
};
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, 2);
This works in both iOS 4 and 5.
I had to face the same problem as a tried to fill a view with a gradient. Finally I found out that the specific UIColor( redColor, whiteColor, etc) is incompatible with CGColors.
Using ([UIColor whiteColor].CGColor) serves a CGColor which is not imcompatible or not supported in ios 4.3. For that reason I tried out to use [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0].CGColor. This could be converted into CGColor space and finally I had a gradient in my view also with ios 4.3 and ios 5.x
If you're using supported API and you're getting different results from one version of the OS to another, file a bug report.

How do I apply an opacity gradient on a UIView?

I would like my UIView to fade from 100% opacity to 0% opacity. Any thoughts on how I can do this?
Ray Wenderlich has a great tutorial on setting gradients for objects. My suggestion is to modify the following code to suit your needs.
//Ray's code:
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor,
CGColorRef endColor) {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = [NSArray arrayWithObjects:(id)startColor, (id)endColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,
(CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
If the view, together with its labels, is sitting on some static background image or color, a quick and dirty trick is to create gradient PNGs of that image or color and place them over the view.
Otherwise, see if you can adapt this CALayer gradient mask solution for your view.