Trabajando con CorePlot Creando un gráfico circular

Cuando se trabaja con aplicaciones de uso intensivo de datos, un desarrollador a menudo debe hacer algo más que mostrar listas de registros de datos en una vista de tabla. La biblioteca CorePlot le permitirá agregar impresionantes visualizaciones de datos a sus aplicaciones. Descubre cómo en esta serie Tuts + Premium.!


También disponible en esta serie:

  1. Trabajando con CorePlot: Configuración del proyecto
  2. Trabajando con CorePlot: Fundamental Fundamentos
  3. Trabajar con CorePlot: modelar y agregar gráficos
  4. Trabajando con CorePlot: Creando un gráfico de barras
  5. Trabajando con CorePlot: Creando un gráfico circular

Donde dejamos

La última vez que aprendimos cómo crear un gráfico de barras, cómo personalizar los colores de la barra y cómo agregar etiquetas personalizadas a nuestro eje, si queremos mostrar texto personalizado en lugar de solo números.


Lo que cubriremos hoy

Esta vez cubriremos cómo abstraer la lógica de nuestro controlador en un objeto fuente de datos más reutilizable. Una vez que hayamos dominado esto, veremos cómo mostrar los mismos datos que el gráfico de barras, excepto que esta vez lo mostraremos como un gráfico circular.!


Paso 1: Configuración

Antes de empezar a abstraer la lógica de los datos, vamos a crear nuestras clases base para el gráfico circular. Al igual que la última vez, necesitaremos crear un nuevo controlador de vista para el gráfico. Llamémoslo 'STPieGraphViewController'.


Tenga en cuenta que no necesitamos crear una vista esta vez porque podremos usar 'STGraphView'. Antes de comenzar a configurar las cosas, saltemos a STStudentListViewController.h e importemos STPieGraphViewController.h. También debemos cumplir con el protocolo STPieGraphViewControllerDelegate (que crearemos más adelante):

 @interface STStudentListViewController: UIViewController 

