Статьи

Работа с CorePlot: основы построения

При работе с приложениями, интенсивно использующими данные, разработчик часто должен делать больше, чем просто показывать списки записей данных в табличном представлении. Библиотека CorePlot позволит вам добавить потрясающие визуализации данных в ваши приложения. Узнайте, как в этой серии Tuts + Premium!


  1. Работа с CorePlot: настройка проекта
  2. Работа с CorePlot: основы построения
  3. Работа с CorePlot: стилизация и добавление графиков
  4. Работа с CorePlot: создание гистограммы
  5. Работа с CorePlot: создание круговой диаграммы

В прошлый раз мы представили инфраструктуру CorePlot и обсудили, что она может делать и как мы можем использовать ее для улучшения визуализации данных в наших приложениях. Мы также изучили пример приложения, которое мы собираемся создать в этой серии, и способы добавления CorePlot в наше приложение. Чтобы получить снимок кода с того места, где мы остановились в прошлый раз, загрузите исходный код этого руководства (в противном случае не стесняйтесь использовать существующую базу кода и сэкономьте время загрузки!).


Прежде чем мы сможем создать график, нам понадобится представление, чтобы сделать это. Мы позволим пользователю щелкнуть UIBarButtonItem во вкладке «Студенты», который вызовет лист действий, содержащий список графиков для Пользователь на выбор. Как только выбор сделан, модальное представление покажет график с соответствующими данными на нем.

Мы собираемся создать новую группу под названием «График» под группой «StudentTracker». Создайте под этим группу «Представления» и «Контроллеры», как в «Списке учащихся» и «Списке субъектов».

Создайте новый класс с именем «STLineGraphViewController» (подкласс UIViewController) в группе «View Controllers». При выборе места добавления файлов лучшее место для их размещения — папка «Классы / Графика / Просмотр контроллеров» (вам нужно будет создать каталог «Графики / Просмотр контроллеров»).


Мы собираемся вернуться и поработать над этим позже. Прямо сейчас мы собираемся реализовать этот код, который позволяет пользователю выбирать график для просмотра.

Сначала откройте STStudentListViewController.h и добавьте объявления протоколов «UIActionSheetDelegate» и «STLineGraphViewControllerDelegate». Этот протокол еще не существует, но мы создадим его позже (также убедитесь, что вы импортировали файл ‘STLineGraphViewController.h’).

1
@interface STStudentListViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, AddStudentViewControllerDelegate, UIActionSheetDelegate, STLineGraphViewControllerDelegate>

Затем откройте файл .m и реализуйте метод actionSheet: clickedButtonAtIndex: со следующим кодом:

01
02
03
04
05
06
07
08
09
10
11
12
13
— (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 0)
    {
        STLineGraphViewController *lineGraphVC = [[STLineGraphViewController alloc] init];
        [lineGraphVC setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
        [lineGraphVC setDelegate:self];
        [lineGraphVC setManagedObjectContext:[self managedObjectContext]];
          
        [self presentModalViewController:lineGraphVC animated:YES];
        [lineGraphVC release];
    }
}

Этот код не должен слишком много объяснять. Мы просто создаем LineGraph View Controller и представляем его модально. Мы ставим себя делегатом, чтобы знать, когда следует отклонить модальное представление. Мы также предоставляем контроллеру представления контекст управляемого объекта для работы с ним, чтобы он мог взаимодействовать с основными данными. Эти два последних метода создадут предупреждение (или ошибку при использовании ARC), потому что свойства еще не существуют, но мы создадим их позже.

Далее мы создадим метод для вызова листа действий и добавим UITabBarItem для его вызова. Добавьте объявление метода в интерфейсе .m с именем graphButtonWasSelected:

1
2
3
4
5
6
@interface STStudentListViewController ()
@property (nonatomic, strong) NSArray *studentArray;
  
— (void)addStudent:(id)sender;
— (void)graphButtonWasSelected:(id)sender;
@end

Далее добавьте реализацию метода:

