add UIBezierPath to UIView - ios

i have a UIView with layout constraint (width:400, height:180) and i want to add a bezier inside the view.
my bezier is this:
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
// origin, height | left border
[bezierPath moveToPoint: CGPointMake(0, 20)];
// origin, height | left border
[bezierPath addLineToPoint: CGPointMake(0, _donationView.frame.size.height)];
// height, width | bottom border
[bezierPath addLineToPoint: CGPointMake(_donationView.frame.size.width , _donationView.frame.size.height)];
[bezierPath addLineToPoint: CGPointMake(_donationView.frame.size.width, 20)];
[bezierPath addLineToPoint: CGPointMake(_donationView.frame.size.width - 20, 20)];
[bezierPath addLineToPoint: CGPointMake(_donationView.frame.size.width - 30, 15)];
[bezierPath addLineToPoint: CGPointMake(_donationView.frame.size.width - 40, 20)];
[bezierPath addLineToPoint: CGPointMake(0, 20)];
[bezierPath closePath];
[UIColor.grayColor setFill];
[bezierPath fill];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
after creating bezier i pass to:
CAShapeLayer *shapeView = [[CAShapeLayer alloc] initWithLayer:_donationView.layer];
[shapeView setPath:bezierPath.CGPath];
_donationView.layer.mask = shapeView;
but the bezier goes to this CGRect(0, 0, 240, 128);
how can i have a CGRect of (0,0,400, 180)?
thanks guys

The problem is that you're doing this too soon, before donationView has attained its final size. Thus, when you read off its frame.size, you are getting the wrong result. You need to postpone the addition of the mask until after layout has taken place.
You can also run into further issues if donationView is ever resized later. It is quite annoying that a mask layer (or any layer) doesn't get to participate in autolayout. So if donationView is resized, you will have to remove this mask and create a new mask with the correct size and add it again.

Related

How to create a multi colour progress view with different values for each colour

I've tried different ways and other resources on how to create a progress bar with 3 different colours and show progress according the values provided from the server but i can't find anything. How can i create a progress bar like this one below. I want to show the progress according to the values i can get from the backend which can change for different users. Here is the example json. Green -> "Won", Red -> "Lost" and darkGray -> "undecided"
"condition": {
"won": 2,
"lost": 3,
"undecided": 0
}
- (void)setCurrentProgressWithIndex1:(float)index1 index2:(float)index2 index3:(float)index3 {
_index1 = index1;
_index2 = index2;
_index3 = index3;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self drawProgressWithContext:ctx];
}
- (void)drawProgressWithContext:(CGContextRef)ctx {
float width = self.frame.size.width;
// index
float start_index = 0.0f;
if (_index1 != 0.0f) {
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
bezierPath.lineCapStyle = kCGLineCapRound;
bezierPath.lineJoinStyle = kCGLineJoinBevel;
[bezierPath moveToPoint: CGPointMake(start_index, 0)];
[bezierPath addLineToPoint: CGPointMake(start_index, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index1, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index1, 0)];
[UIColor.redColor setFill];
[bezierPath fill];
start_index += width * _index1;
}
if (_index2 != 0.0f) {
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
bezierPath.lineCapStyle = kCGLineCapRound;
bezierPath.lineJoinStyle = kCGLineJoinBevel;
[bezierPath moveToPoint: CGPointMake(start_index, 0)];
[bezierPath addLineToPoint: CGPointMake(start_index, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index2, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index2, 0)];
[UIColor.greenColor setFill];
[bezierPath fill];
start_index += width * _index2;
}
if (_index3 != 0.0f) {
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
bezierPath.lineCapStyle = kCGLineCapRound;
bezierPath.lineJoinStyle = kCGLineJoinBevel;
[bezierPath moveToPoint: CGPointMake(start_index, 0)];
[bezierPath addLineToPoint: CGPointMake(start_index, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index3, 20)];
[bezierPath addLineToPoint: CGPointMake(start_index + width * _index3, 0)];
[UIColor.blueColor setFillx];
[bezierPath fill];
}
}
you can use this framework to get the output
https://github.com/minjoongkim/MJProgressView
another related framework
https://github.com/mac-gallagher/MultiProgressView
https://github.com/fxm90/GradientProgressBar

