ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C часть 3

25. Quartz 2D

Основы Quartz 2D

Quartz 2D — это основной механизм для рисования 2D объектов в iOS. В этом уроке я расскажу о том, как выполняется 2D рисунок. Приложение, которое мы создадим в этом уроке будет содержать класс-наследник от UIView. В этом классе присутствует метод drawRect, который мы переопределим для выпонлнения различных операций рисования 2D. Но ближе к делу, создадим  новый проект на основе шаблона Single View Application и назовем его Draw2D.

 

Как я уже говорил, первое, что нам нужно сделать — это добавить свой UIView, в котором будет происходить вся отрисовка. Для этого нажмите правой кнопкой на папке проекта и во всплывающем меню выберите пункт New File…

 

 

В открывшемся меню выберите пункт Cocoa Touch, а в правой части окна Objective-C class и нажмите кнопку Next.

 

 

В поле Class укажите имя будущего класса (Draw2D), а в Subclass of — выберите UIView и снова нажмите Next.

 

 

После этого Xcode создаст новый класс и добавит его в наш проект. Но класс этот мы пока что не используем. Импортируйте его в основной контроллер представления (в нашем случае это ViewController) и переопределите класс у основного представления:

 

 

Можете запустить проект, чтобы убедиться, что все сделано правильно. Если все скомпилировалось без ошибок — можна приступать ко второму этапу. Перейдите к реализации только что добавленного класса (Draw2D.m). Там вы найдете закомментированный метод — (void)drawRect:(CGRect)rect, а в нем — комментарий //Drawing code. То есть, это как раз то место, где следует поместить код рисования. В оставшейся части этого урока мы будем изменять код в методе drawRect для выполнения различных операций рисования.

 

Рисование линии

Для того, чтобы нарисовать линию на экране, используя Quartz 2D — в первую очередь необходимо получить графический контекст для представления:

 

<code data-result="[object Object]">CGContextRef context = UIGraphicsGetCurrentContext();</code>

 

После того как контекст получен, следует указать ширину линии:

 

<code data-result="[object Object]">CGContextSetLineWidth(context, 2.0f);</code>

 

Далее, нам необходимо создать ссылку на цвет цвет. Мы можем сделать это, указав RGBA компоненты нужного цвета (в данном случаенепрозрачного синего):

 

<code data-result="[object Object]">CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = {0.0f, 0.0f, 1.0f, 1.0f};
CGColorRef color = CGColorCreate(colorspace, components);</code>

 

С помощью ссылки цветов и контекста мы можем указать цвет, который будет использоваться при составлении линии:

 

<code data-result="[object Object]">CGContextSetStrokeColorWithColor(context, color);</code>

 

Следующим шагом является переход к начальной точке линии, которую будем рисовать:

 

<code data-result="[object Object]">CGContextMoveToPoint(context, 0.0f, 0.0f);</code>

 

Эта строка кода указывает, что начальной точкой для линии является верхний левый угол дисплея. Теперь нам нужно указать конечную точку линии, к примеру 300, 400:

 

<code data-result="[object Object]">CGContextAddLineToPoint(context, 300.0f, 400.0f);</code>

 

Определив ширину линии, цвета и путь, мы готовы провести черту и освободить цветовую ссылку и и ссылку цветового пространства:

 

<code data-result="[object Object]">CGContextStrokePath(context);
CGColorSpaceRelease(colorspace);
CGColorRelease(color);</code>

 

Если сложить все вышеизложенное вместе — мы получим метод drawRect, который выглядит вот так:

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 2.0f);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGFloat components[] = {0.0f, 0.0f, 1.0f, 1.0f};
    CGColorRef color = CGColorCreate(colorspace, components);
    CGContextSetStrokeColorWithColor(context, color);
    CGContextMoveToPoint(context, 0.0f, 0.0f);
    CGContextAddLineToPoint(context, 300.0f, 400.0f);
    CGContextStrokePath(context);
    CGColorSpaceRelease(colorspace);
    CGColorRelease(color);
}</code>

 

После компиляции и запуска вы должны получить примерно следующую картинку:

 

 

Отметим, что в приведенном выше примере мы вручную создали цветовое пространство и цветовую ссылку. Но quartz цвета также могут быть созданы с помощью класса UIColor. Например, тот же результат, как описано выше, может быть достигнут с меньшим числом строк кода:

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 2.0f);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextMoveToPoint(context, 0.0f, 0.0f);
    CGContextAddLineToPoint(context, 300.0f, 400.0f);
    CGContextStrokePath(context);
}</code>

 

Рисование путей

Как вы могли заметить, в приведенном выше примере мы рисуем одну линию, определяя пути между двумя точками. Определение пути, который состоит из нескольких точек позволяет отрисовать последовательность прямых. Реализовать это можно с помощью повторного вызова функции CGContextAddLineToPoint() или  CGContextAddArc().

 

