Trabajando con CorePlot Creando un gráfico de barras

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 repasamos cómo personalizar el aspecto y el estilo de un gráfico de líneas (o diagrama de dispersión) utilizando clases como CPTMutableTextStyle y CPTMutableLineStyle. Aprendimos cómo personalizar los incrementos de los ejes X e Y y los estilos numéricos utilizando las clases CPTXYAxisSet y CPTXYAxis. También vimos cómo agregar múltiples gráficos a su gráfica y modificar los métodos de fuente de datos para proporcionar los datos correctos para los gráficos correctos usando identificadores de gráficos.


Lo que cubriremos hoy

Hoy vamos a trabajar con un gráfico completamente nuevo. Vamos a crear un gráfico de barras que muestre el número total de estudiantes en cada materia con cada barra que representa una materia. También veremos cómo personalizar la apariencia de la gráfica. Empecemos!


Paso 1: Configuración

Primero debemos agregar las clases relevantes a nuestro proyecto. Vamos a crear un ViewController llamado 'STBarGraphViewController' y un 'STGraphView'. (Asegúrate de ponerlos en los grupos relevantes)


Observe la denominación de la vista como 'STGraphView' en lugar de 'STBarGraphView'. En el futuro usaremos esta vista para mostrar los gráficos de barras y de pastel..

Antes de comenzar a trabajar con cualquier código, debemos agregar un botón a la hoja de Acción de nuestra vista de lista de estudiantes. Primero abra 'STStudentListViewController.h' e importe STBarGraphViewController. Agregue STBarGraphViewControllerDelegate a la lista de protocolos registrados (el protocolo en realidad aún no existe, pero lo crearemos más adelante):

 @interface STStudentListViewController: UIViewController 

A continuación, vaya al archivo .m y localice el método 'graphButtonWasSelected:'. Agregue 'Inscripción por tema' a la lista de 'otherButtonTitles:':

 UIActionSheet * graphSelectionActionSheet = [[[UIActionSheet alloc] initCon Titulo: @ "Elegir un gráfico" delegado: self cancelButtonTitle: @ "Cancel" destructiveButtonTitle: nil otherPut de los grupos de personas. ;

Ahora encuentre el método 'actionSheet: clickedButtonAtIndex:' y modifíquelo para que funcione con buttonIndex == 1:

 if (buttonIndex == 0) STLineGraphViewController * graphVC = [[STLineGraphViewController alloc] init]; [graphVC setModalTransitionStyle: UIModalTransitionStyleFlipHorizontal]; [graphVC setModalPresentationStyle: UIModalPresentationFullScreen]; [graphVC setDelegate: self]; [graphVC setManagedObjectContext: [self managedObjectContext]]; [self presentModalViewController: graphVC animated: 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 animated: YES]; [GraphVC release]; 

Nuevamente, esto mostrará algunas advertencias porque aún no hemos implementado las propiedades delegado o managedObjectContext en STBarGraphViewController.

Ahora salta en STBarGraphViewController.h. Importe CorePlot-CocoaTouch.h y agregue la siguiente declaración de propiedad:

 @protocol STBarGraphViewControllerDelegate @required - (void) doneButtonWasTapped: (id) sender; @fin

Ahora agregue las siguientes propiedades:

 @property (nonatomic, strong) CPTGraph * graph; @ propiedad (no atómica, asignar) id delegar; @ propiedad (no atómica, fuerte) NSManagedObjectContext * managedObjectContext;

Finalmente, registre que esta clase seguirá estos protocolos:

 @interface STBarGraphViewController: UIViewController 

Tenga en cuenta que, aparte de cumplir con diferentes protocolos, esta clase es la misma que STLineViewController. Idealmente, tendría una clase base que tendría estas propiedades que subclasificaría para reducir la repetición de código. Este tutorial se centra solo en la trama principal, por lo que solo nos centramos en cómo trabajar mejor con el marco CorePlot. Si tiene el conocimiento y el tiempo, no dude en crear la clase base y usar la herencia, pero vamos a mantenerlo simple en el código de ejemplo aquí..

A continuación, salte al archivo .m, sintetice las propiedades y libérelas en el método dealloc.

Ahora vamos a vincular la clase de vista como la vista del controlador. Importe 'STBarGraphView.h', y agregue el siguiente método:

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

Ahora estamos listos para trabajar con la vista. Abra STGraphView.h, importe el marco de la trama central (CorePlot-CocoaTouch.h), y agregue la siguiente declaración de propiedad:

 @ propiedad (no atómica, fuerte) CPTGraphHostingView * chartHostingView;

Salte a .m, sintetice la propiedad y libere la propiedad en el método dealloc. Luego cree el CPTGraphHostingView en el método 'initWithFrame:':

 - (id) initWithFrame: (CGRect) frame self = [super initWithFrame: frame]; if (self) [self setChartHostingView: [[CPTGraphHostingView alloc] initWithFrame: CGRectZero] autorelease]]; [chartHostingView setBackgroundColor: [UIColor purpleColor]]; [auto addSubview: chartHostingView];  devuélvete a ti mismo; 

Finalmente, cree el método 'layoutSubviews' con el siguiente código:

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

Notará que el código utilizado para crear esta vista es exactamente el mismo que STLineGraphView. Podemos usar esta vista básica para trabajar con todas las vistas gráficas en el futuro..

Salte de nuevo a la vista 'STBarGraphViewController.m' y localice el método 'viewDidLoad'. Primero, queremos crear un CPTBarPlot y agregarlo a nuestro gráfico:

 [[graphView chartHostingView] setHostedGraph: [auto graph]]; CPTBarPlot * subjectBarPlot = [[CPTBarPlot alloc] initWithFrame: [graph lines]]; [subjectBarPlot setIdentifier: @ "subjectEnrollement"]; [subjectBarPlot setDelegate: self]; [subjectBarPlot setDataSource: self]; [[auto-gráfico] addPlot: subjectBarPlot];

Finalmente, agreguemos una barra de navegación con un botón para que el usuario pueda regresar:

 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]

