Introducing JBChartView - Jawbone's iOS-based charting library for both line and bar graphs. It is easy to set-up, and highly customizable.
- Drop-in UIView subclass supported across all devices.
- Line and bar graph support.
- Simple to use protocols modeled after a UITableView.
- Highly customizable.
- Expand & collapse animation support.
Refer to the <a href="https://github.com/Jawbone/JBChartView/blob/master/CHANGELOG.md"">changelog for an overview of JBChartView's feature history.
- Requires iOS 6 or later
- Requires Automatic Reference Counting (ARC)
Build and run the JBChartViewDemo project in Xcode. The demo demonstrates the use of both the line and bar charts. It also outlines how a chart's appearance can be customized.
- Amsombe's Quartet: project showcasing the use of JBChartView in the classic data visualization example known as Anscombe's Quartet.
- Spark Friends: project showcasing the use of JBChartView in the context of sparklines and (fake) user step data.
CocoaPods is the recommended method of installing JBChartView.
Simply add the following line to your Podfile
:
platform :ios, '6.0'
pod 'JBChartView'
The simplest way to use JBChartView with your application is to drag and drop the /Classes folder into you're Xcode 5 project. It's also recommended you rename the /Classes folder to something more descriptive (ie. "Jawbone - JBChartView").
All JBChartView implementations have a similiar data source and delgate pattern to UITableView. If you're familiar with using a UITableView or UITableViewController, using a JBChartView subclass should be a breeze!
To use JBCartView in a Swift project add the following to your bridging header (JBChartView-Bridging-Header.h):
#import <UIKit/UIKit.h>
#import "JBChartView.h"
#import "JBBarChartView.h"
#import "JBLineChartView.h"
For more information about adding bridging headers see Swift and Objective-C in the Same Project.
To initialize a JBBarChartView, you only need a few lines of code (see below). Bar charts can also be initialized via a nib or with a frame.
JBBarChartView *barChartView = [[JBBarChartView alloc] init];
barChartView.dataSource = self;
barChartView.delegate = self;
[self addSubview:barChartView];
Just like you would for a UITableView
, ensure you clear these properties in your dealloc
:
- (void)dealloc
{
JBBarChartView *barChartView = ...; // i.e. _barChartView
barChartView.delegate = nil;
barChartView.dataSource = nil;
}
At a minimum, you need to inform the data source how many bars are in the chart:
- (NSUInteger)numberOfBarsInBarChartView:(JBBarChartView *)barChartView
{
return ...; // number of bars in chart
}
Secondly, you need to inform the delegate the height of each bar (automatically normalized across the entire chart):
- (CGFloat)barChartView:(JBBarChartView *)barChartView heightForBarViewAtIndex:(NSUInteger)index
{
return ...; // height of bar at index
}
Lastly, ensure you have set the frame of your barChartView & call reloadData at least once:
barChartView.frame = CGRectMake( ... );
[barChartView reloadData];
Note: subsequent changes to the chart's frame will not invoke reloadData; it must be called directly afterwards for any changes to take effect.
Similiarily, to initialize a JBLineChartView, you only need a few lines of code (see below). Line charts can also be initialized via a nib or with a frame.
JBLineChartView *lineChartView = [[JBLineChartView alloc] init];
lineChartView.dataSource = self;
lineChartView.delegate = self;
[self addSubview:lineChartView];
Just like you would for a UITableView
, ensure you clear these properties in your dealloc
:
- (void)dealloc
{
JBLineChartView *lineChartView = ...; // i.e. _lineChartView
lineChartView.delegate = nil;
lineChartView.dataSource = nil;
}
At a minimum, you need to inform the data source how many lines and vertical data points (for each line) are in the chart:
- (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView
{
return ...; // number of lines in chart
}
- (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex
{
return ...; // number of values for a line
}
Secondly, you need to inform the delegate of the y-position of each point (automatically normalized across the entire chart) for each line in the chart:
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex
{
return ...; // y-position (y-axis) of point at horizontalIndex (x-axis)
}
Note: You can return NAN instead of CGFloat to indicate missing values. The chart's line will begin at the first non-NAN value and end at the last non-NAN value. The line will interopolate any NAN values in between (ie. the line will not be interrupted).
return [[NSNumber numberWithFloat:NAN] floatValue];
Lastly, ensure you have set the frame of your lineChartView & call reloadData at least once:
lineChartView.frame = CGRectMake( ... );
[lineChartView reloadData];
Note: subsequent changes to the chart's frame will not invoke reloadData; it must be called directly afterwards for any changes to take effect.
Both the line and bar charts support a robust set of customization options. Read more about them here.
By default, a chart's minimum and maximum values are equal to the min and max supplied by the dataSource. You can override either value via:
- (void)setMinimumValue:(CGFloat)minimumValue;
- (void)setMaximumValue:(CGFloat)maximumValue;
If value(s) are supplied, they must be >= 0, otherwise an assertion will be thrown. To reset the values back to their original defaults:
- (void)resetMinimumValue;
- (void)resetMaximumValue;
The min/max values are clamped to the ceiling and floor of the actual min/max values of the chart's data source; for example, if a maximumValue of 20 is supplied & the chart's actual max is 100, then 100 will be used. For min/max modifications to take effect, reloadData must be called.
The nature of charting is to display all available information, unlike a UITableView, which can cache rows that are offscreen. JBChartView's performance will suffer if the number of data points exceed the resolution of the device. The same issue exists with MKMapView, when hundreds of pins are supplied within a certain geographic area. It's why Apple recommends clustering to avoid performance issues. As such, for large datasets, we recommend that your dataSource to supply a subset of points; clustering those that are close to one another.
Usage is provided under the Apache License (v2.0). See LICENSE for full details.