CAShapeLayer drawing path primitives overlay - ios

I need to draw complex shape, something like this:
So I try:
CGFloat offsetAngle = 15 * M_PI/180;
CGPathAddArc(path, nil, 39/2, 30, 39/2-0.1, offsetAngle, -M_PI-offsetAngle, YES);
CGFloat linesXOffset = 2.5;
CGFloat linesYOffset = 30;
CGPathMoveToPoint(path, nil, linesXOffset, linesYOffset);
CGPathAddLineToPoint(path, nil, 39-linesXOffset, linesYOffset);
CGPathAddLineToPoint(path, nil,39/2,//X
linesYOffset + CGRectGetHeight(self.bounds)/2-220/2-linesYOffset);
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setPath:path];
So shape consists of triangle and arc.
But I have the following overlay problem:
Is it possible to fix?

1) Easier:
CGFloat linesYOffset = 35;
2) Another way:
CGFloat offsetAngle = 15 * M_PI/180;
CGMutablePathRef path1 = CGPathCreateMutable();
CGPathAddArc(path1, nil, 39/2, 30, 39/2-0.1, offsetAngle, -M_PI-offsetAngle, YES);
CGMutablePathRef path2 = CGPathCreateMutable();
CGFloat linesXOffset = 5.5;
CGFloat linesYOffset = 30;
CGPathMoveToPoint(path2, nil, linesXOffset, linesYOffset);
CGPathAddLineToPoint(path2, nil, 39-linesXOffset, linesYOffset);
CGPathAddLineToPoint(path2, nil,39/2,//X
linesYOffset + 600/2-220/2-linesYOffset);
CAShapeLayer *shapeLayer1 = [CAShapeLayer layer];
[shapeLayer1 setPath:path1];
CAShapeLayer *shapeLayer2 = [CAShapeLayer layer];
[shapeLayer2 setPath:path2];
CAShapeLayer* container = [CAShapeLayer layer];
[container addSublayer:shapeLayer1];
[container addSublayer:shapeLayer2];

Related

Draw Triangle iOS

