Colin Hancey
Colin Hancey

Reputation: 229

Draw multiple lines in Swift 4

In Swift 4, with the Xcode IDE, I can easily draw a circle with the following:

let circlePath = UIBezierPath(arcCenter: CGPoint(
    x: 100,
    y: 100),
    radius: 50,
    startAngle: CGFloat(0),
    endAngle:CGFloat(Double.pi * 2),
    clockwise: true)

let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath

shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.lineWidth = 2.0

However, I want to draw a line from point A to point B, not draw a circle. I can draw a line by following the example code here: https://www.youtube.com/watch?v=9sJxtzTo8W0

In order for the line to appear the same way that it does in the example, I need to change the view in the main storyboard.

Upvotes: 3

Views: 2091

Answers (1)

Rob
Rob

Reputation: 438232

Is there a way to simply draw a line on the fly, in the same way that I can draw a circle on the fly?

Sure, just create a UIBezierPath with addLine(to:) and then use the following path in your CAShapeLayer, just like you did for the circle in your example:

let startPoint = CGPoint(x: 10, y: 10)
let endPoint = CGPoint(x: 20, y: 5)

let linePath = UIBezierPath()
linePath.move(to: startPoint)
linePath.addLine(to: endPoint)

let shapeLayer = CAShapeLayer()
shapeLayer.path = linePath.cgPath

shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.lineWidth = 2

view.layer.addSublayer(shapeLayer)

Why does the circle render without me needing to define a class object, but the line requires me to define a class object?

The line doesn’t require you to define class object. It’s just another way of doing it and you can do both lines and circles (and whatever other types of shapes you want) using either the CAShapeLayer technique or the UIView subclass technique. They both work fine.

A UIView subclass approach, you first define your class:

class LineView: UIView {

    var startPoint: CGPoint? { didSet { setNeedsDisplay() } }
    var endPoint: CGPoint?   { didSet { setNeedsDisplay() } }

    override func draw(_ rect: CGRect) {
        guard let startPoint = startPoint, let endPoint = endPoint else { return }

        let linePath = UIBezierPath()
        linePath.move(to: startPoint)
        linePath.addLine(to: endPoint)
        linePath.lineWidth = 2

        UIColor.black.setStroke()
        linePath.stroke()
    }
}

And then instantiate one and add it to your view hierarchy:

let lineView = LineView()
lineView.backgroundColor = .lightGray
lineView.translatesAutoresizingMaskIntoConstraints = false
lineView.startPoint = CGPoint(x: 10, y: 10)
lineView.endPoint = CGPoint(x: 20, y: 5)
view.addSubview(lineView)

NSLayoutConstraint.activate([
    lineView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
    lineView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
    lineView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
    lineView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10)
])

Does this mean that I can only have 1 type of custom class in my entire project that is viewable at any given time?

Just like you can add whatever shapelayers you want, you can add your own custom UIView subclasses and add however many you want. So you could theoretically have a couple of subclass types, one for different types of shapes (circles, lines, rounded rectangles, whatever) and instantiate them and add them as subviews to whatever view on whatever scene you wanted.

Upvotes: 2

Related Questions