1
2
3
4
5
6
— (void)graphButtonWasSelected:(id)sender
{
UIActionSheet *graphSelectionActionSheet = [[[UIActionSheet alloc] initWithTitle:@»Choose a graph» delegate:self cancelButtonTitle:@»Cancel» destructiveButtonTitle:nil otherButtonTitles:@»Enrolment over time», nil] autorelease];
      
    [graphSelectionActionSheet showInView:[[UIApplication sharedApplication] keyWindow]];
}

Затем нам нужно добавить UIBarButtonItem, чтобы пользователь мог выбрать, когда он хочет просмотреть график. Мы сделаем это в методе viewDidLoad, где создадим элемент правой кнопки панели:

1
[[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc] initWithTitle:@»Graphs» style:UIBarButtonItemStylePlain target:self action:@selector(graphButtonWasSelected:)] autorelease] animated:NO];

Наконец, нам нужно реализовать метод протокола STLineGraphViewController, который сообщит контроллеру о необходимости закрыть модальное представление, когда пользователь закончит смотреть на график:

1
2
3
4
— (void)doneButtonWasTapped:(id)sender
{
    [self dismissModalViewControllerAnimated:YES];
}

Теперь мы готовы начать создавать график!


Сначала нам нужно создать собственный класс представления для нашего LineGraphViewController. В группе Graphing> Views создайте новый класс, который расширяет UIView с именем ‘STLineGraphView’ (убедитесь, что поместили его в правильную папку при выборе места сохранения в файловой системе).


Мы собираемся установить графические аспекты представления в классе представления. Сначала перейдите в файл .h и (после импорта файла «CorePlot-CocoaTouch.h») и добавьте следующее объявление свойства:

1
@property (nonatomic, strong) CPTGraphHostingView *chartHostingView;

CPTGraphHostingView — это просто UIView, который отвечает за содержание графика и позволяет взаимодействовать с пользователем (о чем мы поговорим в следующем уроке).

Синтезируйте chartHostingView и создайте представление размещения диаграммы в методе initWithFrame:

1
2
[self setChartHostingView:[[[CPTGraphHostingView alloc] initWithFrame:CGRectZero] autorelease]];
[self addSubview:chartHostingView];

Вышесказанное должно быть достаточно понятным. Мы создаем CPTGraphHostingView и устанавливаем его как наше свойство chartHostingVIew. Затем мы добавим его в качестве подпредставления.

Далее нам нужно установить размеры фрейма graphViews в методе «layout subviews»:

1
2
3
4
5
6
7
[super layoutSubviews];
      
float chartHeight = self.frame.size.height — 40;
float chartWidth = self.frame.size.width;
      
[[self chartHostingView] setFrame:CGRectMake(0, 0, chartWidth, chartHeight)];
[[self chartHostingView] setCenter:[self center]];

Опять же, все вышеперечисленное должно быть простым делом. Прежде чем мы начнем работать с контроллером, убедитесь, что вы освободили свойство ‘chartHostingView’ в методе viewsloc, если вы этого еще не сделали.


Большая часть работы, которую мы собираемся сделать сейчас, будет в контроллере. Откройте STLineGraphViewController.h и добавьте следующие объявления свойств:

1
2
3
4
5
@interface STLineGraphViewController : UIViewController <CPTScatterPlotDataSource, CPTScatterPlotDelegate>
  
@property (nonatomic, strong) CPTGraph *graph;
@property (nonatomic, assign) id<STLineGraphViewControllerDelegate> delegate;
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;

CPTGraph — это абстрактный класс, который отвечает за отрисовку элементов графика и управление различными графиками. Он также отвечает за применение тем, перезагрузку графических данных и многое другое! Мы также указываем, что будем соблюдать протоколы CPTScatterPlotDataSource и CPTScatterPlotDelegate.

Нам также нужно добавить собственный протокол, чтобы модальное представление можно было отклонить. Поместите следующий код над объявлением интерфейса:

1
2
3
4
5
@protocol STLineGraphViewControllerDelegate
@required
— (void)doneButtonWasTapped:(id)sender;
  
