Happy Pi Day! — Add a Pie to the Ring Graph

For those of us in the U.S. with a geekiness toward math March 15, 2015 at 9:26:53 is going to be the best pi day ever. As the date in the American calendar format is 3/14/15, the closest approximation to pi the mm dd yy hh:mm:ss format can have. To celebrate this, I’m going to add pie wedges to the ring graph I wrote about last Tuesday.

I’m going to assume for this you have completed the ring graph project. If not,  take a look at it first.

The key to the ring graph was using arcs in Core Graphics. Arc are lines. Everything else in the app was set up for these few lines of code.

CGContextSetStrokeColorWithColor(context, arcColor.CGColor)
CGContextSetLineWidth(context, arcWidth * 0.8 )
CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, start, end, 0)
CGContextStrokePath(context)

This code first sets the line color. Then it sets the width of the line. It makes a path for the line drawn, then finally draws the line. What if instead of drawing the line we fill inside of it? Consider this code:

CGContextMoveToPoint(context, centerPoint.x, centerPoint.y)
CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, start, end, 0)
CGContextFillPath(context)

This code does slightly more. It makes a more complicated path. We move the beginning of the path to the center of the circle. We then make the arc, and Core Graphics draws a straight line path between the center point and the beginning of the path. Core Graphics will connect the two open points for us, so when we call CGContextFillPath(context), we get a filled pie wedge.

For our method it would be best to add this as a setting. Replace this code:

        //draw the arc
        CGContextSetStrokeColorWithColor(context, arcColor.CGColor)
        CGContextSetLineWidth(context, arcWidth * 0.8 )
        //CGContextSetLineWidth(context, arcWidth)
        CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, start, end, 0)
        CGContextStrokePath(context)

with this code:

if isPie {
            CGContextSetFillColorWithColor(context, arcColor.CGColor)
            CGContextMoveToPoint(context, centerPoint.x, centerPoint.y)
            CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, start, end, 0)
            CGContextFillPath(context)
        }else{
            CGContextSetStrokeColorWithColor(context, arcColor.CGColor)
            CGContextSetLineWidth(context, arcWidth * 0.8 )
            CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, start, end, 0)
            CGContextStrokePath(context)
        }

We will use a new Boolean property isPie to decide if we want an arc or a wedge. Add the property to the class, defaulting to an arc graph.

var isPie = false

Changes to the View Controller

Head over to the view controller. Change viewDidLoad to this:

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let backgroundTrackColor = UIColor(white: 0.15, alpha: 1.0)

        circleGraph.arcBackgroundColor = backgroundTrackColor
        circleGraph.arcWidth = 35.0
        circleGraph.isPie = true
        circleGraph.endArc = 0.3141592653 // 3/14/15 9:26:53
 }

The circleGraph will now be a pie shape.  Build and run and you get this:

Screenshot 2015-03-14 09.00.06 Screenshot 2015-03-14 08.41.33

We get the background rings for the other two graphs. We can get rid of them by changing the arcBackgroundColor attribute to UIColor.clearColor(),  or deleting them, but we will add a white ring to the graph, with a new arcBackgroundColor. Add this to viewDidLoad:

whiteGraph.arcWidth = 25.0
whiteGraph.arcColor = UIColor.whiteColor()
whiteGraph.endArc = 0.5
whiteGraph.arcBackgroundColor = whiteGraph.arcColor.colorWithAlphaComponent(0.75)

Build and run:

Screenshot 2015-03-14 09.08.41

The code whiteGraph.arcColor.colorWithAlphaComponent(0.75) takes our current white foreground color and makes it slightly transparent. This is closer to what they do in the Apple Watch fitness app. Add the redGraph back in with the wedge with all these changes:

redGraph.endArc = 0.25
redGraph.arcColor = UIColor.redColor()
redGraph.arcWidth = 20.0
redGraph.isPie = true
redGraph.arcBackgroundColor = redGraph.arcColor.colorWithAlphaComponent(0.35)

Build and run:

Screenshot 2015-03-14 09.14.12

Enjoy your Pi Day. Hopefully these are helpful hints on some cool graphics.

The Whole Code

Downloadable Source

The download for this contains the three critical source code files.  Download it  here:  SwiftRingGraph_Source  and unzip it. You will find three files.

  • ViewController.swift
  • Main.Storyboard
  • CircleGraphView.swift

