Simon Shi

放飞梦想,专注于Mobile开发

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: :: 订阅 订阅 :: 管理 ::

From: http://www.raywenderlich.com/13269/how-to-draw-graphs-with-core-plot-part-1

This is a blog post by iOS Tutorial Team member Steve Baranski, the founder of komorka technology, a provider of iOS development and consulting services.

If you’ve ever wanted to include charts or graphs in your app, chances are you’ve considered the following two options:

  1. DIY. One option is to write all the drawing code yourself using Core Graphics/Quartz. Apple even has an example of this, in the SimpleStocks sample project.
  2. Buy it! Another option is to purchase a commercial framework like ShinobiControls.

But what if you don’t want to spend the time and effort to write it yourself, and don’t want to shell out a ton of money? That’s where a third option comes in handy: use the open-source Core Plot library!

Core Plot is an open-source plotting framework for both iOS and Mac OS X. It leverages Apple frameworks like Quartz and Core Animation, and even provides support for ARC. Perhaps more importantly, the framework has solid test coverage. Core Plot is distributed with a permissive BSD license, and Version 1.0 was released in February 2012.

In this 2-part tutorial series, we’ll show how you can use Core Plot to create pie charts, bar graphs, scatter plots, and more. We’ll even cover some cool stuff like theming the graphs and charts with different styles, interacting with charts, and more!

To go through this tutorial, you need to have Xcode 4 installed, and a working knowledge of Objective-C, Interface Builder and storyboards. If you are new to any of these topics, you should go through some of the other tutorials on this site first.

So without further ado, let’s get to work!

Getting Started: Project Configuration

Begin by launching Xcode and creating a new project – select the iOS\Application\Tabbed Application template.

Name the product CorePlotDemo; enter your company identifier; use CPD as the Class Prefix (or you can leave the Class Prefix blank); select iPhone as the Device Family; and check the boxes labeled Use Storyboards and Use Automatic Reference Counting. Click Next and select a location to create your project.

Note: In case you were wondering, the Class Prefix is simply a way to conveniently name all your source files. Every new class file that’s automatically generated by Xcode will have its name prefixed with the Class Prefix. This is a good practice because you don’t have to worry about your class names conflicting with class names from Apple or other third parties.

Now that your project is in place, proceed to add Core Plot. You can download it here. At the time of writing this tutorial, the latest release is CorePlot_1.0.zip, so download that and extract the contents of the ZIP file to a temporary folder. Core Plot can be used in a project in two different ways:

  1. Dependent Project: You can add the Core Plot Xcode project to your project so that the Core Plot source is compiled along with your project source.
  2. Static Library: You can build (or download) the Core Plot library separately, and then add the binary library to your Xcode project so that the Core Plot library is linked in when your app is built.

For this tutorial, you’ll use approach #2. Detailed instructions for this process can be found here, but the basic steps are as follows:

  1. Click on the project root class folder (CorePlotDemo) in the Project Navigator and select New Group. Name the new group “CorePlot.”
  2. Right-click on your newly-created group and select Add Files to “CorePlotDemo”… When the file dialog appears, navigate to the folder where you extracted the Core Plot ZIP file and drill down to the following location: CorePlot_1.0\Binaries\iOS. Select both CorePlotHeaders and the static library named libCorePlot-CocoaTouch.a. Make sure that the box labeled “Copy items into destination group’s folder (if needed)” is checked.

Now that you’ve added the framework, you need to wire it in. This involves the following steps:

  1. Click the CorePlotDemo root in the Project Navigator, select the CorePlotDemo target in the center pane, and then click Build Settings. Find the Other Linker Flags field and enter -ObjC into the field.
  2. Now change from Build Settings to Build Phases in the center pane. Expand the group named “Link Binary with Libraries.” Check to make sure that Xcode added the Core Plot static library when you added the Core Plot files to your project. If for some reason the Core Plot library is not listed, click the plus (+) button at the bottom of the section, click the “Add Other …” button, and navigate to your project folder to find the Core Plot library.
  3. Finally, click the plus (+) button and proceed to add the QuartzCore framework.

You should be able to Build (via Cmd-B) your project without errors at this point. Make sure that this is indeed the case.

Adding Resources

Now that you’ve incorporated Core Plot, download the resources for this project.

These resources include a few constants used throughout your app, as well as a singleton class that contains fictional stock price data for Apple (AAPL), Google (GOOG), and Microsoft (MSFT) stock.

Once you download and extract the archive, drag the “Starter Files” folder to the root of your Xcode project. Again, make sure that “Copy items into destination group’s folder (if needed)” is checked, as shown below.

If you check the new folder, you’ll see it contains two sets of source and header files:

  • CPDConstants: This class is for a few constants that will be used throughout the project.
  • CPDStockPriceStore: This class serves as the stock price data source for the project.

Now that you’ve added these files to the project, add a few import statements to CorePlotDemo-Prefix.pch. Navigate to the “Supporting Files” folder, open it and select CorePlotDemo-Prefix.pch. Add the following to the file right before the final #endif:

#import "CorePlot-CocoaTouch.h"
#import "CPDConstants.h"
#import "CPDStockPriceStore.h"

Note: CorePlotDemo-Prefix.pch is the precompiled header file for this project. The precompiled header is automatically included for every source file in the project. So it’s a convenient location to add imports to that would need to be in almost every class file.

While you’ll add imports to CorePlotDemo-Prefix.pch in this tutorial for the sake of making things easier to follow, note that loading the prefix header with a lot of import statements is generally frowned upon, since it can lead to code which is not very portable.

Try building and running (Cmd-R) your code now. You’ll notice that the code compiles fine, but that you don’t see any graphs or charts. Instead, you simply see the standard screens from the tabbed application template. That’s because you haven’t set up the UI yet.

Storyboarding the App

You’ll now proceed to tailor the storyboard for your app to set up the initial UI. If you select MainStoryboard.storybard in the Project navigator and show the Document Outline, you should see something like the following.

You’ll see that the Xcode template created a tab bar controller with two scenes corresponding to two separate view controllers. To modify the existing storyboard to accommodate your app’s intended design, do the following:

  1. Select First View Controller in the Document Outline (in the center panel).
  2. Drill down to the root view for First View Controller in the left sidebar (you might need to first expand the list of sub-components by using the triangle next to First View Controller), and delete the label and text view.
  3. Select the Tab Bar Item immediately below the view. Look for the Attributes Inspector on the right sidebar (if you don’t see it, you might need to enable it using the buttons at the top of the Xcode window, under the View section). In the Attributes Inspector, change the Title to “Pie Chart” and clear the Image name (it should be set to “first”).

Repeat the above steps for Second View Controller, but change the Title to “Bar Graph” instead of “Pie Chart” in step #3.

Now build and run your project again:

The app currently displays in portrait orientation, but you want it to be in landscape in order to use the available space optimally for the charts. To fix this, edit CorePlotDemo-Info.plist in the “Supporting Files” folder. Select the PLIST file, expand the bottom row that reads “Supported interface orientations,” and delete all but “Landscape (left home button).”

Next add a new entry to the file. First, collapse “Supported interface orientations” (if it’s expanded), and then right-click the bottom row and select “Add Row.” Enter “Initial interface orientation” as the key and select “Landscape (left home button)” as the corresponding value.

Now build and run the project again to make sure that the orientation changes took effect.

With the app running in landscape, it’s time to finish setting up the project.

  1. Select CPDFirstViewController.h in the Project Navigator.
  2. Double-click the word “CPDFirstViewController” in the editor pane so that the word is selected.
  3. Right-click, select Refactor\Rename…
  4. Change the class name to CPDPieChartViewController.
  5. Ensure that “Rename related files” remains checked, and click “Preview.”
  6. Verify the changes (if necessary), and then click “Save” to make the changes. You can opt to create a snapshot before the changes or disable the feature, your choice.

Repeat the above steps for CPDSecondViewController, but this time, name it CPDBarGraphViewController.

Next, create a third view controller. Note that although you won’t be doing much with the second and third view controllers for this tutorials, they will have a much bigger role to play in the second part of the tutorial, and it’s easiest to get them ready in advance now.

Create a new file with the iOS\Cocoa Touch\UIViewController subclass template. Leave the boxes unchecked and click Next. Name the class CPDScatterPlotViewController, and create/save it.

Now, switch back to MainStoryboard.storyboard and drag a view controller from the Object Library in Utilities (right sidebar) to the Document Outline, as shown.

Select your new view controller in the Document Outline, then select the Identity Inspector (third tab of the top half of the right sidebar) from Utilities and set the Class of the view controller to that of your new view controller: CPDScatterPlotViewController. You’ll also want to set the title in the Attributes Inspector (fourth tab of the right sidebar) to “Scatter Plot.”

Next, hook the new view controller up to the tab bar controller. Select the tab bar controller from the Tab Bar Controller scene, and open the Connections Inspector (last tab on the right sidebar) in Utilities.

You’ll notice that relationships are set up for other two view controllers. Click on that relationship node (the little black circle at the top right of the relationships list) and drag it to the Scatter Plot view controller. The relationship connection will now show three entries. Additionally, the primary storyboard pane will show a connection from the tab bar view controller to the new view controller.

Finally, select the Scatter Plot View Controller in the left sidebar, drill down to the Tab Bar item and change its title to “Scatter Plot.”

Now build and run the project once again to see the fruits of your labor.

Wait a minute! What’s going on? The app is displayed in landscape mode, but the tabs are placed on the left side of the screen rather than the bottom.

The answer might surprise you: the documentation for UIViewController states that YES is returned for every possible orientation, but the default Xcode templates override this method to return YES for every orientation except for UIInterfaceOrientationPortraitUpsideDown. This behavior interferes with your implementation. To fix, it you need to tidy up all three of the chart view controllers.

For each view controller (CPDPieChartViewController, CPDBarGraphViewController, CPDScatterPlotViewController), edit the .m file as follows:

  1. Delete the default implementation for viewDidLoad.
  2. Delete the default implementation for viewDidUnload.
  3. Replace the default implementation for shouldAutorotateToInterfaceOrientation: with the following:
#pragma mark - Rotation
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

Build and Run again, and you should see something similar to this:

Excellent! Now you can proceed to add your first chart.

The Pie Chart

Your first plot will be a pie chart that represents your portfolio as of close of trading on May 1, 2012.

Begin by making the necessary modifications to CPDPieChartViewController in MainStoryboard.storyboard. Drag a toolbar from the Object Library to the top of the view. Set its style in the Attributes Inspector to Default, as shown below.

Select the Bar Button Item in the Document Outline, switch to the Attributes Inspector, and change the “Title” value to Theme.

Next, create outlets for these elements in your view controller. Open CPDPieChartViewController.m and add the following lines at the top of the class continuation category (the section between the @interface and @end lines, right below the #import section):

@property (nonatomic, strong) IBOutlet UIToolbar *toolbar;
@property (nonatomic, strong) IBOutlet UIBarButtonItem *themeButton;
 
-(IBAction)themeTapped:(id)sender;

You need to synthesize the properties declared above, so add the following lines just below the @implementation line:

@synthesize toolbar = toolbar_;
@synthesize themeButton = themeButton_;

Finally, add a blank implementation for themeTapped: to the end of the file, just before the @end:

#pragma mark - IBActions
-(IBAction)themeTapped:(id)sender {    
}

Now switch to CPDPieChartViewController.h and add the following to the end of the class declaration (the @interface line):

<CPTPlotDataSource, UIActionSheetDelegate>

The above declares CPDPieChartViewController as a delegate for CPTPlotDataSource and UIActionSheetDelegate. The former notes that the view controller will serve as the data source for your plot-in-progress; the latter will allow you to handle the UIActionSheet you’ll need to present via the themeTapped: action later on. This requires the addition of several delegate methods skeletons.

Add the following to the end of CPDPieChartViewController.m:

#pragma mark - CPTPlotDataSource methods
-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot {
    return 0;
}
 
-(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index {
    return 0;
}
 
-(CPTLayer *)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)index {
    return nil;
}
 
-(NSString *)legendTitleForPieChart:(CPTPieChart *)pieChart recordIndex:(NSUInteger)index {
    return @"";
}
 
#pragma mark - UIActionSheetDelegate methods
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
}