@end

Переключитесь на файл * .m и синтезируйте график и делегируйте свойства. После этого добавьте следующий код перед методом viewDidLoad:

1
2
3
4
5
6
7
[self setView:[[[STLineGraphView alloc] initWithFrame:self.view.frame] autorelease]];
      
CPTTheme *defaultTheme = [CPTTheme themeNamed:kCPTPlainWhiteTheme];
      
[self setGraph:(CPTGraph *)[defaultTheme newGraph]];
      
[defaultTheme release];

В этом разделе происходит несколько вещей. Во-первых, мы создаем и устанавливаем представление контроллера как наш собственный STLineGraphView. Затем мы создаем объект CPTTheme. Объект CPTTheme управляет стилем графика со стилями линий, стилем текста и любыми необходимыми заливками. Простой способ получить CPTGraph, предварительно сконфигурированный с помощью базового CPTTheme, — создать CPTTheme с одним из предопределенных имен темы, а затем использовать метод «newGraph», чтобы получить график, стилизованный под него.

Далее, мы собираемся поместить следующий код в метод ‘viewDidLoad’:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[super viewDidLoad];
STLineGraphView *graphView = (STLineGraphView *)[self view];
[[self graph] setDelegate:self];
      
[[graphView chartHostingView] setHostedGraph:[self graph]];
      
CPTScatterPlot *studentScatterPlot = [[CPTScatterPlot alloc] initWithFrame:[graph bounds]];
[studentScatterPlot setIdentifier:@»studentEnrollment»];
[studentScatterPlot setDelegate:self];
[studentScatterPlot setDataSource:self];
      
[[self graph] addPlot:studentScatterPlot];
  
UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:self.title];
    [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:@»Done» style:UIBarButtonItemStyleDone target:[self delegate]
                                                                           action:@selector(doneButtonWasTapped:)] autorelease] animated:NO];

В приведенном выше коде мы получаем наше представление и устанавливаем размещенный граф для хоста представления диаграммы представлений для нашего объекта графа. Затем мы создаем «график» для размещения на графике. Мы используем «CPTScatterPlot», когда хотим создать линейный график. Идентификатор — это то, что мы можем использовать для идентификации графика позже. Затем мы устанавливаем делегат и источник данных для класса контроллера, так как он будет отвечать за предоставление данных для графа. Наконец, мы добавляем недавно созданный график графика.

После того, как мы поработаем с графиком, мы создадим элемент навигации и панель для контроллера модального представления, чтобы отобразить заголовок и кнопку «Готово», которая вернет их к исходному виду.

Попробуйте запустить проект сейчас и перейти к линейному графику. Вы должны увидеть что-то вроде следующего:


У нас есть начало графика! Теперь добавим некоторые данные:


Графики в CorePlot используют два основных метода источника данных для получения данных: «numberOfRecordsForPlot» и «numberForPlot: field: recordIndex:». Это очень похоже на работу табличных представлений. Сначала мы хотим указать количество записей для сюжета:

1
2
3
4
— (NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot
{
    return 8;
}

Мы показываем, сколько зачислений происходило в каждый день недели. Поскольку есть 7 возможных дней, когда студент мог бы записаться, у нас есть 8 записей (потому что мы начинаем с 0).

Теперь мы хотим указать, какими должны быть значения x и y для каждой записи:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
— (NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
    NSUInteger x = index;
    NSUInteger y = 0;
      
    NSError *error;
      
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@»STStudent» inManagedObjectContext:managedObjectContext];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@»dayEnrolled == %d», index];
      
    [fetchRequest setEntity:entity];
    [fetchRequest setPredicate:predicate];
      
    y = [managedObjectContext countForFetchRequest:fetchRequest error:&error];
      
    [fetchRequest release];
      
      
    switch (fieldEnum)
    {
        case CPTScatterPlotFieldX:
            NSLog(@»x value for %d is %d», index, x);
            return [NSNumber numberWithInt:x];
            break;
        case CPTScatterPlotFieldY:
            NSLog(@»y value for %d is %d», index, y);
            return [NSNumber numberWithInt:y];
            break;
              
        default:
            break;
    }
      
    return nil;
}

