Werken met CorePlot een staafdiagram maken

Bij het werken met gegevensintensieve toepassingen moet een ontwikkelaar vaak meer doen dan alleen lijsten met gegevensrecords weergeven in een tabelweergave. Met de CorePlot-bibliotheek kunt u verbluffende gegevensvisualisaties toevoegen aan uw toepassingen. Ontdek hoe in deze Tuts + Premium-serie!


Ook verkrijgbaar in deze serie:

  1. Werken met CorePlot: projectinstellingen
  2. Werken met CorePlot: Plot Fundamentals
  3. Werken met CorePlot: stylen en percelen toevoegen
  4. Werken met CorePlot: een staafdiagram maken
  5. Werken met CorePlot: een cirkeldiagram maken

Waar we gebleven zijn

De laatste keer hebben we besproken hoe u het uiterlijk en de stijl van een lijngrafiek (of spreidingsplot) kunt aanpassen met behulp van klassen zoals CPTMutableTextStyle en CPTMutableLineStyle. We hebben geleerd hoe we de incrementen en nummerstijlen van de X- en Y-as kunnen aanpassen met behulp van de klassen CPTXYAxisSet en CPTXYAxis. We hebben ook gekeken naar hoe u meerdere plots aan uw grafiek kunt toevoegen en de gegevensbronmethoden kunt aanpassen om de juiste gegevens voor de juiste plots te leveren met behulp van plot-id's.


Wat we vandaag behandelen

Vandaag werken we met een compleet nieuwe grafiek. We maken een staafdiagram dat het totale aantal studenten in elk onderwerp weergeeft, waarbij elke balk een onderwerp vertegenwoordigt. We zullen ook kijken naar hoe je het uiterlijk van de grafiek kunt aanpassen. Laten we beginnen!


Stap 1: instellen

Eerst moeten we de relevante klassen toevoegen aan ons project. Laten we een ViewController maken genaamd 'STBarGraphViewController' en een 'STGraphView'. (Zorg ervoor dat je ze in de relevante groepen plaatst)


Let op de naamgeving van de weergave op 'STGraphView' in plaats van 'STBarGraphView'. In de toekomst zullen we deze weergave gebruiken om de staaf- en cirkelgrafieken weer te geven.

Voordat we met een code beginnen te werken, moeten we een knop toevoegen aan het actievel van onze lijst met studentenlijsten. Open eerst 'STStudentListViewController.h' en importeer STBarGraphViewController. Voeg STBarGraphViewControllerDelegate toe aan de lijst met geregistreerde protocollen (het protocol bestaat eigenlijk nog niet maar we zullen het later aanmaken):

 @interface STStudentListViewController: UIViewController 

Spring vervolgens in het .m-bestand en zoek de 'graphButtonWasSelected:' methode. Voeg 'Enrollment by subject' toe aan de lijst met 'otherButtonTitles:':

 UIActionSheet * graphSelectionActionSheet = [[[UIActionSheet alloc] initWithTitle: @ "Kies een grafiek" delegate: self cancelButtonTitle: @ "Cancel" destructiveButtonTitle: nil otherButtonTitles: @ "Enrollment in time", @ "Enrollment by subject", nel] autorelease] ;

Zoek nu de 'actionSheet: clickedButtonAtIndex:' methode en pas deze aan om met buttonIndex == 1 te werken:

 if (buttonIndex == 0) STLineGraphViewController * graphVC = [[STLineGraphViewController alloc] init]; [graphVC setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal]; [graphVC setModalPresentationStyle: UIModalPresentationFullScreen]; [graphVC setDelegate: self]; [graphVC setManagedObjectContext: [self managedObjectContext]]; [self presentModalViewController: graphVC geanimeerd: YES]; [graphVC release];  else if (buttonIndex == 1) STBarGraphViewController * graphVC = [[STBarGraphViewController alloc] init]; [graphVC setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal]; [graphVC setModalPresentationStyle: UIModalPresentationFullScreen]; [graphVC setDelegate: self]; [graphVC setManagedObjectContext: [self managedObjectContext]]; [self presentModalViewController: graphVC geanimeerd: YES]; [graphVC release]; 

Nogmaals, dit zal enkele waarschuwingen tonen, omdat we de gedelegeerde of managedObjectContext-eigenschappen nog niet op STBarGraphViewController hebben geïmplementeerd.

Spring nu in STBarGraphViewController.h. Importeer CorePlot-CocoaTouch.h en voeg de volgende eigenschapverklaring toe:

 @protocol STBarGraphViewControllerDelegate @required - (void) doneButtonWasTapped: (id) afzender; @einde

Voeg nu de volgende eigenschappen toe:

 @property (nonatomic, strong) CPTGraph * grafiek; @property (nonatomic, assign) id delegeren; @property (nonatomic, strong) NSManagedObjectContext * managedObjectContext;

