Monday, January 12, 2015

A Pie Chart View Control for iOS


Objective


PieChart was concived after my learning of linear algebra's simple vectors arithmetic. I found interesting the idea to start practicing my reacent learning in an interactive Pie Chart control. This control so far is in its basic stage but can be used by anybody and extended to fit your needs. This is a work in progress and any feedback ideas or improvements will be greatly appreciated. The code can be found here.

How to use it

You need to include in your project the following files:

- IMPieChartView.m
- IMPieChartView.h
- IMPieChartLayer.m
- IMPieChartLayer.h
- IMPieSliceDescriptor.m
- IMPieSliceDescriptor.h
- IMPieChartDecoratingView.m
- IMPieChartDecoratingView.h
- IMMacroToolbox.h

1) Then on your View Controller implement the protocols IMPieChartViewDelegate and IMPieChartViewDataSource

@interface IMPieChartViewController : UIViewController < IMPieChartViewDelegate, IMPieChartViewDataSource, IMPieChartDecoratingViewDelegate >

2) Declare a property of type IMPieChartView and of type IMPieChartDecoratingView

@property (nonatomicstrongIBOutlet IMPieChartView * pieChartView;
@property (nonatomic, strong) IBOutlet IMPieChartDecoratingView * decoratingView;

3) declare a property of type UIImageView which will be used as the selector view, but here you can use any custom view you implement

@property (nonatomic, strong) UIImageView * selectorView;

4) Now on the viewDidLoad method you configure the pie using the following properties:

-(void)viewDidLoad {
   // create the chart pie
   self.pieChartView = [[IMPieChartView alloc] initWithFrame:CGRect(self.view.size.width, 0, 300, 300)];
   
   // setup the delegate
   self.pieChartView.pieChartDelegate = self;
   
   // setup the datasource
   self.pieChartView.pieChartDataSource = self;
   
   // this will enable showing a drop shadow under the pie chart
   self.pieChartView.enableShadow = YES;
   
   // this is a factor applied to the radius so the drop shadow can be shown without clipping
   self.pieChartView.radiusFactor = 0.9;
   
   // you can specify the type of animation you want the pie perform when dragging ends
   // or an slice is selected
   self.pieChartView.animationType = IMPieChartAnimationTypeMechanicGear;
   
   // you can locate the selection point in the top, left, right or bottom of the pie chart:
   // - IMPieChartSelectionTypeTop
   // - IMPieChartSelectionTypeBottom
   // - IMPieChartSelectionTypeLeft
   // - IMPieChartSelectionTypeRight
   self.pieChartView.selectionType = IMPieChartSelectionTypeBottom;
 
   // you can specify the animation type which could be one of the following
   // - IMPieChartAnimationTypeBouncing
   // - IMPieChartAnimationTypeMechanicGear
   self.pieChartView.animationType = IMPieChartAnimationTypeBouncing;
   
   ...

}

5) Now configure the decorating view at the bottom of viewDidLoad and add it to the view controller's root view. The decorating view is the one placed at the top of the view controller's root view and it contains the pie chart view and the selector view:


   self.decoratingView = [[IMPieChartDecoratingView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
   // The decorating view calculates the size of the selector view base on this is
   // a percentage number of the size desired for the selector view.
   self.decoratingView.sizingFactor = 0.16;
   
   // this is the decorator's delegate which provide the selector view as well as the
   // contained pie chart view
   self.decoratingView.decoratorDelegate = self;



   // add decorator view to the view controller's root view
   [self.view addSubview:self.pieChartDecoratingView];

6) Add the pieChartView to the decorating view:

   [self.pieChartDecoratingView addSubview:self.pieChartView];


7) now you can add data per slice using the IMPieSliceDescriptor class. So for that declare a property of type NSArray on your view controller

   @property (nonatomic, strong) NSArray * data;

8) Now on your viewDidLoad method at the bottom after adding the pieChartView to the decoratingView, type the following:

self.data = @[[IMPieSliceDescriptor sliceWithCaption:@"Slice 1" 
                                               value:35 
                                               color:IMOpaqueHexColor(0xffd019)],
              [IMPieSliceDescriptor sliceWithCaption:@"Slice 2"
                                               value:29
                                               color:IMOpaqueHexColor(0xff7619)],
              [IMPieSliceDescriptor sliceWithCaption:@"Slice 3"
                                               value:11
                                               color:IMOpaqueHexColor(0x0066ff)],
              [IMPieSliceDescriptor sliceWithCaption:@"Slice 4"
                                               value:10
                                               color:IMOpaqueHexColor(0xff00cc)],
              [IMPieSliceDescriptor sliceWithCaption:@"Slice 5"
                                               value:8
                                               color:IMOpaqueHexColor(0xc60329)],
              [IMPieSliceDescriptor sliceWithCaption:@"Slice 6"
                                               value:7
                                               color:IMOpaqueHexColor(0x9bd305)]];



NOTE:

IMOpaqueHexColor is a macro declared in IMMacroToolbox.h that is used to get some more interesting colors out from the ones that come in stock, and it uses as input the hex value of the color.

9) Now let's implement the pie chart's delegate methods as follows:
-(void)pieChart:(IMPieChartView *)pieChart didSelectSlice:(IMPieSliceDescriptor *)slice {
   // Here you get the selected slice, which is the slice that passes
   // through the selection point.
   self.infoLabel.text = [NSString stringWithFormat:@"%@ - %@%%", slice.caption, slice.value];
}

-(void)pieChartIsReady:(IMPieChartView *)pieChart {
   // Here you can perform some extra initialisation code whenever you load new data
   // this method will be called. In this case we are rotating to the first slice in
   // the pie.
   [pieChart rotateToSlice:0 animated:YES];

}

NOTE:

The macro im_log allows to print debug messages in the console, this macro is declared in the IMMacroToolbox.h and is available when the DEBUG global macro is defined. Basically when you are using the DEBUG scheme.

10) Now let's implement the data source methods as follows:

-(NSUInteger)pieChartSliceCount:(IMPieChartView *)pieChart {
   // Here you provide the number of items in the data array.
   return self.data.count;
}

-(IMPieSliceDescriptor *)pieChart:(IMPieChartView *)pieChart sliceForIndex:(NSInteger)index {
   // Here you provide the slice descriptor for the index requested.
   return self.data[index];

}

11) Finally let's implement the decorator's delegate methods:

-(UIView *)decoratorSelectorView {
   return self.selectorView;
}

-(IMPieChartView *)decoratorPieChartView {
   return self.pieChartView;
}

That's pretty much it.

No comments: