Henry
Henry

Reputation: 479

CGContext Drawing two adjacent rhomboids produce a very thin gap, how to reduce it?

The two adjacent rectangle is ok as image below.

override func draw(_ rect: CGRect) {
    // Drawing code
    let leftTop = CGPoint(x:50,y:50)
    let rightTop = CGPoint(x:150,y:100)

    let leftMiddle = CGPoint(x:50,y:300)
    let rightMiddle = CGPoint(x:150,y:300)

    let leftDown = CGPoint(x:50,y:600)
    let rightDown = CGPoint(x:150,y:650)

    let context = UIGraphicsGetCurrentContext()
    context?.addLines(between: [leftTop,rightTop,rightMiddle,leftMiddle])
    UIColor.black.setFill()
    context?.fillPath()

    context?.addLines(between: [leftMiddle,rightMiddle,rightDown,leftDown])
    context?.fillPath()


    let leftTop1 = CGPoint(x:200,y:50)
    let rightTop1 = CGPoint(x:300,y:100)

    let leftMiddle1 = CGPoint(x:200,y:300)
    let rightMiddle1 = CGPoint(x:300,y:350)

    let leftDown1 = CGPoint(x:200,y:600)
    let rightDown1 = CGPoint(x:300,y:650)

    context?.addLines(between: [leftTop1,rightTop1,rightMiddle1,leftMiddle1])
    UIColor.black.setFill()
    context?.fillPath()

    context?.addLines(between: [leftMiddle1,rightMiddle1,rightDown1,leftDown1])
    context?.fillPath()
}

You may need to zoom in to see the gap. If I draw a thin line to cover the gap, then any width may overlap if the color has an alpha channel.

enter image description here

Change shapeColor to let shapeColor = UIColor(white: 0.0, alpha: 0.5) and add context?.setShouldAntialias(false) let shapeColor = UIColor(white: 0.0, alpha: 0.5)

Upvotes: 4

Views: 297

Answers (2)

Mariam Hakobyan
Mariam Hakobyan

Reputation: 196

This is related to screen resolution. On devices with high resolution one point actually has 2 or 3 pixels. That's why to draw an inclined line iOS draws some edge pixels with some opacity, when 2 lines are next to each other these edge pixels overlap. In your case I was able to fix the issue by making the following change

        let leftTop1 = CGPoint(x:200,y:50)
    let rightTop1 = CGPoint(x:300,y:100)

    let leftMiddle1 = CGPoint(x:200,y:300)
    let rightMiddle1 = CGPoint(x:300,y:350)

    let leftMiddle2 = CGPoint(x:200,y:300.333)
    let rightMiddle2 = CGPoint(x:300,y:350.333)

    let leftDown1 = CGPoint(x:200,y:600)
    let rightDown1 = CGPoint(x:300,y:650)

    context?.addLines(between: [leftTop1,rightTop1,rightMiddle1,leftMiddle1])
    UIColor.black.setFill()

    context?.fillPath()

    context?.addLines(between: [leftMiddle2,rightMiddle2,rightDown1,leftDown1])
    context?.fillPath()

Basically moving down the second rhomboid by 1 pixel (1/3 = 0.333...), I have tested on a screen with 1:3 pixel density, for the solution to work on all devices you'll need to check the scaleFactor of the screen.

Upvotes: 1

Y.Bonafons
Y.Bonafons

Reputation: 2349

I have the same result using the CGContext even if I use setShouldAntialias(true) or if I try to call strokePath() on the context. But it works fine with sublayers, CGPath and strokeColor

class RhombView: UIView {
    let shapeColor: UIColor = .black

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setupView()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setupView()
    }

    func setupView() {
        let leftTop1 = CGPoint(x:200.0,y:50.0)
        let rightTop1 = CGPoint(x:300.0,y:100.0)

        let leftMiddle1 = CGPoint(x:200.0,y:300.0)
        let rightMiddle1 = CGPoint(x:300.0,y:350.0)

        let leftDown1 = CGPoint(x:200.0,y:600.0)
        let rightDown1 = CGPoint(x:300.0,y:650.0)

        var path = UIBezierPath()

        path.move(to: leftTop1)
        path.addLine(to: rightTop1)
        path.addLine(to: rightMiddle1)
        path.addLine(to: leftMiddle1)
        path.close()

        let subLayer1 = CAShapeLayer()
        subLayer1.path = path.cgPath
        subLayer1.frame = self.layer.frame
        subLayer1.fillColor = shapeColor.cgColor
        subLayer1.strokeColor = shapeColor.cgColor
        self.layer.addSublayer(subLayer1)

        path = UIBezierPath()

        path.move(to: leftMiddle1)
        path.addLine(to: rightMiddle1)
        path.addLine(to: rightDown1)
        path.addLine(to: leftDown1)
        path.close()

        let subLayer2 = CAShapeLayer()
        subLayer2.path = path.cgPath
        subLayer2.frame = self.layer.frame
        subLayer2.fillColor = shapeColor.cgColor
        subLayer2.strokeColor = shapeColor.cgColor
        self.layer.addSublayer(subLayer2)

        self.layer.backgroundColor = UIColor.white.cgColor
    }
}

If you remove both subLayer.strokeColor you will see the gap.

Upvotes: 3

Related Questions