In Xcode, Open up a new single view project named SwiftRingGraph using Swift as the language and Universal device. Delete the ViewController.Swift and Main.Storyboard and send them to the trash can. Drag the three files you unzipped to the project’s navigator. In the dialog box that appears, make sure  that copy files is checked on, and finish the dialog box.  You now have a working copy.

ViewController.swift

//
//  ViewController.swift
//  Swift Ring Graph
//
//  Created by Steven Lipton on 3/10/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var percentLabel: UILabel!
    @IBOutlet weak var circleGraph: CircleGraphView!
    @IBOutlet weak var whiteGraph: CircleGraphView!
    @IBOutlet weak var redGraph: CircleGraphView!
    @IBAction func slider(sender: UISlider) {
        circleGraph.endArc = CGFloat(sender.value)
        percentLabel.text = String(format:" %0.2f %%",sender.value * 100)
    }
    @IBAction func whiteSlider(sender: UISlider) {
        whiteGraph.endArc = CGFloat(sender.value)
    }
    @IBAction func redSlider(sender: UISlider) {
        redGraph.endArc = CGFloat(sender.value)
    }
    override func viewDidLoad() {
        super.viewDidLoad()

        let backgroundTrackColor = UIColor(white: 0.15, alpha: 1.0)
        circleGraph.arcBackgroundColor = backgroundTrackColor
        circleGraph.arcWidth = 35.0
        circleGraph.isPie = true
        circleGraph.endArc = 0.314159 

        whiteGraph.arcWidth = 25.0
        whiteGraph.arcColor = UIColor.whiteColor()
        whiteGraph.endArc = 0.5
        whiteGraph.arcBackgroundColor = whiteGraph.arcColor.colorWithAlphaComponent(0.75)

        redGraph.endArc = 0.25
        redGraph.arcColor = UIColor.redColor()
        redGraph.arcWidth = 20.0
        redGraph.isPie = true
        redGraph.arcBackgroundColor = redGraph.arcColor.colorWithAlphaComponent(0.35)

    }

}

CircleGraphView.swift

//
//  CircleGraphView.swift
//  Swift Ring Graph
//
//  Created by Steven Lipton on 3/10/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class CircleGraphView: UIView {
    var endArc:CGFloat = 0.0{   // in range of 0.0 to 1.0
        didSet{
            setNeedsDisplay()
        }
    }
    var arcWidth:CGFloat = 10.0
    var arcColor = UIColor.yellowColor()
    var arcBackgroundColor = UIColor.blackColor()
    var isPie = false

    override func drawRect(rect: CGRect) {

        //Important constants for circle
        let fullCircle = 2.0 * CGFloat(M_PI)
        let start:CGFloat = -0.25 * fullCircle
        let end:CGFloat = endArc * fullCircle + start

        //find the centerpoint of the rect
        var centerPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect))

        //define the radius by the smallest side of the view
        var radius:CGFloat = 0.0
        if CGRectGetWidth(rect) > CGRectGetHeight(rect){
            radius = (CGRectGetWidth(rect) - arcWidth) / 2.0
        }else{
            radius = (CGRectGetHeight(rect) - arcWidth) / 2.0
        }

        //starting point for all drawing code is getting the context.
        let context = UIGraphicsGetCurrentContext()
        //set colorspace
        let colorspace = CGColorSpaceCreateDeviceRGB()
        //set line attributes
        CGContextSetLineWidth(context, arcWidth)
        CGContextSetLineCap(context, kCGLineCapRound)

        //make the circle background

        CGContextSetStrokeColorWithColor(context, arcBackgroundColor.CGColor)
        CGContextSetFillColorWithColor(context, arcBackgroundColor.CGColor)
        CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, 0, fullCircle, 0)
            CGContextStrokePath(context)

        //draw the arc or pie

        if isPie {
            CGContextSetFillColorWithColor(context, arcColor.CGColor)
            CGContextMoveToPoint(context, centerPoint.x, centerPoint.y)
            CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, start, end, 0)
            CGContextFillPath(context)
        }else{
            CGContextSetStrokeColorWithColor(context, arcColor.CGColor)
            CGContextSetLineWidth(context, arcWidth * 0.8 )
            CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, start, end, 0)
            CGContextStrokePath(context)
        }

    }

}

7 Replies to “Happy Pi Day! — Add a Pie to the Ring Graph”

    1. Good question. There’s a lot to it to put in a post like this. Essentially you have to get information from the traits and UiViews about the size of the view, after the traits are all assigned, and auto layout fished with sizing the view. It gets a bit messy, but it is on my bucket list of topics to cover. Since you asked I’ll make it next week’s topic.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s