När du arbetar med dataintensiva applikationer måste en utvecklare ofta göra mer än bara visa listor med dataposter i en tabellvy. CorePlot-biblioteket låter dig lägga till fantastiska datavisualiseringar till dina applikationer. Ta reda på hur i denna Tuts + Premium-serie!
Förra gången vi kom fram till hur man skapade ett stapeldiagram, hur man anpassar stapelfärgerna och hur man lägger till egna etiketter på axeln om vi vill visa anpassad text istället för bara siffror.
Den här gången kommer vi att täcka hur man abstrakt logiken från vår controller till ett mer återanvändbart datakällaobjekt. När vi har behärskat det här går vi över hur vi visar samma data som stapeldiagrammet, förutom den här tiden som visas som ett cirkeldiagram!
Innan vi går in i att abstrahera datalogiken ska vi skapa våra basklasser för cirkeldiagrammet. Precis som förra gången måste vi skapa en ny bildkontroll för grafen. Låt oss kalla det "STPieGraphViewController".
Observera att vi inte behöver skapa en vy den här gången eftersom vi kommer att kunna använda 'STGraphView'. Innan vi börjar ställa in saker laddar vi oss till STStudentListViewController.h och importerar STPieGraphViewController.h. Vi måste också överensstämma med protokollet STPieGraphViewControllerDelegate (som vi kommer att skapa senare):
@interface STStudentListViewController: UIViewController
Byt till .m-filen. Vi måste lägga till en knapp i handlingsbladet. Leta reda på metoden graphButtonWasSelected:. Vi ska redigera den andra knapptexten och lägga till en tredje:
- (void) graphButtonWasSelected: (id) avsändare UIActionSheet * graphSelectionActionSheet = [[[UIActionSheet alloc] initWithTitle: @ "Välj en graf" delegat: self cancelButtonTitle: @ "Cancel" destructiveButtonTitle: nil otherButtonTitles: @ "Registrering över tiden" "Ämnes Totals - Bar", @ "Ämnes Totals - Pie", Nil] autorelease]; [graphSelectionActionSheet showInView: [[UIApplication sharedApplication] keyWindow]];
Hoppa nu in i actionSheet: clickedButtonAtIndex: metod och lägg till en klausul för buttonIndex == 2:
annars om (buttonIndex == 2) STPieGraphViewController * graphVC = [[STPieGraphViewController alloc] init]; [graphVC setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal]; [graphVC setModalPresentationStyle: UIModalPresentationFullScreen]; [graphVC setDelegate: self]; [graphVC setManagedObjectContext: [self managedObjectContext]]; [self presentModalViewController: graphVC animated: YES]; [graphVC release];
Precis som förra gången kommer det att visas några varningar eftersom STPieGraphViewController inte har en delegat eller managedObjectContext-egenskap.
Så hoppa till STPieGraphViewController.h. Importera 'CorePlot-CocoaTouch.h' och lägg till följande egenskaper och protokolldeklarationer:
@protocol STPieGraphViewControllerDelegate @required - (void) doneButtonWasTapped: (id) avsändare; @end @interface STPieGraphViewController: UIViewController@property (nonatomic, strong) CPTGraph * graph; @property (nonatomic, assign) id delegera; @property (nonatomic, strong) NSManagedObjectContext * managedObjectContext; @slutet
Det är viktigt att påpeka att vi inte följer CPTPieChartDataSource den här gången. Detta beror på att vi kommer att abstrahera grafdatalogiken från STBarGraphViewController till en separat dataSource-klass. Innan vi gör det ändå, låt oss avsluta med att ställa allt upp. I .m-filen, importera "STGraphView.h", syntetisera egenskaperna och implementera deallocmetoden.
Slutligen, sätt in loadView och viewDidLoad-metoderna nedan:
- (void) loadView [super loadView]; [self setTitle: @ "Inskrivning per ämne"]; [self setView: [[[STGraphView alloc] initWithFrame: self.view.frame] autorelease]]; CPTTheme * defaultTheme = [CPTTheme themeNamed: kCPTPlainWhiteTheme]; [self setGraph: (CPTGraph *) [defaultTheme newGraph]]; - (void) viewDidLoad [super viewDidLoad]; STGraphView * graphView = (STGraphView *) [självvisning]; [[graphView chartHostingView] setHostedGraph: [self graph]]; // Tillåt användaren att gå tillbaka UINavigationItem * navigationItem = [[[UINavigationItem alloc] initWithTitle: self.title] autorelease]; [navigationItem setHidesBackButton: JA]; UINavigationBar * navigationBar = [[[UINavigationBar alloc] initWithFrame: CGRectMake (0, 0, self.view.frame.size.width, 44.0f)] autorelease]; [navigationBar pushNavigationItem: navigationItem animated: NO]; [self.view addSubview: navigationBar]; [navigationItem setRightBarButtonItem: [[[UIBarButtonItem alloc] initWithTitle: @ "Klar" stil: UIBarButtonItemStyleDone mål: [self delegate] action: @selector (doneButtonWasTapped :)] autorelease] animated: NO];
Ovanstående bör vara bekant nu. Så låt oss se på att abstrahera graflogiken i en separat klass.
Vi har redan skrivit logiken för att få data för det totala antalet studenter i alla ämnen; vi vill inte skriva det igen, lyckligtvis behöver vi inte göra det. Alla datakälla protokoll för tomterna ärva från "CPTPlotDataSource", och det är detta protokoll som innehåller numberOfRecordsForPlot: och numberForPlot: fieldEnum: recordIndex metoder.
Skapa en klass som heter 'STAbstractSubjectDataSource.h' (arv från NSObject) i en ny grupp som heter 'DataSource' i grafgruppen. För huvudfilerna importera 'CorePlot-CocoaTouch.h' och ange följande egenskaper och metoddeklarationer:
@interface STAbstractSubjectEnrollementDataSource: NSObject@property (nonatomic, strong) NSManagedObjectContext * managedObjectContext; - (id) initWithManagedObjectContext: (NSManagedObjectContext *) aManagedObjectContext; - (float) getTotalSubjects; - (float) getMaxEnrolled; - (NSArray *) getSubjectTitlesAsArray; @slutet
Vi prenumererar på protokollet "CPTPlotDataSource". Vi skapar en anpassad init-metod som passerar genom en managedObjectContext så att objektet kan komma åt databutiken. Slutligen finns det tre hjälpar metoder som kan hjälpa till med att få information om ämnena och inskrivningen. Dessa är samma metoder som för närvarande finns i STBarGraphViewController. Vi ska flytta dem ut och in i datakällan.
Bortsett från init-metoden innehåller inte .m-filen någon ny kod som du inte har sett tidigare. Det handlar bara om att flytta all befintlig kod från STBarGraphViewController till dataSource-objektet. De metoder du ska flytta är:
Se även till att du lägger till i anpassade init-metoden:
- (id) initWithManagedObjectContext: (NSManagedObjectContext *) aManagedObjectContext self = [super init]; om (själv) [self setManagedObjectContext: aManagedObjectContext]; återvänd själv
Nu har vi ett datakällaobjekt som kan ge basdata för både cirkeln och stapeldiagrammet. Den abstrakta datakällan ger oss inte allt vi behöver, men barFillForBarPlot: recordIndex kan inte implementeras eftersom det ingår i CPTBarPlotDataSource. Vi kommer att behöva förlänga vår abstrakta klass till något specifikt för barplaner.
Skapa ett nytt objekt i datakällargruppen som heter 'STBarGraphSubjectEnrollementDataSource' som utökar vår abstrakta klass. I rubriken prenumerera på "CPTBarPlotDataSource:
- (id) initWithManagedObjectContext: (NSManagedObjectContext *) aManagedObjectContext @interface STBarGraphSubjectEnrollementDataSource: STAbstractSubjectEnrollementDataSource
Och i .m-filen, implementera barFillForBarPlot-metoden:
#pragma mark - CPTBarPlotDataSource Methods - (CPTFill *) barFillForBarPlot: (CPTBarPlot *) barPlot recordIndex: (NSUInteger) index CPTColor * areaColor = nil; switch (index) fall 0: areaColor = [CPTColor redColor]; ha sönder; fall 1: areaColor = [CPTColor blueColor]; ha sönder; fall 2: areaColor = [CPTColor orangeColor]; ha sönder; fall 3: areaColor = [CPTColor greenColor]; ha sönder; standard: areaColor = [CPTColor purpleColor]; ha sönder; CPTFill * barFill = [CPTFill fillWithColor: areaColor]; returnera barFill;
Gå nu tillbaka till din STBarGraphViewControllers header-fil och importera den nya streckdiagramdatakällan. Du kan nu ta bort prenumerationen "CPTBarPlotDataSource". Hoppa in i .m-filen och ta bort alla metoder utom loadView, viewDidLoad och dealloc. Vi behöver dem inte längre.
Vi behöver behålla en pekare till datakällan och släpp sedan pekaren när vyn är klar med den. I det privata gränssnittet, förklara egenskapen och sedan syntetisera:
@interface STBarGraphViewController () @property (nonatomic, behåll) STBarGraphSubjectEnrollementDataSource * barGraphDataSource; @end @implementation STBarGraphViewController @synthesize delegate; @synthesize managedObjectContext; @synthesize graph; @synthesize barGraphDataSource;
Se till att du släpper den i deallokmetoden också. Skapa en ny instans och ställ den som vår egendom i loadView-metoden:
[self setBarGraphDataSource: [[[STBarGraphSubjectEnrollementDataSource alloc] initWithManagedObjectContext: [self managedObjectContext]] autorelease]];
Nu i viewDidLoad-metoden behöver vi använda vår datakällas hjälpmetoder för att beräkna plottutrymmet och de anpassade etiketterna:
CPTXYPlotSpace * studentPlotSpace = (CPTXYPlotSpace *) [graph defaultPlotSpace]; [studentPlotSpace setXRange: [CPTPlotRange plotRangeWithLocation: CPTDecimalFromInt (0) längd: CPTDecimalFromInt ([[self barGraphDataSource] getTotalSubjects] + 1)]]; [studentPlotSpace setYRange: [CPTPlotRange plotRangeWithLocation: CPTDecimalFromInt (0) längd: CPTDecimalFromInt ([[self barGraphDataSource] getMaxEnrolled] + 1)]];
NSArray * subjectsArray = [[self barGraphDataSource] getSubjectTitlesAsArray];
Spara, bygg och kör. Allt ska fungera som tidigare. Om det gör kan vi komma igång med att skapa vår cirkeldiagram!
Vi vill skapa en viss datakälla för cirkeldiagrammet precis som vi gjorde för stapeldiagrammet om vi behöver implementera några specifika datakällans metoder. Skapa en klass som heter 'STPieGraphSubjectEnrollementDataSource' som ärar från 'STAbstractSubjectEnrollementDataSource'. I huvudfilen, prenumerera på protokollet "CPTPieChartDataSource". Vi kommer inte att implementera några specifika tjuvkarta datakällan metoder, men vi kommer tillbaka till det senare.
Nu när vi har datakällor är det enkelt att skapa cirkeldiagrammet! Hoppa in i STBPieGraphViewController.m och importera data för kildgrafdatakälla. Förklara det som en egendom i .m-filen, som vi gjorde förra gången:
@interface STPieGraphViewController () @property (nonatomic, strong) STPieGraphSubjectEnrollementDataSource * pieChartDataSource; @end @implementation STPieGraphViewController @synthesize managedObjectContext; @synthesize delegat; @synthesize graph; @synthesize pieChartDataSource;
Skapa nu och sätt den i loadView:
[self setPieChartDataSource: [[[STPieGraphSubjectEnrollementDataSource alloc] initWithManagedObjectContext: [self managedObjectContext]] autorelease]];
Slutligen, i viewDidLoad-metoden behöver vi skapa vårt cirkeldiagram, lägga till det i vår GraphView och ta bort standardaxeln:
STGraphView * graphView = (STGraphView *) [självvisning]; [[graphView chartHostingView] setHostedGraph: [self graph]]; CPTPieChart * pieChart = [[CPTPieChart-allokering] initWithFrame: [graph bounds]]; [pieChart setPieRadius: 100,00]; [pieChart setIdentifier: @ "Subject"]; [pieChart setStartAngle: M_PI_4]; [pieChart setSliceDirection: CPTPieDirectionCounter Clockwise]; [pieChart setDataSource: pieChartDataSource]; [graph addPlot: pieChart]; [graf setAxisSet: nil]; [graf setBorderLineStyle: nil];
De flesta av ovanstående bör se bekanta. Observera att det inte uttryckligen kallas en "plot" eftersom den inte är beroende av en x-axel eller y-axel, men vi behandlar det fortfarande mycket lika. Det finns några cirkeldiagramspecifika saker som vi gör här också. Vi skapar en cirkelradie och startvinkel. Vi satte också en skivriktning. Slutligen ställer vi in 'axelset' för grafen till noll så att vi inte får x- och y-linjerna.
Och det borde vara allt. Bygg och kör för att se ditt cirkeldiagram.
Det här är bra, men det kan göra med någon form av indikation på vad varje färg representerar. Ett bra sätt att göra detta är att använda legender. För att göra detta skapar vi ett "CPTLegend" -objekt som vi lägger till i vårt diagram och implementerar en delegatmetod som returnerar den relevanta titeln för legenden.
Låt oss först skapa CPTLegend-objektet. I vår viewDidLoad-metod anger du följande kod nedanför där vi skapar vårt cirkeldiagram:
CPTLegend * theLegend = [CPTLegend legendWithGraph: [self graph]]; [theLegend setNumberOfColumns: 2]; [[självgraf] setLegend: theLegend]; [[självgraf] setLegendAnchor: CPTRectAnchorBottom]; [[självgraf] setLegendDisplacement: CGPointMake (0.0, 30.0)];
Detta skapar en legend och lägger till den i vårt grafobjekt. Antalet kolumner bestämmer hur det kommer att beskriva legendtitlarna. Vi ställer sedan in några attribut på grafobjektet som bestämmer var legenden ska placeras (botten) och en del förskjutning för att säkerställa att den visas fullt ut i vyn.
Vi behöver fortfarande ge legenden med titlar men. Det finns en metod som är specifik för CPTPieChartDataSource som tillåter oss att göra detta. Hoppa in i kakediagramdatakällan och implementera följande kod:
#pragma mark - CPTPieChartDataSource - (NSString *) legendTitleForPieChart: (CPTPieChart *) pieChart recordIndex: (NSUInteger) index NSError * error = nil; NSFetchRequest * request = [[NSFetchRequest tilldela] init]; NSEntityDescription * entity = [NSEntityDescription entityForName: @ "STSubject" inManagedObjectContext: [self managedObjectContext]]; NSPredicate * predicate = [NSPredicate predicateWithFormat: @ "subjectID ==% d", index]; [förfrågan setEntity: entity]; [förfrågan setResultType: NSDictionaryResultType]; [request setPredicate: predicate]; [request setReturnsDistinctResults: NO]; [request setPropertiesToFetch: [NSArray arrayWithObject: @ "subjectName"]]; NSArray * titleStrings = [[self managedObjectContext] executeFetchRequest: begäran fel: & error]; NSDictionary * fetchedSubject = [titleStrings objectAtIndex: 0]; returnera [fetchedSubject objectForKey: @ "subjectName"];
Denna metod får helt enkelt indexet för legenden och får titeln från datalagret som en sträng och returnerar den.
Bygg och kör och du borde ha en informativ cirkeldiagram!
Vi har täckt hur man abstraherar datalogiken från regulatorn till ett separat objekt som är lättare att hantera och utöka. Vi har också täckt grunderna för att skapa ett cirkeldiagram.
Det tar oss till slutet av serien. Jag hoppas att du har hittat dessa handledning bra. Det finns mycket mer som CorePlot kan göra, men det borde ge dig en solid grund att bygga på. Lycka till att lägga till grafer till dina egna projekt!