The code below is drawing me a circle, how can I modify the existing code to draw a triangle instead?
_colorDotLayer = [CALayer layer];
CGFloat width = self.bounds.size.width-6;
_colorDotLayer.bounds = CGRectMake(0, 0, width, width);
_colorDotLayer.allowsGroupOpacity = YES;
_colorDotLayer.backgroundColor = self.annotationColor.CGColor;
_colorDotLayer.cornerRadius = width/2;
_colorDotLayer.position = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
While there are shown several Core Graphics solution, I want to add a Core Animation based solution.
- (void)viewDidLoad
{
[super viewDidLoad];
UIBezierPath* trianglePath = [UIBezierPath bezierPath];
[trianglePath moveToPoint:CGPointMake(0, view3.frame.size.height-100)];
[trianglePath addLineToPoint:CGPointMake(view3.frame.size.width/2,100)];
[trianglePath addLineToPoint:CGPointMake(view3.frame.size.width, view2.frame.size.height)];
[trianglePath closePath];
CAShapeLayer *triangleMaskLayer = [CAShapeLayer layer];
[triangleMaskLayer setPath:trianglePath.CGPath];
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0,0, size.width, size.height)];
view.backgroundColor = [UIColor colorWithWhite:.75 alpha:1];
view.layer.mask = triangleMaskLayer;
[self.view addSubview:view];
}
code based on my blog post.
#implementation TriangleView {
CAShapeLayer *_colorDotLayer;
}
- (void)layoutSubviews {
[super layoutSubviews];
if (_colorDotLayer == nil) {
_colorDotLayer = [CAShapeLayer layer];
_colorDotLayer.fillColor = self.annotationColor.CGColor;
[self.layer addSublayer:_colorDotLayer];
}
CGRect bounds = self.bounds;
CGFloat radius = (bounds.size.width - 6) / 2;
CGFloat a = radius * sqrt((CGFloat)3.0) / 2;
CGFloat b = radius / 2;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, -radius)];
[path addLineToPoint:CGPointMake(a, b)];
[path addLineToPoint:CGPointMake(-a, b)];
[path closePath];
[path applyTransform:CGAffineTransformMakeTranslation(CGRectGetMidX(bounds), CGRectGetMidY(bounds))];
_colorDotLayer.path = path.CGPath;
}
- (void)awakeFromNib {
[super awakeFromNib];
self.annotationColor = [UIColor redColor];
}
#end
Result:
I think is more easy than this solutions:
UIBezierPath *path = [UIBezierPath new];
[path moveToPoint:(CGPoint){20, 0}];
[path addLineToPoint:(CGPoint){40, 40}];
[path addLineToPoint:(CGPoint){0, 40}];
[path addLineToPoint:(CGPoint){20, 0}];
// Create a CAShapeLayer with this triangular path
// Same size as the original imageView
CAShapeLayer *mask = [CAShapeLayer new];
mask.frame = self.viewTriangleCallout.bounds;
mask.path = path.CGPath;
This is my white triangle:
You can change points or rotate if you want:
UIBezierPath *path = [UIBezierPath new];
[path moveToPoint:(CGPoint){0, 0}];
[path addLineToPoint:(CGPoint){40, 0}];
[path addLineToPoint:(CGPoint){20, 20}];
[path addLineToPoint:(CGPoint){0, 0}];
// Create a CAShapeLayer with this triangular path
// Same size as the original imageView
CAShapeLayer *mask = [CAShapeLayer new];
mask.frame = self.viewTriangleCallout.bounds;
mask.path = path.CGPath;
Example code, it is based on this SO Answer which draws stars:
#implementation TriangleView
-(void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
int sides = 3;
double size = 100.0;
CGPoint center = CGPointMake(160.0, 100.0);
double radius = size / 2.0;
double theta = 2.0 * M_PI / sides;
CGContextMoveToPoint(context, center.x, center.y-radius);
for (NSUInteger k=1; k<sides; k++) {
float x = radius * sin(k * theta);
float y = radius * cos(k * theta);
CGContextAddLineToPoint(context, center.x+x, center.y-y);
}
CGContextClosePath(context);
CGContextFillPath(context); // Choose for a filled triangle
// CGContextSetLineWidth(context, 2); // Choose for a unfilled triangle
// CGContextStrokePath(context); // Choose for a unfilled triangle
}
#end
Use UIBezeierPaths
CGFloat radius = 20;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, (center.x + bottomLeft.x) / 2, (center.y + bottomLeft.y) / 2);
CGPathAddArcToPoint(path, NULL, bottomLeft.x, bottomLeft.y, bottomRight.x, bottomRight.y, radius);
CGPathAddArcToPoint(path, NULL, bottomRight.x, bottomRight.y, center.x, center.y, radius);
CGPathAddArcToPoint(path, NULL, center.x, center.y, bottomLeft.x, bottomLeft.y, radius);
CGPathCloseSubpath(path);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithCGPath:path];
CGPathRelease(path);

How to make a UIView with optional rounded corners and border?

I am applying corner radius to a UIView i.e. UIRectCornerTopLeft and UIRectCornerTopRight. When I apply this, the border is gone at the corners. How to avoid this?
This is how I apply border to UIView:
[self.middleView addRoundedCorners:UIRectCornerTopLeft|UIRectCornerTopRight withRadii:CGSizeMake(4, 4)];
self.middleView.layer.borderWidth = 0.5f;
self.middleView.layer.borderColor = [[UIColor colorWith8BitRed:0 green:0 blue:0 alpha:0.25]
And this is a category I am using for applying optional rounded corners:
#import "UIView+Roundify.h"
#implementation UIView (Roundify)
- (void)addRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii {
CALayer *tMaskLayer = [self maskForRoundedCorners:corners withRadii:radii];
self.layer.mask = tMaskLayer;
}
- (CALayer*)maskForRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii {
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.bounds;
UIBezierPath *roundedPath = [UIBezierPath bezierPathWithRoundedRect:
maskLayer.bounds byRoundingCorners:corners cornerRadii:radii];
maskLayer.fillColor = [[UIColor whiteColor] CGColor];
maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
maskLayer.path = [roundedPath CGPath];
return maskLayer;
}
Try below code it work
Your view which you want to rounded TopLeft and TopRight
UIView *view1 = [[UIView alloc]initWithFrame:CGRectMake(50, 100, 100, 100)];
[view1 setBackgroundColor:[UIColor grayColor]];
[self.view addSubview:view1];
Set Corner as below code
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:view1.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.view.bounds;
maskLayer.path = maskPath.CGPath;
view1.layer.mask = maskLayer;
OUTPUT IS:
Found this piece of code. Have not actually tried it, but seems like it is what you need.
- (void)drawDashedBorderAroundView:(UIView *)v {
//border definitions
CGFloat cornerRadius = 10;
CGFloat borderWidth = 2;
NSInteger dashPattern1 = 8;
NSInteger dashPattern2 = 8;
UIColor *lineColor = [UIColor orangeColor];
//drawing
CGRect frame = v.bounds;
CAShapeLayer *_shapeLayer = [CAShapeLayer layer];
//creating a path
CGMutablePathRef path = CGPathCreateMutable();
//drawing a border around a view
CGPathMoveToPoint(path, NULL, 0, frame.size.height - cornerRadius);
CGPathAddLineToPoint(path, NULL, 0, cornerRadius);
CGPathAddArc(path, NULL, cornerRadius, cornerRadius, cornerRadius, M_PI, -M_PI_2, NO);
CGPathAddLineToPoint(path, NULL, frame.size.width - cornerRadius, 0);
CGPathAddArc(path, NULL, frame.size.width - cornerRadius, cornerRadius, cornerRadius, -M_PI_2, 0, NO);
CGPathAddLineToPoint(path, NULL, frame.size.width, frame.size.height - cornerRadius);
CGPathAddArc(path, NULL, frame.size.width - cornerRadius, frame.size.height - cornerRadius, cornerRadius, 0, M_PI_2, NO);
CGPathAddLineToPoint(path, NULL, cornerRadius, frame.size.height);
CGPathAddArc(path, NULL, cornerRadius, frame.size.height - cornerRadius, cornerRadius, M_PI_2, M_PI, NO);
//path is set as the _shapeLayer object's path
_shapeLayer.path = path;
CGPathRelease(path);
_shapeLayer.backgroundColor = [[UIColor clearColor] CGColor];
_shapeLayer.frame = frame;
_shapeLayer.masksToBounds = NO;
[_shapeLayer setValue:[NSNumber numberWithBool:NO] forKey:#"isCircle"];
_shapeLayer.fillColor = [[UIColor clearColor] CGColor];
_shapeLayer.strokeColor = [lineColor CGColor];
_shapeLayer.lineWidth = borderWidth;
_shapeLayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:dashPattern1], [NSNumber numberWithInt:dashPattern2], nil];
_shapeLayer.lineCap = kCALineCapRound;
//_shapeLayer is added as a sublayer of the view, the border is visible
[v.layer addSublayer:_shapeLayer];
v.layer.cornerRadius = cornerRadius;
}
This piece of code adds a dashed line, but you can modify that by _shapeLayer.lineDashPattern.
Unless there is some specific requirement which we're not aware of, the bezier path and mask are unnecessary if all you're trying to do is round the corners and have a border. I would normally just do this:
myView.layer.borderWidth=2;
myView.layer.cornerRadius=5;
Is it that you only want the top corners rounded that you need to not use the layer rounding? If so, why not use that and then overlay a thin view to cover the bottom bit? A bit fiddly, but I find that the more you can rely on the standard controls to draw themselves rather than having to step into core graphics, the better.
Edit: ok, given that it needs to have the bottom corners not rounded, how about if you had a category on UIView with 2 subviews: 1 with 4 rounded corners and another layed over the top (self bringSubviewToFront) which simply covers the rounded view's "footer" with a non-rounded strip, ie a view with equal width and tiny height which is equal to the rounded corner radius. If you have a solid color background then just make both subviews the same; if you have some texture or image background, make them both transparent and put the texture/image on the super view (the parent view who is using your category's specific layout method). Then finally, put the border on that same superview. Should work, let me know what you think.

iOS shape with border and corner cutoff

I have this shape:
http://screencast.com/t/9UUhAXT5Wu
But the border isn't following it at the cutoff - how could I fix it?
This is my code for the current view:
self.view.backgroundColor = color;
CALayer *backgroundLayer = self.view.layer;
CAShapeLayer *mask = CAShapeLayer.new;
mask.frame = backgroundLayer.bounds;
mask.fillColor = [[UIColor blackColor] CGColor];
CGFloat width = backgroundLayer.frame.size.width;
CGFloat height = backgroundLayer.frame.size.height;
CGMutablePathRef path = CGPathCreateMutable();
int cornerCutSize = 30;
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, nil, width, 0);
CGPathAddLineToPoint(path, nil, width, height - cornerCutSize);
CGPathAddLineToPoint(path, nil, width - cornerCutSize, height);
CGPathAddLineToPoint(path, nil, width, height);
CGPathAddLineToPoint(path, nil, 0, height);
CGPathAddLineToPoint(path, nil, 0, 0);
CGPathCloseSubpath(path);
mask.path = path;
CGPathRelease(path);
backgroundLayer.mask = mask;
//add border
CGColorRef borderColor = [UIColor redColor].CGColor;
[backgroundLayer setBorderColor:borderColor];
[backgroundLayer setBorderWidth:3];
Setting the mask layer doesn't change the shape of the border. You'll need to create two layers and assign one to the mask and stroke one, something like:
CAShapeLayer *mask = [CAShapeLayer new];
mask.frame = backgroundLayer.bounds;
mask.path = path;
backgroundLayer.mask = mask;
CAShapeLayer *stroke = [CAShapeLayer new];
stroke.frame = backgroundLayer.bounds;
stroke.path = path;
stroke.lineWidth = 3;
stroke.strokeColor = [UIColor redColor].CGColor;
stroke.fillColor = nil;
[backgroundLayer addSublayer:stroke];