You’ll fill in the above skeleton methods as the tutorial continues.

Making Connections

Switch back to the storyboard and select CPDPieChartViewController in the Document Outline. You’re going to make three connections using the Connections inspector:

  1. Connect the “themeButton” outlet to the bar button item.
  2. Connect “toolbar” to the toolbar containing the bar button item.
  3. Connect themeTapped: to the bar button item as well.

Return to CPDPieChartController.m and add the following below the existing property declarations at the top of the file:

@property (nonatomic, strong) CPTGraphHostingView *hostView;
@property (nonatomic, strong) CPTTheme *selectedTheme;

Of the two properties added above, one is an instance of CPTGraphHostingView, the container for all Core Plot drawing. The second is for CPTTheme, a theming class used in modifying the look and feel of Core Plot graphs.

In the same class continuation category section, add the following method declarations below the one for themeTapped::

-(void)initPlot;
-(void)configureHost;
-(void)configureGraph;
-(void)configureChart;
-(void)configureLegend;

Next, synthesize the new properties in the @implementation section:

@synthesize hostView = hostView_;
@synthesize selectedTheme = selectedTheme_;

Then, add the following code just below the @synthesize lines you added above:

#pragma mark - UIViewController lifecycle methods
-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    // The plot is initialized here, since the view bounds have not transformed for landscape until now
    [self initPlot];    
}

To recap, you’ve done a few things:

  • You’ve synthesized the host view and selected theme properties.
  • You’ve declared that you’re going to initialize the plot.
  • You’ve added the method declarations for a few private methods.

Now it’s time to add the private methods you declared above. Add the following code immediately below the themeTapped: implementation:

#pragma mark - Chart behavior
-(void)initPlot {
    [self configureHost];
    [self configureGraph];
    [self configureChart];
    [self configureLegend];
}
 
-(void)configureHost {    
}
 
-(void)configureGraph {    
}
 
-(void)configureChart {    
}
 
-(void)configureLegend {    
}

You’ll flesh out these skeleton methods over the rest of the tutorial. As you can see from the above code, when you initialize the plot, you do the following:

  1. Configure the host.
  2. Configure the graph within that host.
  3. Configure the chart.
  4. And finally, configure the legend for the chart.

You’ll implement the code for each of these steps in turn, but first, compile and run the app to make sure everything’s okay up to this point. You should see something like the following:

Meet the Host

You will now configure the chart host view. Add the following lines to configureHost:

// 1 - Set up view frame
CGRect parentRect = self.view.bounds;
CGSize toolbarSize = self.toolbar.bounds.size;
parentRect = CGRectMake(parentRect.origin.x, 
    (parentRect.origin.y + toolbarSize.height), 
    parentRect.size.width, 
    (parentRect.size.height - toolbarSize.height));
// 2 - Create host view
self.hostView = [(CPTGraphHostingView *) [CPTGraphHostingView alloc] initWithFrame:parentRect];
self.hostView.allowPinchScaling = NO;
[self.view addSubview:self.hostView];

In section #1, you take the parent view bounds (its position and dimensions) and calculate the bounds for a smaller view. The new frame excludes the bounds of the toolbar so that the host view can reside within the parent view without obscuring/overlapping the toolbar.

In section #2, you instantiate a CPTGraphHostingView and add it to the parent view. As the Core Plot documentation notes, a CPTGraphHostingView is simply a container view for a CPTGraph instance. These views enable pinch zooming by default, but the code above disables pinch zooming for the chart in this example.

