Drawing a rectangle in UIGraphics context does not work - iphone

I am trying to overlay an image with some extra information: some text and rectangles. Right now I am stuck at drawing the rectangles. They just won't show up. What am I doing wrong? The image itself is being drawn, so the graphics context must be ok.
- (void)drawTag:(NSString *)tag withRect:(CGRect)rect
{
// Set the color in the current graphics context for future draw operations
[[UIColor yellowColor] setStroke];
[[UIColor yellowColor] setFill];
// Create our drawing path
UIBezierPath* drawingPath = [UIBezierPath bezierPathWithRect:rect];
// actually draw it
[drawingPath stroke];
}
- (IBAction)showDetails:(id)sender
{
// draw the image
UIGraphicsBeginImageContext(self.userImage.size);
// This one shows up:
[self.userImage drawAtPoint:CGPointZero];
// This one does not:
[self drawTag:#"Test" withRect:CGRectMake(10, 10, 50, 50)];
// Show the whole thing:
self.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageViewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:self.imageViewController animated:YES];
}

You should set the lineWidth property of UIBezierPath to a value >= 1

Related

How to create an oval shape UIButton

I have to draw an oval shape button, i don't want to set the image in oval shape.
I have read that you can draw different shaper with UIBezierPath but i am unable to draw a proper oval shape.
Here is the code is am using to create a circular shape.
self.layer.borderWidth=1.0;
self.clipsToBounds = YES;
[self setTitleColor:ktextColor forState:UIControlStateNormal];
self.layer.cornerRadius = self.bounds.size.width/2;//half of the width
self.layer.borderColor=[[UIColor whiteColor]CGColor];
Any help is appreciated !!
You can set the delegate for your button's layer and implement the delegate method `displayLayer:'
-(void)displayLayer:(CALayer *)layer
{
UIGraphicsBeginImageContext(layer.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(0.0, 0.0, CGRectGetWidth(layer.frame), CGRectGetHeight(layer.frame))];
[[UIColor whiteColor] setFill];
[ovalPath fill];
[[UIColor blackColor] setStroke];
ovalPath.lineWidth = 1;
[ovalPath stroke];
UIImage *imageBuffer = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
layer.contents = (id)[imageBuffer CGImage];
}
You can also create a CAShapelayer and than add gesture to the layer as below:
-(void)generateOvalWithSize:(CGSize)size origin:(CGPoint)origin {
CAShapeLayer ovalLayer = [CAShapeLayer layer];
CGMutablePathRef ovalPath = CGPathCreateMutable();
CGPathAddEllipseInRect(ovalPath, NULL, CGRectMake(origin.x, origin.y, size.width, size.height));
CGPathCloseSubpath(ovalPath);
ovalLayer.path = ovalPath;
// Configure the apperence of the circle
ovalLayer.fillColor = [UIColor whiteColor].CGColor;
ovalLayer.strokeColor = [UIColor whiteColor].CGColor;
ovalLayer.lineWidth = 2;
// Add to parent layer
[[self layer] addSublayer:ovalLayer];
}
i have been using these two functions for a while, i haven't come across any issues yet, but if someone sees an issue please let me know.
you can pass anything that is a subclass of UIView (ie. UIButton, UICollectionViewCell, etc.)
+ (void)setCornerRadius:(CGFloat)radius forView:(UIView*)view
{
[view.layer setCornerRadius:radius];
[view.layer setMasksToBounds:YES];
}
+ (void)setBordersForView:(UIView*)view width:(CGFloat)width color:(UIColor*)color
{
view.layer.borderColor = color.CGColor;
view.layer.borderWidth = width;
}
the above yields me the effect below in a collection view:
the collection view cell has a single button which is bound all the way around the edges (auto layout) and takes up the full height and width of the cell.
then i pass in the entire cell (or self.viewForBaseLineLayout if calling from within the cell) as the view parameter in both functions listed above.
for the radius parameter i am passing 20.0f.
(but this value will vary depending on the size the view you are passing; you just have to play around with it)
i know this post is old, but maybe it will help someone new : )
You can use the OBShapedButton class Download Here

External Fill Color in iOS

