I have a square UIView which i need to divide into cross section with two different colors as shown
here is my code to achieve that graph:
//Define the drawing path
UIBezierPath *path1 = [[UIBezierPath alloc] init];
//path Move to start drawing position
[path1 moveToPoint:CGPointMake(200, 100)];
//Draw a straight line from the starting position to(100, 200)
[path1 addLineToPoint:CGPointMake(100, 100)];
//To draw a line from (100, 200) to (200, 200)
[path1 addLineToPoint:CGPointMake(100, 200)];
//close path
[path1 closePath];
CAShapeLayer *layer1 = [[CAShapeLayer alloc] init];
layer1.path = path1.CGPath;
layer1.fillColor = [UIColor colorWithRed:0.88 green:0.87 blue:0.87 alpha:1.0].CGColor;
[self.view.layer addSublayer:layer1];
UIBezierPath *path2 = [[UIBezierPath alloc] init];
[path2 moveToPoint:CGPointMake(200, 100)];
[path2 addLineToPoint:CGPointMake(200, 200)];
[path2 addLineToPoint:CGPointMake(100, 200)];
[path2 closePath];
CAShapeLayer *layer2 = [[CAShapeLayer alloc] init];
layer2.path = path2.CGPath;
layer2.fillColor = [UIColor colorWithRed:0.89 green:0.57 blue:0.53 alpha:1.0].CGColor;
[self.view.layer addSublayer:layer2];
Here is the result:
result image
Related
I'm making mask using UIBezierPath and CAShapeLayer with this code:
- (void)createPath {
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(0, 0)];
[path addLineToPoint:CGPointMake(100, 100)];
[path moveToPoint:CGPointMake(100, 100)];
[path addLineToPoint:CGPointMake(0, 100)];
[path moveToPoint:CGPointMake(0, 100)];
[path addLineToPoint:CGPointMake(0, 0)];
[path closePath];
CAShapeLayer *layer = [CAShapeLayer new];
layer.frame = self.contentView.bounds;
layer.path = path.CGPath;
self.contentView.layer.mask = layer;
}
But instead of masking my contentView disappears completely. I tried to look at path in debugger and it looks exactly as I want it to look.
When using the layer.mask, the first is to get the right path. You don't need to move to a new point every time. By that way, your path is made of three or four subpaths which can not be closed to form a right path.
The second is trying to use in the View class itself, not to call for other subviews, like contentView. Because you may not know when to call it in subviews. Run the following in a UIView subclass, like in a UITableViewCell (awake from nib). You can get what I mean. If you really want to use contentView, just find the right position to put your layer code. like override setNeedLayout, etc.
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
[self createPath];
}
- (void)createPath {
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(0, 0)];
[path addLineToPoint:CGPointMake(100, 100)];
// [path moveToPoint:CGPointMake(100, 100)];
[path addLineToPoint:CGPointMake(0, 100)];
// [path moveToPoint:CGPointMake(0, 100)];
[path addLineToPoint:CGPointMake(0, 0)];
[path closePath];
CAShapeLayer *layer = [CAShapeLayer new];
layer.frame = self.contentView.bounds;
layer.path = path.CGPath;
self.layer.mask = layer; // not self.contentView.layer.mask;
}
I need help in adding arrow head to UIBezierPath.
I have created multiple lines by using UIBezierPath, which are in different directions.
Now, I want to create arrow head at the end point of the line.
I tried to add UIImage to UILable using NSAttributedString to UILable with NSTextAttachment.
But, here problem is arrow head is always down. And I want to align arrow along the line.
Here is my code:
UIBezierPath *ShotPath = [UIBezierPath bezierPath];
NSValue *PointValue = [[[PathArray objectAtIndex:i] valueForKey:#"ShotPath"] objectAtIndex:k];
CGPoint FirstPoint; = [PointValue CGPointValue];
[ShotPath moveToPoint:FirstPoint];
CAShapeLayer *line = [CAShapeLayer layer];
line.lineWidth = 4.0;
line.path=ShotPath.CGPath;
line.fillColor = [UIColor clearColor].CGColor;
line.strokeColor = [UIColor colorWithRed:231/255.0 green:83/255.0 blue:73/255.0 alpha:1].CGColor;
[[BaseView layer] addSublayer:line];
NSValue *PointValue = [[[PathArray objectAtIndex:i] valueForKey:#"ShotPath"] objectAtIndex:[[[PathArray objectAtIndex:i] valueForKey:#"ShotPath"] count]-1];
CGPoint OtherPoint = [PointValue CGPointValue];
UILabel *lbl = [[UILabel alloc]initWithFrame:CGRectMake(OtherPoint.x-6, OtherPoint.y-8, 12, 12)];
lbl.backgroundColor =[UIColor clearColor];
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
attachment.image = [UIImage imageNamed:#"DropDown_Icon"];
NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:attachment];
lbl.attributedText = attachmentString;
[lbl setTextColor:[UIColor blackColor]];
lbl.font =[UIFont boldSystemFontOfSize:22.0];
[BaseView addSubview:lbl];
Here is my current output :
By the reference of this answer:
append arrowhead to UIBezierPath
Solved my query:
Answer:
- (void)viewDidLoad {
[super viewDidLoad];
UIBezierPath *path = [UIBezierPath bezierPath];
CGPoint point1 = CGPointMake(100, 100);
CGPoint point2 = CGPointMake(300, 300);
[path moveToPoint:point1];
[path addQuadCurveToPoint:point2 controlPoint:point1];
CAShapeLayer *shape = [CAShapeLayer layer];
shape.path = path.CGPath;
shape.lineWidth = 10;
shape.strokeColor = [UIColor blueColor].CGColor;
shape.fillColor = [UIColor clearColor].CGColor;
shape.frame = self.view.bounds;
[self.view.layer addSublayer:shape];
CGFloat angle = atan2f(point2.y - point1.y, point2.x - point1.x);
CGFloat distance = 15.0;
path = [UIBezierPath bezierPath];
[path moveToPoint:point2];
[path addLineToPoint:[self calculatePointFromPoint:point2 angle:angle + M_PI_2 distance:distance]]; // to the right
[path addLineToPoint:[self calculatePointFromPoint:point2 angle:angle distance:distance]]; // straight ahead
[path addLineToPoint:[self calculatePointFromPoint:point2 angle:angle - M_PI_2 distance:distance]]; // to the left
[path closePath];
shape = [CAShapeLayer layer];
shape.path = path.CGPath;
shape.lineWidth = 2;
shape.strokeColor = [UIColor redColor].CGColor;
shape.fillColor = [UIColor redColor].CGColor;
shape.frame = self.view.bounds;
[self.view.layer addSublayer:shape];
}
- (CGPoint)calculatePointFromPoint:(CGPoint)point angle:(CGFloat)angle distance:(CGFloat)distance {
return CGPointMake(point.x + cosf(angle) * distance, point.y + sinf(angle) * distance);
}
I'm using dr-Charts API to draw bar graphs in my application. I'm drawing the bar graph:
BarChart *barChartView = [[BarChart alloc] initWithFrame:CGRectMake(0, 150, WIDTH(self.view), HEIGHT(self.view) - 600)];
[barChartView setDataSource:self];
[barChartView setDelegate:self];
[barChartView setLegendViewType:LegendTypeHorizontal];
[barChartView setShowCustomMarkerView:TRUE];
[barChartView drawBarGraph];
[barChartView setDrawGridY:TRUE];
[barChartView setDrawGridX:FALSE];
[self.view addSubview:barChartView];
Actually, I want my bar chart rectangles to be rounded rect, something similar to this:
So, In BarChart.m, I started playing with API CAShapeLayer & UIBezierPath:
- (UIBezierPath *)drawBarPathWithStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint{
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y)];
//UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y) byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(10, 10)];
[path stroke];
return path;
}
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
[shapeLayer setPath:[[self drawBarPathWithStartPoint:endPoint endPoint:startPoint] CGPath]];
[shapeLayer setStrokeColor:[barData.barColor CGColor]];
[shapeLayer setFillColor:[barData.barColor CGColor]];
[shapeLayer setFillRule:kCAFillRuleEvenOdd];
[shapeLayer setLineWidth:0.5f];
[shapeLayer setOpacity:0.7f];
[shapeLayer setShadowRadius:0.0f];
[shapeLayer setShadowColor:[[UIColor clearColor] CGColor]];
[shapeLayer setShadowOpacity:0.0f];
[shapeLayer setValue:[barData.yAxisArray objectAtIndex:i] forKey:#"data"];
After following many answers on StackOverflow, I couldn't succeed.
I tried many answers like shapeLayer.cornerRadius = 10;
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y) byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(10, 10)];
Could you please help.
Try to draw wide line with rounded corners, for example
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:CGPointMake(topX, topY)];
[linePath addLineToPoint:CGPointMake(bottomX, bottomY)];
CAShapeLayer *line = [CAShapeLayer layer];
line.frame = some_frame;
line.lineCap = kCALineCapRound;
line.path = linePath.CGPath;
line.fillColor = nil;
line.lineWidth = 6.0;
line.cornerRadius = 3.0;
line.opacity = 1.0;
line.strokeColor = [UIColor greenColor].CGColor;
[line setMasksToBounds:YES];
Can anybody help me to get frame of CAShapeLayer which is been added in UIImageView as a SubLayer?
Following is my code for the same:
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.fillColor = [[UIColor redColor] colorWithAlphaComponent:0.15].CGColor;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.lineWidth = 3.0;
shapeLayer.zPosition = 1;
[_imgBackground.layer insertSublayer:shapeLayer atIndex:1];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(50, 50)]; // left top point of rack
[path addLineToPoint:CGPointMake(100, 50)]; // right top point of rack
[path addLineToPoint:CGPointMake(100, 100)]; // right bottom point of rack
[path addLineToPoint:CGPointMake(50, 100)]; // left bottom point of rack
[path closePath]; // closing rectangle/path
shapeLayer.path = path.CGPath;
As I know we can get frame from the following line:
CGRect bounds = CGPathGetBoundingBox(shapeLayer.path);
But it only returns frame according to self.view
I want its frame according to UIImageView.
Thanks in advance.
Well I got the answer from James's comment. Following line saved my day:
CGRect bounds = [self.view convertRect:CGPathGetBoundingBox(shapeLayer.path) toView:_imgBackground];
from this reference.
I was tinkering around with BezierPath and noticed something that I can't seem to figure out. This is the code-
UIBezierPath *path = [UIBezierPath new];
CGSize screenDimensions = CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
CGPoint firstPoint = CGPointMake(screenDimensions.width/2, screenDimensions.height/2);
CGPoint secondPoint = CGPointMake(screenDimensions.width/2 + 10, screenDimensions.height/2 + 10);
CGPoint thirdPoint = CGPointMake(screenDimensions.width/2 - 10, screenDimensions.height/2 + 10);
[path moveToPoint:firstPoint];
[path addLineToPoint:secondPoint];
[path addLineToPoint:thirdPoint];
[path addLineToPoint:firstPoint];
[path closePath];
CAShapeLayer *tempLayer = [CAShapeLayer new];
[tempLayer setPath:path.CGPath];
UIView *tempView = [[UIView alloc] initWithFrame:CGRectMake(screenDimensions.width/2 - 30, screenDimensions.height/2 - 30, 100, 100)];
// UIView *tempView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
[tempView setBackgroundColor:[UIColor blueColor]];
tempView.layer.mask = tempLayer;
[self.view addSubview:tempView];
If i run the above code block, nothing is drawn on the UIView that is added on the screen. But if i were to comment the current "tempView" and uncomment the currently commented allocation, it would draw on screen perfectly. Can anyone please point out what am I doing wrong here when setting the frame or is it something else?
UIBezierPath *path = [UIBezierPath bezierPath];
CGPoint point1 = CGPointMake(100, 100);
CGPoint point2 = CGPointMake(200, 200);
CGPoint point3 = CGPointMake(300, 300);
[path moveToPoint:point1];
[path addQuadCurveToPoint:point3 controlPoint:point2];
CAShapeLayer *shape = [CAShapeLayer layer];
shape.path = path.CGPath;
shape.lineWidth = 5;
shape.strokeColor = [UIColor blueColor].CGColor;
shape.fillColor = [UIColor clearColor].CGColor;
shape.frame = self.view.bounds;
[self.view.layer addSublayer:shape];
You can see below output :
It's a simple problem.
Let's say the screen dimensions are 320x480 (and old iPhone). This means that firstPoint will be 160, 240.
In the current code, your view's frame is 130, 210, 100, 100. It's width and height are both 100.
The shape layer will be drawn relative to the view's bounds, not its frame. So as far as the layer and bezier path are concerned, the view's bounds are 0, 0, 100, 100. Since firstPoint is outside those bounds, it doesn't appear. It's actually drawn but outside the visible bounds of the view.
When you switch the two lines that create the view, the view's frame becomes 0, 0, 320, 480. This means its bounds is also 0, 0, 320, 480. The view is much larger now and the layer and bezier path fit and can be seen.
The proper solution is to create the coordinates of the bezier path based on the size of the view it will be applied to, not the size of the screen. This way, the bezier path will fit inside its view no matter how big it is.
More like this:
CGSize screenDimensions = CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
UIView *tempView = [[UIView alloc] initWithFrame:CGRectMake(screenDimensions.width/2 - 30, screenDimensions.height/2 - 30, 100, 100)];
// UIView *tempView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIBezierPath *path = [UIBezierPath new];
CGPoint firstPoint = CGPointMake(tempView.bounds.size.width/2, tempView.bounds.size.height/2);
CGPoint secondPoint = CGPointMake(tempView.bounds.size.width/2 + 10, tempView.bounds.size.height/2 + 10);
CGPoint thirdPoint = CGPointMake(tempView.bounds.size.width/2 - 10, tempView.bounds.size.height/2 + 10);
[path moveToPoint:firstPoint];
[path addLineToPoint:secondPoint];
[path addLineToPoint:thirdPoint];
[path addLineToPoint:firstPoint];
[path closePath];
CAShapeLayer *tempLayer = [CAShapeLayer new];
[tempLayer setPath:path.CGPath];
[tempView setBackgroundColor:[UIColor blueColor]];
tempView.layer.mask = tempLayer;
[self.view addSubview:tempView];