Rob
Rob

Reputation: 4947

CAShapeLayer missing pixel in top-left corner

I'm using a CAShapeLayer with a UIBezierPath to draw a bordered box in a view.

This is working fine, but the very first pixel (top, left) is not drawn.

This is my code:

let focusSize = CGRect(x: focusX, y: focusY, width: focusWidth, height: focusHeight)
let focusPath = UIBezierPath(roundedRect: focusSize, cornerRadius: 0)

let borderLayer = CAShapeLayer()
borderLayer.path = focusPath.cgPath
borderLayer.fillColor = UIColor.clear.cgColor
borderLayer.strokeColor = UIColor.white.cgColor
borderLayer.lineWidth = 2
borderLayer.frame = self.someView.bounds
self.someView.layer.addSublayer(borderLayer)

The result (note the pixel in the top, left corner):

Weird pixel

I thought this might be related to antialiasing, but playing around with the x, y and borderWidth does not seem to fix the issue. Does anyone know what is causing this?

Upvotes: 4

Views: 442

Answers (3)

James P
James P

Reputation: 4836

For some reason when you create a path using (roundedRect rect: CGRect, cornerRadius: CGFloat) it is not closed (despite what it says in the documentation).

Calling focusPath.close() closes the path and removes the missing pixel.

However, if you don't want rounded corners just use a normal rect and it will draw correctly.

let focusPath = UIBezierPath(rect: focusSize)

For anyone interested here are the resulting paths:

UIBezierPath(roundedRect:  CGRect(x: 10, y: 10, width: 100, height: 100), cornerRadius: 0)
<UIBezierPath: 0x6180000b8000; <MoveTo {10, 10}>,
 <LineTo {110, 10}>,
 <LineTo {110, 110}>,
 <LineTo {10, 110}>,
 <LineTo {10, 10}>,
 <LineTo {10, 10}>

//After calling path.close()
<UIBezierPath: 0x6180000b8000; <MoveTo {10, 10}>,
 <LineTo {110, 10}>,
 <LineTo {110, 110}>,
 <LineTo {10, 110}>,
 <LineTo {10, 10}>,
 <LineTo {10, 10}>,
 <Close>

 UIBezierPath(rect: CGRect(x: 10, y: 10, width: 100, height: 100))
<UIBezierPath: 0x6180000b7f40; <MoveTo {10, 10}>,
 <LineTo {110, 10}>,
 <LineTo {110, 110}>,
 <LineTo {10, 110}>,
 <Close>

Setting the lineCap may make the box appear correctly, but isn't fixing the issue as it just extends the visible end points and covers up the missing bit. Set borderLayer.lineJoin = kCALineJoinBevel to see why this can be a problem.

Upvotes: 6

Girish Nair
Girish Nair

Reputation: 5216

Yes as per the doc it should be a rect but I dont think it mentions the lineWidth

This seems to be the reason why you see a pixel missing

borderLayer.lineWidth = 2

Now when you change the width to 1 then it draws a perfect rectangle without any missing pixels

Changing the lineWidth to 10 shows considerable difference in the gap

The solution to your problem would be using this

borderLayer.lineCap = kCALineCapSquare

Check these lineCap & Line Cap Values

Upvotes: 2

deadbeef
deadbeef

Reputation: 5563

The reason you're seeing a missing pixel on your rect is because you haven't configured the lineCap property correctly.

Try adding this line :

borderLayer.lineCap = kCALineCapSquare

For more on line cap (and line join which is also important to understand) see this page : http://calayer.com/core-animation/2016/05/22/cashapelayer-in-depth.html#line-cap

Upvotes: 2

Related Questions