Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Create a Simple Apple Watch App Using CoreGraphics

0.00/5 (No votes)
14 Oct 2015 1  
This article shows the steps to create an Apple Watch app that draws a pie chart using CoreGraphics.

Introduction

The goal of this article is to show how easy it is to create a Watch app that uses CoreGraphics to draw a pie chart. I was writing an app that needed a sequence of simple images as animation frames. I find it extremely time consuming to generate many images manually and cumbersome to make minor image resizing changes. So, I did some research and found CoreGraphics as a solution.

The complete source code can be downloaded from github: https://github.com/yuetwong/PieChart.

The sample app looks like the following:

Getting Started

Start with a New Project in Xcode

Let's start by creating a new project In Xcode. Note that I am using WatchOS 2.0 and Xcode 7.0.1. To create a new project, navigate to File\New\Project.. and select the watchOS\Application\iOS App with WatchKit App template.

Enter the product name as PieChart and uncheck Include Notification Scene because we will not be using Notification Scene.

On the next screen, choose a directory to store the new project.

Designing the Interface

The new project should have the following structure:

Open the Interface.storyboard under the PieChart WatchKit App folder. You should see an empty interface controller. Add the following interface objects to the empty interface controller: 2 labels and a picker.

Configure the objects by selecting the object in the storyboard and click the "Show the Attributes inspector" icon.

  1. Label
    • Change the label text with this message : Dial the digital crown to adjust the pie chart
    • Change the text color to yellow
    • Change the font to System, size 12
    • Change the line number to 2 to allow the text to span 3 lines
    • Change the text color to orange
    • Change the text alignment to "center"
    • Change the label alignment to "center"
  2. Picker
    • Change the picker style from List to Sequence
    • Set the picker's horizontal alignment to "center"

  3. A label to display the pie chart percentage.
    • Change the Text to 0%
    • Set the text alignment to "center"
    • Set the label's horizontal alignment to "center"

Coding

Alright, we have the interface, but it does not do anything right now. We need to write some code to bring it to life. In summary, we need:

  • to create a method to draw pie chart images
  • to setup picker with images
  • to display the selected pie chart image
  • to display the selected pie chart percentage

PieChart Class

Since we will be making many images as animation frames for the picker, we will create a method that can be called many times to create the pie charts of different sizes.

We will create a PieChart class with a method to draw the pie chart. We will create the PieChart class in the WatchKit Extension since it will be used by the Watch App. To create the class, go to the Project Navigator, right click on the PieChart WatchKit Extension and click New Files... on the popup menu.

Select the watchOS\Source\WatchKit Class template:

Create a PieChart class by subclassing on NSObject.

2 files are created: PieChart.h and PieChart.m. PieChart.h is the header file and PieChart.m is source file.

The PieChart class needs to use CoreGraphics. We need to import the UIKit.h file in order to use the CoreGraphics library functions. Add the following line in the PieChart.h header file to import the UIKit.h file.

#import <UIKit/UIKit.h>

The PieChart class will have one method, PieChartImageBySize, which will create a pie chart image. The + sign at the beginning of the method indicates that the method is a class method. You can call a class method directly without creating an instance of the class, unlike the instance method.

The PieChart.h file will look like below:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface PieChart : NSObject

// class method for drawing a pie chart
+(UIImage * )PieChartImageBySize:(float)imageSize outlineWidth:(CGFloat)outlineWidth
                        pieColor:(UIColor *)pieColor startRatio:(CGFloat)startRatio
                        endRatio:(CGFloat)endRatio;

@end    

Now, we are ready to write the method for creating a pie chart image. Open the PieChart.m file and add the code for PieChartImageBySize between the lines: @implementation and @end.

The logic of the PieChartImageBySize method can be broken down as follows:

  1. Constants for circle
    • fullCircle = 2π, is the angle of a full circle. initialAngle is used to adjust the startAngle and endAngle passed as input parameters to the CGContextAddArc function, which measures angles in radian from the positive-x axis which is at the 3 o'clock position. Since we want the pie chart to start from the 12 o'clock position, we adjust the angles by 3π/2. centerPoint is the center of the pie chart. The radius of the pie chart is adjusted by outlineWidth to avoid edges being cut off.
  2. Get graphics context
    • context is the area to draw on. UIGraphicsBeginImageContextWithOptions creates the context with the specified size. Setting the scale parameter to 0.0 will scale the image according to the scale factor of the device's main screen. This will ensure the image appears sharp on different devices of different sizes.
  3. Draw the pie chart
    • Before drawing the pie chart, we set the color by calling the function CGContextSetFillColorWithColor. Then, we call the function CGContextAddArc to draw the arc of the pie chart. The CGContextAddLineToPoint function draws a line from the end of the arc to the center of the pie chart. The function CGContextClosePath closes the path by drawing a line from the center point to the starting point. The last function, CGContextFillPath, paints the pie chart with the color set earlier.
  4. Draw the outline of the pie chart
    • Drawing the outline is similar to drawing the pie chart. Instead of calling CGContextSetFillColorWithColor, we call CGContextSetStrokeColorWithColor to set the colour of the outline. Instead of calling CGContextFillPath, we call CGContextStrokePath to draw the outline.
  5. Return the image
    • The last step is to call the function, UIGraphicsGetImageFromCurrentImageContext, to get the image and return to the caller. We also close the context using function, UIGraphicsEndImageContext.