I have two rectangles (two closed sub cgpaths).
Rectangle B is small and is present inside Rectangle A. All edges within it.
Is there a direct way to fill color area external to the rectangle B.
CAShapeLayer fillExternalColor something like that? If not a direct way, how to do this programmatically?
A - Purple color
B - Yellow color
So I tried drawing A and then drawing B. I wanted B to be of clear color (for now I put Yellow) but then I see purple color of A..
I found CGRectIntersection method that gives AnB and CGRectUnion method that gives AuB.
Is there a method that gives the rest of the area which is AuB - AnB?
You are using a CAShapeLayer. If your fillColor and your strokeColor are both opaque (alpha 1.0), you can simply set the layer's backgroundColor to fill those pixels that are within the layer's bounds but outside of its stroked and filled path.
My test code:
#implementation ViewController {
CAShapeLayer *layer;
}
- (void)viewDidLoad {
[super viewDidLoad];
layer = [CAShapeLayer layer];
layer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 250, 250)].CGPath;
layer.fillColor = [UIColor yellowColor].CGColor;
layer.strokeColor = [UIColor whiteColor].CGColor;
layer.lineWidth = 4;
layer.backgroundColor = [UIColor purpleColor].CGColor;
[self.view.layer addSublayer:layer];
}
- (void)viewDidLayoutSubviews {
layer.frame = self.view.bounds;
}
#end
Result:
I wanted to add red border inner rectangle on UIImage with masked outer part.
I came through this page. The following code is using CGContextEOFillPath can be helpful to others like me. (Some of the code is gathered from other pages.)
-(UIImage ) imageByDrawingBorderRectOnImage:(UIImage )image theRect:(CGRect)theRect
{
// begin a graphics context of sufficient size
UIGraphicsBeginImageContext(image.size);
// draw original image into the context
[image drawAtPoint:CGPointZero];
// get the context for CoreGraphics
CGContextRef ctx = UIGraphicsGetCurrentContext();
// set stroking color and to draw rect
[[UIColor redColor] setStroke];
// drawing with a red stroke color
CGContextSetRGBStrokeColor(ctx, 1.0, 0.0, 0.0, 1.0);
// the line width to 3
CGContextSetLineWidth(ctx, 3.0);
// Add Stroke Rectangle,
CGContextStrokeRect(ctx, theRect);
// Now draw fill outside part with partial alpha gray color
// drawing with a gray stroke color
CGMutablePathRef aPath = CGPathCreateMutable();
// outer rectangle
CGRect rectangle = CGRectMake( 0, 0, image.size.width, image.size.height);
CGPathAddRect(aPath, nil, rectangle);
// innter rectangle
CGPathAddRect(aPath, nil, theRect);
// set gray transparent color
CGContextSetFillColorWithColor(ctx, [UIColor colorWithRed:0.75 green:0.75 blue:0.75 alpha:0.5].CGColor);
// add the path to Context
CGContextAddPath(ctx, aPath);
// This method uses Even-Odd Method to draw in outer rectangle
CGContextEOFillPath(ctx);
// make image out of bitmap context
UIImage *retImage = UIGraphicsGetImageFromCurrentImageContext();
// free the context
UIGraphicsEndImageContext();
return retImage;
}
Regards.
Assuming A is the bigger shape, B is the smaller, inner shape, and bounds refers to the containing rect of A (probably the bounds of the view)
Clip shape B to the context (-addClip should do it)
CGContextClearRect the bounds
Fill shape A
Edit, found the source of this code
Given HoleView : UIView you must set:
self.opaque = NO;
self.backgroudColor = [UIColor clearColor];
and in drawRect:
[[UIColor purpleColor] setFill];
UIRectFill(A);
CGRect gapRectIntersection = CGRectIntersection(B, A);
[[UIColor clearColor] setFill];
UIRectFill(gapRectIntersection);
kCAFillRuleEvenOdd
Specifies the even-odd winding rule. Count the total number of path crossings. If the number of crossings is even, the point is outside the path. If the number of crossings is odd, the point is inside the path and the region containing it should be filled.
Available in OS X v10.6 and later.
Declared in CAShapeLayer.h.
Refer to :CAShapeLayer Class reference

iOS - How to achieve emboss effect for the text on UILabel?

Can I know how to have the emboss effect as the text "Reminders" as shown on the picture?
It looks like the text are embedded?
Thanks
UPDATE FOR iOS 7.0
In iOS 7.0, Apple added a new attribute, NSTextEffectAttributeName, for attributed strings. If your deployment target is iOS 7.0 or later, you can set this attribute to NSTextEffectLetterpressStyle to draw an attributed string in an embossed style.
ORIGINAL
I can't say for certain how Apple draws the embossed text. It looks to me like they fill the string glyphs with a reddish color, then apply a shadow around the interior edges of the glyphs, and also apply a very faint shadow along the top outside edges of the glyphs. I tried it out and here's what it looks like:
On top is my rendering. Below that is a simple UILabel with shadow as Chris suggested in his answer. I put a screen shot of the Reminders app in the background.
Here's my code.
First, you need a function that creates an image mask of your string. You'll use the mask to draw the string itself, and then to draw a shadow that only appears around the inside edges of the string. This image just has an alpha channel and no RGB channels.
- (UIImage *)maskWithString:(NSString *)string font:(UIFont *)font size:(CGSize)size
{
CGRect rect = { CGPointZero, size };
CGFloat scale = [UIScreen mainScreen].scale;
CGColorSpaceRef grayscale = CGColorSpaceCreateDeviceGray();
CGContextRef gc = CGBitmapContextCreate(NULL, size.width * scale, size.height * scale, 8, size.width * scale, grayscale, kCGImageAlphaOnly);
CGContextScaleCTM(gc, scale, scale);
CGColorSpaceRelease(grayscale);
UIGraphicsPushContext(gc); {
[[UIColor whiteColor] setFill];
[string drawInRect:rect withFont:font];
} UIGraphicsPopContext();
CGImageRef cgImage = CGBitmapContextCreateImage(gc);
CGContextRelease(gc);
UIImage *image = [UIImage imageWithCGImage:cgImage scale:scale orientation:UIImageOrientationDownMirrored];
CGImageRelease(cgImage);
return image;
}
Second, you need a function that inverts that mask. You'll use this to make CoreGraphics draw a shadow around the inside edges of the string. This needs to be a full RGBA image. (iOS doesn't seem to support grayscale+alpha images.)
- (UIImage *)invertedMaskWithMask:(UIImage *)mask
{
CGRect rect = { CGPointZero, mask.size };
UIGraphicsBeginImageContextWithOptions(rect.size, NO, mask.scale); {
[[UIColor blackColor] setFill];
UIRectFill(rect);
CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, mask.CGImage);
CGContextClearRect(UIGraphicsGetCurrentContext(), rect);
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
You can use those in a function that draws the string in red and applies a shadow to its interior edges.
-(UIImage *)imageWithInteriorShadowAndString:(NSString *)string font:(UIFont *)font textColor:(UIColor *)textColor size:(CGSize)size
{
CGRect rect = { CGPointZero, size };
UIImage *mask = [self maskWithString:string font:font size:rect.size];
UIImage *invertedMask = [self invertedMaskWithMask:mask];
UIImage *image;
UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale); {
CGContextRef gc = UIGraphicsGetCurrentContext();
// Clip to the mask that only allows drawing inside the string's image.
CGContextClipToMask(gc, rect, mask.CGImage);
// We apply the mask twice because we're going to draw through it twice.
// Only applying it once would make the edges too sharp.
CGContextClipToMask(gc, rect, mask.CGImage);
mask = nil; // done with mask; let ARC free it
// Draw the red text.
[textColor setFill];
CGContextFillRect(gc, rect);
// Draw the interior shadow.
CGContextSetShadowWithColor(gc, CGSizeZero, 1.6, [UIColor colorWithWhite:.3 alpha:1].CGColor);
[invertedMask drawAtPoint:CGPointZero];
invertedMask = nil; // done with invertedMask; let ARC free it
image = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();
return image;
}
Next you need a function that takes an image and returns a copy with a faint upward shadow.
- (UIImage *)imageWithUpwardShadowAndImage:(UIImage *)image
{
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale); {
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, -1), 1, [UIColor colorWithWhite:0 alpha:.15].CGColor);
[image drawAtPoint:CGPointZero];
}
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
Finally, you can combine those functions to create an embossed image of your string. I put my final image into a UIImageView for easy testing.
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect rect = self.imageView.bounds;
NSString *string = #"Reminders";
UIFont *font = [UIFont systemFontOfSize:33];
UIImage *interiorShadowImage = [self imageWithInteriorShadowAndString:string
font:font
textColor:[UIColor colorWithHue:0 saturation:.9 brightness:.7 alpha:1]
size:rect.size];
UIImage *finalImage = [self imageWithUpwardShadowAndImage:interiorShadowImage];
self.imageView.image = finalImage;
}
That just looks like a shadow around the text. You set it in IB in the same area where you set the text color - pick an appropriate color for the shadow and set how you want the shadow offset (in the example you posted, it looks like they set the shadow color to the same color as the text and offset it 0 horizontal and -1 vertical (which means one pixel up).
In code, the properties are set like this (assuming you have already set up a UILabel named, appropriately, "label":
label.shadowColor = [UIColor blackColor]; // Choose your color here - in the example
// posted, they probably chose a similar color
// to the text color and then set the alpha
// down around 0.7 or so so the shadow would be
// faint.
label.shadowOffset = CGSizeMake(0,-1); // First parameter is horizontal, second is vertical
You can configure your effect on the basis of this example

drawRect: isn't working

I want to place some "handles" on a UIView I have on-screen.
So, I'm creating more UIViews, one at each corner of the UIView's frame rectangle, and I simply want to draw a circle that "fills" the rectangle that is the frame of the "handle" UIView.
Here's what I mean:
How I create the "HandleView":
CGPoint upperLeft = CGPointMake([[self viewToMove] frame].origin.x - 5, [[self viewToMove] frame].origin.y - 5);
HandleView *uL = [[HandleView alloc] initWithFrame:CGRectMake(upperLeft.x, upperLeft.y, 10, 10)];
[self setUpperLeftHandle:uL];
[uL release];
[[self view] addSubview:[self upperLeftHandle]];
drawRect: for HandleView:
- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor orangeColor] set];
CGContextFillEllipseInRect(context, [self frame]);
}
What I'm getting is just a set of black rectangles where I place the HandleViews. I'm not sure why they're black, but I can change the color by changing the [HandleView backgroundColor] property. I cannot, though, get anything to DRAW on this view. Calling setNeedsDisplay doesn't seem to make any difference.
Also, the drawRect: method IS being called, so there's a problem with the code there, probably not anywhere else.
It's been a while since I've messed with custom drawing, but I don't remember it being this hard.
What am I missing?
Thanks!!!
Update:
Modified code to this:
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
[self setBackgroundColor:[UIColor clearColor]];
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 100, 100, 100, 1.0);
CGContextFillEllipseInRect(context, rect);
}
Now, I see the circle, but only where it overlays another view. Where it's just sitting on top of the "background" UIView, I get nothing I can see. Also, even though the color is (100, 100, 100), it shows up white/light-light-gray.
What's the deal?
This might work:
- (void)drawRect:(CGRect)rect {
// Drawing code
[[UIColor orangeColor] setFill];
[[UIBezierPath bezierPathWithOvalInRect:rect] fill];
}
The problem is with the following line:
CGContextFillEllipseInRect(context, [self frame]);
You need to change this to the following:
CGContextFillEllipseInRect(context, [self bounds]);
Origin of frame is the left-top corner location of view in the parent. So, it can be any value within the limits of the size of parent view.
So, while drawing with frame, you will be offsetting Fillellipse rect location which happens to be beyond the visible rect of the handle view.
Hence, you were not able to see anything.
As for the color showing up as white, it's because CGContextSetRGBFillColor() expects values from 0.0 to 1.0 for the color component values (not 0-255). So, by passing 100, you were effectively passing 1.0, which was creating a white color.