Ahora vamos a empezar a trabajar en los métodos de fuente de datos:


Paso 2: Dar los datos del gráfico de barras

El proceso será similar a cómo creamos el gráfico de líneas, pero haremos las cosas un poco más dinámicas. Estaremos creando métodos personalizados que proporcionarán un espacio de trazado apropiado. También agregaremos algunos métodos adicionales de fuente de datos para proporcionar un color específico para cada barra, así como títulos para el eje x.

Antes de ver cómo proporcionar los datos del gráfico, debemos configurar el eje y los rangos visibles. Para hacer esto necesitamos dos métodos que calculen los valores máximos de x y máximos de y. Declare los siguientes métodos en la parte superior del archivo .m:

 [[graphView chartHostingView] setHostedGraph: [auto graph]]; @interface STBarGraphViewController () - (float) getTotalSubjects; - (float) getMaxEnrolled; @fin

Ahora implementarlos como a continuación:

 #pragma mark - Métodos privados - (float) getTotalSubjects NSError * error = nil; NSFetchRequest * fetchRequest = [[[NSFetchRequest alloc] init] autorelease]; NSEntityDescription * entidad = [NSEntityDescription entityForName: @ "STSubject" enManagedObjectContext: managedObjectContext]; [fetchRequest setEntity: entidad]; return [managedObjectContext countForFetchRequest: fetchRequest error: & error];  - (float) getMaxEnrolled float maxEnrolled = 0; NSError * error = nil; para (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;  devolver maxEnrolled; 

'getTotalSubjects' simplemente obtiene un recuento de todos los temas en el almacén de datos principales. 'getMaxEnrolled' recorre todas las materias y busca la mayor cantidad de estudiantes en cada una y devuelve el valor más alto.

Con estos métodos, podemos regresar a nuestro método 'viewDidLoad' y agregar el siguiente código a continuación, donde agregamos el gráfico de barras a nuestro gráfico:

 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)]]; [[graph plotAreaFrame] setPaddingLeft: 40.0f]; [[graph plotAreaFrame] setPaddingTop: 10.0f]; [[graph plotAreaFrame] setPaddingBottom: 120.0f]; [[graph 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: nil]; [xAxis setLabelingPolicy: CPTAxisLabelingPolicyFixedInterval]; [xAxis setLabelTextStyle: textStyle]; CPTXYAxis * yAxis = [axisSet yAxis]; [yAxis setMajorIntervalLength: CPTDecimalFromInt (1)]; [yAxis setMinorTickLineStyle: nil]; [yAxis setLabelingPolicy: CPTAxisLabelingPolicyFixedInterval];

Sin embargo, la mayor parte de lo anterior debería ser familiar desde la última vez, pero en lugar de establecer valores codificados para nuestra longitud de rango de trazado x e y max, estamos utilizando nuestros nuevos métodos para crear los valores dinámicamente. Después de eso, simplemente configuramos un formato de eje básico y le damos al marco del trazado un relleno para que el eje se muestre correctamente.

Ahora debemos proporcionar el gráfico con datos utilizando los métodos de origen de datos. Proporcionar el número de registros es simple:

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

Ahora para proporcionar los valores x e y para los registros:

 - (NSNumber *) numberForPlot: (CPTPlot *) campo de trazado: (NSUInteger) fieldEnum recordIndex: (NSUInteger) index int x = index + 1; int y = 0; Error NSError *; NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription * entidad = [NSEntityDescription entityForName: @ "STStudent" inManagedObjectContext: managedObjectContext]; NSPredicate * predicate = [NSPredicate predicateWithFormat: @ "subjectID ==% d", índice]; [fetchRequest setEntity: entidad]; [fetchRequest setPredicate: predicate]; y = [managedObjectContext countForFetchRequest: fetchRequest error: & error]; [fetchRequest release]; switch (fieldEnum) case CPTScatterPlotFieldX: return [NSNumber numberWithInt: x]; descanso; case CPTScatterPlotFieldY: return [NSNumber numberWithInt: y]; descanso; por defecto: break;  devuelve nil; 

El método anterior es muy similar a cómo proporcionamos datos a un diagrama de dispersión. Cambiamos la llamada al almacén de datos para obtener un recuento de todos los estudiantes inscritos en una materia. También establecemos el valor de x en +1. Esto es para que la barra comience en '1' y proporcione un poco de relleno entre la primera barra y la línea del eje y.

Guarda y ejecuta el proyecto ... deberías ver tu gráfico de barras!



Paso 3: Toques finales

Podemos hacer un poco más para que esto sea más fácil de ver. Queremos dar a cada barra un color diferente y proporcionar el nombre del sujeto como la etiqueta del eje x en lugar de un número. Podemos hacer lo primero con un método CPTBarPlotDataSource llamado 'barFillForBarPlot: recordIndex':

 -(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; 

Esto devolverá un color diferente para cada barra en nuestro gráfico. Si quisieras hacerlo aún más elegante, podrías hacerlo un degradado. También es una parte de nuestro gráfico que no es totalmente dinámico, porque si alguien agrega un nuevo tema, usará el color predeterminado. Tal vez una forma de evitar esto en una aplicación de la vida real sería tener un color al crear un nuevo sujeto que esté almacenado en el almacén de datos.


Por último, vamos a añadir algunos títulos de eje x personalizados. Para hacer esto, necesitamos trabajar con el eje x. Localice donde configuramos el eje x y modificamos el código para hacer lo siguiente:

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

Hay algunos cambios en el código anterior. Primero, estamos cambiando la política de etiquetado a ninguna. Esto asegurará que CorePlot no imprima la etiqueta en sí y en su lugar usará lo que le damos. A continuación, configuramos la rotación de la etiqueta para que se alinee mejor con el gráfico. Finalmente, establecemos la propiedad 'Etiquetas del eje' que toma un NSSet de valores NSString. Creamos el NSSet utilizando un NSArray creado por el método 'getSubjectTitlesAsArray'. Ese método aún no existe, así que vamos a crearlo. Agregue la declaración al inicio del archivo .m y luego escriba la siguiente implementación:

 - (NSArray *) getSubjectTitlesAsArray NSError * error = nil; NSFetchRequest * request = [[NSFetchRequest alloc] init]; NSSortDescriptor * sortDescriptor = [NSSortDescriptor sortDescriptorWithKey: @ "subjectID" ascendente: YES]; NSEntityDescription * entidad = [NSEntityDescription entityForName: @ "STSubject" enManagedObjectContext: managedObjectContext]; [petición setEntity: entidad]; [solicitar setSortDescriptors: [NSArray arrayWithObject: sortDescriptor]]; [solicitud setResultType: NSDictionaryResultType]; [solicitud setReturnsDistinctResults: NO]; [solicitar setPropertiesToFetch: [NSArray arrayWithObject: @ "subjectName"]]; NSArray * titleStrings = [managedObjectContext executeFetchRequest: error de solicitud: & error]; NSMutableArray * labelArray = [matriz NSMutableArray]; CPTMutableTextStyle * textStyle = [CPTMutableTextStyle textStyle]; [textStyle setFontSize: 10]; para (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]; 

Hay mucho que hacer en el código anterior. Para dar a un gráfico etiquetas personalizadas, necesitamos pasar un NSSet que contenga objetos del tipo 'CPTAxisLabel'. Primero, obtenemos una matriz de todos los nombres de los sujetos ordenados por subjectID, por lo que estará en el mismo orden que el gráfico. Luego, para la cantidad de nombres que recibimos, hacemos un bucle y creamos una CPTAxisLabel con la cadena del nombre del sujeto y un estilo de texto predefinido. La 'ubicación de tick' es para qué tick aparecerá. Necesitamos agregar 1 a nuestro valor porque estamos comenzando nuestra primera barra en 1 en lugar de 0. Luego, establecemos una rotación y un desplazamiento y lo agregamos a una matriz. Finalmente, devolvemos la matriz de axisLabels..

Si guardamos y ejecutamos el proyecto, tenemos un gráfico de barras colorido y completo con etiquetas personalizadas!



La próxima vez

Hemos aprendido bastante sobre CorePlot esta sesión. Hemos aprendido cómo crear una gráfica de barras, modificar los colores de la barra e incluso agregar etiquetas personalizadas al eje.

La próxima vez, veremos cómo hacer un gráfico circular impresionante que muestre los mismos datos que el gráfico de barras. Te veo la proxima vez!