30.01.2011
für CorePlot 0.2.2, Xcode 3.2
Tutorial: CorePlot Liniendiagramm
Im Folgenden wird erklärt, wie man mit dem Framework CorePlot ein einfaches Liniendiagramm auf dem Mac erstellt.
Als erstes muß ein neues Projekt in Xcode angelegt werden. Im Gegensatz zu dem Tutorial Erstellen eines Balkendiagramm, in dem eine Quartz Composer Application verwendet wurde, wird diesmal eine Cocoa Application als Projekt-Template verwendet.
Als Name wird bei dem Beispiel CorePlotLinienDiagramm gewählt. Das neue Projekt sollte dann ungefähr so aussehen:
Als erstes sollte das CorePlot.Framework und das QuartzCore.framework hinzugefügt werden. Dafür muß unter Groups&Files der Ordner Frameworks selektiert werden. Nach einem Rechtsklick, kann im Popup-Menü Add->Existing Framework ausgewählt werden. In dem Auswahldialog wird das CorePlot.framework und das QuartzCore.framework selektiert und dann mit Add hinzufügt. (Zum Selektieren des zweiten Frameworks einfach cmd gedrückt halten.)
Wenn das CorePlot.framework in dem Dialog nicht erscheint, dann muß das Binary des Frameworks an die richtige Stelle kopiert werden. Das ist hier unter Punkt 2 beschrieben.
In der Datei CorePlotLiniendiagrammAppDelegate.h müssen die folgenden zwei Zeilen entfernt werden:
NSWindow *window;
@property (assign) IBOutlet NSWindow *window;
Danach wird die Datei so angepaßt, daß sie folgendermaßen aussieht:
#import <Cocoa/Cocoa.h>
#import <CorePlot/CorePlot.h>
@interface CorePlotLiniendiagrammAppDelegate : NSObject <CPPlotDataSource> {
IBOutlet CPLayerHostingView *view;
CPXYGraph *graph;
NSArray *data;
}
@end
Ganz oben in der Datei muß die Import-Anweisung für CorePlot ergänzt werden. Außerdem wird ein IBOutlet
vom Typ CPLayerHostingView
definiert, in dem das Liniendiagramm später angezeigt wird. Der Graph mit den Linien ist vom Typ CPXYGraph
. Das NSArray
wird dazu verwendet, um die Daten zu speichern, die angezeigt werden sollen. Außerdem muß noch das Protokoll CPPlotDataSource
für die DataSource angegeben werden.
In der Datei CorePlotLiniendiagrammAppDelegate.m kann noch die Zeile @synthesize window
gelöscht werden.
Nach dem Löschen, sollte die Datei folgendermaßen aussehen:
#import "CorePlotLiniendiagrammAppDelegate.h"
@implementation CorePlotLiniendiagrammAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
@end
Als erstes wird die dealloc
-Methode ergänzt, damit nach dem Schließen der Applikation der Speicher wieder aufgeräumt wird.
-(void)dealloc
{
[data release];
[graph release];
[super dealloc];
}
Außerdem wird die Methode awakeFromNib
benötigt. In ihr wird weiter unten der Quellcode ergänzt, der das Aussehen des Diagramms bestimmt.
- (void) awakeFromNib
{
...
}
Als nächstes kommt die Implementierung der DataSource-Methoden:
#pragma mark -
#pragma mark Plot Data Source Methods
-(NSUInteger)numberOfRecordsForPlot:(CPPlot *)plot
{
return data.count;
}
-(NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
return [[data objectAtIndex:index] objectForKey:[NSNumber numberWithInt:fieldEnum]];
}
Die Methode numberOfRecordsForPlot
liefert die Anzahl der Datensätze, d.h. die Anzahl der Punkte im Liniendiagramm, zurück. Dieser Wert kann direkt durch eine Abfrage der Elementanzahl in data
bestimmt werden. Weitere Informationen zur Methode numberForPlot:field:recordIndex
werden weiter unten gegeben.
Die meiste Logik steckt in der Methode awakeFromNib
. Hier werden die Daten angelegt und das Aussehen des Liniendiagramms wird definiert.
In der Methode awakeFromNib
wird zuerst der Code ergänzt, der das Array mit den anzuzeigenden Daten enthält.
data = [[NSArray alloc]initWithObjects: [NSDecimalNumber numberWithInt:100],
[NSDecimalNumber numberWithInt:130],
[NSDecimalNumber numberWithInt:30],
[NSDecimalNumber numberWithInt:40],
[NSDecimalNumber numberWithInt:60],
[NSDecimalNumber numberWithInt:80],
[NSDecimalNumber numberWithInt:100],
[NSDecimalNumber numberWithInt:120],
[NSDecimalNumber numberWithInt:10],
[NSDecimalNumber numberWithInt:15],
[NSDecimalNumber numberWithInt:20],
[NSDecimalNumber numberWithInt:100],
nil ];
Als nächstes kommt der Code, der den Graph erzeugt:
// Create graph and set a theme
graph = [[CPXYGraph alloc] initWithFrame:CGRectZero];
CPTheme *theme = [CPTheme themeNamed:kCPDarkGradientTheme];
[graph applyTheme:theme];
view.hostedLayer = graph;
Es wird ein Graph initialisiert und das CPTheme
festgelegt. Der Graph wird abschließend dem HostedLayer (hostedLayer
) der view
zugewiesen.
Als nächste wird eine Zeichenfläche definiert.
// Define the space for the bars. (12 Points with a max height of 150)
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0.0f)
length:CPDecimalFromFloat(150.0f)];
plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0.0f)
length:CPDecimalFromFloat(11.0f)];
Hier wird zuerst die Höhe y mit 150 angegeben. Für die Breite x wird der Wert 11 gewählt, weil 12 Punkte angezeigt werden sollen. Der erste Punkt wird bei der x-Koordinate 0 gesetzt und der letzte Punkt bei der x-Koordinate 11.
// ScatterPlot
CPScatterPlot *linePlot = [[[CPScatterPlot alloc] init] autorelease];
linePlot.identifier = @"LinienDiagramm";
linePlot.dataLineStyle.lineWidth = 3.f;
linePlot.dataLineStyle.lineColor = [CPColor orangeColor];
linePlot.dataSource = self;
[graph addPlot: linePlot];
Abschließend wird ein CPScatterPlot
allokiert und initialisiert. Als Identifier wird der Wert LinienDiagramm
zugewiesen, um das Diagramm eindeutig identifizieren zu können. Für die Breite einer Linie wird 3 (lineWidth
) angegeben. Über die Variable lineColor
wird die Farbe Orange gesetzt.
Die DataSource für den CPScatterPlot
wird auf self
gesetzt, so daß die oben angelegten DataSource-Methoden aufgerufen werden, wenn der ScatterPlot nach den anzuzeigenden Daten fragt.
Zum Schluß wird der ScatterPlot dem Graphen zugewiesen. Damit ist die Methode awakeFromNib
vollständing.
In der Methode numberForPlot:field:recordIndex
werden die Werte aus dem Array ausgelesen, wenn die Ansicht (view
) die Werte von ihrer DataSource abfragt. Dies funktioniert folgendermaßen:
-(NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
switch ( fieldEnum ) {
case CPScatterPlotFieldX:
return (NSDecimalNumber *)[NSDecimalNumber numberWithUnsignedInteger:index];
case CPScatterPlotFieldY:
return [data objectAtIndex:index];
}
return nil;
}
In der Methode werden zwei Fälle unterschieden. Einmal wird nach dem X-Wert (CPScatterPlotFieldX
) gefragt und einmal nach dem Y-Wert (CPScatterPlotFieldY
). Bei dem X-Wert wird einfach der index
zurückgeliefert. Wird der Y-Wert angefragt, dann wird der entsprechende Wert aus dem NSArray
data
ausgelesen.
Die Implementierung ist damit abgeschlossen. Jetzt muß noch die Ansicht (view
) eingerichtet werden. Dazu wird die Datei MainMenu.xib im Resources-Folder ausgewählt. Nach einem Doppelklick auf die Datei öffnet sich der InterfaceBuilder.
Wenn die Library nicht schon geöffnet ist, dann sollte man sie mit cmd-shift-L öffnen. Dort Classes anklicken und unten im Suchfeld CPLayerHostingView eintippen.
Diese Ansicht (View
) wird selektiert und mit gedrückter Mousetaste in das Fenster (Window
) gezogen und auf die gewünschte Größe angepaßt.
Was jetzt noch fehlt, ist die Verbindung zwischen View und Controller. Diese wird durch ein crtl-mousedrag vom CorePlot Liniendiagramm App Delegate auf die CPLayerHostingView
hinzugefügt. Jetzt ist auch das Outlet
im Controller mit der Ansicht (View
) verbunden.
Nach dem Kompilieren und Starten, sollte folgendes Liniendiagramm zu sehen sein: