Når du arbeider med dataintensive applikasjoner, må en utvikler ofte gjøre mer enn bare å vise lister over dataposter i en tabellvisning. CorePlot-biblioteket lar deg legge til fantastiske datavisualiseringer i dine applikasjoner. Finn ut hvordan i denne Tuts + Premium-serien!
Sist gang vi gikk over hvordan du tilpasser utseendet og stilen til en linjediagram (eller scatter plot) ved hjelp av klasser som CPTMutableTextStyle og CPTMutableLineStyle. Vi lærte å tilpasse trinnene X og Y-akser og tallstiler ved hjelp av CPTXYAxisSet og CPTXYAxis-klassene. Vi så også på hvordan du legger til flere tomter i grafen din og modifiserer datakildemetoder for å gi de riktige dataene for de riktige tomter ved hjelp av plottidentifikatorer.
I dag jobber vi med en helt ny graf. Vi skal opprette et linjediagram som viser totalt antall studenter i hvert fag med hver linje som representerer et emne. Vi vil også se på hvordan du tilpasser utseendet på grafen. La oss komme i gang!
Først må vi legge til de relevante klassene i prosjektet vårt. La oss lage en ViewController kalt 'STBarGraphViewController' og en 'STGraphView'. (Pass på at du legger dem i de aktuelle gruppene)
Legg merke til navngivelsen av visningen til 'STGraphView' i stedet for 'STBarGraphView'. Fremover vil vi bruke denne visningen til å vise stang- og kakediagrammer.
Før vi begynner å jobbe med noen kode, må vi legge til en knapp i handlingsarket i vår elevlistevisning. Først åpne 'STStudentListViewController.h' og importer STBarGraphViewController. Legg til STBarGraphViewControllerDelegate til listen over registrerte protokoller (protokollen eksisterer ikke, men vi vil lage den senere):
@interface STStudentListViewController: UIViewController
Deretter hopper du inn i .m-filen og finner 'graphButtonWasSelected:' -metoden. Legg til 'Registrering av emne' til listen over 'otherButtonTitles:':
UIActionSheet * graphSelectionActionSheet = [[[UIActionSheet alloc] initWithTitle: @ "Velg en graf" delegere: self cancelButtonTitle: @ "Cancel" destructiveButtonTitle: nil otherButtonTitles: @ "Registrering over tid", @ "Registrering av emne", null] autorelease] ;
Finn nå 'ActionSheet: clickedButtonAtIndex:' -metoden og modifiser den for å fungere med buttonIndex == 1:
hvis (buttonIndex == 0) STLineGraphViewController * graphVC = [[STLineGraphViewController allokere] init]; [graphVC setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal]; [graphVC setModalPresentationStyle: UIModalPresentationFullScreen]; [graphVC setDelegate: self]; [graphVC setManagedObjectContext: [self managedObjectContext]]; [self presentModalViewController: graphVC animated: YES]; [graphVC release]; annet hvis (buttonIndex == 1) STBarGraphViewController * graphVC = [[STBarGraphViewController alloc] init]; [graphVC setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal]; [graphVC setModalPresentationStyle: UIModalPresentationFullScreen]; [graphVC setDelegate: self]; [graphVC setManagedObjectContext: [self managedObjectContext]]; [self presentModalViewController: graphVC animated: YES]; [graphVC release];
Igjen, dette vil vise noen advarsler fordi vi ikke har implementert delegatene eller managedObjectContext egenskaper på STBarGraphViewController ennå.
Hopp nå til STBarGraphViewController.h. Importer CorePlot-CocoaTouch.h og legg til følgende eiendomserklæring:
@protocol STBarGraphViewControllerDelegate @required - (void) doneButtonWasTapped: (id) avsender; @slutt
Legg nå følgende egenskaper:
@property (nonatomic, strong) CPTGraph * graf; @property (nonatomic, assign) iddelegat; @property (nonatomic, strong) NSManagedObjectContext * managedObjectContext;
Endelig registrer deg at denne klassen vil følge disse protokollene:
@interface STBarGraphViewController: UIViewController
Legg merke til at, bortsett fra å overholde forskjellige protokoller, er denne klassen den samme som STLineViewController. Ideelt sett ville du ha en grunnklass som ville ha disse egenskapene som du ville subclass for å redusere kode repetisjon. Denne opplæringen fokuserer bare på kjerneplott, så vi fokuserer bare på hvordan det er best å jobbe med CorePlot-rammeverket. Hvis du har kunnskap og tid, er du velkommen til å lage grunnklassen og bruke arv, men vi skal holde det enkelt i prøvekoden her.
Deretter hopper du inn i .m-filen, syntetiser egenskapene og slipper dem i deallokmetoden.
La oss nå koble visningsklassen som kontrollerens visning. Importer 'STBarGraphView.h', og legg til følgende metode:
- (ugyldig) loadView [super loadView]; [self setTitle: @ "Registrering etter emne"]; [self setView: [[[STGraphView alloc] initWithFrame: self.view.frame] autorelease]]; CPTTheme * defaultTheme = [CPTTheme themeNamed: kCPTPlainWhiteTheme]; [self setGraph: (CPTGraph *) [defaultTheme newGraph]];
Nå er vi klare til å jobbe med visningen. Åpne STGraphView.h, importer kjerneplottrammen (CorePlot-CocoaTouch.h), og legg til følgende eiendomserklæring:
@property (nonatomic, strong) CPTGraphHostingView * chartHostingView;
Gå inn i .m, syntetiser eiendommen, og slipp egenskapen i deallokmetoden. Deretter lager du CPTGraphHostingView i metoden 'initWithFrame:':
- (id) initWithFrame: (CGRect) ramme self = [super initWithFrame: frame]; hvis (selv) [self setChartHostingView: [[[CPTGraphHostingView alloc] initWithFrame: CGRectZero] autorelease]]; [chartHostingView setBackgroundColor: [UIColor purpleColor]]; [self addSubview: chartHostingView]; returner selv;
Endelig opprett "layoutSubviews" metoden med følgende kode:
- (void) layoutSubviews [super layoutSubviews]; float chartHeight = self.frame.size.height; float chartWidth = self.frame.size.width; [[self chartHostingView] setFrame: CGRectMake (0, 0, chartWidth, chartHeight)]; [[self chartHostingView] setCenter: [selvsenter]];
Du vil legge merke til at koden som brukes til å opprette denne visningen, er nøyaktig den samme som STLineGraphView. Vi kan bruke denne grunnleggende visningen til å fungere med alle grafevisninger fremover.
Gå tilbake til visningen 'STBarGraphViewController.m' og finn 'viewDidLoad'-metoden. Først vil vi lage en CPTBarPlot og legge den til i vår graf:
[[graphView chartHostingView] setHostedGraph: [selvgrafikk]]; CPTBarPlot * subjectBarPlot = [[CPTBarPlot tildeling] initWithFrame: [graph bounds]]; [subjectBarPlot setIdentifier: @ "subjectEnrollement"]; [subjectBarPlot setDelegate: self]; [subjectBarPlot setDataSource: self]; [[selvgrafikk] addPlot: subjectBarPlot];
Til slutt, la oss legge til en navigasjonslinje med en ferdig knapp, slik at brukeren kan komme tilbake:
UINavigationItem * navigationItem = [[[UINavigationItem alloc] initWithTitle: self.title] autorelease]; [navigasjonItem setHidesBackButton: JA]; UINavigationBar * navigationBar = [[[UINavigationBar alloc] initWithFrame: CGRectMake (0, 0, self.view.frame.size.width, 44.0f)] autorelease]; [navigasjonBar pushNavigationItem: navigasjonItem animert: NEI]; [self.view addSubview: navigasjonBar]; [navigasjonItem setRightBarButtonItem: [[[UIBarButtonItem alloc] initWithTitle: @ "Ferdig" stil: UIBarButtonItemStyleDone mål: [self delegate] handling: @selector (doneButtonWasTapped :)] autorelease] animated: NO];
La oss nå begynne å jobbe med datakildemetodene:
Prosessen skal likne hvordan vi opprettet linjediagrammet, men vi vil gjøre ting litt mer dynamiske. Vi skal skape tilpassede metoder som vil gi et passende plottingsrom. Vi vil også legge til noen ekstra datakilde metoder for å gi en bestemt farge for hver linje samt x-akse titler.
Før vi ser på å gi grafdataene, må vi sette opp akse og synlige områder. For å gjøre dette trenger vi to metoder som beregner maksimale x og maksimum y verdier. Erklære følgende metoder øverst i .m-filen:
[[graphView chartHostingView] setHostedGraph: [selvgrafikk]]; @interface STBarGraphViewController () - (float) getTotalSubjects; - (float) getMaxEnrolled; @slutt
Nå implementer dem som nedenfor:
#pragma mark - Private Methods - (float) getTotalSubjects NSError * error = null; NSFetchRequest * fetchRequest = [[[NSFetchRequest alloker] init] autorelease]; NSEntityDescription * entity = [NSEntityDescription entityForName: @ "STSubject" inManagedObjectContext: managedObjectContext]; [fetchRequest setEntity: entity]; returnere [managedObjectContext countForFetchRequest: fetchRequest error: & error]; - (float) getMaxEnrolled float maxEnrolled = 0; NSError * error = nil; for (int i = 0; i < [self getTotalSubjects]; i++) NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"STStudent" inManagedObjectContext:managedObjectContext]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"subjectID == %d", i]; [fetchRequest setEntity:entity]; [fetchRequest setPredicate:predicate]; float subjectMax = [managedObjectContext countForFetchRequest:fetchRequest error:&error]; NSLog(@"enrolled for Subject %d is %f", i, subjectMax); [fetchRequest release]; if (subjectMax > maxEnrolled) maxEnrolled = subjectMax; returnere maxEnrolled;
'getTotalSubjects' får bare en rekke av alle fagene i kjerne datalager. 'getMaxEnrolled' løkker gjennom alle fagene og ser etter det høyeste antallet studenter i hver og gir den høyeste verdien.
Med disse metodene gjort, kan vi gå tilbake til vår "viewDidLoad" -metode og legge til følgende kode nedenfor hvor vi legger til linjeproten i grafen vår:
CPTXYPlotSpace * studentPlotSpace = (CPTXYPlotSpace *) [graph defaultPlotSpace]; [studentPlotSpace setXRange: [CPTPlotRange plotRangeWithLocation: CPTDecimalFromInt (0) lengde: CPTDecimalFromInt ([self getTotalSubjects] + 1)]]; [studentPlotSpace setYRange: [CPTPlotRange plotRangeWithLocation: CPTDecimalFromInt (0) lengde: CPTDecimalFromInt ([self getMaxEnrolled] + 1)]]; [[graph plotAreaFrame] setPaddingLeft: 40.0f]; [[graph plotAreaFrame] setPaddingTop: 10.0f]; [[graph plotAreaFrame] setPaddingBottom: 120.0f]; [[graph plotAreaFrame] setPaddingRight: 0.0f]; [[graph plotAreaFrame] setBorderLineStyle: null]; CPTMutableTextStyle * textStyle = [CPTMutableTextStyle textStyle]; [textStyle setFontSize: 12.0f]; [textStyle setColor: [CPTColor colorWithCGColor: [[UIColor grayColor] CGColor]]]; CPTXYAxisSet * axisSet = (CPTXYAxisSet *) [graph axisSet]; CPTXYAxis * xAxis = [axisSet xAxis]; [xAxis setMajorIntervalLength: CPTDecimalFromInt (1)]; [xAxis setMinorTickLineStyle: null]; [xAxis setLabelingPolicy: CPTAxisLabelingPolicyFixedInterval]; [xAxis setLabelTextStyle: textStyle]; CPTXYAxis * yAxis = [axisSet yAxis]; [yAxis setMajorIntervalLength: CPTDecimalFromInt (1)]; [yAxis setMinorTickLineStyle: null]; [yAxis setLabelingPolicy: CPTAxisLabelingPolicyFixedInterval];
De fleste av de ovennevnte burde være kjent fra forrige gang, men i stedet for å sette hardkodede verdier for x- og y-maksimal rekkevidde, bruker vi våre nye metoder for å dynamisk skape verdiene. Etter det har vi bare satt opp noen grunnleggende akseformatering og gi plottrammen noe polstring slik at aksen viser riktig.
Nå må vi gi grafen data med datakildemetoder. Å gi antall poster er enkelt:
#pragma mark - CPTBarPlotDataSourceMethods - (NSUInteger) numberOfRecordsForPlot: (CPTPlot *) plot return [self getTotalSubjects];
Nå for å gi x- og y-verdiene for postene:
- (NSNummer *) numberForPlot: (CPTPlot *) plottfelt: (NSUInteger) -feltEn recordIndex: (NSUInteger) indeks int x = indeks + 1; int y = 0; NSError * feil; NSFetchRequest * fetchRequest = [[NSFetchRequest alloker] init]; NSEntityDescription * entity = [NSEntityDescription entityForName: @ "STStudent" inManagedObjectContext: managedObjectContext]; NSPredicate * predicate = [NSPredicate predicateWithFormat: @ "subjectID ==% d", indeks]; [fetchRequest setEntity: entity]; [fetchRequest setPredicate: predicate]; y = [managedObjectContext countForFetchRequest: fetchRequest error: & error]; [fetchRequest release]; bytte (fieldEnum) tilfelle CPTScatterPlotFieldX: returnere [NSNummer nummerWithInt: x]; gå i stykker; saken CPTScatterPlotFieldY: returnere [NSNummer nummerWithInt: y]; gå i stykker; standard: break; returner null;
Ovennevnte metode ligner veldig på hvordan vi leverer data til et spredningsdiagram. Vi bytter anropet til datalageret slik at vi får en telling av alle studenter som er registrert i et emne. Vi stiller også x-verdien til +1. Dette er slik at linjen begynner på '1' og gir litt polstring mellom den første linjen og y-akselinjen.
Lagre og kjør prosjektet ... du bør se bargrafen din!
Det er litt mer vi kan gjøre for å gjøre dette enklere å se på. Vi vil gi hver linje en annen farge og gi navnet på emnet som x-akse-etiketten i stedet for et tall. Vi kan gjøre det første med en CPTBarPlotDataSource-metode kalt 'barFillForBarPlot: recordIndex':
-(CPTFill *) barFillForBarPlot: (CPTBarPlot *) barPlot recordIndex: (NSUInteger) indeks CPTColor * areaColor = nil; bytte (indeks) case 0: areaColor = [CPTColor redColor]; gå i stykker; tilfelle 1: areaColor = [CPTColor blueColor]; gå i stykker; tilfelle 2: areaColor = [CPTColor orangeColor]; gå i stykker; tilfelle 3: areaColor = [CPTColor greenColor]; gå i stykker; standard: areaColor = [CPTColor purpleColor]; gå i stykker; CPTFill * barFill = [CPTFill fillWithColor: areaColor]; return barFill;
Dette vil returnere en annen farge for hver linje i grafen vår. Hvis du vil gjøre det enda mer stilig, kan du faktisk gjøre det til en gradient. Det er også en del av grafen vår som ikke er fullt dynamisk, fordi hvis noen legger til et nytt emne, vil det bruke standardfargen. Kanskje en måte å omgå dette i et virkelighetsapplikasjon ville være å ha en farge når du oppretter et nytt emne som er lagret i datalageret
Til slutt, la oss legge til noen egendefinerte x-akse titler. For å gjøre dette må vi jobbe med x-aksen. Finn hvor vi setter opp x-aksen og modifiser koden for å gjøre følgende:
CPTXYAxis * xAxis = [axisSet xAxis]; [xAxis setMajorIntervalLength: CPTDecimalFromInt (1)]; [xAxis setMinorTickLineStyle: null]; [xAxis setLabelingPolicy: CPTAxisLabelingPolicyNone]; [xAxis setLabelTextStyle: textStyle]; [xAxis setLabelRotation: M_PI / 4]; NSArray * subjectsArray = [self getSubjectTitlesAsArray]; [xAxis setAxisLabels: [NSSet setWithArray: subjectsArray]];
Det er noen endringer i koden ovenfor. For det første endrer vi merkingspolitikken til ingen. Dette vil sikre at CorePlot ikke skriver ut etiketten selv og vil i stedet bruke det vi gir det. Deretter setter vi etikettrotasjonen slik at den stemmer overens med grafen bedre. Endelig setter vi egenskapen 'Axis labels' som tar et NSSet av NSString-verdier. Vi lager NSSet ved hjelp av en NSArray opprettet av metoden 'getSubjectTitlesAsArray'. Denne metoden eksisterer ikke ennå, så la oss lage den. Legg til erklæringen øverst i .m-filen, og skriv deretter følgende implementering:
- (NSArray *) getSubjectTitlesAsArray NSError * error = nil; NSFetchRequest * request = [[NSFetchRequest alloker] init]; NSSortDescriptor * sortDescriptor = [NSSortDescriptor sortDescriptorWithKey: @ "subjectID" stigende: JA]; NSEntityDescription * entity = [NSEntityDescription entityForName: @ "STSubject" inManagedObjectContext: managedObjectContext]; [be om setEntity: entity]; [request setSortDescriptors: [NSArray arrayWithObject: sortDescriptor]]; [forespørsel setResultType: NSDictionaryResultType]; [request setReturnsDistinctResults: NO]; [request setPropertiesToFetch: [NSArray arrayWithObject: @ "subjectName"]]; NSArray * titleStrings = [managedObjectContext executeFetchRequest: forespørselfeil: & feil]; NSMutableArray * labelArray = [NSMutableArray array]; CPTMutableTextStyle * textStyle = [CPTMutableTextStyle textStyle]; [textStyle setFontSize: 10]; for (int i = 0; i < [titleStrings count]; i++) NSDictionary *dict = [titleStrings objectAtIndex:i]; CPTAxisLabel *axisLabel = [[CPTAxisLabel alloc] initWithText:[dict objectForKey:@"subjectName"] textStyle:textStyle]; [axisLabel setTickLocation:CPTDecimalFromInt(i + 1)]; [axisLabel setRotation:M_PI/4]; [axisLabel setOffset:0.1]; [labelArray addObject:axisLabel]; [axisLabel release]; return [NSArray arrayWithArray:labelArray];
Det skjer mye i koden ovenfor. For å kunne gi en graf tilpassede etiketter, må vi passere et NSSet som inneholder objekter av typen 'CPTAxisLabel'. Først får vi en rekke av alle fagnavnene bestilt av subjektID, så det vil være i samme rekkefølge som grafen. Da, for mengden av navn vi kommer tilbake, slår vi gjennom og lager en CPTAxisLabel med emneavnstrengen og en forhåndsdefinert tekststil. 'Tippestedet' er hvilket tikk det vises for. Vi må legge til 1 til vår verdi fordi vi starter vår første bar på 1 i stedet for 0. Vi setter deretter en rotasjon og offset og legger den til en matrise. Til slutt returnerer vi en rekke akselabeller.
Hvis vi lagrer og kjører prosjektet, har vi en ferdig, fargerik strekdi med tilpassede etiketter!
Vi har lært en del om CorePlot denne sesjonen. Vi har lært hvordan du oppretter et linjediagram, endrer linjens farger, og til og med legger du til egendefinerte etiketter på aksen.
Neste gang vil vi dekke hvordan du lager et kjempeflott kakenskjema som viser de samme dataene som linjediagrammet. Fang deg neste gang!