Здесь происходит нечто большее. Этот метод должен указать значения x и y для данного индекса, и то, какое значение он возвращает, основано на значении ‘fieldEnum’ (которое в нашем случае является либо CPTScatterPlotFieldX, либо CPTScatterPlotFieldY). Индекс указывает запись, которую он собирается нанести на график, а график относится к фактическому графику, на котором отображаются данные. Когда у нас есть контроллер, управляющий более чем 1 графиком, мы можем посмотреть на идентификатор графика, чтобы определить, какой набор данных предоставить (мы рассмотрим этот процесс в следующем уроке).

Я считаю, что проще всего указать значения ‘x’ и ‘y’ в начале метода, работая с обоими значениями, а затем на основе перечисления поля, возвращающего правильное значение в форме NSNumber (это формат, который CorePlot требует этого в).

Значение х легко понять. Поскольку он отображает дни зачисления, x равен текущему индексу. Значение y будет подсчитывать всех студентов, зачисленных в этот день. Мы можем получить это, позвонив в наше основное хранилище данных и найдя все записи STStudent со значением индекса dayEnrolled. Если вы не знакомы с основными данными и не понимаете всего, что происходит, не беспокойтесь, пока все в порядке, что это работает. Сосредоточьтесь на изучении одной вещи за один раз!

Если вы сохраните и запустите свое приложение сейчас, вы все равно не увидите ничего отображенного на графике, но вы должны увидеть следующий вывод в консоли:


Это означает, что график получает правильные значения для x и y (убедитесь, что он такой же или аналогичен выводу на рисунке. Однако он все равно не отображается на графике. Это потому, что если вы посмотрите на график, неверный отображаемый диапазон. Мы смотрим от -1,0 до 0 на осях X и Y. Нам нужно установить диапазон, чтобы посмотреть, прежде чем мы сможем увидеть точки данных.

Пространство сюжета во многом определяет способ просмотра и форматирования графика. Мы расскажем немного об этом уроке и подробнее рассмотрим следующий.

Чтобы установить диапазон x и y, на который смотрит пользователь, нам нужно работать с объектом CPTXYPlotSpace. Этот объект позволяет нам установить видимый диапазон для графика.

Перейдите к методу viewDidLoad и добавьте следующий код прямо под тем местом, где мы добавляем график к нашему графику:

1
2
3
CPTXYPlotSpace *studentPlotSpace = (CPTXYPlotSpace *)[graph defaultPlotSpace];
[studentPlotSpace setXRange:[CPTPlotRange plotRangeWithLocation:CPTDecimalFromInt(0) length:CPTDecimalFromInt(7)]];
[studentPlotSpace setYRange:[CPTPlotRange plotRangeWithLocation:CPTDecimalFromInt(0) length:CPTDecimalFromInt(10)]];

Сначала мы получаем объект CPTXYPlotSpace из пространства графиков по умолчанию для графиков (требуется некоторое приведение). Затем мы просто устанавливаем диапазон x и y. Диапазон представляет собой объект ‘CPTPlotRange’, который мы статически создаем с помощью метода plotRangeWithLocation: length: ‘. Этот метод принимает NSDecimals, но corePlot предоставляет нам функцию, которую мы можем использовать для получения десятичного числа из целого числа, называемого CPTDecimalFromInt (есть также один для float, если это требуется).

Теперь сохраните и запустите приложение, и вы должны увидеть начало вашего первого графика!



У нас есть запуск графа, но ему нужно немного поработать, прежде чем он станет очень полезным. В следующий раз мы обсудим, как устанавливать и форматировать метки оси, линии отметок и приращения. Мы также собираемся обсудить, как настроить внешний вид графика, и, наконец, как добавить и управлять несколькими графиками на одном графике. Увидимся в следующий раз!