Draw Line With Rounder Corners – WWDC20 Scholarship

Having a basic working engine of the game, I had an idea to make a smooth animation selecting winning fields. The idea sounds easy, a line from one place to another. What can be difficult in drawing a simple line?

It’s very simple to draw a tiny line, it looks good but I need a thick line and there is one small challenge. The line always is a rectangle, like this:

It doesn’t look nice so I started to look for any solution to draw the line with rounded corners. Unfortunately, I didn’t find anything, nobody even asked such questions on StackOverflow. I decided to find a solution on my own. And, I have one.

The final result looks like this:

The solution is very simple, draw a circle at the starting point, draw another circle in the starting point and move it to finish point, during moving the second circle start drawing a line from the first circle to the second. Beneath it looks like:

Combined together:

Let’s code it.

Firstly, you need a starting circle.

let startPointPath = UIBezierPath(arcCenter: startPoint, radius: finishLineWidth, startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)

let startPointShapeLayer = CAShapeLayer()
startPointShapeLayer.
fillColor = finishLineColor
startPointShapeLayer.
strokeColor = finishLineColor
startPointShapeLayer.
path = startPointPath.cgPath
self.finishLineView.layer.addSublayer(startPointShapeLayer)

There are few constant, you can change them in your project based on your needs. Below are mine:

let finishLineColor = #colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1).cgColor
let finishLineWidth: CGFloat = 12
let finishLineAnimationDuration: CFTimeInterval = 2

var finishLineView: UIView = {
let view = UIView()
    view.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.30)
    view.frame = frameOfMainView
    view.isUserInteractionEnabled = true
    return view
}()

frameOfMainView is the whole screen.
startPoint and endPoint are based on winning fields, there are instances of CGPoint.

Next, you need to draw a circle that will be moved.

let linePath = UIBezierPath(arcCenter: startPoint, radius: finishLineWidth, startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)

let lineShapeLayer = CAShapeLayer()
lineShapeLayer.
fillColor = finishLineColor
lineShapeLayer.
strokeColor = finishLineColor
lineShapeLayer.
path = linePath.cgPath

let lineAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.position))
lineAnimation.
fromValue =  CGPoint(x: 0, y: 0)
lineAnimation.
toValue = CGPoint(x: endPoint.xstartPoint.x, y: endPoint.y startPoint.y)
lineAnimation.
isRemovedOnCompletion = false
lineAnimation.
duration = finishLineAnimationDuration
lineAnimation.
fillMode = .forwards

lineShapeLayer.
add(lineAnimation, forKey: “animation”)
self.finishLineView.layer.addSublayer(lineShapeLayer)

Note that toValue doesn’t point to endPint, it needs relative coordinates.

lineAnimation.isRemovedOnCompletion = false
lineAnimation.fillMode = .forwards

Prevent from looping animation and after animation circle will stay in the same place.

Another step is to draw a line.

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

let finishPointShapeLayer = CAShapeLayer()
finishPointShapeLayer.
strokeColor = finishLineColor
finishPointShapeLayer.
lineWidth = finishLineWidth * 2 + 1
finishPointShapeLayer.
path = finishPointPath.cgPath

let finishPointAnimation = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.strokeEnd))
finishPointAnimation.
fromValue = 0
finishPointAnimation.
duration = finishLineAnimationDuration

finishPointShapeLayer.
add(finishPointAnimation, forKey: “animation”)
self.finishLineView.layer.addSublayer(finishPointShapeLayer)

Note finishPointShapeLayer.lineWidth finishLineWidth * 2 + 1. It has a multiplication of 2 because previously it was used as a radius. Adding 1 makes that line looks better.

The last step is to add finishLineView to view.

view.addSubview(finishLineView)

Change width and color based on your needs.

This post is another step in my learning journey of learning Playground preparing for WWDC Scholarship this year, more information.