+(UIImage * )PieChartImageBySize:(float)imageSize outlineWidth:(CGFloat)outlineWidth 
pieColor:(UIColor *)pieColor startRatio:(CGFloat)startRatio endRatio:(CGFloat)endRatio
{
    // 1. Constants for circle   
    CGFloat fullCircle = 2.0 * M_PI;
    CGFloat initialAngle = 3.0 * M_PI_2;
    CGFloat startAngle = startRatio * fullCircle + initialAngle;
    CGFloat endAngle = endRatio * fullCircle + initialAngle;
    CGPoint centerPoint = CGPointMake(imageSize/2, imageSize/2);
    CGFloat radius = (imageSize/2) - outlineWidth;

    // 2. Get graphics context
    CGSize contextSize = CGSizeMake(imageSize, imageSize);
    bool opaque = YES;
    CGFloat scale = 0.0;
    UIGraphicsBeginImageContextWithOptions(contextSize, opaque, scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
       
    // 3. draw the pie chart
    CGContextSetFillColorWithColor(context, pieColor.CGColor);
    CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, startAngle, endAngle, 0);
    CGContextAddLineToPoint(context, centerPoint.x, centerPoint.y);
    CGContextClosePath(context);
    CGContextFillPath(context);
    
    // 4. draw the outline
    CGContextSetLineWidth(context, outlineWidth);
    CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor);
    CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, 0, fullCircle, 0);
    CGContextStrokePath(context);
    
    // 5. return the image
    UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return result;    
}

Now that we have a function that can create a pie chart image, we can code the next function which will create 101 pie chart images. Open the InterfaceController.m file in the PieChart WatchKit Extension folder and add the following line to import the PieChart.h header file.

#import "PieChart.h"

Then, add the loadImage function. The function generates 101 pie chart images by calling the function PieChartImageBySize. The start ratio is 0.0 which means the pie chart starts from the 12 o'clock position. The end ratio is calculated by the current image index divided by the total number of images. The function packs all the pie chart images into an animated image and returns it to the caller.

-(UIImage *)loadImage {
    
    UIImage        * image;
    float            start = 0.0;
    float            end = 0.0;
    int              totalImages = 100;
    float            imageSize = 100;
    float            outlineWidth = 5.0;
    UIColor        * color = [UIColor cyanColor];    
    NSMutableArray * animationFrames = [NSMutableArray array];  

    // generate pie chart images for the values of 0 to 100
    for( int i = 0; i <= totalImages; i++)
    {
        end = (float)i/(float)totalImages;
        image = [PieChart PieChartImageBySize:imageSize outlineWidth:outlineWidth
                                     pieColor:color startRatio:start endRatio:end];
        
        [animationFrames addObject:image];
    }

    // set the animation duration to be very short as the digital crown can dial very fast
    float animationDuration = 0.0;
    return [UIImage animatedImageWithImages:animationFrames duration:animationDuration];
}

Next, we will configure the PieChartPicker and PercentageLabel.

  1. Open the interface.storyboard file in the PieChart WatchKit App folder.
  2. Click the "Show Assistant Editor" icon on the top menu and select the InterfaceController.m file
  3. CTRL-dragging the picker and label from the storyboard to the @interface section in the InterfaceController.m file. We will name the picker as PieChartPicker and the label as PercentageLabel.

CTRL-drag the picker to the @implementation section to add a function to change the percentage value displayed by the PercentageLabel.

Add the following line in the pickerValueChanged function so that the label will display the new percentage value when the digital crown is scrolled.

// update the label to display the new percentage
self.PercentageLabel.text = [NSString stringWithFormat:@"%d%%", value];

Finally, add the following code to the awakeWithContext to initialize the picker at watch app start up time.

  1. Generate the animated images by calling the loadImage function.
  2. Load the picker:
    • ?the for-loop populates the pickerItems array and stores the pie chart image in the contentImage field of the picker item.
    • the PieChartPicker is initialized with pie chart images by the call to setItems:pickerItems
  3. Set the initial pie chart to be 30%.
- (void)awakeWithContext:(id)context {
    [super awakeWithContext:context];

    // Configure interface objects here.
    
    // 1. generate the animated images
    UIImage *image = [self loadImage];
    
    // 2. load the picker with images
    NSMutableArray * pickerItems = [NSMutableArray array];
    for( int i=0; i< image.images.count; i++)
    {
        WKPickerItem * item = [[WKPickerItem alloc]init];
        item.contentImage = [WKImage imageWithImage:[image.images objectAtIndex:i]];
        [pickerItems addObject:item];
    }
    [self.PieChartPicker setItems:pickerItems];    
    
    // 3. set the initial pie chart to 30%
    [self.PieChartPicker setSelectedItemIndex:30];
}

Build and Run!!

You should be able to build and run the app on simulator by selecting the "iPhone X + Apple Watch" scheme and clicking the ? "play" button.

The first time the app may take a little while to load. Simply re-run it if the simulator crashes with error message: cannot find application id. The simulator may also crash if any of the interface objects on the storyboard is not mapped correctly. Right click on the interface controller on the storyboard and correct any incorrect mapping.

The End

Hope you enjoyed the article and it is helpful to you. Happy coding...

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here