Draw Line using CGContext - iphone

I want to draw line in table view cell so that I can place textfield & switch in single cell. I increased the height of cell. How can I draw line in cell?
I have subclass of UIView which contains following code
//Get the CGContext from this view
CGContextRef context = UIGraphicsGetCurrentContext();
//Set the stroke (pen) color
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
//Set the width of the pen mark
CGContextSetLineWidth(context, 1.0);
// Draw a line
//Start at this point
CGContextMoveToPoint(context, 10.0, 30.0);
//Give instructions to the CGContext
//(move "pen" around the screen)
CGContextAddLineToPoint(context, 310.0, 30.0);
//Draw it
CGContextStrokePath(context);
Then I have a tableViewController with grouped table style. In cellForRowAtIndexPath I have following code
//code to add textfield
DrawLineView *drawLine = [[DrawLineView alloc]init];
[cell addSubview:drawLine];
//code add switch
But it is not drawing any line. I can't use 2 different cells. I have to Please help me. This is my first to deal with graphics i iphone. Thanks

If all you want to do is draw a line, it would be a lot better to use a CAShapeLayer, pass it a path with a line, and then attach that as a sublayer of the cells content view. The table should perform better than using a view with a custom drawRect.
Example of drawing a line via CALayer and a path:
// You'll also need the QuartzCore framework added to the project
#import <QuartzCore/QuartzCore.h>
CAShapeLayer *lineShape = nil;
CGMutablePathRef linePath = nil;
linePath = CGPathCreateMutable();
lineShape = [CAShapeLayer layer];
lineShape.lineWidth = 4.0f;
lineShape.lineCap = kCALineCapRound;;
lineShape.strokeColor = [[UIColor blackColor] CGColor];
int x = 0; int y = 0;
int toX = 30; int toY = 40;
CGPathMoveToPoint(linePath, NULL, x, y);
CGPathAddLineToPoint(linePath, NULL, toX, toY);
lineShape.path = linePath;
CGPathRelease(linePath);
[self.layer addSublayer:lineShape];

Two things...
First, you usually don't draw into the cell itself.
You normally draw into the content view. Sometimes it makes sense draw into the cell's backgroundView or selectedBackgroundView.
[cell.contentView addSubview:drawLine];
Second, the default cell text labels cell.textLabel and cell.detailTextLabel have non-transparent background. Try setting their background colors to [UIColor clearColor].
Edit: one more thing: you need to set a proper frame for your drawLine
DrawLineView *drawLine = [[DrawLineView alloc]initWithFrame:cell.contentView.bounds];

I think a very simplified way of drawing a line is to create a UIView and fill it with desired color, then choose its width accordingly, and setup height to be 1 pixel like so:
var lineView : UIView = {
let view = UIView()
view.backgroundColor = UIColor.blackColor()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
self.view.addSubview(lineView)
self.view.addConstraints(NSLayoutConstraint.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["view": lineView])))
self.view.addConstraints(NSLayoutConstraint.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-50-[view(1)]", options: NSLayoutFormatOptions(), metrics: nil, views: ["view": lineView])))

Related

Change color of UIBezierPath in Swift

I am working on a form of design project. In this project, you select an object and can change the color or size, or move.
Started a prototype of the app.
I will use UIBezierPath to draw the ways of the shapes, and will use CAShapeLayer to add to a sublayer of UIView.
This code draw one shape for example:
let insetRect = CGRect(x: 20, y: 20, width: 100, height: 100)
let path = UIBezierPath(roundedRect: insetRect, cornerRadius: 30)
UIColor.redColor().setFill()
path.fill()
path.lineWidth = 100
UIColor.blackColor().setStroke()
path.stroke()
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.CGPath
shapeLayer.lineWidth = 1.0
self.view.layer.addSublayer(shapeLayer)
When the app starts, the shape is created without problems.
I wish I could click on the screen and automatically change the color (random) from shape.
Another problem: The shapes are random, and they rectangle, ellipse, rounded square. And due to these forms, I can not directly create on a View (reason for use the UIBezierPath).
Some can help me? I am trying to solve this for a few hours, and so far, found nothing.
Instead of adding bezierPath as sub layer, you can use a custom view. Pass a bezier path coordinates and color to the view. Keep instance of bezier path at class level and draw in DrawRect: method.
When user touches view in controller, pass custom view message to change color and redraw it.
You can simply change bezier path color by using:
yourColor.setStroke();
You can force your view to redraw bezier with changed color using:
customView.setNeedsDisplay();
EDIT
CustomView.m
-(id) initWithPath:(UIBezierPath *) path color:(UIColor *) color
{
if(self = [super init])
{
bezierPath = path;
pathColor = color;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[bezierPath stroke];
}
In controller, you can initialize as,
CustomView *customView = [[CustomView alloc] initWithPath:yourBezierPath color:[UIColor redColor];
customView.frame = yourFrame;
[self.view addSubview:customView];
To change color,
[customView setPathColor:[UIColor greenColor];
[customView setNeedsDisplay];
Hope it helps!

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

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.

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.

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.