1

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 :)

linus_hologram
  • 1,595
  • 13
  • 38
  • i am pretty sure the "cut off" thing is because you are painting out of your rect... ;) don't paint your line at 0,0 but add your linewidth to it...so move to (linewidth/2, linewidth/2) instead of (0,0)....and do the same on all edges... – Chris Feb 16 '20 at 16:18
  • @Chris okay but if I look at my code I'm strictly using the view's frame so how is it even possible to paint out of my rect? – linus_hologram Feb 16 '20 at 16:19
  • because of the linewidth ...it is 3 – Chris Feb 16 '20 at 16:20
  • What if you turn off antialiasing? – matt Feb 16 '20 at 16:33
  • thanks @matt I found the issue.... look at the accepted answer - I didn't take the line with into consideration... – linus_hologram Feb 16 '20 at 16:38

2 Answers2

2

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

Rob
  • 415,655
  • 72
  • 787
  • 1,044
1

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

Chris
  • 7,579
  • 3
  • 18
  • 38
  • thanks for your answer... this is interesting. The problem I have is that I can't make the line thicker than 3 - the curve will be quite small on the screen so I can't make it 10 – linus_hologram Feb 16 '20 at 16:30