Lavorare con CorePlot creazione di un grafico a torta

Quando si lavora con applicazioni ad uso intensivo di dati, uno sviluppatore deve spesso fare molto di più che mostrare solo elenchi di record di dati in una vista tabella. La libreria CorePlot ti consentirà di aggiungere incredibili visualizzazioni di dati alle tue applicazioni. Scopri come in questa serie di Tuts + Premium!


Disponibile anche in questa serie:

  1. Lavorare con CorePlot: installazione del progetto
  2. Lavorare con CorePlot: Plot Fundamentals
  3. Lavorare con CorePlot: Styling e aggiunta di grafici
  4. Lavorare con CorePlot: creazione di un grafico a barre
  5. Lavorare con CorePlot: creazione di un grafico a torta

Da dove siamo partiti

L'ultima volta che abbiamo avuto modo di creare un grafico a barre, come personalizzare i colori delle barre e come aggiungere etichette personalizzate ai nostri assi se vogliamo visualizzare testo personalizzato anziché solo numeri.


Cosa copriremo oggi

Questa volta vedremo come astrarre la logica dal nostro controller in un oggetto di origine dati più riutilizzabile. Una volta che abbiamo imparato questo, andremo su come visualizzare gli stessi dati del grafico a barre, tranne questa volta che lo mostra come un grafico a torta!


Passaggio 1: impostazione

Prima di iniziare ad astrarre la logica dei dati, creeremo le nostre classi base per il grafico a torta. Proprio come l'ultima volta, avremo bisogno di creare un nuovo controller di visualizzazione per il grafico. Chiamiamolo 'STPieGraphViewController'.


Si noti che questa volta non è necessario creare una visualizzazione perché sarà possibile utilizzare "STGraphView". Prima di iniziare a configurare le cose, passiamo a STStudentListViewController.h e importiamo STPieGraphViewController.h. Dobbiamo inoltre conformarci al protocollo STPieGraphViewControllerDelegate (che creeremo in seguito):

 @interface STStudentListViewController: UIViewController 

Passare al file .m. Abbiamo bisogno di aggiungere un pulsante al foglio d'azione. Individua il metodo graphButtonWasSelected: Stiamo andando a modificare il testo del secondo pulsante e aggiungere un terzo:

 - (void) graphButtonWasSelected: (id) sender UIActionSheet * graphSelectionActionSheet = [[[UIActionSheet alloc] initWithTitle: @ "Scegli un grafico" delegato: self cancelButtonTitle: @ "Annulla" distructiveButtonTitle: nil otherButtonTitles: @ "Registrazione nel tempo", @ "Totale oggetto - Bar", @ "Totale oggetto - Torta", nil] autorelease]; [graphSelectionActionSheet showInView: [[UIApplication sharedApplication] keyWindow]]; 

Ora salta nell'actionSheet: clickkedButtonAtIndex: method e aggiungi una clausola per buttonIndex == 2:

 else if (buttonIndex == 2) STPieGraphViewController * graphVC = [[STPieGraphViewController alloc] init]; [graphVC setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal]; [graphVC setModalPresentationStyle: UIModalPresentationFullScreen]; [graphVC setDelegate: self]; [graphVC setManagedObjectContext: [self managedObjectContext]]; [auto presenteModalViewController: graphVC animato: YES]; [versione GraphVC]; 

Proprio come l'ultima volta, mostrerà alcuni avvisi perché STPieGraphViewController non ha una proprietà delegato o managedObjectContext.

Quindi entra in STPieGraphViewController.h. Importa "CorePlot-CocoaTouch.h" e aggiungi le seguenti proprietà e dichiarazioni di protocollo:

 @protocol STPieGraphViewControllerDelegate @required - (void) doneButtonWasTapped: (id) sender; @end @interface STPieGraphViewController: UIViewController  @property (nonatomic, strong) grafico CPTGraph *; @property (nonatomic, assegna) id delegare; @property (nonatomic, strong) NSManagedObjectContext * managedObjectContext; @fine

È importante sottolineare che questa volta non stiamo aderendo a CPTPieChartDataSource. Questo perché estraiamo la logica dei dati del grafico da STBarGraphViewController in una classe dataSource separata. Prima di farlo, finiamo di impostare tutto. Nel file .m, importare "STGraphView.h", sintetizzare le proprietà e implementare il metodo dealloc.

