Adding shadow to a UINavigationControllerer - iphone

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.

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

Drawing a transparent circle on top of a UIImage - iPhone SDK

I am having a lot of trouble trying to find out how to draw a transparent circle on top of a UIImage within my UIImageView. Google-ing gives me clues, but I still can't find a working example.
Are there any examples that anyone knows of that demonstrate this?
Easiest way is simply to create a semi-transparent square UIView, then set the cornerRadius of its layer to be half of its width/height. Something like:
UIView *squareView = [[UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
squareView.alpha = 0.5;
squareView.layer.cornerRadius = 50;
...
[squareView release];
This has got to be the simplest solution:
CGFloat r = 150;
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0,0,1.5*r,1.5*r)];
lbl.text = #"●";
lbl.transform = CGAffineTransformMakeTranslation(0.0f, -r/6);
lbl.textAlignment = UITextAlignmentCenter;
lbl.backgroundColor = [UIColor clearColor];
lbl.textColor = [UIColor redColor];
lbl.font = [UIFont systemFontOfSize:2*r];
lbl.alpha = 0.5;
lbl.center = self.view.center;
[self.view addSubview:lbl];
One way would be to add a CAShapeLayer with a circular path, either directly to the layer of the UIImageView or as the layer of a new UIView that is added to the UIImageView.
If you actually want to modify the image, then create a mutable copy of it by drawing it into a CGBitmapContext then creating a new image from the modified bitmap.
CGPathRef circlePath = CGPathCreateMutable();
CGPathAddEllipseInRect( circlePath , NULL , CGRectMake( 0,0,20,20 ) );
CAShapeLayer *circle = [[CAShapeLayer alloc] init];
circle.path = circlePath;
circle.opacity = 0.5;
[myImageView.layer addSublayer:circle];
CGPathRelease( circlePath );
[circle release];
You can implement a custom sub-class of UIView that draws your image and then the circle in the drawRect method:
#interface CircleImageView : UIView {
UIImage * m_image;
CGRect m_viewRect;
// anything else you need in this view?
}
Implementation of drawRect:
- (void)drawRect:(CGRect)rect {
// first draw the image
[m_image drawInRect:m_viewRect blendMode:kCGBlendModeNormal alpha:1.0];
// then use quartz to draw the circle
CGContextRef context = UIGraphicsGetCurrentContext ()
// stroke and fill black with a 0.5 alpha
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 0.5);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.5);
// now draw the circle
CGContextFillEllipseInRect (context, m_viewRect);
}
You will need to set up the m_viewRect and m_image member functions on init.

How to style UITextView with a 3D shadowed border?

In another post, luvieere shared a means by which rounded corners can be applied to a text view using the QuartzCore framework (see post). It would seem to me that a 3D border, like that found on a UITextField, could be created via layers instead of using a background image.
Does anyone know if or how this can be done? I'd really like to find a method to add a 3D border WITHOUT having to fire up a graphics editor and create a 3D shadowed background. Thanks!
In View Controller:
newCommentBody.layer.cornerRadius = 7;
newCommentBody.clipsToBounds = YES;
Make new class TextView inherits UITextView
#import "TextView.h"
#import <CoreGraphics/CoreGraphics.h>
#import <CoreGraphics/CGColor.h>
#implementation TextView
-(void) drawRect:(CGRect)rect {
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(currentContext, 1.0); //or whatever width you want
CGContextSetRGBStrokeColor(currentContext, 0.6, 0.6, .6, 1.0);
CGRect myRect = CGContextGetClipBoundingBox(currentContext);
//printf("rect = %f,%f,%f,%f\n", myRect.origin.x, myRect.origin.y, myRect.size.width, myRect.size.height);
float myShadowColorValues[] = {0,0,0,1};
CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef colorRef = CGColorCreate(myColorSpace, myShadowColorValues);
CGContextSetShadowWithColor(currentContext, CGSizeMake(0, -1), 3, colorRef);
// CGContextSetShadow(currentContext, CGSizeMake(0, -1), 3);
CGContextStrokeRect(currentContext, myRect);
UIImage *backgroundImage = (UIImage *)UIGraphicsGetImageFromCurrentImageContext();
UIImageView *myImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
[myImageView setImage:backgroundImage];
[self addSubview:myImageView];
[backgroundImage release];
UIGraphicsEndImageContext();
}
#end
m_txtViewSource.layer.borderWidth = 1;
m_txtViewSource.layer.borderColor = [[UIColor grayColor] CGColor];
it's not 3D but it simpler and safe code

CALayer drawInContext vs addSublayer

How can I use both of these in the same UIView correctly?
I have one custom subclassed CALayer in which I draw a pattern within drawInContext
I have a another in which I set an overlay PNG image as the contents.
I have a third which is just a background.
How do I overlay all 3 of these items?
[self.layer addSublayer:bottomLayer]; // this line is the problem
[squaresLayer drawInContext:viewContext];
[self.layer addSublayer:imgLayer];
The other 2 by themselves draw correctly if I do them in that order. No matter where I try and put bottomLayer, it always prevents squaresLayer from drawing. The reason I need 3 layers is I intend to animate the colors in the background and custom layers. The top layer is just a graphical overlay.
Might as well paste the code in in case anyone is trying to animate stacked CALayers that have their own internal draw routines
- (void)drawRect:(CGRect)rect {
[imgLayer removeFromSuperlayer];
CGFloat w = [self.blockViewDelegate w];
CGFloat h = [self.blockViewDelegate h];
CGFloat wb = w/4;
CGFloat hb = h/4;
CGContextRef viewContext = UIGraphicsGetCurrentContext();
// Layers
[bottomLayer sizes:wb :hb :1];
bottomLayer.frame = CGRectMake(0, 0, w, h);
bottomLayer.opaque = NO;
[topLayer sizes:wb :hb :0];
topLayer.frame = CGRectMake(0, 0, w, h);
topLayer.opaque = NO;
// Overlay
imgLayer.frame = CGRectMake(0, 0, w, h);
imgLayer.opaque = NO;
imgLayer.opacity = 1.0f;
UIImage *overlay = [self.blockViewDelegate image];
CGImageRef img = overlay.CGImage;
imgLayer.contents = (id)img;
// Add layers
[bottomLayer drawInContext:viewContext];
[topLayer drawInContext:viewContext];
[self.layer addSublayer:imgLayer];
}
blockViewDelegate is where I am storing width, height, and image information, it is the controller for this UIView.
topLayer and bottomLayer are of a custom UIView subclass which draw some shapes in the view with variable color information. Later on in my animation I just run "setNeedsDisplay" repeatedly with a timer and this routine repeats, the layers re-draw using updated parameters.

Gradients on UIView and UILabels On iPhone [duplicate]

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...