Now that your graph host has been configured, you need to add a graph to it. Do that by adding the following lines to configureGraph:

// 1 - Create and initialize graph
CPTGraph *graph = [[CPTXYGraph alloc] initWithFrame:self.hostView.bounds];
self.hostView.hostedGraph = graph;
graph.paddingLeft = 0.0f;
graph.paddingTop = 0.0f;
graph.paddingRight = 0.0f;
graph.paddingBottom = 0.0f;
graph.axisSet = nil;
// 2 - Set up text style
CPTMutableTextStyle *textStyle = [CPTMutableTextStyle textStyle];
textStyle.color = [CPTColor grayColor];
textStyle.fontName = @"Helvetica-Bold";
textStyle.fontSize = 16.0f;
// 3 - Configure title
NSString *title = @"Portfolio Prices: May 1, 2012";
graph.title = title;    
graph.titleTextStyle = textStyle;
graph.titlePlotAreaFrameAnchor = CPTRectAnchorTop;    
graph.titleDisplacement = CGPointMake(0.0f, -12.0f);         
// 4 - Set theme
self.selectedTheme = [CPTTheme themeNamed:kCPTPlainWhiteTheme];    
[graph applyTheme:self.selectedTheme];

Let’s review each portion of the above code section-by-section:

  1. Create an instance of CPTXYGraph and designate it as the hosted graph of the host view you created earlier. The CPTGraph is really where the magic happens. It’s important to note that CPTGraph encompasses everything you see in a standard chart or graph: the border, the title, the plotted data, axes, and legend.

    In the detailed documentation for Core Plot found here, you’ll notice that the designated initializer for CPTXYGraph has a default padding of 20.0f on each side. In your method, you remov the extra padding by setting the padding to 0.0f .

  2. Set up the text style you want to use for your graph title. CPTMutableTextStyle allows you to configure the size, font, and weight of any text you wish to add to the graph.
  3. Set up the title for the graph and set its style to use the style you set up in step #2. You’ve also positioned the title of your graph 12 pixels from the top of the view’s bounding rectangle.
  4. Declare that the graph will use the “plain white” theme provided by Core Plot. A CPTTheme defines text styles (like the one you configured in step #2), line styles, and even the fills used by a graph. You can use one out of the five built-in themes offered by Core Plot, or alternately, create a customized theme for your app.

Build and run the app to see how you’re progressing. You should see the chart’s title displayed at the top of the screen:

Charting Your Progress

Time to add a pie chart to the graph you just instantiated! Add the following lines of code to configureChart:

// 1 - Get reference to graph
CPTGraph *graph = self.hostView.hostedGraph;    
// 2 - Create chart
CPTPieChart *pieChart = [[CPTPieChart alloc] init];
pieChart.dataSource = self;
pieChart.delegate = self;
pieChart.pieRadius = (self.hostView.bounds.size.height * 0.7) / 2;
pieChart.identifier = graph.title;
pieChart.startAngle = M_PI_4;
pieChart.sliceDirection = CPTPieDirectionClockwise;    
// 3 - Create gradient
CPTGradient *overlayGradient = [[CPTGradient alloc] init];
overlayGradient.gradientType = CPTGradientTypeRadial;
overlayGradient = [overlayGradient addColorStop:[[CPTColor blackColor] colorWithAlphaComponent:0.0] atPosition:0.9];
overlayGradient = [overlayGradient addColorStop:[[CPTColor blackColor] colorWithAlphaComponent:0.4] atPosition:1.0];
pieChart.overlayFill = [CPTFill fillWithGradient:overlayGradient];
// 4 - Add chart to graph    
[graph addPlot:pieChart];

Let’s walk through the above code section-by-section:

  1. Obtain a reference to your graph instance via the host view.
  2. Instantiate and configure the pie chart, designating the view controller as the delegate and data source (more on this in a moment). You also define the radius for the pie chart (it is, after all, a circle), and give the chart an identifier. The identifier is similar to a view’s tag, but instead of an integer, it’s an NSString instance. You finish by noting the start angle for constructing the pie chart elements and declaring the direction in which new slices are added to the chart.
  3. Define a CPTGradient and use it to fill the chart. A radial gradient is well suited for a circular pie chart. The gradient is configured to have color stops toward the outside of the circle (between 0.9 and 1.0), adding a shadow to the edge of the pie chart. You can experiment with the colors, alpha values, and/or stop positions to create your own look.
  4. Add the pie chart to the graph.

If you compile and run the app right now, you’ll be disappointed to see that nothing appears to have changed. The reason is that you’ve designated the view controller as the chart data source, but the data source delegate methods are just stubs at the moment – they don’t return any relevant data.

To remedy this situation, turn your attention to numberOfRecordsForPlot:. As you’ve guessed from the name, this method simply informs the chart how many slices should be displayed. Replace the current placeholder code in numberOfRecordsForPlot: with the following:

return [[[CPDStockPriceStore sharedInstance] tickerSymbols] count];

The above line simply calls the CPDStockPriceStore singleton to get an array of ticker symbols, and then counts how many items are in the array to determine the number of slices for the pie chart.

Next, replace the placeholder code in numberForPlot:field:recordIndex: with the following:

if (CPTPieChartFieldSliceWidth == fieldEnum) 
{
    return [[[CPDStockPriceStore sharedInstance] dailyPortfolioPrices] objectAtIndex:index];
}
return [NSDecimalNumber zero];

This method receives the plot to be drawn as well as an index for the record to be displayed. It also receives an enumeration value that differs based on the plot type. In the case of CPTPieChart, you usually look for CPTPieChartFieldSliceWidth and when you find it, you return the daily portfolio prices in the same order as the stock ticker symbols (i.e., 0 for AAPL, 1 for GOOG, 2 for MSFT).

Add data labels to the pie chart by replacing the placeholder code in dataLabelForPlot:recordIndex::

// 1 - Define label text style
static CPTMutableTextStyle *labelText = nil;
if (!labelText) {
    labelText= [[CPTMutableTextStyle alloc] init];
    labelText.color = [CPTColor grayColor];
}
// 2 - Calculate portfolio total value
NSDecimalNumber *portfolioSum = [NSDecimalNumber zero];
for (NSDecimalNumber *price in [[CPDStockPriceStore sharedInstance] dailyPortfolioPrices]) {
    portfolioSum = [portfolioSum decimalNumberByAdding:price];
}
// 3 - Calculate percentage value
NSDecimalNumber *price = [[[CPDStockPriceStore sharedInstance] dailyPortfolioPrices] objectAtIndex:index];
NSDecimalNumber *percent = [price decimalNumberByDividingBy:portfolioSum];
// 4 - Set up display label
NSString *labelValue = [NSString stringWithFormat:@"$%0.2f USD (%0.1f %%)", [price floatValue], ([percent floatValue] * 100.0f)];
// 5 - Create and return layer with label text
return [[CPTTextLayer alloc] initWithText:labelValue style:labelText];

This method returns a CPTLayer, similar to a Core Animation layer. CPTLayers are abstracted to work on both Mac OS X and iOS, and they provide some other drawing niceties used by Core Plot.

In this instance, the method returns a CPTTextLayer. To construct that CPTTextLayer, you define another mutable text style and use it to create a string that represents each individual stock’s price and its percentage of the overall portfolio.

Now if you build and run, you’ll see a nifty-looking pie chart with a nice shadow gradient, and the text labels you just created. A lot simpler than writing all the Core Graphics rendering code yourself, eh? :]

