I am setting a mask with all rounded corners path to a view like this:
CGFloat cornerRadius = CGRectGetHeight(self.myView.bounds) / 2;
CGSize cornerRadiusSize = CGSizeMake(cornerRadius, cornerRadius);
CGRect bounds = self.myView.bounds;
CAShapeLayer *maskLayer = [CAShapeLayer layer];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:cornerRadiusSize];
maskLayer.path = maskPath.CGPath;
self.myView.layer.mask = maskLayer;
that looks like this:
I am trying to animate it to a path with top left and top right rounded corners like this:
UIBezierPath *newMaskPath = [UIBezierPath bezierPathWithRoundedRect:bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:cornerRadiusSize];
CABasicAnimation *maskPathAnimation = [CABasicAnimation animationWithKeyPath:#"path"];
maskPathAnimation.duration = 0.3;
maskPathAnimation.fromValue = (id)maskLayer.path;
maskPathAnimation.toValue = (id)newMaskPath.CGPath;
[maskLayer addAnimation:maskPathAnimation forKey:nil];
maskLayer.path = newMaskPath.CGPath;
that looks like this:
But the animation is mangled, it looks like it animates some corners to other corners:
I solved it by creating the mask paths by hand using this method:
- (UIBezierPath *)getMaskPathForView:(UIView *)view withCorners:(UIRectCorner)corners {
CGSize size = view.bounds.size;
CGFloat cornerRadius = size.height / 2;
UIBezierPath *maskPath = [UIBezierPath bezierPath];
// top left
if (corners & UIRectCornerTopLeft) {
[maskPath moveToPoint:CGPointMake(0, cornerRadius)];
[maskPath addQuadCurveToPoint:CGPointMake(cornerRadius, 0) controlPoint:CGPointZero];
} else {
CGPoint topLeftCornerPoint = CGPointZero;
[maskPath moveToPoint:topLeftCornerPoint];
[maskPath addQuadCurveToPoint:topLeftCornerPoint controlPoint:topLeftCornerPoint];
}
// top right
if (corners & UIRectCornerTopRight) {
[maskPath addLineToPoint:CGPointMake(size.width - cornerRadius, 0)];
[maskPath addQuadCurveToPoint:CGPointMake(size.width, cornerRadius) controlPoint:CGPointMake(size.width, 0)];
} else {
CGPoint topRightCornerPoint = CGPointMake(size.width, 0);
[maskPath addLineToPoint:topRightCornerPoint];
[maskPath addQuadCurveToPoint:topRightCornerPoint controlPoint:topRightCornerPoint];
}
// bottom right
if (corners & UIRectCornerBottomRight) {
[maskPath addLineToPoint:CGPointMake(size.width, size.height - cornerRadius)];
[maskPath addQuadCurveToPoint:CGPointMake(size.width - cornerRadius, size.height) controlPoint:CGPointMake(size.width, size.height)];
} else {
CGPoint bottomRightCornerPoint = CGPointMake(size.width, size.height);
[maskPath addLineToPoint:bottomRightCornerPoint];
[maskPath addQuadCurveToPoint:bottomRightCornerPoint controlPoint:bottomRightCornerPoint];
}
// bottom left
if (corners & UIRectCornerBottomLeft) {
[maskPath addLineToPoint:CGPointMake(cornerRadius, size.height)];
[maskPath addQuadCurveToPoint:CGPointMake(0, size.height - cornerRadius) controlPoint:CGPointMake(0, size.height)];
} else {
CGPoint bottomLeftCornerPoint = CGPointMake(0, size.height);
[maskPath addLineToPoint:bottomLeftCornerPoint];
[maskPath addQuadCurveToPoint:bottomLeftCornerPoint controlPoint:bottomLeftCornerPoint];
}
[maskPath closePath];
return maskPath;
}
like this:
CAShapeLayer *maskLayer = [CAShapeLayer layer];
UIBezierPath *maskPath = [self getMaskPathForView:self.myView withCorners:UIRectCornerAllCorners];
maskLayer.path = maskPath.CGPath;
self.myView.layer.mask = maskLayer;
and:
UIBezierPath *newMaskPath = [self getMaskPathForView:self.myView withCorners:UIRectCornerTopLeft | UIRectCornerTopRight];
CABasicAnimation *maskPathAnimation = [CABasicAnimation animationWithKeyPath:#"path"];
maskPathAnimation.duration = 0.3;
maskPathAnimation.fromValue = (id)maskLayer.path;
maskPathAnimation.toValue = (id)newMaskPath.CGPath;
[maskLayer addAnimation:maskPathAnimation forKey:nil];
maskLayer.path = newMaskPath.CGPath;
I used this to round top two corners of my UITextFeild
CAShapeLayer * maskLayer = [CAShapeLayer layer];
maskLayer.path = [UIBezierPath bezierPathWithRoundedRect: self.emailFeild.bounds byRoundingCorners: UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii: (CGSize){10.0, 10.}].CGPath;
I also have border added to this UITextField as
self.emailFeild.layer.borderColor = [[self colorFromHexString:#"0x3263A3"]CGColor];
self.emailFeild.layer.borderWidth = 1.0f;
self.emailFeild.layer.mask = maskLayer;
but when I run it. It did not round the corners of border and I get this result
The mask layer doesn't get drawn, just used to compute the mask. Try:
-(void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius
{
CGRect bounds = self.bounds;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(radius, radius)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
CAShapeLayer* frameLayer = [CAShapeLayer layer];
frameLayer.frame = bounds;
frameLayer.path = maskPath.CGPath;
frameLayer.strokeColor = [UIColor redColor].CGColor;
frameLayer.fillColor = nil;
[self.layer addSublayer:frameLayer];
}
-(void)roundTopCornersRadius:(CGFloat)radius
{
[self roundCorners:(UIRectCornerTopLeft|UIRectCornerTopRight) radius:radius];
}
I have used a scrollView in my page in order to display maximum 10 Images. Images are displayed in a hexagonal shape but not in the shape i want. Now it looks like current hex shape
but i need to show like this- hex shape i need
I am posting my code below. Please anyone can guide me.
-(CAShapeLayer*)ChangeShape:(UIView*)view
{
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.frame = view.bounds;
CGFloat width = view.frame.size.width;
CGFloat height = view.frame.size.height;
CGFloat hPadding = width * 1 / 8 / 2;
UIGraphicsBeginImageContext(view.frame.size);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(width/2, 0)];
[path addLineToPoint:CGPointMake(width - hPadding, height / 4)];
[path addLineToPoint:CGPointMake(width - hPadding, height * 3 / 4)];
[path addLineToPoint:CGPointMake(width / 2, height)];
[path addLineToPoint:CGPointMake(hPadding, height * 3 / 4)];
[path addLineToPoint:CGPointMake(hPadding, height / 4)];
[path closePath];
[path closePath];
[path fill];
[path stroke];
maskLayer.path = path.CGPath;
UIGraphicsEndImageContext();
return maskLayer;
}
Change your ChangeShape function like this
-(CAShapeLayer*)ChangeShape:(UIView*)view{
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.frame = view.bounds;
CGFloat width = view.frame.size.width;
CGFloat height = view.frame.size.height;
CGFloat hPadding = width * 1 / 8 / 2;
UIGraphicsBeginImageContext(view.frame.size);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, height / 2)];
[path addLineToPoint:CGPointMake(width / 4 , hPadding)];
[path addLineToPoint:CGPointMake(width * 3 / 4, hPadding)];
[path addLineToPoint:CGPointMake(width, height / 2)];
[path addLineToPoint:CGPointMake(width * 3 / 4, height - hPadding)];
[path addLineToPoint:CGPointMake(width / 4, height - hPadding)];
[path closePath];
[path fill];
[path stroke];
maskLayer.path = path.CGPath;
UIGraphicsEndImageContext();
return maskLayer;
}
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);
i try to draw a circle for a given percent value (like if i have 25% i just draw a quarter of the circle). At the moment im just able to draw a full circle within my view. Any ideas to my problem?
Code atm:
- (UIBezierPath *)makeCircleAtLocation:(CGPoint)location radius:(CGFloat)radius
{
self.circleCenter = location;
self.circleRadius = radius;
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:self.circleCenter
radius:self.circleRadius
startAngle:0.0
endAngle:M_PI * 2.0
clockwise:YES];
return path;
}
- (void)drawCircleForLocation{
CGPoint location = CGPointZero;
location.x = self.frame.size.width/2;
location.y = self.frame.size.height/2;
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [[self makeCircleAtLocation:location radius:9] CGPath];
shapeLayer.strokeColor = [[UIColor whiteColor] CGColor];
shapeLayer.fillColor = nil;
shapeLayer.lineWidth = 1.5;
[self.layer addSublayer:shapeLayer];
}
2 Pi RAD = 360° = full circle.
25% of circle = 2 PI * 25%
- (UIBezierPath *)makeCircleAtLocation:(CGPoint)location radius:(CGFloat)radius percent:(CGFloat)percent
{
self.circleCenter = location; //????
self.circleRadius = radius; //????
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:location
radius:radius
startAngle:0.0
endAngle:((M_PI * 2.0) * percent)
clockwise:YES];
[path addLineToPoint:location];
[path closePath];
return path;
}