Infine, imposta i metodi loadView e viewDidLoad come di seguito:

 - (void) loadView [super loadView]; [Self setTitle: @ "Iscrizione per argomento"]; [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 *) [visualizzazione automatica]; [[graphView chartHostingView] setHostedGraph: [grafico autonomo]]; // Permetti all'utente di tornare indietro 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 animato: NO]; [self.view addSubview: navigationBar]; [navigationItem setRightBarButtonItem: [[[UIBarButtonItem alloc] initWithTitle: @ Stile "Done": UIBarButtonItemStyleDone target: [self delegate] action: @selector (doneButtonWasTapped :)] autorelease] animato: NO]; 

Quanto sopra dovrebbe essere familiare ormai. Diamo un'occhiata all'astrazione della logica del grafico in una classe separata.


Passaggio 2: Separazione della logica del grafico

Abbiamo già scritto la logica per ottenere dati per il numero totale di studenti in tutte le materie; non vogliamo doverlo scrivere di nuovo, per fortuna non dobbiamo. Tutti i protocolli dell'origine dati per i grafici ereditano da "CPTPlotDataSource" ed è questo protocollo che contiene numberOfRecordsForPlot: e numberForPlot: fieldEnum: metodi RecordIndex.

Crea una classe chiamata 'STAbstractSubjectDataSource.h' (ereditata da NSObject) in un nuovo gruppo chiamato 'DataSource' nel gruppo di grafici. Per il file di intestazione importare "CorePlot-CocoaTouch.h" e inserire le seguenti proprietà e dichiarazioni dei metodi:

 @interface STAbstractSubjectEnrollementDataSource: NSObject  @property (nonatomic, strong) NSManagedObjectContext * managedObjectContext; - (id) initWithManagedObjectContext: (NSManagedObjectContext *) aManagedObjectContext; - (float) getTotalSubjects; - (float) getMaxEnrolled; - (NSArray *) getSubjectTitlesAsArray; @fine

Ci iscriviamo al protocollo 'CPTPlotDataSource'. Creiamo un metodo init personalizzato che passa attraverso un oggetto ManagedObjectContext in modo che l'oggetto possa accedere all'archivio dati. Infine, ci sono tre metodi di supporto che possono aiutare a ottenere informazioni sui soggetti e sull'iscrizione. Questi sono gli stessi metodi attualmente esistenti in STBarGraphViewController. Sposteremo quelli fuori e nel metodo di origine dei dati.

A parte il metodo init, il file .m non contiene alcun nuovo codice che non hai mai visto prima. Si tratta semplicemente di spostare tutto il codice esistente da STBarGraphViewController nell'oggetto dataSource. I metodi che dovresti spostare sono:

  • (float) getTotalSubjects
  • (Float) getMaxEnrolled
  • (NSArray *) getSubjectTitlesAsArray
  • (NSUInteger) numberOfRecordsForPlot: (CPTPlot *) grafico
  • (NSNumber *) numberForPlot: (CPTPlot *) campo di trama: (NSUInteger) fieldEnum recordIndex: indice (NSUInteger)

Assicurati inoltre di aggiungere il metodo init personalizzato:

 - (id) initWithManagedObjectContext: (NSManagedObjectContext *) aManagedObjectContext self = [super init]; if (self) [self setManagedObjectContext: aManagedObjectContext];  return self; 

Ora abbiamo un oggetto di origine dati che può fornire i dati di base per la torta e il grafico a barre. Tuttavia, la sorgente di dati astratta non ci dà tutto ciò di cui abbiamo bisogno, il barFillForBarPlot: recordIndex non può essere implementato perché fa parte di CPTBarPlotDataSource. Dovremo estendere la nostra classe astratta a qualcosa di specifico per i grafici a barre.

Crea un nuovo oggetto nel gruppo Origine dati chiamato "STBarGraphSubjectEnrollementDataSource" che estende la nostra classe astratta. Nell'intestazione, sottoscrivi 'CPTBarPlotDataSource:

 - (id) initWithManagedObjectContext: (NSManagedObjectContext *) aManagedObjectContext @interface STBarGraphSubjectEnrollementDataSource: STAbstractSubjectEnrollementDataSource 