Например, следующий код рисует ромб:

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 4.0f);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextMoveToPoint(context, 100.0f, 100.0f);
    CGContextAddLineToPoint(context, 150.0f, 150.0f);
    CGContextAddLineToPoint(context, 100.0f, 200.0f);
    CGContextAddLineToPoint(context, 50.0f, 150.0f);
    CGContextAddLineToPoint(context, 100.0f, 100.0f);
    CGContextStrokePath(context);
}</code>

 

Результат его работы вы можете увидеть на картинке ниже:

 

 

Рисование прямоугольника

Прямоугольники рисуются так же, как и любой другой путь, с тем исключением, что путь определяется заданием х и у координатами верхнего левого угла прямоугольника, плюс его высота и ширина. Эти размеры хранятся в структуре CGRect и передаются в качестве параметра в функцию CGContextAddRect:

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 4.0f);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGRect rectangle = CGRectMake(60.0f, 170.0f, 200.0f ,80.0f);
    CGContextAddRect(context, rectangle);
    CGContextStrokePath(context);
}</code>

 

Результатом работы этой части кода будет следующее изображение:

 

 

Рисование эллипса или круга

Круги и эллипсы рисуются путем определения прямоугольной области, форма которой должна соответствовать кругу, а затем вызвать функцию CGContextAddEllipseInRect():

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 4.0f);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGRect rectangle = CGRectMake(60.0f, 170.0f, 200.0f, 80.0f);
    CGContextAddEllipseInRect(context, rectangle);
    CGContextStrokePath(context);
}</code>

 

В этом случае вы увидите:

 

 

Для того, чтобы нарисовать круг, просто задайте прямоугольник с равными длиннами сторон (квадрат).

 

Заливка цветом

Путь может быть заполнена цветом, используя различные функции Quartz 2D. Прямоугольные и эллиптические пути могут быть заполнена с использованием CGContextFillRect() и CGContextFillEllipse() соответственно. Кроме того, путь может быть заполнена с использованием функции CGContextFillPath(). Перед выполнением операции заполнения, цвет заливки должны быть указаны с помощью CGContextSetFillColorWithColor().

 

Следующий пример определяет путь, а затем наполняет его красным цветом:

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextMoveToPoint(context, 100.0f, 100.0f);
    CGContextAddLineToPoint(context, 150.0f, 150.0f);     
    CGContextAddLineToPoint(context, 100.0f, 200.0f);
    CGContextAddLineToPoint(context, 50.0f, 150.0f);
    CGContextAddLineToPoint(context, 100.0f, 100.0f);
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillPath(context);
}</code>

 

Результат выполнения этого кода выглядит примерно так:

 

 

Следующий код рисует такой же квадрат, но синим цветом, а затем заливате красным его внутреннее пространство:

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 4.0f);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGRect rectangle = CGRectMake(60.0f, 170.0f, 200.0f, 80.0f);
    CGContextAddRect(context, rectangle);
    CGContextStrokePath(context);
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillRect(context, rectangle);
}</code>

 

В этом случае на экране мы увидим это:

 

 

Рисование дуги

Дуги можно нарисовать указанием двух точек касания и радиус, используя функцию CGContextAddArcToPoint():

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 4.0f);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextMoveToPoint(context, 100.0f, 100.0f);
    CGContextAddArcToPoint(context, 100.0f, 200.0f, 300.0f ,200.0f, 100.0f);
    CGContextStrokePath(context);
}</code>

 

На экране увидим ожидаемый результат:

 

 

Рисование кубической кривой Безье

Кривую Безье можно нарисовать путем перехода к начальной точке, а затем мимо двух контрольных точек и до конечной точки, а поможем в этом функция CGContextAddCurveToPoint():

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 4.0f);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextMoveToPoint(context, 10.0f, 10.0f);
    CGContextAddCurveToPoint(context, 0.0f, 50.0f, 300.0f, 250.0f, 300.0f, 400.0f);
    CGContextStrokePath(context);
}</code>

 

И соответственно, результат:

 

Рисование квадратной кривой Безье

Квадратная кривая Безье отрисовывается способом аналогичным кубической. Только в этом случае мы используем функцию CGContextAddQuadCurveToPoint():

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 4.0f);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextMoveToPoint(context, 10.0f, 200.0f);
    CGContextAddQuadCurveToPoint(context, 150.0f, 10.0f, 300.0f, 200.0f);
    CGContextStrokePath(context);
}</code>

 

Соответственно:

 

 

Пунктирная линия

До сих пор в этом уроке мы выполнили все наши рисунок со сплошной линиией. Quartz 2D также обеспечивает поддержку для рисования пунктирных линий. Это достигается с помощью функции CGContextSetLineDash(), которая принимает в качестве параметров следующие:

 

  • context — контекст графики представления, в которых рисунок состоится
  • phase — значение начала изображения
  • lengths — массив, содержащий значения длины окрашенных и неокрашенных участков линии. Например, массив, содержащий 5 и 6 циклов по 5 окрашены пространства единицы следуют 6 неокрашенной пространства устройства.
  • count — подсчет количества элементов в массиве длины

 

