I've created an arc at a 12 o'clock position on a CAShapeLayer and added it as a sublayer onto a UIView. The arc is rough or bumpy. I tried antialiasing, corner radius, line join, and have also tried rasterizing at one point but no luck. Any ideas why it's not smooth and what to do to make it smoother? If make the same arc at 9 or 3 o'clock they are smooth, but not 12 or 6 o'clock. Appreciate any help.
Here's the code:
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, nil, 38.25, 38.25, 25, degreesToRadians(304), degreesToRadians(236), YES);
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setAllowsEdgeAntialiasing:YES];
[shapeLayer setEdgeAntialiasingMask:kCALayerLeftEdge | kCALayerRightEdge | kCALayerBottomEdge | kCALayerTopEdge];
[shapeLayer setCornerRadius:2.0];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:[color CGColor]];
[shapeLayer setLineCap:kCALineCapRound];
[shapeLayer setLineWidth:3.0];
[shapeLayer setPath:path];
Here's a link to the arcs. Top and bottom are not smooth like on the left. They look less smooth on a device. Screen capture sort of smoothed them out more than they actually are.
http://oi57.tinypic.com/2wf4i9c.jpg
It looks like your layer is off the pixel bounds. That is, it’s rasterizing the layer, then shifting it by a fractional number of pixels before drawing. My first suggestion would be to round those x.25 values to whole numbers.
By the way, why are you passing 304, 236 as the startAngle and endAngle in CGPathAddArc()? Those parameters are in radians, not degrees.
edgeAntialiasingMask and allowsEdgeAntialiasing has to do with how the edges of the layer’s rectangular bounds are rendered, and should not affect the contents.
Edit: the difference is subtle, but when I stop using fractional values, the slight bumps in the line go away. By the way, if you post your images in .png format, it’s easier to see what’s going on, because they don’t get the artifacting from jpg compression.
// Fractional values
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, nil, 138.25, 138.25, 25, degreesToRadians(304), degreesToRadians(236), YES);
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:[UIColor blueColor].CGColor];
[shapeLayer setLineCap:kCALineCapRound];
[shapeLayer setLineWidth:3.0];
[shapeLayer setPath:path];
CGPathRelease(path);
[self.view.layer addSublayer:shapeLayer];
// Integral values
CGMutablePathRef newPath = CGPathCreateMutable();
CGPathAddArc(newPath, nil, 200, 138, 25, degreesToRadians(304), degreesToRadians(236), YES);
CAShapeLayer *newShapeLayer = [CAShapeLayer layer];
[newShapeLayer setLineJoin:kCALineJoinRound];
[newShapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[newShapeLayer setStrokeColor:[UIColor blueColor].CGColor];
[newShapeLayer setLineCap:kCALineCapRound];
[newShapeLayer setLineWidth:3.0];
[newShapeLayer setPath:newPath];
CGPathRelease(newPath);
[self.view.layer addSublayer:newShapeLayer];
And the result:
When I overlay them in Difference Mode in Photoshop, you can see where the pixels differ:
Related
got a _imageView which is properly added and located
if a layer is assigned to _imageView.layer.mask, it worked:
CAShapeLayer *maskLayer = [CAShapeLayer layer];
[maskLayer setPath:maskPath;
[maskLayer setPosition:CGPointMake(50, 50)]
[_imageViewLayer setMask:maskLayer];
now the mask blocks content outside its path
however,if a layer is assigned to one of _imageView.layer.sublayers, it blocks everything:
_imageViewLayer = [CAShapeLayer layer];
CGPathRef circlePath = CGPathCreateWithEllipseInRect(CGRectMake(0, 0, 100, 100), nil);
[_imageViewLayer setPath: circlePath];
[_imageViewLayer setPosition: CGPoinMake(50, 50)];
CGPathRelease(circlePath);
[_imageViewLayer setFillColor:[[UIColor redColor] CGColor]];
[[_imageView layer] addSublayer:_imageViewLayer];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
CGPathRef maskPath = CGPathCreateWithEllipseInRect(CGRectMake(0, 0, 10, 10), nil);
[maskLayer setPath:maskPath];
CGPathRelease(maskPath);
[maskLayer setPosition: CGPoinMake(50, 50)];
[_imageViewLayer setMask: maskLayer];
if i remove the maskLayer, i can see a red circle on _imageView
could anyone give me a hint, or it's just a bug?
From Apple:
/* A layer whose alpha channel is used as a mask to select between the
* layer's background and the result of compositing the layer's
* contents with its filtered background. Defaults to nil. When used as
* a mask the layer's `compositingFilter' and `backgroundFilters'
* properties are ignored. When setting the mask to a new layer, the
* new layer must have a nil superlayer, otherwise the behavior is
* undefined. Nested masks (mask layers with their own masks) are
* unsupported. */
#property(nullable, strong) CALayer *mask;
"When setting the mask to a new layer, the new layer must have a nil superlayer, otherwise the behavior is undefined. "
Your _imageViewLayer has already had a superlayer [_imageView layer] when you are setting maskLayer to _imageViewLayer as a mask.
I am trying to animate loading animation along the path of rounded rect,using following code. (I need to animate border of square as loader ,partial border.) It does not happen to be smooth ,as soon as it reaches the end of path it jumps to the starting start stroke.
shapeLayer = [CAShapeLayer layer];
CGRect shapeRect = _border.bounds;//CGRectMake(0.0f, 0.0f, 200.0f, 100.0f);
[shapeLayer setBounds:shapeRect];
[shapeLayer setContents:_border.image];
shapeLayer.contentsGravity = kCAGravityCenter;
NSLog(#"center of border %#",NSStringFromCGPoint(_border.center) );
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:shapeLayer.bounds cornerRadius:BORDER_ANGLE];
[shapeLayer setPath:path.CGPath];
shapeLayer.position = _border.center;
[[self.view layer] addSublayer:shapeLayer];
[[self.view layer] insertSublayer:shapeLayer above:_border.layer];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor whiteColor] CGColor]];
[shapeLayer setLineWidth:2.5f];
shapeLayer.strokeStart = 0;
shapeLayer.strokeEnd = 0.1;
UIImageView * image = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"ball"]];
image.center = CGPointMake(187, 220.5);
startAnim = [CABasicAnimation animationWithKeyPath:#"strokeStart"];
startAnim.duration = SPEED;
startAnim.delegate = self;
startAnim.repeatCount=200;
startAnim.removedOnCompletion = FALSE;
startAnim.toValue = [NSNumber numberWithFloat:0.3];
endAnim = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
endAnim.toValue = [NSNumber numberWithFloat:0.99];
endAnim.duration = SPEED;
endAnim.repeatCount = 200;
endAnim.removedOnCompletion = NO;
endAnim.cumulative = NO;
[shapeLayer addAnimation:startAnim forKey:nil];
[shapeLayer addAnimation:endAnim forKey:nil];
But above code jitters after reaching at endAnim's tovalue and starts again,its not smooth.
Please help me know which parts going wrong. I saw many tutorials for loader but all those were for circular path not square.
[enter image description here][1]
! [1]: https://i.stack.imgur.com/FwT01.png
just want to fill 50% of border and rotate it continuously
Okay. So do not use animation of a shape layer. Draw the square. Now make a mask that is a filled square with half of it empty. Place the mask on your square. So now we see only 50% of your square because the rest is masked out. Got it?
Now rotate the mask.
I want to draw a circle in iOS. It can be an image view or view, I want to adjust its size on gestures. Like, dragging from outside to the center, the radius of the circle should reduce and so vice versa.
Please note that I want to fix the position of that view, circle or image. I just want to adjust the size as it is dragged.
try this
- (IBAction)pinch:(UIPinchGestureRecognizer *)sender {
double scale=[sender scale];
self.view1.frame=CGRectMake(self.view1.frame.origin.x*scale, self.view1.frame.origin.y*scale, self.view1.frame.size.width*scale, self.view1.frame.size.height*scale);
[self.view1.layer setCornerRadius:5];
}
Try this.
- (IBAction)pan:(UIPanGestureRecognizer *)sender {
for (CALayer *layer in self.imageView.layer.sublayers) {
[layer removeFromSuperlayer];
}
CGPoint scale =[sender locationInView:sender.view];
CAShapeLayer *circleLayer = [CAShapeLayer layer];
[circleLayer setBounds:CGRectMake(0.0f, 0.0f, [_imageView bounds].size.width,
[_imageView bounds].size.height)];
[circleLayer setPosition:CGPointMake(CGRectGetMidX([_imageView bounds]),CGRectGetMidY([_imageView bounds]))];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, CGRectGetWidth(_imageView.frame)+scale.x, CGRectGetHeight(_imageView.frame)+scale.y)];
[circleLayer setPath:[path CGPath]];
[circleLayer setStrokeColor:[[UIColor redColor] CGColor]];
[circleLayer setLineWidth:1.0f];
[circleLayer setFillColor:[[UIColor clearColor] CGColor]];
[[_imageView layer] addSublayer:circleLayer];
}
I am drawing jigsaw using CAShapeLayer.Drawing a line that is easy part and also i did it.But now i want to draw ellipse with cutting some portion of that ellipse.Now my question is how can i cut ellipse using CGPathAddEllipseInRect?Here is my code :
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setBounds:self.bounds];
[shapeLayer setPosition:self.center];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
[shapeLayer setLineWidth:3.0f];
// Setup the path
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 10, 10);
CGPathAddLineToPoint(path, NULL, 75,10);
CGPathAddEllipseInRect(path, NULL, CGRectMake(75, 10, 50, 20));
[shapeLayer setPath:path];
CGPathRelease(path);
[[self layer] addSublayer:shapeLayer];
Hint will be appreciated.
Don't use CGPathAddEllipseInRect, instead you need to calculate the start and end positions and use CGPathAddArcToPoint or, more likely, CGPathAddQuadCurveToPoint.
I am using this code to draw a circle inside a UIImageView.
CAShapeLayer *circleLayer = [CAShapeLayer layer];
// Give the layer the same bounds as your image view
[circleLayer setBounds:CGRectMake(0.0f, 0.0f, [photoView bounds].size.width,
[photoView bounds].size.height)];
// Position the circle anywhere you like, but this will center it
// In the parent layer, which will be your image view's root layer
[circleLayer setPosition:CGPointMake(coordsFinal.x + 130, coordsFinal.y + 200)];
// Create a circle path.
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:
CGRectMake(0.0f, 0.0f, 50.0f, 50.0f)];
// Set the path on the layer
[circleLayer setPath:[path CGPath]];
// Set the stroke color
[circleLayer setStrokeColor:[color CGColor]];
// Set the stroke line width
[circleLayer setLineWidth:2.0f];
// Add the sublayer to the image view's layer tree
[[photoView layer] addSublayer:circleLayer];
I would like the circle to be empty, and to have only the border. How can I remove the fill?
Set the fill color to transparent (alpha 0)
[circleLayer setFillColor:[[UIColor colorWithWhite:0 alpha:0] CGColor]];
AKA...
[circleLayer setFillColor:[[UIColor clearColor] CGColor]];
(thanks Zev)
The CAShapeLayer manual page explicitly says set fillColor to nil to perform no fill operation. That is not the same as filling with a transparent color, which some have suggested doing, as the latter will overwrite existing contents making them clear/transparent, and should be considered a destructive operation.