Inner Shadow in UILabel

is it possible to create such a UILabel with inner and outer shadow?
alt text http://dl.getdropbox.com/u/80699/Bildschirmfoto%202010-07-12%20um%2021.28.57.png
i only know shadowColor and shadowOffset
zoomed:
alt text http://dl.getdropbox.com/u/80699/Bildschirmfoto%202010-07-12%20um%2021.39.56.png
thanks!
The answer by dmaclach is only suitable for shapes that can easily be inverted. My solution is a custom view that works with any shape and also text. It requires iOS 4 and is resolution independent.
First, a graphical explanation of what the code does. The shape here is a circle.
The code draws text with a white dropshadow. If it's not required, the code could be refactored further, because the dropshadow needs to be masked differently. If you need it on an older version of iOS, you would have to replace the block and use an (annoying) CGBitmapContext.
- (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;
}
- (void)drawRect:(CGRect)rect {
UIFont *font = [UIFont fontWithName:#"HelveticaNeue-Bold" size:40.0f];
CGSize fontSize = [text_ sizeWithFont:font];
CGImageRef mask = [self createMaskWithSize:rect.size shape:^{
[[UIColor blackColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
[[UIColor whiteColor] setFill];
// custom shape goes here
[text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), 0) withFont:font];
[text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -1) withFont:font];
}];
CGImageRef cutoutRef = CGImageCreateWithMask([self blackSquareOfSize:rect.size].CGImage, mask);
CGImageRelease(mask);
UIImage *cutout = [UIImage imageWithCGImage:cutoutRef scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
CGImageRelease(cutoutRef);
CGImageRef shadedMask = [self createMaskWithSize:rect.size shape:^{
[[UIColor whiteColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, 1), 1.0f, [[UIColor colorWithWhite:0.0 alpha:0.5] CGColor]);
[cutout drawAtPoint:CGPointZero];
}];
// create negative image
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
[[UIColor blackColor] setFill];
// custom shape goes here
[text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -1) withFont:font];
UIImage *negative = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRef innerShadowRef = CGImageCreateWithMask(negative.CGImage, shadedMask);
CGImageRelease(shadedMask);
UIImage *innerShadow = [UIImage imageWithCGImage:innerShadowRef scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
CGImageRelease(innerShadowRef);
// draw actual image
[[UIColor whiteColor] setFill];
[text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -0.5) withFont:font];
[[UIColor colorWithWhite:0.76 alpha:1.0] setFill];
[text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -1) withFont:font];
// finally apply shadow
[innerShadow drawAtPoint:CGPointZero];
}
Although Steve's answer does work, it didn't scale to my particular case and so I ended up creating an inner shadow by applying a shadow to a CGPath after clipping my context:
CGContextRef context = UIGraphicsGetCurrentContext();
// clip context so shadow only shows on the inside
CGPathRef roundedRect = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:4].CGPath;
CGContextAddPath(context, roundedRect);
CGContextClip(context);
CGContextAddPath(context, roundedRect);
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, 0), 3, [UIColor colorWithWhite:0 alpha:1].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:0 alpha:1].CGColor);
CGContextStrokePath(context);
Which results in:
You can change the shadow offset and blur to change the shadow style. If you need it to be darker, you can also successively add and then stroke the same CGPath, which will make the shadows pile up.
I use FXLabel for inner shadow, look here https://github.com/nicklockwood/FXLabel.
Yes, I have a blog post about it here. The short answer is to borrow the JTAInnerShadowLayer from here, subclass UILabel, and change its layerclass by overriding + (Class)layerClass so that it returns JTAInnerShadowLayer class like this:
+ (Class)layerClass
{
return [JTAInnerShadowLayer class];
}
Then in the init method for the layer set up the JTAInnerShadowLayer something like this:
[self setBackgroundColor:[UIColor clearColor]];
JTAInnerShadowLayer *innerShadow = (JTAInnerShadowLayer *)[self layer];
[innerShadow setClipForAnyAlpha:YES];
[innerShadow setOutsideShadowSize:CGSizeMake(0.0, 1.0) radius:1.0];
[innerShadow setInsideShadowSize:CGSizeMake (0.0, 4.0) radius:6.0];
/* Uncomment this to make the label also draw the text (won't work well
with black text!*/
//[innerShadow drawOriginalImage];
(or just borrow the JTAIndentLabel class).
You will have to do your own custom drawing to get an inner shadow. There's no way currently to do it with a standard UILabel.
Here's a reasonable explanation on doing inner shadow with Quartz.
Inner shadow in Core Graphics
Try PaintCode trial version! For beginners, you'll learn how to create custom drawings. It's very nice! :3 You can use it for practice or for learning, but don't use it all the time, you should also learn how to create drawings on your own.
Disclaimer: I'm not an endorser, I was just amazed by my discovery. ;)