How to change the frame of striped progress bar in iOS?

i want to obtain the progress bar like in the image below:
Striped loading progress bar with triangle at the end
Here is what i've done so far, with the help of the project from github https://github.com/iluuu1994/ITProgressBar-iOS, so here is what i attempted:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(42, self.frame.size.height-10), NO, 0.f);
UIColor* color = [UIColor colorWithRed:251.0f/255.0f green:235.0f/255.0f blue:77.0f/255.0f alpha:1.0f];
UIBezierPath *bezierPath = [UIBezierPath new];
[bezierPath moveToPoint:CGPointMake(0.0, 0.0)];
[bezierPath addLineToPoint:CGPointMake(23, 0)];
[bezierPath addLineToPoint:CGPointMake(42, 29)];
[bezierPath addLineToPoint:CGPointMake(23, 70)];
[bezierPath addLineToPoint:CGPointMake(0, 70)];
[bezierPath addLineToPoint:CGPointMake(20, 29)];
[bezierPath addLineToPoint:CGPointMake(0, 0)];
[bezierPath closePath];
[color setFill];
[bezierPath fill];
}];
}
I tried to override the drawRect method from UIView but nothing seems to work, so i fought to apply a mask to the layer I'am using.

How to add an arrow in UIImageView?

I have to an arrow in a UIImageView. it may act like a transparent space in UIImageView. a sample image is attached and highlighted with yellow box.
Use mask,for example you have a 200*100 image
Code
CAShapeLayer * shapeLayer = [CAShapeLayer layer];
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(0, 0)];
[bezierPath addLineToPoint:CGPointMake(0,100)];
[bezierPath addLineToPoint:CGPointMake(10,100)];
[bezierPath addLineToPoint:CGPointMake(20,90)];
[bezierPath addLineToPoint:CGPointMake(30, 100)];
[bezierPath addLineToPoint:CGPointMake(200,100)];
[bezierPath addLineToPoint:CGPointMake(200, 0)];
[bezierPath closePath];
shapeLayer.path = bezierPath.CGPath;
self.imageview.layer.mask = shapeLayer;
Note:The clip part is can still catch touch event or gesture as part of Imageview,you may rewrite pointInside to Ignore touch event
Edit About how it works
You just need to create any shape you like ,the about code I just create a shape like this,then use this shape to set mask
So,if you want make a right arrow,just create a shape like this
CAShapeLayer * shapeLayer = [CAShapeLayer layer];
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(0, 0)];
[bezierPath addLineToPoint:CGPointMake(0,100)];
[bezierPath addLineToPoint:CGPointMake(170,100)];
[bezierPath addLineToPoint:CGPointMake(180,90)];
[bezierPath addLineToPoint:CGPointMake(190, 100)];
[bezierPath addLineToPoint:CGPointMake(200,100)];
[bezierPath addLineToPoint:CGPointMake(200, 0)];
[bezierPath closePath];
shapeLayer.path = bezierPath.CGPath;
Shape screenshot
So,what you need is
Figure out what shape you want(the outline mask shape)
Use bezierPath to draw
Set mask

How to draw an open ended triangle with a specific angle in an UIView?

