Чтобы создать линейный график, я собираюсь повторно использовать тот же проект, который мы использовали для рисования полос в предыдущей части серии. Нам больше не понадобится логика для рисования баров, поэтому закомментируйте дюжину строк кода в конце метода drawRect
, тех, которые имеют дело с барами.
Я также изменил значение kOffsetX
на 0, чтобы линейный график kOffsetX
с левого края представления. Нам нужно добавить пару значений в набор данных, и вы можете выбрать любые значения от 0 до 1. Вот что я выбрал:
float data[] = {0.7, 0.4, 0.9, 1.0, 0.2, 0.85, 0.11, 0.75, 0.53, 0.44, 0.88, 0.77, 0.99, 0.55};
Создайте новый пустой метод, убедившись, что он идет перед drawRect
:
- (void)drawLineGraphWithContext:(CGContextRef)ctx { }
Мы будем постепенно заполнять этот метод кодом. Первым шагом является настройка среды, в этом случае, чтобы определить ширину и цвет линии, которую мы будем использовать. Следующие две строки кода не должны вас удивлять:
CGContextSetLineWidth(ctx, 2.0); CGContextSetStrokeColorWithColor(ctx, [[UIColor colorWithRed:1.0 green:0.5 blue:0 alpha:1.0] CGColor]);
Вы уже знакомы с рисованием контуров — мы нарисовали столбцы в виде контуров — поэтому следующие строки кода, хотя и немного отличающиеся, должны быть полностью понятными:
int maxGraphHeight = kGraphHeight - kOffsetY; CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, kOffsetX, kGraphHeight - maxGraphHeight * data[0]); for (int i = 1; i < sizeof(data); i++) { CGContextAddLineToPoint(ctx, kOffsetX + i * kStepX, kGraphHeight - maxGraphHeight * data[i]); } CGContextDrawPath(ctx, kCGPathStroke);
Это все, что нам нужно, чтобы нарисовать простой линейный график. Запустите приложение, и вы должны увидеть следующее:
Для некоторых простых случаев этот график может быть приемлемым, но есть несколько способов, которыми мы можем улучшить его. Один из них — сделать точки данных более заметными.
Подчеркивая точки данных
Одно из популярных решений — нарисовать маленький круг поверх каждой точки данных, так что давайте сделаем это. Прежде всего, мы хотим, чтобы круги были закрашены цветом, поэтому добавьте следующую строку кода в самый конец метода drawLineGraphWithContext
:
CGContextSetFillColorWithColor(ctx, [[UIColor colorWithRed:1.0 green:0.5 blue:0 alpha:1.0] CGColor]);
Далее давайте определим радиус окружности в заголовочном файле:
#define kCircleRadius 3
В Quartz 2D, чтобы нарисовать круг или эллипс, нам сначала нужно создать прямоугольник ( CGRect
), который будет определять размеры этого круга. Самый простой способ создать такой прямоугольник — использовать функцию CGRectMake
которая принимает четыре параметра: координаты x и y верхнего левого угла прямоугольника, затем его ширину и высоту.
Мы будем создавать прямоугольники таким образом, чтобы их центры совпадали с точками, через которые мы только что нарисовали линейный график. Затем мы вписываем круг в каждый из прямоугольников и, наконец, обводим и заполняем круги. В целом, код выглядит так:
for (int i = 1; i < sizeof(data) - 1; i++) { float x = kOffsetX + i * kStepX; float y = kGraphHeight - maxGraphHeight * data[i]; CGRect rect = CGRectMake(x - kCircleRadius, y - kCircleRadius, 2 * kCircleRadius, 2 * kCircleRadius); CGContextAddEllipseInRect(ctx, rect); } CGContextDrawPath(ctx, kCGPathFillStroke);
Добавьте его в конец метода drawLineGraphWithContext
, и это результат, который вы должны увидеть:
Следующее улучшение, которое мы могли бы сделать, — заполнить пространство под графиком цветом.
Заполнение графика
Шаги, необходимые для создания графа, заполненного цветом, очень похожи на шаги для создания самой линии графика, с той лишь разницей, что нам нужно создать замкнутый путь. Посмотрим, как это сделать.
В начале метода drawLineGraphWithContext
, сразу после определения maxGraphHeight
, давайте определим цвет заливки — тот же цвет, который мы используем для рисования самого графика, но полупрозрачный:
CGContextSetFillColorWithColor(ctx, [[UIColor colorWithRed:1.0 green:0.5 blue:0 alpha:0.5] CGColor]);
Затем мы в основном рисуем график, но добавляем несколько линий, чтобы окружить замкнутое пространство. Наконец, мы заполняем полученный путь, но не обводим его. Следуйте коду, и вы увидите, что происходит:
CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, kOffsetX, kGraphHeight); CGContextAddLineToPoint(ctx, kOffsetX, kGraphHeight - maxGraphHeight * data[0]); for (int i = 1; i < sizeof(data); i++) { CGContextAddLineToPoint(ctx, kOffsetX + i * kStepX, kGraphHeight - maxGraphHeight * data[i]); } CGContextAddLineToPoint(ctx, kOffsetX + (sizeof(data) - 1) * kStepX, kGraphHeight); CGContextClosePath(ctx); CGContextDrawPath(ctx, kCGPathFill);
После того, как заливка завершена, мы рисуем саму линию графика и круги, как и прежде, поверх нее. Если вы запустите приложение сейчас, вы увидите, что график стал немного лучше:
Чтобы сделать его еще приятнее, мы можем захотеть заполнить график градиентом. Вы уже знаете, как создать градиент, так что во второй раз должно быть проще.
Заполнение графика градиентом
Вот код, который готовит градиент, без комментариев на этот раз. Вставьте его прямо перед кодом, который мы написали в предыдущем разделе для создания сплошной заливки:
CGGradientRef gradient; CGColorSpaceRef colorspace; size_t num_locations = 2; CGFloat locations[2] = {0.0, 1.0}; CGFloat components[8] = {1.0, 0.5, 0.0, 0.2, // Start color 1.0, 0.5, 0.0, 1.0}; // End color colorspace = CGColorSpaceCreateDeviceRGB(); gradient = CGGradientCreateWithColorComponents(colorspace, components, locations, num_locations); CGPoint startPoint, endPoint; startPoint.x = kOffsetX; startPoint.y = kGraphHeight; endPoint.x = kOffsetX; endPoint.y = kOffsetY;
Теперь мы собираемся повторно использовать закрытый путь, который мы создали в предыдущем разделе. На этот раз это будет работать как обтравочный контур. Закомментируйте эту строку:
CGContextDrawPath(ctx, kCGPathFill);
и замените его двумя другими:
CGContextSaveGState(ctx); CGContextClip(ctx);
Теперь мы готовы вылить градиент в график. Преуспевать:
CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);
Наконец, выполните очистку:
CGContextRestoreGState(ctx); CGColorSpaceRelease(colorspace); CGGradientRelease(gradient);
Вот результат, который вы должны увидеть:
Не плохо ли это? И, конечно, вы можете настроить градиент, если хотите.
Теперь мы знаем, как нарисовать гистограмму и линейный график. Они хорошо выглядят, но еще не интерактивны. Было бы хорошо, если бы, скажем, когда пользователь нажимал на панель, на графике отображалось сообщение, соответствующее этой панели. Это именно то, с чем мы будем иметь дело в следующей части серии.
Кварц 2D Индекс
Серия Александра Колесникова «Создание графика с использованием Quartz 2D» была разбита на 5 частей. Вы можете сослаться на серию, используя Quartz 2D Tag, и получить доступ к отдельным статьям, используя ссылки ниже.
- Создание графика с помощью Quartz 2D: часть 1
- Создание графика с помощью Quartz 2D: часть 2
- Создание графика с помощью Quartz 2D: часть 3
- Создание графика с помощью Quartz 2D: часть 4
- Создание графика с помощью Quartz 2D: часть 5