Change the default shape of UIImageview shape rectangle

Now I m developing an project in that I need to show the image with border like below shape.
How can I do this? I have no idea to do this. Please any idea to solve.
May be this code will help you...
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:YourImageVIew.bounds byRoundingCorners:(UIRectCornerTopRight | UIRectCornerBottomRight) cornerRadii:CGSizeMake(50.0, 50.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = YourImageVIew.bounds;
maskLayer.path = maskPath.CGPath;
YourImageVIew.layer.mask = maskLayer;
Here is how you can create the path for masking:
- (UIBezierPath *)curvedRectWithFrame:(CGRect)frame radius:(CGFloat)radius
{
double halfFrameHeight = ((double)frame.size.height / 2);
// Check if the radius is too small.
if (radius < halfFrameHeight) {
radius = halfFrameHeight;
}
CGFloat arcAngle = asin(halfFrameHeight/radius);
CGFloat centerX = frame.origin.x + (frame.size.width - radius);
CGFloat centerY = frame.origin.y + halfFrameHeight;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:frame.origin];
[path addLineToPoint:CGPointMake(centerX + radius * cos(arcAngle), frame.origin.y)];
[path addArcWithCenter:CGPointMake(centerX, centerY) radius:radius startAngle:-arcAngle endAngle:arcAngle clockwise:YES];
[path addLineToPoint:CGPointMake(frame.origin.x, path.currentPoint.y)];
[path closePath];
return path;
}
Then you can apply the shape mask to your image:
const CGFloat kCurveRadius = 500.;
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = yourImageView.bounds;
maskLayer.path = [self curvedRectWithFrame:maskLayer.bounds radius:kCurveRadius].CGPath;
yourImageView.layer.mask = maskLayer;

Rotate CAShapeLayer around center of UIView

I am working on animation of CAShapeLayer. I am drawing quarter circle(refer image) which is in 1st quadrant(as per my knowledge). Now I want to rotate it 90 degree so it should fix in 2nd quadrant with animation on single tap gesture. I tried it but it is not fixing into 2nd quadrant. I want to rotate around center of UIView.
Single tap gesture:
- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture {
CGPoint touchPoint=[gesture locationInView:self];
NSArray* subLayerArray = [self.layer sublayers];
for (int i = 0; i < subLayerArray.count; i++) {
CAShapeLayer* layer = [subLayerArray objectAtIndex:i];
if(CGPathContainsPoint(layer.path, NULL, touchPoint, NO)){
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, self.bounds.size.width/2-layer.frame.size.width/2, self.bounds.size.height/2-layer.frame.size.height/2, 100, 0*M_PI, M_PI, YES);
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = path;
anim.rotationMode = kCAAnimationRotateAuto;
anim.calculationMode = kCAAnimationPaced;
anim.fillMode = kCAFillModeForwards;
anim.removedOnCompletion = NO;
anim.duration = 10.0;
[layer addAnimation:anim forKey:#""];
}
}
}
Drawing Code:
- (void)drawRect:(CGRect)rect {
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, rect.size.width/2, rect.size.height/2, 100, (0), (M_PI_2), NO);
CGPathAddArc(path, NULL, rect.size.width/2, rect.size.height/2, 100-50, (M_PI_2), (0), YES);
CGPathCloseSubpath(path);
CAShapeLayer* arcLayer = [[CAShapeLayer alloc]init];
arcLayer.path = path;
arcLayer.frame = CGPathGetPathBoundingBox(path);
arcLayer.fillColor = [UIColor yellowColor].CGColor;
arcLayer.bounds = CGPathGetPathBoundingBox(path);
[self.layer addSublayer:arcLayer];
}
and Image:

Resources