I'm stumped here and need some assistance with this. For testing purposes I have a UIView that I draw into via drawRect (code below). I'm able to draw a circle with an specific angle and such but it's filled and closed. I need it to be open and stroked. I'm currently using UIBezierPath to do this but is there better way to do this? I can draw the open ended triangle like I want in UIBezierPath but I can't specify the angle that I need it to be (code below also). Any assistance with this would be much appreciated. Thanks.
The picture below is how I would like it to look when I draw it with a specific angle.
#import "AngleView.h"
#define DEGREES_TO_RADIANS(degrees) ((M_PI * degrees)/ 180)
#implementation AngleView
-(void)drawRect:(CGRect)rect {
UIBezierPath *aPath = [UIBezierPath bezierPathWithArcCenter:CGPointZero
radius:50
startAngle:0
endAngle:DEGREES_TO_RADIANS(45)
clockwise:NO];
[aPath fill];
}
#end
and here is how I can draw it without a specific angle.
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(86.5, 33.5)];
[bezierPath addLineToPoint: CGPointMake(60.5, 62.5)];
[bezierPath addLineToPoint: CGPointMake(87.5, 62.5)];
[[UIColor blackColor] setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
Instead of trying draw one single line you can draw two separate originating from same point and you can give angle to one of them.
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0, 119.5); //here you can set originating point of line
CGContextRotateCTM(context, -45 * M_PI / 180); //Change Angle here -45
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(0, 0)];
[bezierPath addCurveToPoint: CGPointMake(39, 0) controlPoint1: CGPointMake(39, 0) controlPoint2: CGPointMake(39, 0)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
CGContextRestoreGState(context);
//Second UIBezierPath with 0 angle
context = UIGraphicsGetCurrentContext();
UIBezierPath* bezier2Path = UIBezierPath.bezierPath;
[bezier2Path moveToPoint: CGPointMake(0, 119.5)];
[bezier2Path addCurveToPoint: CGPointMake(38.5, 119.5) controlPoint1: CGPointMake(38.5, 119.5) controlPoint2: CGPointMake(38.5, 119.5)];
[UIColor.blackColor setStroke];
bezier2Path.lineWidth = 1;
[bezier2Path stroke];
CGContextRestoreGState(context);
}

Creating a triangle shape in a UIButton

I am trying to create a button with a triangle icon/shape in it. I have created the triangle in Paintcode then copied the beizer path and added it to the button layer as below.
-(void)setupShowHideRouteButton {
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(10.5, 8.5)];
[bezierPath addCurveToPoint: CGPointMake(40.5, 8.5) controlPoint1: CGPointMake(39.5, 8.5) controlPoint2: CGPointMake(40.5, 8.5)];
[bezierPath addLineToPoint: CGPointMake(26.39, 22.3)];
[bezierPath addLineToPoint: CGPointMake(25.2, 23.5)];
[bezierPath addLineToPoint: CGPointMake(10.5, 8.5)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = self.showHideRouteViewBtn.bounds;
shapeLayer.path = bezierPath.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.lineWidth = 2;
[self.showHideRouteViewBtn.layer addSublayer:shapeLayer];
}
However, this does not appear to work. I am setting the background color of the UIButton so I know the frame is correct and outlet working as expected it is just not adding the shape?
Second Approach
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(10.5, 8.5)];
[bezierPath addCurveToPoint: CGPointMake(40.5, 8.5) controlPoint1: CGPointMake(39.5, 8.5) controlPoint2: CGPointMake(40.5, 8.5)];
[bezierPath addLineToPoint: CGPointMake(26.39, 22.3)];
[bezierPath addLineToPoint: CGPointMake(25.2, 23.5)];
[bezierPath addLineToPoint: CGPointMake(10.5, 8.5)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
// Create a mask layer and the frame to determine what will be visible in the view.
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = self.showHideRouteViewBtn.bounds;
// Create a path with the rectangle in it.
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
// Set the path to the mask layer.
maskLayer.path = bezierPath.CGPath;
// Release the path since it's not covered by ARC.
CGPathRelease(path);
// Set the mask of the view.
self.showHideRouteViewBtn.layer.mask = maskLayer;
This did not work either.
Try the following:
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = self.showHideRouteViewBtn.bounds;
shapeLayer.path = bezierPath.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.lineWidth = 2;
self.showHideRouteViewBtn.layer.mask = shapeLayer;

Resources