UIBezierPath not equally fat everywhere and cut off

438 Views Asked by At

I am trying to draw a line with a smooth quadratic curve attached to its end. Therefore, I am using this code:

import Foundation
import UIKit

class IndicatingView: UIView {
    var path: UIBezierPath!

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

    func createLineWithCurve() {
        path = UIBezierPath()

        path.move(to: CGPoint(x: 0, y: 0))

        path.addLine(to: CGPoint(x: 0, y: self.frame.height - self.frame.width))

        path.addQuadCurve(to: CGPoint(x: self.frame.width, y: self.frame.height), controlPoint: CGPoint(x: 0, y: self.frame.height))
    }

    override func draw(_ rect: CGRect) {
        self.createLineWithCurve()

        path.lineWidth = 3
        // Specify a border (stroke) color.
        UIColor.purple.setStroke()
        path.stroke()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

And the output I get looks quite good already:

Output

However, as you can see, the quad-curve is fatter than the normal line. Also, the quad-curve seems to be cut off at the bottom. How can I fix that? I want the path to be completely smooth without looking fatter at some points or being cut-off somehow.

Thanks in advance for your help :)

2

There are 2 best solutions below

0
Rob On BEST ANSWER

The problem is that your path is getting clipped by the bounds of the view. For example, in your vertical stroke starting at 0, 0, half of the path’s vertical stroke will fall outside of the left edge of the view.

You should use insetBy(dx:dy:) to inset the stroked path by half the line width, which will ensure the whole stroke will fall within the bounds of the view. And then use minX, maxX, etc. of that resulting inset CGRect to figure out the various CGPoint for your path.

For example:

class IndicatingView: UIView {
    func lineWithCurve() -> UIBezierPath {
        let lineWidth: CGFloat = 3
        let rect = bounds.insetBy(dx: lineWidth / 2, dy: lineWidth / 2)

        let path = UIBezierPath()
        path.lineWidth = lineWidth
        path.lineCapStyle = .square

        path.move(to: CGPoint(x: rect.minX, y: rect.minY))            
        path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY - rect.width))
        path.addQuadCurve(to: CGPoint(x: rect.maxX, y: rect.maxY), controlPoint: CGPoint(x: rect.minX, y: rect.maxY))

        return path
    }

    override func draw(_ rect: CGRect) {
        UIColor.purple.setStroke()
        lineWithCurve().stroke()
    }
}

That yields:

enter image description here

1
Chris On

here you see my "result" -> i made the linewidth a bit bigger that you can see, it paints perfect ;)

Apple draws always exactly on point, so if you paint a line of width 3 on point 0,0, one point is -1,0, one on 0,0 and one on 1,0

enter image description here