E nel file .m, implementa il metodo barFillForBarPlot:

 # mark mark - Metodi CPTBarPlotDataSource - (CPTFill *) barFillForBarPlot: (CPTBarPlot *) barPlot recordIndex: (NSUInteger) indice CPTColor * areaColor = nil; switch (index) case 0: areaColor = [CPTColor redColor]; rompere; caso 1: areaColor = [CPTColor blueColor]; rompere; caso 2: areaColor = [CPTColor orangeColor]; rompere; case 3: areaColor = [CPTColor greenColor]; rompere; default: areaColor = [CPTColor purpleColor]; rompere;  CPTFill * barFill = [CPTFill fillWithColor: areaColor]; return barFill; 

Ora torna al file di intestazione STBarGraphViewControllers e importa la nuova origine dati del grafico a barre. Ora è possibile rimuovere l'abbonamento "CPTBarPlotDataSource". Salta nel file .m ed elimina tutti i metodi tranne loadView, viewDidLoad e dealloc. Non abbiamo più bisogno di loro.

Abbiamo bisogno di mantenere un puntatore all'origine dati e quindi rilasciare il puntatore quando la vista è fatta con esso. Nell'interfaccia privata, dichiarare la proprietà e quindi sintetizzare:

 @interface STBarGraphViewController () @property (nonatomic, retain) STBarGraphSubjectEnrollementDataSource * barGraphDataSource; @end @implementation STBarGraphViewController @synthesize delegate; @synthesize managedObjectContext; @sinteso grafico; @synthesize barGraphDataSource;

Assicurati di rilasciarlo anche nel metodo dealloc. Crea una nuova istanza e impostala come nostra proprietà nel metodo loadView:

 [self setBarGraphDataSource: [[[STBarGraphSubjectEnrollementDataSource alloc] initWithManagedObjectContext: [self managedObjectContext]] autorelease]];

Ora nel metodo viewDidLoad è necessario utilizzare i metodi di supporto della nostra fonte di dati per calcolare lo spazio di stampa e le etichette personalizzate:

 CPTXYPlotSpace * studentPlotSpace = (CPTXYPlotSpace *) [grafico defaultPlotSpace]; [studentPlotSpace setXRange: [CPTPlotRange plotRangeWithLocation: CPTDecimalFromInt (0) length: CPTDecimalFromInt ([[self barGraphDataSource] getTotalSubjects] + 1)]]; [studentPlotSpace setYRange: [CPTPlotRange plotRangeWithLocation: CPTDecimalFromInt (0) length: CPTDecimalFromInt ([[self barGraphDataSource] getMaxEnrolled] + 1)]];
 NSArray * SubjectsArray = [[self barGraphDataSource] getSubjectTitlesAsArray];

Salva, crea ed esegui. Tutto dovrebbe funzionare come prima. Se lo fa, possiamo iniziare a creare il nostro grafico a torta!


Passaggio 3: creazione del grafico a torta

Vogliamo creare un'origine dati specifica per il grafico a torta proprio come abbiamo fatto per il grafico a barre nel caso in cui sia necessario implementare qualsiasi metodo di origine dati specifico del grafico a torta. Crea una classe chiamata 'STPieGraphSubjectEnrollementDataSource' che eredita da 'STAbstractSubjectEnrollementDataSource'. Nel file di intestazione, sottoscrivi il protocollo 'CPTPieChartDataSource'. Non implementeremo ancora alcun metodo specifico per l'origine dei dati del grafico a torta, torneremo più avanti.

Ora che disponiamo di origini dati, la creazione del grafico a torta è semplice! Passare a STBPieGraphViewController.m e importare l'oggetto origine dati del grafico a torta. Dichiaralo come una proprietà nel file .m, come abbiamo fatto l'ultima volta:

 @interface STPieGraphViewController () @property (nonatomic, strong) STPieGraphSubjectEnrollementDataSource * pieChartDataSource; @end @implementation STPieGraphViewController @synthesize managedObjectContext; @ delegato sintetizzato; @sinteso grafico; @synthesize pieChartDataSource;

Ora crea e impostalo in loadView:

 [self setPieChartDataSource: [[[STPieGraphSubjectEnrollementDataSource alloc] initWithManagedObjectContext: [self managedObjectContext]] autorelease]];