Например, массив длинны [2,6,4,2] с толщиной линии 5.0 будет выглядеть следующим образом:

 

 

Соответствующий код, который отрисовал эти линии выглядит так:

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 20.0f);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGFloat dashArray[] = {2.0f ,6.0f ,4.0f, 2.0f};
    CGContextSetLineDash(context, 3.0f, dashArray, 4.0f);
    CGContextMoveToPoint(context, 10.0f, 200.0f);
    CGContextAddQuadCurveToPoint(context, 150.0f, 10.0f, 300.0f, 200.0f);
    CGContextStrokePath(context);
}</code>

 

Рисование изображения в графическом контексте

 

Первое, что нам нужно сделать — это добавить в проект само изображение. Скачайте файл по этой ссылке и перетяните его в проект. Изображение может быть помещено в графический контекст путем указания координат в верхнем левом углу (в этом случае изображение будет выглядеть в полный размер). Если указать конкретную область для отрисовки изображения (CGRect) — оно будет помещено в эту область. В приведенно ниже примере я использовал первый вариант отрисовки:

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    UIImage *myImage = [UIImage imageNamed:@"pets.jpg"];
    CGPoint imagePoint = CGPointMake(0, 0);
    [myImage drawAtPoint:imagePoint];
}
</code>

 

Код второго варианта выглядит следующим образом (в нем мы получаем размер экрана и передаем в функцию drawInRect, таким образом, картинка получается растянутой на весь экран:

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect {
    UIImage *myImage = [UIImage imageNamed:@"pets.jpg"];
    CGRect imageRect =[[UIScreen mainScreen] bounds];
    [myImage drawInRect:imageRect];
}</code>

 

На картинке ниже вы можете сравнить работу обеих вариантов:

 

 

Фильтрация изображений с помощью CoreImage framework

Разобравшись в концепции вывода изображения в iOS, можно переходить к обзору CoreImage framework.

 

Core Image был введен с прошивкой 5 и обеспечивает механизм фильтрации и манипулирования неподвижных изображений и видео. В комплекте с Core Image представляется широкий спектр различных фильтров с возможностью создания пользовательских фильтров в соответствии с конкретными требованиями. Примеры фильтров, которые могут быть применены включают кадрирование, цветовые эффекты, размытие, деформации, трансформации и градиенты. Полный список фильтров доступен в официальной документации Apple.

 

Объект CIImage обычно инициализируется со ссылкой на изображение, чтобы манипулировать им. Затем создаем объект CIFilter с указанием имени нужного фильтра. После инициализации CIFilter следует применение значений фильтра. Результатом работы фильтра является объект CIImage, который, как и раньше, мы отображаем в контексте.

 

В качестве примера Core Image в действии мы модифицируем метод drawRect нашей классе Draw2D, чтобы сделать ранее показаное изображение с применением фильтра CISepiaTone. Не забудьте перед написанием кода добавить в проект фреймверк CoreImage.framework, если кто-то забыл как это делается — можете почитать здесь.

 

<code data-result="[object Object]">- (void)drawRect:(CGRect)rect
{
    UIImage *myimage = [UIImage imageNamed:@"pets.jpg"];
    CIImage *cimage = [[CIImage alloc] initWithImage:myimage];

    CIFilter *sepiaFilter = [CIFilter filterWithName:@"CISepiaTone"];
    [sepiaFilter setDefaults];
    [sepiaFilter setValue:cimage forKey:@"inputImage"];
    [sepiaFilter setValue:[NSNumber numberWithFloat:0.8f] forKey:@"inputIntensity"];

    CIImage *image = [sepiaFilter outputImage];
    CIContext *context = [CIContext contextWithOptions: nil];
    CGImageRef cgImage = [context createCGImage:image fromRect:image.extent];
    UIImage *resultUIImage = [UIImage imageWithCGImage:cgImage];

    CGRect imageRect =[[UIScreen mainScreen] bounds];
    [resultUIImage drawInRect:imageRect];
}</code>

 

Наш метод начинается с загрузки файла изображения. Дело в том, что Core Image работает с объектами CIImage, поэтому нам нужно преобразовать UIImage в CIImage. Далее создается новый объект CIFilter с указанием фильтра CISepiaTone. Прежде чем настроить фильтр, ему устанавливаются значения по умолчанию, после чего применяется интенсивность (0.8). Вот так выглядит результат выполнения нашего кода:

 

 

Наследуя класс UIView и переопределяя его метод drawRect мы научились отрисовывать различные 2D-фигуры. В этой главе мы рассмотрели некоторые возможности с отрисовки графики в Quartz2D. Так же, был представлен краткий обзор Core Image, в котором мы научились применять фильтры к имеющимся изображениям.

Comments are closed.