Registreer tot slot dat deze klasse deze protocollen zal volgen:

 @interface STBarGraphViewController: UIViewController 

Merk op dat, afgezien van het voldoen aan verschillende protocollen, deze klasse hetzelfde is als STLineViewController. Idealiter zou u een basisklasse hebben die deze eigenschappen zou hebben die u zou subclasseren om codeherhaling te verminderen. Deze tutorial richt zich alleen op de kern van de plot, dus we concentreren ons alleen op de beste manier om met het CorePlot-framework te werken. Als je de kennis en tijd hebt, voel je dan vrij om de basisklasse te maken en overerving te gebruiken, maar we houden het hier simpel in de voorbeeldcode..

Spring vervolgens in het .m-bestand, synthetiseer de eigenschappen en laat ze los in de dealloc-methode.

Laten we nu de view-klasse koppelen aan de weergave van de controller. Importeer 'STBarGraphView.h' en voeg de volgende methode toe:

 - (ongeldig) loadView [super loadView]; [self setTitle: @ "Inschrijving op onderwerp"]; [self setView: [[[STGraphView alloc] initWithFrame: self.view.frame] autorelease]]; CPTTheme * defaultTheme = [CPTTheme themeNamed: kCPTPlainWhiteTheme]; [self setGraph: (CPTGraph *) [defaultTheme newGraph]]; 

Nu zijn we klaar om met het uitzicht te werken. Open STGraphView.h, importeer het core-plotraamwerk (CorePlot-CocoaTouch.h) en voeg de volgende eigenschapsverklaring toe:

 @property (nonatomic, strong) CPTGraphHostingView * chartHostingView;

Spring in de .m, synthetiseer de eigenschap en laat de eigenschap vrij in de dealloc-methode. Maak vervolgens de CPTGraphHostingView in de 'initWithFrame:' methode:

 - (id) initWithFrame: (CGRect) -frame self = [super initWithFrame: frame]; if (self) [self setChartHostingView: [[[CPTGraphHostingView alloc] initWithFrame: CGRectZero] autorelease]]; [chartHostingView setBackgroundColor: [UIColor purpleColor]]; [self addSubview: chartHostingView];  terugkeer zelf; 

Maak ten slotte de 'layoutSubviews'-methode met de volgende code:

 - (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: [zelfcentreren]]; 

U zult merken dat de code die wordt gebruikt om deze weergave te maken, precies hetzelfde is als STLineGraphView. We kunnen deze basisweergave gebruiken om in de toekomst alle grafische weergaven te gebruiken.

Spring terug in de weergave 'STBarGraphViewController.m' en zoek de methode 'viewDidLoad'. Eerst willen we een CPTBarPlot maken en deze toevoegen aan onze grafiek:

 [[graphView chartHostingView] setHostedGraph: [zelfgrafiek]]; CPTBarPlot * subjectBarPlot = [[CPTBarPlot alloc] initWithFrame: [graph bounds]]; [subjectBarPlot setIdentifier: @ "subjectEnrollement"]; [subjectBarPlot setDelegate: self]; [subjectBarPlot setDataSource: self]; [[zelfgrafiek] addPlot: subjectBarPlot];

Laten we tot slot een navigatiebalk toevoegen met een knop Klaar, zodat de gebruiker terug kan komen:

 UINavigationItem * navigationItem = [[[UINavigationItem alloc] initWithTitle: self.title] autorelease]; [navigationItem setHidesBackButton: YES]; 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: @ "Gereed" stijl: UIBarButtonItemStyleDone target: [self delegate] actie: @selector (doneButtonWasTapped :)] autorelease] geanimeerd: NO];

Laten we nu gaan werken aan de gegevensbronmethoden:


Stap 2: De staafdiagramgegevens opgeven

Het proces zal vergelijkbaar zijn met hoe we de lijngrafiek hebben gemaakt, maar we zullen de dingen een beetje dynamischer maken. We gaan aangepaste methoden maken die een geschikte plotruimte bieden. We zullen ook enkele extra gegevensbronmethoden toevoegen om een ​​specifieke kleur voor elke staaf te bieden, evenals titels op de x-as.

Voordat we kijken naar het leveren van de grafiekgegevens, moeten we de as en zichtbare bereiken instellen. Hiervoor hebben we twee methoden nodig die de maximale x en maximale y-waarden berekenen. Declareer de volgende methoden bovenaan het .m-bestand:

 [[graphView chartHostingView] setHostedGraph: [zelfgrafiek]]; @interface STBarGraphViewController () - (float) getTotalSubjects; - (float) getMaxEnrolled; @einde

