Gradients on UIView and UILabels On iPhone [duplicate] - iphone

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Manually drawing a gradient in iPhone apps?
My application needs to display text in either a UIView or UILabel but the back ground must be a gradient as opposed to a true UIColor. Using a graphics program to create desired look is no good as the text may vary depending on data returned from a server.
Does anyone know the quickest way to tackle this?
Your thoughts are greatly appreciated.

I realize this is an older thread, but for future reference:
As of iPhone SDK 3.0, custom gradients can be implemented very easily, without subclassing or images, by using the new CAGradientLayer:
UIView *view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 100)] autorelease];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
[view.layer insertSublayer:gradient atIndex:0];
Take a look at the CAGradientLayer docs. You can optionally specify start and end points (in case you don't want a linear gradient that goes straight from the top to the bottom), or even specific locations that map to each of the colors.

You can use Core Graphics to draw the gradient, as pointed to in Mike's response. As a more detailed example, you could create a UIView subclass to use as a background for your UILabel. In that UIView subclass, override the drawRect: method and insert code similar to the following:
- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGGradientRef glossGradient;
CGColorSpaceRef rgbColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 0.35, // Start color
1.0, 1.0, 1.0, 0.06 }; // End color
rgbColorspace = CGColorSpaceCreateDeviceRGB();
glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
CGRect currentBounds = self.bounds;
CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
CGContextDrawLinearGradient(currentContext, glossGradient, topCenter, midCenter, 0);
CGGradientRelease(glossGradient);
CGColorSpaceRelease(rgbColorspace);
}
This particular example creates a white, glossy-style gradient that is drawn from the top of the UIView to its vertical center. You can set the UIView's backgroundColor to whatever you like and this gloss will be drawn on top of that color. You can also draw a radial gradient using the CGContextDrawRadialGradient function.
You just need to size this UIView appropriately and add your UILabel as a subview of it to get the effect you desire.
EDIT (4/23/2009): Per St3fan's suggestion, I have replaced the view's frame with its bounds in the code. This corrects for the case when the view's origin is not (0,0).

Note: The results below apply to older versions of iOS, but when testing on iOS 13 the stepping doesn't occur. I don't know for which version of iOS the stepping was removed.
When using CAGradientLayer, as opposed to CGGradient, the gradient is not smooth, but has noticeable stepping to it. See :
To get more attractive results it is better to use CGGradient.

You could also use a graphic image one pixel wide as the gradient, and set the view property to expand the graphic to fill the view (assuming you are thinking of a simple linear gradient and not some kind of radial graphic).

Mirko Froehlich's answer worked for me, except when i wanted to use custom colors. The trick is to specify UI color with Hue, saturation and brightness instead of RGB.
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = myView.bounds;
UIColor *startColour = [UIColor colorWithHue:.580555 saturation:0.31 brightness:0.90 alpha:1.0];
UIColor *endColour = [UIColor colorWithHue:.58333 saturation:0.50 brightness:0.62 alpha:1.0];
gradient.colors = [NSArray arrayWithObjects:(id)[startColour CGColor], (id)[endColour CGColor], nil];
[myView.layer insertSublayer:gradient atIndex:0];
To get the Hue, Saturation and Brightness of a color, use the in built xcode color picker and go to the HSB tab. Hue is measured in degrees in this view, so divide the value by 360 to get the value you will want to enter in code.

This is what I got working- set UIButton in xCode's IB to transparent/clear, and no bg image.
UIColor *pinkDarkOp = [UIColor colorWithRed:0.9f green:0.53f blue:0.69f alpha:1.0];
UIColor *pinkLightOp = [UIColor colorWithRed:0.79f green:0.45f blue:0.57f alpha:1.0];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = [[shareWordButton layer] bounds];
gradient.cornerRadius = 7;
gradient.colors = [NSArray arrayWithObjects:
(id)pinkDarkOp.CGColor,
(id)pinkLightOp.CGColor,
nil];
gradient.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:0.7],
nil];
[[recordButton layer] insertSublayer:gradient atIndex:0];

I achieve this in a view with a subview that is an UIImageView. The image the ImageView is pointing to is a gradient. Then I set a background color in the UIView, and I have a colored gradient view. Next I use the view as I need to and everything I draw will be under this gradient view. By adding a second view on top of the ImageView, you can have some options whether your drawing will be below or above the gradient...

Related

Getting the CGContextRef from a subview

I'm using UIGraphicsGetCurrentContext() to create a gradient background for a UI element, but I guess I misunderstood where the drawing was taking place. I wanted the drawing to take place on a subview, but instead it's happening in the view itself.
I think the problem is that when I use UIGraphicsGetCurrentContext(), I'm getting a CGContextRef of the view, so that's where the drawing is taking place. What I want to do is have the drawing take place on the subview, so that I can fade it in and out with other related subviews. Can this be done, or do I need to create another UIView subclass just for the background layer?
Here's a simplification of the code I'm using and my goal is to be able to fade in and out the background gradient in topBar while leaving the InterfaceControlsView view visible.
#implementation InterfaceControlsView
- (id)initWithFrame:(CGRect)frame
{
topBar = [[UIView alloc] initWithFrame:CGRectMake(0.0, 20.0, self.frame.size.width, 45.0)];
/* etc. */
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = topBar.frame; // topBar is a subview
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
/* etc. */
}
#end
To create gradient bg for subviews, you dont need to create subclass, use gradiant layers.
Hope this helps
CALayer *layer = _button.layer;
layer.borderWidth = 1.0f;
layer.borderColor = [UIColor lightGrayColor].CGColor;
CAGradientLayer *gLayer = [CAGradientLayer layer];
[gLayer setName:#"gradient"];
gLayer.frame = layer.bounds;
gLayer.colors = [NSArray arrayWithObjects:
(id)[UIColor colorWithRed:26.0/255.0 green:94.0/255.0 blue:74.0/255.0 alpha:1.0].CGColor,
(id)[UIColor colorWithRed:23.0/255.0 green:59.0/255.0 blue:37.0/255.0 alpha:1.0].CGColor,
nil];
[layer addSublayer:gLayer];

How to make a fading effect with Core Graphics?

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.

How to get context of a CALayer plus a few pixels around it?

I am creating a layer with a shadow effect. Instead of adding it as a sublayer to my view's layer, I would like to draw it. But I am having problems being able to draw it with the shadow effect. The problem is that the context size it based on the layer size. But how do I set the rect for the context?!
CALayer *layer = [CALayer layer];
layer.frame = ...;
layer.backgroundColor = [UIColor redColor].CGColor;
layer.cornerRadius = 2.0;
layer.masksToBounds = NO;
layer.shadowColor = [[UIColor whiteColor] CGColor];
layer.shadowOffset = CGSizeMake(0, 1);
layer.shadowRadius = 0.5;
layer.shadowOpacity = 0.2;
[self.layer addSublayer:layer];
UIGraphicsBeginImageContext(layer.bounds.size);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[viewImage drawAtPoint:CGPointMake(0, 0)];
[layer removeFromSuperlayer];
With the code above, what I am getting is the box, but without a few extra pixels of padding with the shadow (on all four sides). If I increase the size of the ImageContext, all I get is more height and width, but still starting from x=0 and y=0, where I would want it to start from x=-5, y=-5 or something like that.
Thanks a bunch!
Make your context 10 pixels larger in both the x and the y.
Then before you draw your layer do a CGContextTranslateCTM(context,5,5);
Hope this helps!
I'm not sure if the following is your problem, but it just caught my eye.
[self.layer addSublayer:layer];
UIGraphicsBeginImageContext(layer.bounds.size);
[layer renderInContext:UIGraphicsGetCurrentContext()];
It looks like you have a property on this file called layer, which you are adding the shadow layer you've created to. You then get the context based on the current CALayer, as opposed to self.layer.
I typically use CALayer to modify UIImageView, and do not have a need to add sub layers, so perhaps that code is fine. Just wanted to point it out, please do let me know if which way is accurate, as I always like to learn.
~Good Luck

How to create a UIView that contains 0 to 1 opacity value (alpha value) from bottom to top?

I want to create a UIView that contains gradually increasing opacity value in it. That is, the UIView's bottom will start with 0.0 as its alpha value and it should end with 1.0 at the top of the UIView. Is it possible to make such view? Or should i separate that view and give them the various alpha value?
Thanks in Advance
For future reference, the term for that sort of effect is a gradient.
You can make a view with a gradient image behind it:
Create a UIImageView behind the UIView you want to set a gradient on
Set the UIView's background color to clear.
Set the background image of the UIImageView to a gradient PNG you have created.
The gradient PNG can be 1px wide and say 64px high (or higher, depending on how smooth you want the gradient to look). Make it in a paint program (GIMP is a decent one).
A way to do it purely in code is using CAGradientLayer:
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.view.bounds;
gradient.colors = [NSArray arrayWithObjects:
(id)[[UIColor colorWithWhite: 0.0 alpha:0.0] CGColor],
(id)[[UIColor colorWithWhite: 0.0 alpha:1.0] CGColor], nil];
gradient.startPoint = CGPointMake(0.5, 0.0); // default; bottom of the view
gradient.endPoint = CGPointMake(0.5, 1.0); // default; top of the view
[self.view.layer insertSublayer:gradient atIndex:0];

Adding shadow to a UINavigationControllerer

I'm trying to add shadow to a UINavigationController. My have is based on NavController and i'm trying to add a shadow like in game centers. I have this so far. It works n a UINavigationnBar but i'm trying to get it to work throughout the entire app.
CGColorRef darkColor = [[UIColor blackColor] colorWithAlphaComponent:.5f].CGColor;
CGColorRef lightColor = [UIColor clearColor].CGColor;
CAGradientLayer *newShadow = [[[CAGradientLayer alloc] init] autorelease];
newShadow.frame = CGRectMake(0, 44, 320, 10);
newShadow.colors = [NSArray arrayWithObjects:(id)darkColor, (id)lightColor, nil];
CALayer *superlayer = self.newShadow.superlayer;
[self.newShadow removeFromSuperlayer];
[superlayer addSublayer:self.newShadow];
[self.navigationBar.layer addSublayer:superlayer];
It works directly on a UINavigationBar but applying to a NavigationController project it fails. It builds but won't add the shadow. Any ideas?
EDIT:
I have been trying different approaches to this. I successfully created the gradient by using a shape.
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
UIImage *backgroundImage;
backgroundImage = [UIImage imageNamed:#"nav.png"];
[backgroundImage drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
size_t num_locations = 2;
CGFloat locations[2] = { 1.0, 0.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.5 };
CGGradientRef myGradient = CGGradientCreateWithColorComponents(myColorspace, components, locations, num_locations);
CGPoint myStartPoint, myEndPoint;
myStartPoint.x = 0.0;
myStartPoint.y = 0.0;
myEndPoint.x = 0.0;
myEndPoint.y = 54.0;
CGContextDrawLinearGradient (context, myGradient, myStartPoint, myEndPoint, 1);
}
I can't get the gradient below the UINavigationBar and its overlaying the image. I can't seem to get this to work. What i'm trying to do is add the same shadow Game Center has. I have tried a few different ways. All I need to do here is get this to lie underneath the UINavigationBar allowing the image to be on top and have a little part of the shadow lie on top on the UITableView so when you scroll up its above the UITableView. If you fire up Game Center you'll see exactly what i'm talking about.
I believe you should add it to the
UINavigationController.view.layer
As the UINavigationController he is not a UIView child.
if already you did so, an other way to effect the navigationbar, consistently all over the app is to use the UINavigationBarCategory:
you sould pace this at the end of your delegate - after the #end
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
//implement your design
}
#end
EDIT
in this link you can find basic info on how to draw the bar:
An iPhone Graphics Drawing Tutorial using Quartz 2D
and here you can find how to add a shadow:adding shadow with quartz 2d
Good luck
You could subclass UIView and use your existing code to draw the shadow by overriding -drawRect:. Then wherever its needed, create it and add it to the view of the navigation controller.
Consider this example
CGFloat screenWidth = [UIScreen mainScreen].applicationFrame.size.width;
ShadowView *shadowView = [[ShadowView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, screenWidth, 20.0f)];
[self.navigationController.view insertSubview:shadowView atIndex:0];
By adding it to the view of the navigationController at index 0, you're effectively overlaying it on top of whatever the view controller is displaying. You could further improve this by checking for the scale of the main screen to appropriately calculate the height of the shadow view so that it will appear correctly on all devices regardless of having a retina display.