Legen … Wait For It… dary!

The chart looks pretty nice, but a user will not know which pie slice corresponds to which stock by simply looking at it. So make things a bit more user-friendly by adding the following to configureLegend:

// 1 - Get graph instance
CPTGraph *graph = self.hostView.hostedGraph;
// 2 - Create legend
CPTLegend *theLegend = [CPTLegend legendWithGraph:graph];
// 3 - Configure legend
theLegend.numberOfColumns = 1;
theLegend.fill = [CPTFill fillWithColor:[CPTColor whiteColor]];
theLegend.borderLineStyle = [CPTLineStyle lineStyle];
theLegend.cornerRadius = 5.0;
// 4 - Add legend to graph
graph.legend = theLegend;     
graph.legendAnchor = CPTRectAnchorRight;
CGFloat legendPadding = -(self.view.bounds.size.width / 8);
graph.legendDisplacement = CGPointMake(legendPadding, 0.0);

The code simply instantiates and positions the legend. But how do you get the legend data for each slice? That’s handled by legendTitleForPieChart:recordIndex: – replace the existing method content with:

if (index < [[[CPDStockPriceStore sharedInstance] tickerSymbols] count]) {
    return [[[CPDStockPriceStore sharedInstance] tickerSymbols] objectAtIndex:index];
}
return @"N/A";

The method simply returns the symbol corresponding to the specified pie slice index.

Build and run. What do you think? I bet Barney would know what to say! :]

One More Thing: Dynamic Theming

You may recall that you configured the app to use the “Plain White” theme that Core Plot offers by default. How about letting the user change the theme at will? To do so, you’re going to modify themeTapped: to present a UIActionSheet.

Add the following to themeTapped::

UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Apply a Theme" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:CPDThemeNameDarkGradient, CPDThemeNamePlainBlack, CPDThemeNamePlainWhite, CPDThemeNameSlate, CPDThemeNameStocks, nil];
[actionSheet showFromTabBar:self.tabBarController.tabBar];

This method references values from the CPDConstants class that you added via the project’s starter resources. These are basically “human readable” names that correspond to the default Core Plot theme identifiers.

Now you need to implement the clickedButtonAtIndex: UIActionSheetDelegate method to change the graph theme based on the user’s selection. Add the following code to actionSheet:clickedButtonAtIndex::

// 1 - Get title of tapped button
NSString *title = [actionSheet buttonTitleAtIndex:buttonIndex];
// 2 - Get theme identifier based on user tap
NSString *themeName = kCPTPlainWhiteTheme;
if ([title isEqualToString:CPDThemeNameDarkGradient] == YES) {
    themeName = kCPTDarkGradientTheme;
} else if ([title isEqualToString:CPDThemeNamePlainBlack] == YES) {
    themeName = kCPTPlainBlackTheme;
} else if ([title isEqualToString:CPDThemeNamePlainWhite] == YES) {
    themeName = kCPTPlainWhiteTheme;
} else if ([title isEqualToString:CPDThemeNameSlate] == YES) {
    themeName = kCPTSlateTheme;
} else if ([title isEqualToString:CPDThemeNameStocks] == YES) {
    themeName = kCPTStocksTheme;
}
// 3 - Apply new theme
[self.hostView.hostedGraph applyTheme:[CPTTheme themeNamed:themeName]];

This method is fairly straightforward. First, it obtains the title for the UIActionSheet button tapped based on the clicked button index. Then it gets the theme identifier for the theme matching that theme name. Finally, it applies the selected theme to the graph.

Build and run, and try switching themes by tapping the “Theme” button. Here’s an example of the Plain Black theme.

Pretty cool, eh?

Where to Go From Here?

Here is an example project with the code from the tutorial so far.

Congrats, you’ve covered a lot of ground in this tutorial!

  • First, you added the Core Plot framework as a static library and set up a simple iPhone app to display three charts.
  • Then you proceeded to implement the pie chart, introducing some of the key framework abstractions along the way.
  • You concluded by adding a dynamic theming mechanism to your app!

You’re ready for Part 2 of this series, where you’ll add a bar graph and a scatter plot to your “work-in-progress” app. You’ll also explore some options to make your plots interactive!

posted on 2012-09-17 15:12  Simon Shi  阅读(441)  评论(0编辑  收藏  举报