Implementeer ze nu zoals hieronder:

 #pragma mark - Private Methods - (float) getTotalSubjects NSError * error = nil; NSFetchRequest * fetchRequest = [[[NSFetchRequest alloc] init] autorelease]; NSEntityDescription * entity = [NSEntityDescription entityForName: @ "STSubject" inManagedObjectContext: managedObjectContext]; [fetchRequest setEntity: entity]; return [managedObjectContext countForFetchRequest: fetchRequest error: & error];  - (float) getMaxEnrolled float maxEnrolled = 0; NSError * error = nil; voor (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;  return maxEnrolled; 

'getTotalSubjects' krijgt gewoon een telling van alle onderwerpen in de kerngegevensopslag. 'getMaxEnrolled' doorloopt alle onderwerpen en zoekt naar het hoogste aantal studenten in elke klas en retourneert de hoogste waarde.

Als deze methoden zijn voltooid, kunnen we teruggaan naar onze 'viewDidLoad'-methode en de onderstaande code toevoegen waar we de staafgrafiek aan onze grafiek toevoegen:

 CPTXYPlotSpace * studentPlotSpace = (CPTXYPlotSpace *) [graph defaultPlotSpace]; [studentPlotSpace setXRange: [CPTPlotRange plotRangeWithLocation: CPTDecimalFromInt (0) length: CPTDecimalFromInt ([self getTotalSubjects] + 1)]]; [studentPlotSpace setYRange: [CPTPlotRange plotRangeWithLocation: CPTDecimalFromInt (0) length: CPTDecimalFromInt ([self getMaxEnrolled] + 1)]]; [[grafiek plotAreaFrame] setPaddingLeft: 40.0f]; [[grafiek plotAreaFrame] setPaddingTop: 10.0f]; [[grafiek plotAreaFrame] setPaddingBottom: 120.0f]; [[grafiek plotAreaFrame] setPaddingRight: 0.0f]; [[graph plotAreaFrame] setBorderLineStyle: nil]; 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: nihil]; [xAxis setLabelingPolicy: CPTAxisLabelingPolicyFixedInterval]; [xAxis setLabelTextStyle: textStyle]; CPTXYAxis * yAxis = [axisSet yAxis]; [yAxis setMajorIntervalLength: CPTDecimalFromInt (1)]; [yAxis setMinorTickLineStyle: nihil]; [yAxis setLabelingPolicy: CPTAxisLabelingPolicyFixedInterval];

Het merendeel van het bovenstaande moet echter van de vorige keer bekend zijn, in plaats van het instellen van harde gecodeerde waarden voor onze x en y max plotbereiklengte, gebruiken we onze nieuwe methoden om de waarden dynamisch te maken. Daarna hebben we gewoon wat basisasopmaak ingesteld en geven het plotframe wat opvulling zodat de as op de juiste manier wordt weergegeven.

Nu moeten we de grafiek voorzien van gegevens met behulp van de gegevensbronmethoden. Het aantal records is eenvoudig:

 #pragma mark - CPTBarPlotDataSourceMethods - (NSUInteger) numberOfRecordsForPlot: (CPTPlot *) plot return [self getTotalSubjects]; 

Nu om de x- en y-waarden voor de records op te geven:

 - (NSNumber *) numberForPlot: (CPTPlot *) plotveld: (NSUInteger) fieldEnum recordIndex: (NSUInteger) index int x = index + 1; int y = 0; NSError * -fout; NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription * entity = [NSEntityDescription entityForName: @ "STStudent" inManagedObjectContext: managedObjectContext]; NSPredicate * predicate = [NSPredicate predicateWithFormat: @ "subjectID ==% d", index]; [fetchRequest setEntity: entity]; [fetchRequest setPredicate: predicate]; y = [managedObjectContext countForFetchRequest: fetchRequest error: & error]; [fetchRequest-release]; switch (fieldEnum) case CPTScatterPlotFieldX: return [NSNumber numberWithInt: x]; breken; case CPTScatterPlotFieldY: return [NSNumber numberWithInt: y]; breken; standaard: pauze;  terug nul; 

De bovenstaande methode lijkt erg op hoe we gegevens aan een scatterplot leveren. We veranderen de oproep naar de datastore zodat we een telling krijgen van alle studenten die voor een vak zijn ingeschreven. We stellen ook de x-waarde in op +1. Dit is zo dat de balk begint bij '1' en een beetje opvulling biedt tussen de eerste en de y-aslijn.

Sla op en voer het project uit ... je zou je staafdiagram moeten zien!



Stap 3: Afwerking

Er is wat meer dat we kunnen doen om dit gemakkelijker te maken. We willen elke balk een andere kleur geven en de naam van het onderwerp als het x-aslabel in plaats van een getal opgeven. We kunnen de eerste doen met een CPTBarPlotDataSource-methode genaamd 'barFillForBarPlot: recordIndex':

 -(CPTFill *) barFillForBarPlot: (CPTBarPlot *) barPlot recordIndex: (NSUInteger) index CPTColor * areaColor = nil; switch (index) case 0: areaColor = [CPTColor redColor]; breken; case 1: areaColor = [CPTColor blueColor]; breken; geval 2: areaColor = [CPTColor orangeColor]; breken; geval 3: areaColor = [CPTColor greenColor]; breken; standaard: areaColor = [CPTColor purpleColor]; breken;  CPTFill * barFill = [CPTFill fillWithColor: areaColor]; terugkeer barFill; 

Hierdoor wordt voor elke balk in onze grafiek een andere kleur geretourneerd. Als je het nog stijlvoller wilde maken, zou je er een verloop van kunnen maken. Het is ook een deel van onze grafiek dat niet volledig dynamisch is, omdat als iemand een nieuw onderwerp toevoegt, het de standaardkleur zal gebruiken. Misschien zou een manier om dit in een echte toepassing te omzeilen zijn om een ​​kleur te hebben bij het maken van een nieuw onderwerp dat is opgeslagen in de gegevensopslag


Laten we tot slot enkele aangepaste x-as titels toevoegen. Om dit te doen, moeten we werken met de x-as. Zoek waar we de x-as hebben ingesteld en pas de code aan om het volgende te doen:

 CPTXYAxis * xAxis = [axisSet xAxis]; [xAxis setMajorIntervalLength: CPTDecimalFromInt (1)]; [xAxis setMinorTickLineStyle: nihil]; [xAxis setLabelingPolicy: CPTAxisLabelingPolicyNone]; [xAxis setLabelTextStyle: textStyle]; [xAxis setLabelRotation: M_PI / 4]; NSArray * subjectsArray = [self getSubjectTitlesAsArray]; [xAxis setAxisLabels: [NSSet setWithArray: subjectsArray]];

Er zijn een paar wijzigingen in de bovenstaande code. Ten eerste veranderen we het etiketteringsbeleid in geen. Dit zorgt ervoor dat CorePlot het label zelf niet afdrukt en in plaats daarvan gebruikt wat we het geven. Vervolgens stellen we de rotatie van het label in zodat deze beter aansluit op de grafiek. Ten slotte stellen we de eigenschap 'As-labels' in die een NSSet van NSString-waarden gebruikt. We maken de NSSet met behulp van een NSArray gemaakt met de methode 'getSubjectTitlesAsArray'. Die methode bestaat nog niet, dus laten we het maken. Voeg de verklaring toe aan de bovenkant van het .m-bestand en schrijf de volgende implementatie:

 - (NSArray *) getSubjectTitlesAsArray NSError * error = nil; NSFetchRequest * request = [[NSFetchRequest alloc] init]; NSSortDescriptor * sortDescriptor = [NSSortDescriptor sortDescriptorWithKey: @ "subjectID" oplopend: YES]; NSEntityDescription * entity = [NSEntityDescription entityForName: @ "STSubject" inManagedObjectContext: managedObjectContext]; [request setEntity: entity]; [request setSortDescriptors: [NSArray arrayWithObject: sortDescriptor]]; [request setResultType: NSDictionaryResultType]; [request setReturnsDistinctResults: NO]; [request setPropertiesToFetch: [NSArray arrayWithObject: @ "subjectName"]]; NSArray * titleStrings = [managedObjectContext executeFetchRequest: request error: & error]; NSMutableArray * labelArray = [NSMutableArray-array]; CPTMutableTextStyle * textStyle = [CPTMutableTextStyle textStyle]; [textStyle setFontSize: 10]; voor (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]; 

Er is veel gaande in de bovenstaande code. Om grafische labels te geven, moeten we een NSSet-bevattende objecten van het type 'CPTAxisLabel' doorgeven. Eerst krijgen we een array met alle onderwerpnamen op volgorde van subjectID zodat deze in dezelfde volgorde als de grafiek staat. Voor de hoeveelheid namen die we terugkrijgen, doorlopen we vervolgens een CPTAxisLabel met de onderwerpnaamstring en een vooraf gedefinieerde tekststijl. De 'vinklokatie' is die vinkje waar het voor zal verschijnen. We moeten 1 toevoegen aan onze waarde omdat we onze eerste bar beginnen met 1 in plaats van 0. We stellen vervolgens een rotatie en offset in en voegen deze toe aan een array. Ten slotte retourneren we de array van axisLabels.

Als we het project opslaan en uitvoeren, hebben we een voltooide, kleurrijke staafgrafiek met aangepaste labels!



De volgende keer

We hebben redelijk veel geleerd over CorePlot deze sessie. We hebben geleerd hoe een staafdiagram te maken, de kleuren van de balk aan te passen en zelfs aangepaste labels aan de as toe te voegen.

De volgende keer bespreken we hoe u een geweldig cirkeldiagram maakt met dezelfde gegevens als de staafgrafiek. Vang je de volgende keer!