Working on my new app, Pocket Color Wheel I learned how easy to use native iOS tools to draw various shapes.
For another app I’m working on, I wanted to draw clockwise and counterclockwise spirals.
In this intermediate Swift tutorial I’m going to show you how to use a CABasicAnimation core animation and a CAShapeLayer to draw two spirals.
Getting Started
First, refresh your memory on how to draw a golden spiral here.
Second, this core graphic tutorial on Ray Wenderlich’s website has an excellent description how to draw arcs and describes some of the geometric conventions in Swift.
Download the starter project for this tutorial here, save it to a convenient location and open it in Xcode.
Open ViewController.swift and declare the following constants for the spiral layers:
let spiralShape1 = CAShapeLayer() let spiralShape2 = CAShapeLayer()
Defining the Path
Update the clockwiseSpiral method with the following code:
func clockwiseSpiral(){ var startAngle:CGFloat = 3*π/2 var endAngle:CGFloat = 0 center = CGPoint(x:bounds.width/3, y: bounds.height/3) // Setup the initial radius radius = bounds.width/90 // Use UIBezierPath to create the CGPath for the layer // The path should be the entire spiral // 1st arc let linePath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true) // 2 - 9 arcs for _ in 2..<10 { startAngle = endAngle switch startAngle { case 0, 2*π: center = CGPoint(x: center.x - radius/2, y: center.y) endAngle = π/2 case π: center = CGPoint(x: center.x + radius/2, y: center.y) endAngle = 3*π/2 case π/2: center = CGPoint(x: center.x , y: center.y - radius/2) endAngle = π case 3*π/2: center = CGPoint(x: center.x, y: center.y + radius/2) endAngle = 2*π default: center = CGPoint(x:bounds.width/3, y: bounds.height/3) } radius = 1.5 * radius linePath.addArcWithCenter(center, radius: radius, startAngle: startAngle, endAngle: endAngle,clockwise: true) } // Setup the CAShapeLayer with the path, line width and stroke color spiralShape1.position = center spiralShape1.path = linePath.CGPath spiralShape1.lineWidth = 6.0 spiralShape1.strokeColor = UIColor.yellowColor().CGColor spiralShape1.bounds = CGPathGetBoundingBox(spiralShape1.path) spiralShape1.fillColor = UIColor.clearColor().CGColor // Add the CAShapeLayer to the view's layer's sublayers view.layer.addSublayer(spiralShape1) // Animate drawing drawLayerAnimation(spiralShape1) }
This app is designed to run in landscape orientation.
The center of the spirals is not hard coded, I’m using auto layout. The starting point for the center of the first spiral is set to the 1/3 of the width of the view. For the second it is 2/3 of the width.
The initial radius is very small, 1/90 of the view width.
The spiral path consists of 9 arc segments. As the path progresses the radius of each segment increases by 50%. The coordinates for the center shift for every π/2 to match the starting point of the new arc with the end point of the previous arc.
Finally, the position of the spiral moves to the last center point, close to the mid height point of the screen.
spiralShape1.position = center
Drawing a counterclockwise spiral is done in a similar way. Only it’s done in the opposite direction.
Update the counterclockwiseSpiral method with the following code:
func counterclockwiseSpiral(){ var startAngle:CGFloat = 3*π/2 var endAngle:CGFloat = π center = CGPoint(x:bounds.width/3 + bounds.width/3, y: bounds.height/3) // Setup the initial radius radius = bounds.width/90 // Use UIBezierPath to create the CGPath for the layer // The path should be the entire spiral // 1st arc let linePath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false) // 2 - 9 arcs for i in 2..<10 { startAngle = endAngle switch startAngle { case 0: center = CGPoint(x: center.x - radius/2, y: center.y) endAngle = 3*π/2 case π: center = CGPoint(x: center.x + radius/2, y: center.y) endAngle = π/2 case π/2: center = CGPoint(x: center.x , y: center.y - radius/2) endAngle = 0 case 3*π/2: center = CGPoint(x: center.x, y: center.y + radius/2) endAngle = π default: center = CGPoint(x:bounds.width/3 + bounds.width/3, y: bounds.height/3) } radius = 1.5 * radius linePath.addArcWithCenter(center, radius: radius, startAngle: startAngle, endAngle: endAngle,clockwise: false) } // Setup the CAShapeLayer with the position, path, line width and stroke color spiralShape2.position = center spiralShape2.path = linePath.CGPath spiralShape2.lineWidth = 6.0 spiralShape2.strokeColor = UIColor(red:0.99, green:0.57, blue:0.18, alpha:1.0).CGColor spiralShape2.bounds = CGPathGetBoundingBox(spiralShape2.path) spiralShape2.fillColor = UIColor.clearColor().CGColor // Add the CAShapeLayer to the view's layer's sublayers view.layer.addSublayer(spiralShape2) // Animate drawing drawLayerAnimation(spiralShape2) }
Creating the animation
Now, lets update the drawLayerAnimation that is called to trigger the spiral animation:
func drawLayerAnimation(layer: CAShapeLayer!){ var layerShape = layer // The starting point layerShape.strokeStart = 0.0 // Don't draw the spiral initially layerShape.strokeEnd = 0.0 // Animate from 0 (no spiral stroke) to 1 (full spiral path) var drawAnimation: CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd") drawAnimation.fromValue = 0.0 drawAnimation.toValue = 1.0 drawAnimation.duration = 1.6 drawAnimation.fillMode = kCAFillModeForwards drawAnimation.removedOnCompletion = false layerShape.addAnimation(drawAnimation, forKey: nil) }
The animation draws the defined spiral path from the starting point 0.0 to 1.0.
And there you have it! For your reference, you can download the complete Xcode project here. Go ahead, tweak the colors, curve paths, animation duration to suit your needs and ideas. Try drawing a logarithmic spiral. Perhaps you can take it further and even draw a snail. How cool would it be!
Hi, Rumiya. I’m Gi-won Seo from Korea.
Can I ask you one quick question? The Euclidean geometry graphic you put is really cool.
How did you do it? By any chance, is it a work from an electronic note-taking gadget?
I’d like to know because I’m recently inclined to buy an electronic note-taking gadget.
Those graphic tools provided on the web doesn’t allow spontaneous drawing.
If you used another tool, could you tell me what it is?
LikeLiked by 1 person
Hi Gi-won, no, I didn’t use any gadgets. If you are interested in how it works, feel free to read the tutorial.
LikeLike