Why the circle path of the cashapelayer is not so round? - ios

The doucumet said that the CAShapLayer is anti-aliased ,why got this result.
I also draw a circle with the CGContext in the drawRect method,It's very perfect.
UIBezierPath *path = [UIBezierPath bezierPath];
[path appendPath:[UIBezierPath bezierPathWithOvalInRect:CGRectMake(150, 300, 136, 136)]];
self.circleLayer = [CAShapeLayer layer];
self.circleLayer.path = path.CGPath;
self.circleLayer.frame = self.bounds;
self.circleLayer.fillColor = [[UIColor whiteColor] colorWithAlphaComponent:0.6].CGColor;
[self.layer addSublayer:self.circleLayer];

According to the UIBezierPath docs, bezierPathWithOvalInRect: "creates a closed subpath that approximates the oval using a sequence of Bézier curves," so it may not draw a perfect circle.

If you use a UIBezierPath to draw a circle will not a perfect circle because its an approximation from Bézier curves.
You can draw a perfect circle from a square UIView. For example:
myView.layer.cornerRadius = myView.frame.size.width/2;
In this topic explain three methods to draw a circle -> How to draw a smooth circle with CAShapeLayer and UIBezierPath?

iOS 12:
I had the same issue on iPhone XS and XS Max. I don't know why, but on iPhone 8 there was no issue. The circle was perfectly round.
To solve the issue on XS and XS Max I set the flatness property of UIBezierPath to 0.0 and made sure that the center of the rect used for UIBezierPath is either an integer number or a floating number of e.g. 25.5. The center of my rect was y=51.166666 and this caused the issue that the oval was not round.

Related

How can we draw Circle progress as "three fourths" - 3/4?

Currently am working on a circular chart,There is no issue on drawing full circle. I also need to draw a 3/4 circle, Is there any specify math to define 3/4 circle path. I tried by reducing missed position values with full circle but cant get accurate result. Could anyone help with this? If any need I
UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f)
radius:(self.frame.size.height * 0.5) - ([_lineWidth floatValue]/2.0f)
startAngle:DEGREES_TO_RADIANS(startAngle)
endAngle:DEGREES_TO_RADIANS(endAngle)
clockwise:clockwise];
circle = [CAShapeLayer layer];
circle.path = circlePath.CGPath;
circle.lineCap = kCALineCapRound;
circle.fillColor = [UIColor clearColor].CGColor;
circle.lineWidth = [_lineWidth floatValue];
circle.zPosition = 1; [self.layer addSublayer:circle];
;
Please refer image below,
Note: I have attached the sample image from web but am sure i need the same results.
You already have the code that you need. The function bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise: takes a start angle and an end angle. If you pass in a start angle of 0 and an end angle of 3π/2 you'll get a 3/4 circle. (3π/2 is 270 degrees, or 3/4 of a full circle.)
Note that if your goal is to animate a circle from 0 to 360 degrees then you need to use a different technique. For that you want to create a path of the full circle, install that into a CAShapeLayer, then animate the layer's strokeEnd property from 0 to 1.

Attempting to mask a circle around an image not working

I have an image that I am attempting to mask a circle around so the image appears round. This somewhat works but the circle comes to a point on the top and bottom.
profileImageView.layer.cornerRadius = profileImageView.frame.size.width/2;
profileImageView.layer.masksToBounds = YES;
Should this code be drawing a perfect circle? It seems to draw a circle in one place but in two other places, its not working correctly.
I have had the best results masking the image view with a CAShapeLayer:
CGFloat radius = self.profileImageView.frame.size.width / 2.0;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius) radius:radius startAngle:0 endAngle:M_PI * 2.0 clockwise:TRUE];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = path.CGPath;
layer.lineWidth = 0;
self.profileImageView.layer.mask = layer;
Should this code be drawing a perfect circle?
Not necessarily. After all, the width and the height of this layer might not be the same. And even if they are, dividing by 2 might not give you a radius that fits perfectly into an integral number of points as they are mapped to pixels on the screen.
It really would be better, if what you want is a mask that's a circle, to give this layer an actual mask that is an actual circle. Misusing the corner radius as you are doing is just lazy (and, as you've discovered, it's error-prone).

Inverse CGPath in Rect

