Custom Axis Labels on iOS Charts (Objective-C and C#)

Torrey Betts / Thursday, April 25, 2013

Introduction

When visualizing data and presenting it to your users, the readability of your presentation is key. Users should be able to look at the graphic like a body of text and have an understanding of what is being presented. One key element that provides easier readability are axis labels, in this article we'll cover how to customize the x and y axis labels found on the NucliOS IGChartView.

The illustration below is a clipped portion of the final result that shows custom axis labels.

image

Requirements

This article requires the use of the following frameworks. The requirements have been split depending on your use of Objective-C or C# (using Xamarin.iOS).

Objective-C

  • QuartzCore
  • IGChart

C#

  • IGChart.dll

Note: The trial for NucliOS can be found here.

Creating the Custom Data Object

Before customizing axis labels and filling an array full of data, we need to build the object that will represent each data item. The data object contains two properties, one for territory and one for cost.

C#
public class GraphObject : NSObject
{
    [Export("Territory")]
    public String Territory {get; set;}
    [Export("Cost")]
    public Double Cost {get; set;}
    public GraphObject() {}
}


Objective-C
@interface GraphObject : NSObject
@property (nonatomic,retain) NSString *territory;
@property (nonatomic, assign) double cost;
@end
@implementation GraphObject
@synthesize cost, territory;
@end

Generating Data

The data source for this article will create random data based on the custom data object named GraphObject that we created in the last step. Each object created is added to an array that will be used with the IGCategorySeriesDataSourceHelper.

C#
private void generateData()
{
    String[] territories = new string[] {@"Canada", @"Finland", @"Holland", @"Japan", @"USA"};
    for (int i = 0; i < 5; i++)
    {
        GraphObject graphObject = new GraphObject();
        graphObject.Territory = territories[i];
        graphObject.Cost = new Random(i).Next(100000);
        _data.Add(graphObject);
    }
}


Objective-C
-(void)generateData
{
    NSArray *territories = [NSArray arrayWithObjects:@"Canada", @"Finland", @"Holland", @"Japan", @"USA", nil];
    for (int i = 0; i < 5; i++)
    {
        GraphObject *graphObject = [[GraphObject alloc] init];
        graphObject.territory = [territories objectAtIndex:i];
        graphObject.cost = arc4random() % 100000;
        [_data addObject:graphObject];
    }
}

Adopting the IGChartViewDelegate Protocol

Adopting the IGChartViewDelegate protocol in Objective-C is simple and consists of adding IGChartViewDelegate to your @interface declaration.

@interface igViewController : UIViewController
@end


In C#, we must create a class that derives from IGChartViewDelegate and override any methods we wish to take advantage of.

public class ChartViewDelegate : IGChartViewDelegate
{
    //..
}

Customizing Axis Labels

With the IGChartViewDelegate protocol adopted, it's now possible to implement the ResolveLabelForAxis (C#) or labelForAxis (Objective-C) method. This method's parameters consist of the IGChartView instance that called the method, the axis, and the data point item or number item depending on the type of axis that is requesting a label string. The sample source used in this article checks the axis key and returns a label string. Since we'll be using a column series chart type, the x-axis will use the supplied territory name as the label, and the double value y-axis will be formatted using NSNumberFormatter to display the value in a currency format.

C#
public override string ResolveLabelForAxis (IGChartView chartView, IGAxis axis, NSObject item)
{
    if (axis.Key.Equals("xAxis"))
    {
        return ((IGCategoryPoint)item).Label;
    }
    else if (axis.Key.Equals("yAxis"))
    {
        NSNumberFormatter formatter = new NSNumberFormatter();
        formatter.NumberStyle = NSNumberFormatterStyle.Currency;
        return formatter.StringFromNumber((NSNumber)item);
    }
    return String.Empty;
}


Objective-C
- (NSString *)chartView:(IGChartView *)chartView labelForAxis:(IGAxis *)axis withItem:(NSObject *)item
{
    if([axis.key isEqualToString:@"xAxis"])
        return ((IGCategoryPoint *)item).label;
    else if ([axis.key isEqualToString:@"yAxis"])
    {
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
        [formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
        return [formatter stringFromNumber:(NSNumber *)item];
    }
    return nil;
}

Putting the Pieces Together

Everything is now in place to generate the data we need and provide the customized axis labels. Using the ViewDidLoad (C#) or viewDidLoad (Objective-C) method, we'll put the pieces together. To start, we'll generate our data and wire it up to the IGCategorySeriesDataSourceHelper.

C#
this.generateData();
_source.Data = _data.ToArray();
_source.ValuePath = @"Cost";
_source.LabelPath = @"Territory";


Objective-C
_data = [[NSMutableArray alloc] init];
[self generateData];
_source = [[IGCategorySeriesDataSourceHelper alloc] init];
_source.data = _data;
_source.valuePath = @"cost";
_source.labelPath = @"territory";


Next we'll create the chart instance and add a column series that uses our generated data with custom axis labels.

C#
RectangleF chartRect = this.View.Frame;
chartRect.Inflate(-30.0f, -30.0f);
_chart.Frame = chartRect;
_chart.AutoresizingMask = UIViewAutoresizing.FlexibleHeight|UIViewAutoresizing.FlexibleWidth;
_chart.Theme = IGChartGradientThemes.IGThemeDark();
_chart.Delegate = new ChartViewDelegate();
this.View.Add(_chart);
_chart.AddSeries(new MonoTouch.ObjCRuntime.Class("IGColumnSeries"), "series", _source, "xAxis", "yAxis");


Objective-C
_chart = [[IGChartView alloc] initWithFrame:CGRectInset(self.view.frame, 30.0, 30.0)];
[_chart setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
_chart.theme = [IGChartGradientThemes IGThemeDark];
_chart.delegate = self;
[self.view addSubview:_chart];
[_chart addSeriesForType:[IGColumnSeries class] usingKey:@"series" withDataSource:_source firstAxisKey:@"xAxis" secondAxisKey:@"yAxis"];


Illustrated below is the final result when the project is ran.

image

Download the Source

The Xcode and Xamarin.iOS project source code for this article can be found at these locations.

Project source for Xamarin.iOS (C#)
Project source for Xcode (Objective-C)


Note: Remember to add IGChart.framework to the project if you're using Xcode, or IGChart.dll to the project if you're using Xamarin.iOS. The trial for NucliOS that contain the necessary files can be found here.

A much easier to read format of this post is available here.

By Torrey Betts