Cambie al archivo .m. Necesitamos agregar un botón a la hoja de acción. Localice el método graphButtonWasSelected: Vamos a editar el segundo texto del botón y agregaremos un tercero:

 - (void) graphButtonWasSelected: (id) sender UIActionSheet * graphSelectionActionSheet = [[[[UIActionSheet alloc] initWithTitle: @ Elija un gráfico en el centro de la acción: "Elija un gráfico" "" "" "" "" "" "" "" "" "" "" "" "" "Totales de materia - Barra", @ "Totales de materia - Pie", nil] autorelease]; [graphSelectionActionSheet showInView: [[UIApplication sharedApplication] keyWindow]]; 

Ahora salte a la actionSheet: clickedButtonAtIndex: method y agregue una cláusula para buttonIndex == 2:

 else if (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]; 

Al igual que la última vez, mostrará algunas advertencias porque STPieGraphViewController no tiene una propiedad delegada o managedObjectContext.

Así que salta a STPieGraphViewController.h. Importe 'CorePlot-CocoaTouch.h' y agregue las siguientes propiedades y declaraciones de protocolo:

 @protocol STPieGraphViewControllerDelegate @required - (void) doneButtonWasTapped: (id) sender; @end @interface STPieGraphViewController: UIViewController  @property (nonatomic, strong) CPTGraph * graph; @ propiedad (no atómica, asignar) id delegar; @ propiedad (no atómica, fuerte) NSManagedObjectContext * managedObjectContext; @fin

Es importante señalar que no estamos cumpliendo con CPTPieChartDataSource esta vez. Esto se debe a que estaremos abstrayendo la lógica de datos del gráfico de STBarGraphViewController en una clase de fuente de datos separada. Antes de hacer eso, sin embargo, vamos a terminar de configurar todo. En el archivo .m, importe 'STGraphView.h', sintetice las propiedades e implemente el método dealloc.

Finalmente, configure los métodos loadView y viewDidLoad como a continuación:

 - (void) loadView [super loadView]; [auto setTitle: @ "Inscripción por tema"]; [self setView: [[[STGraphView alloc] initWithFrame: self.view.frame] autorelease]]; CPTTheme * defaultTheme = [CPTTheme themeNamed: kCPTPlainWhiteTheme]; [auto setGraph: (CPTGraph *) [defaultTheme newGraph]];  - (void) viewDidLoad [super viewDidLoad]; STGraphView * graphView = (STGraphView *) [vista propia]; [[graphView chartHostingView] setHostedGraph: [auto graph]]; // Permitir que el usuario regrese 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: @ estilo "Listo": UIBarButtonItemStyleDone target: [self delegate] action: @selector (doneButtonWasTapped :)] autorelease] 

Lo anterior debería ser familiar a estas alturas. Así que echemos un vistazo a abstraer la lógica del gráfico en una clase separada.


Paso 2: Separando la lógica del gráfico

Ya hemos escrito la lógica para obtener datos para el número total de estudiantes en todas las materias; No queremos tener que escribirlo de nuevo, por suerte no tenemos que hacerlo. Todos los protocolos de origen de datos para las gráficas heredan de 'CPTPlotDataSource', y es este protocolo el que contiene los métodos numberOfRecordsForPlot: y numberForPlot: fieldEnum: recordIndex.

Cree una clase llamada 'STAbstractSubjectDataSource.h' (heredando de NSObject) en un nuevo grupo llamado 'DataSource' en el grupo de gráficos. Para el archivo de encabezado, importe 'CorePlot-CocoaTouch.h' y coloque las siguientes propiedades y declaraciones de métodos:

 @interface STAbstractSubjectEnrollementDataSource: NSObject  @ propiedad (no atómica, fuerte) NSManagedObjectContext * managedObjectContext; - (id) initWithManagedObjectContext: (NSManagedObjectContext *) aManagedObjectContext; - (float) getTotalSubjects; - (float) getMaxEnrolled; - (NSArray *) getSubjectTitlesAsArray; @fin

Nos suscribimos al protocolo 'CPTPlotDataSource'. Creamos un método de inicio personalizado que pasa a través de un managedObjectContext para que el objeto pueda acceder al almacén de datos. Finalmente, hay tres métodos de ayuda que pueden ayudar a obtener información sobre los temas y la inscripción. Estos son los mismos métodos que existen actualmente en STBarGraphViewController. Vamos a moverlos fuera del método de origen de datos.

Aparte del método init, el archivo .m no contiene ningún código nuevo que no haya visto antes. Es solo una cuestión de mover todo el código existente de STBarGraphViewController al objeto dataSource. Los métodos que debes mover son:

  • (float) getTotalSubjects
  • (float) getMaxEnrolled
  • (NSArray *) getSubjectTitlesAsArray
  • (NSUInteger) numberOfRecordsForPlot: (CPTPlot *) plot
  • (NSNumber *) numberForPlot: (CPTPlot *) campo de trazado: (NSUInteger) fieldEnum recordIndex: (NSUInteger) index

También asegúrese de agregar el método de inicio personalizado:

 - (id) initWithManagedObjectContext: (NSManagedObjectContext *) aManagedObjectContext self = [super init]; if (self) [self setManagedObjectContext: aManagedObjectContext];  devuélvete a ti mismo; 

Ahora tenemos un objeto de origen de datos que puede proporcionar los datos base tanto para el gráfico circular como para el gráfico de barras. El origen de datos abstractos no nos proporciona todo lo que necesitamos, sin embargo, barFillForBarPlot: recordIndex no se puede implementar porque forma parte de CPTBarPlotDataSource. Vamos a tener que extender nuestra clase abstracta a algo específico para parcelas de barras..

Cree un nuevo objeto en el grupo de origen de datos llamado 'STBarGraphSubjectEnrollementDataSource' que amplíe nuestra clase abstracta. En el encabezado, suscríbase a 'CPTBarPlotDataSource:

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

Y en el archivo .m, implemente el método barFillForBarPlot:

 #pragma mark - CPTBarPlotDataSource Methods - (CPTFill *) barFillForBarPlot: (CPTBarPlot *) barPlot recordIndex: (NSUInteger) index CPTColor * areaColor = nil; interruptor (índice) caso 0: areaColor = [CPTColor redColor]; descanso; caso 1: areaColor = [CPTColor blueColor]; descanso; caso 2: areaColor = [CPTColor orangeColor]; descanso; caso 3: areaColor = [CPTColor greenColor]; descanso; por defecto: areaColor = [CPTColor purpleColor]; descanso;  CPTFill * barFill = [CPTFill fillWithColor: areaColor]; volver barFill; 

Ahora regrese al archivo de cabecera STBarGraphViewControllers e importe la nueva fuente de datos del gráfico de barras. Ahora puede eliminar la suscripción 'CPTBarPlotDataSource'. Salte al archivo .m y elimine todos los métodos excepto loadView, viewDidLoad y dealloc. Ya no los necesitamos.

Necesitamos mantener un puntero al origen de datos y luego soltar el puntero cuando la vista se realiza con él. En la interfaz privada, declare la propiedad y luego sintetice:

 @interface STBarGraphViewController () @property (nonatomic, retener) STBarGraphSubjectEnrollementDataSource * barGraphDataSource; @end @implementation STBarGraphViewController @synthesize delegate; @synthesize managedObjectContext; gráfico @synthesize; @synthesize barGraphDataSource;

Asegúrese de que también lo libera en el método dealloc. Cree una nueva instancia y establézcala como nuestra propiedad en el método loadView:

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

Ahora, en el método viewDidLoad, necesitamos utilizar los métodos de ayuda de nuestra fuente de datos para calcular el espacio de trazado y las etiquetas personalizadas:

 CPTXYPlotSpace * studentPlotSpace = (CPTXYPlotSpace *) [graph 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 * subjectArray = [[self barGraphDataSource] getSubjectTitlesAsArray];

Guardar, construir y ejecutar. Todo debería funcionar igual que antes. Si lo hace, podemos comenzar a crear nuestro gráfico circular!


Paso 3: Creando el gráfico circular

Queremos crear un origen de datos específico para el gráfico circular tal como lo hicimos para el gráfico de barras en caso de que necesitemos implementar algún método de origen de datos específico de gráfico circular. Cree una clase llamada 'STPieGraphSubjectEnrollementDataSource' que hereda de 'STAbstractSubjectEnrollementDataSource'. En el archivo de encabezado, suscríbase al protocolo 'CPTPieChartDataSource'. Aún no implementaremos ningún método de fuente de datos de gráfico circular específico, volveremos a eso más adelante.

Ahora que tenemos fuentes de datos, crear el gráfico circular es simple. Salte al STBPieGraphViewController.m e importe el objeto de origen de datos del gráfico circular. Declárelo como una propiedad en el archivo .m, como hicimos la última vez:

 @interface STPieGraphViewController () @property (nonatomic, strong) STPieGraphSubjectEnrollementDataSource * pieChartDataSource; @end @implementation STPieGraphViewController @synthesize managedObjectContext; @synthesize delegate; gráfico @synthesize; @synthesize pieChartDataSource;

Ahora crea y configúralo en el loadView:

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

Finalmente, en el método viewDidLoad necesitamos crear nuestro gráfico circular, agregarlo a nuestro GraphView y eliminar el eje estándar:

 STGraphView * graphView = (STGraphView *) [vista propia]; [[graphView chartHostingView] setHostedGraph: [auto graph]]; CPTPieChart * pieChart = [[CPTPieChart alloc] initWithFrame: [graph lines]]; [pieChart setPieRadius: 100.00]; [pieChart setIdentifier: @ "Subject"]; [pieChart setStartAngle: M_PI_4]; [pieChart setSliceDirection: CPTPieDirectionCounterClockwise]; [pieChart setDataSource: pieChartDataSource]; [gráfico addPlot: pieChart]; [gráfico setAxisSet: nil]; [gráfico setBorderLineStyle: nil];

La mayoría de los anteriores deben ser familiares. Tenga en cuenta que no se llama explícitamente "gráfico" porque no se basa en un eje x o en un eje y, pero aún así lo tratamos de la misma manera. Hay algunas cosas específicas del gráfico circular que hacemos aquí también. Creamos un radio circular y un ángulo de inicio. También establecemos una dirección de corte. Finalmente establecemos el 'axisSet' del gráfico en nil para que no obtengamos las líneas x e y.

Y eso debería ser todo. Construye y corre para ver tu gráfico circular.


Esto es bueno, pero podría hacer con algún tipo de indicación en cuanto a lo que representa cada color. Una buena manera de hacer esto es usar leyendas. Para hacer esto, creamos un objeto 'CPTLegend' que agregamos a nuestro gráfico e implementamos un método de delegado que devuelve el título relevante para la leyenda.

Vamos a crear el objeto CPTLegend primero. En nuestro método viewDidLoad ingrese el siguiente código debajo de donde creamos nuestro gráfico circular:

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

Esto crea una leyenda y la agrega a nuestro objeto gráfico. El número de columnas determina cómo distribuirá los títulos de leyenda. Luego, establecemos algunos atributos en el objeto gráfico que determina dónde se colocará la leyenda (la parte inferior) y algún desplazamiento para garantizar que se muestre completamente en la vista..

Todavía tenemos que proporcionar títulos a la leyenda. Existe un método específico para CPTPieChartDataSource que nos permite hacer esto. Salte a la fuente de datos del gráfico circular e implemente el siguiente código:

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

Este método simplemente obtiene el índice de la leyenda y obtiene el título del almacén de datos como una cadena y lo devuelve.

Construye y corre y deberías tener un gráfico circular informativo.!



Envolver

Hemos cubierto cómo abstraer la lógica de datos del controlador en un objeto separado que sea más fácil de administrar y extender. También hemos cubierto los conceptos básicos de la creación de un gráfico circular.

Eso nos lleva al final de la serie. Espero que hayas encontrado útiles estos tutoriales. Hay mucho más que CorePlot puede hacer, pero esto debería darle una base sólida sobre la cual construir. Buena suerte agregando gráficas a tus propios proyectos.!