I'm looking to inverse a CGPath (relative to a Rect). By this, I mean I have defined a CGPath and I want to get a CGPath that outlines all other areas in the rect.
I've been experimenting different ways and been googling for a couple days now with no luck. I simply just cannot work it out!
Consider the following rect with defined CGPath (where green is the defined CGPath filled):
Now, with what I want (the inversed CGPath) would produce the following:
Specifically, this is for usage with an SKCropNode and SKPhysicsBody in the SpriteKit framework, however i'm producing the polygon for the mask node using CGPaths (and storing the path in an ivar).
Is inversing a CGPath possible, or some other way? If so, how?
Thanks in advance.
I copied the code from the answer #DavidRonnqvist linked to, but changed it to retrieve the UIBezierPath from a CGPath, which is what you need:
CGPath filledPath = ...; //This is the inner filled shape
CAShapeLayer *invertedLayer = [CAShapeLayer layer];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithCGPath:filledPath];
[invertedLayer setPath:[maskPath CGPath]];
[invertedLayer setFillRule:kCAFillRuleEvenOdd];
[invertedLayer setFillColor:[[UIColor colorOfYourChoice] CGColor]];
I didn't test this but in theory it should work.
Code sample, using UIBezierPath.reversing():
let cgPath: CGPath = ...
let size = cgPath.boundingBox.size
let inverted = UIBezierPath(rect: CGRect(origin: .zero, size: size))
inverted.append(UIBezierPath(cgPath: cgPath).reversing())
//draw result
let image = UIGraphicsImageRenderer(size: size).image { context in
context.cgContext.setFillColor(UIColor.red.cgColor)
context.cgContext.addPath(inverted.cgPath)
context.cgContext.fillPath()
}

Clipping a UIView with a bezier path. BezierPath seems not to be applied completely

I have to add a border with a wooden texture to a UIView. In order to do this I thought the following solution:
I have the original UIView (say uiViewA)
I create another UIView (say uiViewB) with the same size of the first one
I create a bezier path with a 8px width
Once created the path I apply this to uiViewB
I add uiViewB to uiViewA's subviews
The code to apply the bezier path is the following:
UIView* uiViewB = [[UIView alloc] initWithFrame:uiViewA.bounds];
UIImage* wood = [UIImage imageNamed:#"texture_wood"];
[uiViewB setBackgroundColor:[UIColor colorWithPatternImage:wood]];
// creation of the bezier path
UIBezierPath* borderPath = ... ;
[borderPath setLineWidth:8.0];
[borderPath moveToPoint:CGPointMake(0.0, 0.0)];
borderPath addLineToPoint:...
borderPath addArcWithCenter:...
borderPath addLineToPoint:...
borderPath addArcWithCenter:...
borderPath addLineToPoint:...
CAShapeLayer* borderMaskLayer = [CAShapeLayer layer];
[borderMaskLayer setFrame:uiViewA.bounds];
borderMaskLayer.path = [borderPath CGPath];
uiViewB.layer.mask = borderMaskLayer;
[uiViewB.layer setMasksToBounds:YES];
I would like to obtain the following result:
But the result I obtain is the following:
Do you have any idea of why bezier path seems not to be applied in the right way?
From the code you have there it looks like you are using the default fill and stroke of the shape layer that is used as a mask, thus the path is filled and you get the result that you are seeing.
What you want to do is set a clear fill color and set some random opaque stroke color. Then you will have to set an appropriate line width, line cap, line round, etc. to configure how the stroke looks.

Why is making two corners round slower than make all corners round?

Edit: Language updated to improve readability.
I made an image view with 2 rounded corners like this:
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.photoImageView.bounds byRoundingCorners:UIRectCornerBottomLeft|UIRectCornerBottomRight cornerRadii:CGSizeMake(10, 10)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = maskPath.CGPath;
self.photoImageView.layer.mask = maskLayer;
But it is slower than making all the corners round using this code.
self.photoImageView.layer.cornerRadius = 10;
Would anyone know what why and how I can improve my '2 corner' code please?
Your code is adding another stage to the drawing. Normally, the background is drawn (with the given cornerRadius) directly to the target, but with a mask specified, it is drawn to a temporary surface, then copied to the target using the mask.
There isn't any built-in functionality for only rounding some background corners in the standard CALayer object.
I do wonder how slow "slower" really is; is this premature optimisation?

Resources