Infine, nel metodo viewDidLoad dobbiamo creare il nostro grafico a torta, aggiungerlo al nostro GraphView e rimuovere l'asse standard:

 STGraphView * graphView = (STGraphView *) [visualizzazione automatica]; [[graphView chartHostingView] setHostedGraph: [grafico autonomo]]; CPTPieChart * pieChart = [[CPTPieChart alloc] initWithFrame: [limiti del grafico]]; [pieChart setPieRadius: 100,00]; [pieChart setIdentifier: @ "Subject"]; [pieChart setStartAngle: M_PI_4]; [pieChart setSliceDirection: CPTPieDirectionCounterClockwise]; [pieChart setDataSource: pieChartDataSource]; [grafico addPlot: pieChart]; [grafico setAxisSet: nil]; [grafico setBorderLineStyle: nil];

La maggior parte di quanto sopra dovrebbe sembrare familiare. Si noti che non viene chiamato esplicitamente un "grafico" perché non si basa su un asse x o su un asse y, ma lo trattiamo ancora allo stesso modo. Ci sono alcune cose specifiche del grafico a torta che facciamo anche qui. Creiamo un raggio di torta e un angolo di partenza. Abbiamo anche impostato una direzione della sezione. Infine impostiamo il parametro 'axisSet' del grafico su zero in modo da non ottenere le linee x e y.

E quello dovrebbe essere tutto. Costruisci e corri per vedere il grafico a torta.


Questo è buono, ma potrebbe fare con qualche tipo di indicazione su cosa rappresenta ciascun colore. Un buon modo per farlo è usare le leggende. Per fare questo creiamo un oggetto 'CPTLegend' che aggiungiamo al nostro grafico e implementiamo un metodo delegato che restituisce il titolo pertinente per la legenda.

Creiamo prima l'oggetto CPTLegend. Nel nostro metodo viewDidLoad inserisci il codice seguente sotto il quale creiamo il nostro grafico a torta:

 CPTLegend * theLegend = [CPTLegend legendWithGraph: [self graph]]; [theLegend setNumberOfColumns: 2]; [[auto grafico] setLegend: theLegend]; [[auto grafico] setLegendAnchor: CPTRectAnchorBottom]; [[grafico autonomo] setLegendDisplacement: CGPointMake (0.0, 30.0)];

Questo crea una legenda e la aggiunge al nostro oggetto grafico. Il numero di colonne determina in che modo verranno disposti i titoli delle legende. Quindi impostiamo alcuni attributi sull'oggetto grafico che determina dove verrà posizionata la legenda (in basso) e alcuni spostamenti per assicurarsi che appaia completamente nella vista.

Tuttavia, dobbiamo comunque fornire la legenda con i titoli. Esiste un metodo specifico per CPTPieChartDataSource che ci consente di farlo. Salta nell'origine dati del grafico a torta e implementa il seguente codice:

 #pragma mark - CPTPieChartDataSource - (NSString *) legendTitleForPieChart: (CPTPieChart *) pieChart recordIndex: (NSUInteger) index NSError * error = nil; NSFetchRequest * request = [[NSFetchRequest alloc] init]; NSEntityDescription * entity = [NSEntityDescription entityForName: @ "STSubject" inManagedObjectContext: [self managedObjectContext]]; NSPredicate * predicate = [NSPredicate predicateWithFormat: @ "subjectID ==% d", index]; [request setEntity: entity]; [request setResultType: NSDictionaryResultType]; [request setPredicate: predicate]; [request setReturnsDistinctResults: NO]; [request setPropertiesToFetch: [NSArray arrayWithObject: @ "subjectName"]]; NSArray * titleStrings = [[self managedObjectContext] executeFetchRequest: request error: & error]; NSDictionary * fetchedSubject = [titleStrings objectAtIndex: 0]; return [fetchedSubject objectForKey: @ "subjectName"]; 

Questo metodo ottiene semplicemente l'indice della legenda e ottiene il titolo dall'archivio dati sotto forma di stringa e lo restituisce.

Costruisci ed esegui e dovresti avere un grafico a torta informativo!



Incartare

Abbiamo spiegato come astrarre la logica dei dati dal controller in un oggetto separato che è più facile da gestire ed estendere. Abbiamo anche spiegato le basi della creazione di un grafico a torta.

Questo ci porta alla fine della serie. Spero che tu abbia trovato utili questi tutorial. C'è molto di più che può fare CorePlot, ma questo dovrebbe darti una solida base su cui costruire. Buona fortuna aggiungendo grafici ai propri progetti!