iOS. Приемы программирования Нахавандипур Вандад
CGFloat *startColorComponents =
(CGFloat *)CGColorGetComponents([startColor CGColor]);
UIColor *endColor = [UIColor blueColor];
CGFloat *endColorComponents =
(CGFloat *)CGColorGetComponents([endColor CGColor]);
CGFloat colorComponents[8] = {
/* Четыре компонента оранжевого цвета (RGBA (RGBA) */
startColorComponents[0],
startColorComponents[1],
startColorComponents[2],
startColorComponents[3], /* Первый цвет = оранжевый */
/* Четыре компонента голубого цвета (RGBA) */
endColorComponents[0],
endColorComponents[1],
endColorComponents[2],
endColorComponents[3], /* Второй цвет = голубой */
};
CGFloat colorIndices[2] = {
0.0f, /* Цвет 0 в массиве colorComponents */
1.0f, /* Цвет 1 в массиве colorComponents */
};
CGGradientRef gradient = CGGradientCreateWithColorComponents
(colorSpace,
(const CGFloat *)&colorComponents,
(const CGFloat *)&colorIndices,
2);
CGColorSpaceRelease(colorSpace);
CGPoint startPoint, endPoint;
startPoint = CGPointMake(120,
260);
endPoint = CGPointMake(200.0f,
220);
CGContextDrawLinearGradient (currentContext,
gradient,
startPoint,
endPoint,
kCGGradientDrawsBeforeStartLocation |
kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
CGContextRestoreGState(currentContext);
}
Возможно, вам не совсем понятно, как при смешивании значений kCGGradientDrawsBeforeStartLocation и kCGGradientDrawsAfterEndLocation, переданных процедуре CGContextDrawLinearGradient, получается диагональный эффект, как на рис. 17.28. Поэтому уберем эти значения и зададим для этого параметра процедуры CGContextDrawLinearGradient значение 0 — как и раньше. Результат получится как на рис. 17.29.
Рис. 17.29. Осевой градиент без распространения цветов
На рис. 17.28 и 17.29 изображены одинаковые градиенты. Но у градиента на рис. 17.28 цвета начальной и конечной точек распространяются по обе стороны градиента на весь графический контекст, поэтому весь экран оказывается закрашен.
См. также
Раздел 17.3.
17.11. Перемещение фигур, нарисованных в графических контекстах
Постановка задачи
Требуется переместить все, что изображено в графическом контексте, на новое место, не изменяя при этом кода отрисовки, либо просто без труда сместить содержимое графического контекста.
Решение
Воспользуйтесь функцией CGAffineTransformMakeTranslation для создания аффинного преобразования сдвига.
Обсуждение
В разделе 17.8 было упомянуто о преобразованиях. Преобразование — это, в сущности, просто изменение способа отображения рисунка. Преобразования в Core Graphics — это объекты, применяемые к фигурам перед отрисовкой последних. Например, можно создать преобразование сдвига (Translation Transformation). «Сдвига чего?» — могли бы спросить вы. Дело в том, что преобразование сдвига — это механизм, позволяющий сместить фигуру или графический контекст.
Среди других типов преобразований следует также назвать вращение (см. раздел 17.13) и масштабирование (см. раздел 17.12). Все это примеры аффинных преобразований, то есть при таком преобразовании каждая точка оригинала сопоставляется с другой точкой в окончательной версии. Все преобразования, о которых мы будем говорить в этой книге, являются аффинными.
В ходе преобразования сдвига актуальное положение фигуры на пути или в графическом контексте сдвигается на другую относительную позицию. Например, если вы поставите точку с координатами (10; 20), примените к ней преобразование сдвига (30; 40) и снова ее поставите, точка окажется расположенной в координатах (40; 60), поскольку 40 = 10 + 30, а 60 = 20 + 40.
Чтобы создать новое преобразование сдвига, используется функция CGAffineTransformMakeTranslation, которая возвращает аффинное преобразование типа CGAffineTransform. Два параметра этой функции указывают сдвиг по осям X и Y в точках.
В разделе 17.8 мы изучили, что процедура CGPathAddRect принимает в качестве второго параметра объект преобразования типа CGAffineTransform. Чтобы сместить прямоугольник с его исходной позиции на другую, можно просто создать аффинное преобразование, указывающее изменения, которые вы хотели бы применить к координатам x и y, и передать преобразование второму параметру процедуры CGPathAddRect, как показано далее:
— (void)drawRect:(CGRect)rect{
/* Сначала создаем путь. Просто описатель пути. */
CGMutablePathRef path = CGPathCreateMutable();
/* Это границы прямоугольника. */
CGRect rectangle = CGRectMake(10.0f,
10.0f,
200.0f,
300.0f);
/* Мы хотим сместить прямоугольник на 100 точек вправо,
не изменив при этом его положения по оси Y. */
CGAffineTransform transform = CGAffineTransformMakeTranslation(100.0f,
0.0f);
/* Добавляем прямоугольник к пути. */
CGPathAddRect(path,
&transform,
rectangle);
/* Получаем описатель текущего контекста. */
CGContextRef currentContext =
UIGraphicsGetCurrentContext();
/* Добавляем путь к контексту. */
CGContextAddPath(currentContext,
path);
/* Задаем голубой в качестве цвета заливки. */
[[UIColor colorWithRed:0.20f
green:0.60f
blue:0.80f
alpha:1.0f] setFill];
/* Задаем для обводки коричневый цвет. */
[[UIColor brownColor] setStroke];
/* Задаем для ширины (обводки) значение 5. */
CGContextSetLineWidth(currentContext,
5.0f);
/* Проводим путь в контексте и применяем к нему заливку. */
CGContextDrawPath(currentContext,
kCGPathFillStroke);
/* Избавляемся от пути. */
CGPathRelease(path);
}
На рис. 17.30 показан результат выполнения этого блока кода внутри объекта-вида.
Сравните рис. 17.30 и 17.22. Видите разницу? Еще раз просмотрите исходный код для обеих фигур и убедитесь в том, что положения по осям X и Y, указанные для обоих прямоугольников, в обоих блоках кода идентичны. Различие заключается только в том, что на рис. 17.30 мы видим результат применения к прямоугольнику аффинного преобразования, когда прямоугольник добавляется к пути.
Кроме применения преобразований к фигурам, отрисовываемым относительно путей, мы можем применять преобразования и к графическому контексту с помощью процедуры CGContextTranslateCTM. Она применяет преобразование к текущей матрице преобразований (Current Transformation Matrix, CTM). Хотя это название и может показаться сложным, понять его смысл не составляет труда. Считайте CTM правилами, определяющими расположение центра вашего графического контекста, а также правилами проецирования каждой отрисовываемой точки на экране. Например, если вы приказываете Core Graphics поставить точку с координатами (0; 0), Core Graphics найдет центр экрана, получив эту информацию из текущей матрицы преобразований. Затем CTM выполнит определенные вычисления и сообщит Core Graphics, что искомая точка расположена в верхнем левом углу экрана. С помощью таких процедур, как CGContextTranslateCTM, можно изменить конфигурацию этой матрицы, после чего заставить все фигуры, отрисованные в графическом контексте, занять на холсте другие позиции. Вот пример, в котором мы достигаем точно такого же эффекта, как и на рис. 17.30, но применяем преобразование сдвига не к самому прямоугольнику, а к текущей матрице преобразований:
Рис. 17.30. Прямоугольник с аффинным преобразованием сдвига
— (void)drawRect:(CGRect)rect{
/* Сначала создаем путь. Просто описатель пути. */
CGMutablePathRef path = CGPathCreateMutable();
/* Это границы прямоугольника. */
CGRect rectangle = CGRectMake(10.0f,
10.0f,
200.0f,
300.0f);
/* Добавляем прямоугольник к пути. */
CGPathAddRect(path,
NULL,
rectangle);
/* Получаем описатель текущего контекста. */
CGContextRef currentContext = UIGraphicsGetCurrentContext();
/* Сохраняем состояние контекста, чтобы позже
можно было восстановить это состояние. */
CGContextSaveGState(currentContext);
/* Сдвигаем текущую матрицу преобразований
на 100 точек вправо. */
CGContextTranslateCTM(currentContext,
100.0f,
0.0f);
/* Добавляем путь к контексту. */
CGContextAddPath(currentContext,
path);
/* Задаем голубой в качестве цвета заливки. */
[[UIColor colorWithRed:0.20f
green:0.60f
blue:0.80f
alpha:1.0f] setFill];
/* Задаем для обводки коричневый цвет. */
[[UIColor brownColor] setStroke];
/* Задаем для ширины (обводки) значение 5. */
CGContextSetLineWidth(currentContext,
5.0f);
/* Проводим путь в контексте и применяем к нему заливку. */
CGContextDrawPath(currentContext,
kCGPathFillStroke);
/* Избавляемся от пути. */
CGPathRelease(path);
/* Восстанавливаем состояние контекста. */
CGContextRestoreGState(currentContext);
}
Запустив эту программу, вы получите точно такие же результаты, как и на рис. 17.30.
См. также
Разделы 17.8, 17.12 и 17.13.
17.12. Масштабирование фигур, нарисованных в графических контекстах
Постановка задачи
Требуется динамически масштабировать фигуры, отрисованные в графическом контексте, в сторону уменьшения или увеличения.
Решение
Создайте аффинное преобразование масштаба, воспользовавшись функцией CGAffineTransformMakeScale.
Обсуждение
В разделе 17.11 было объяснено, что такое преобразование и как применять его к фигурам и графическим контекстам. Один из вариантов преобразования, которым вы можете воспользоваться, — это масштабирование. Core Graphics позволяет без проблем масштабировать фигуру, например круг, на 100 % относительно ее исходного размера.
Чтобы создать аффинное преобразование масштаба, пользуйтесь функцией CGAffineTransformMakeScale, которая возвращает объект преобразования типа CGAffineTransform. Если вы хотите применить преобразование масштаба непосредственно к графическому контексту, примените процедуру CGContextScaleCTM, которая масштабирует CTM. Подробнее о CTM (текущей матрице преобразований) рассказано в разделе 17.11.
Функции преобразования масштаба принимают два параметра: масштабирование по оси X и масштабирование по оси Y. Еще раз обратимся к прямоугольнику с рис. 17.22. Если мы хотим масштабировать этот прямоугольник, чтобы его исходные длина и ширина уменьшились вполовину, то можно просто масштабировать по оси X и Y на 0,5 (вполовину от исходного значения), как показано здесь:
/* Масштабируем прямоугольник, уменьшая его на половину. */
CGAffineTransform transform =
CGAffineTransformMakeScale(0.5f, 0.5f);
/* Добавляем прямоугольник к пути. */
CGPathAddRect(path,
&transform,
rectangle);
На рис. 17.31 показано, что получится, когда мы применим преобразование масштаба к коду, написанному в разделе 17.8.
Рис. 17.31. Масштабирование прямоугольника
Дополнительно к функции CGAffineTransformMakeScale можно использовать процедуру CGContextScaleCTM, помогающую применить преобразование масштаба к графическому контексту. Следующий код даст тот же эффект, что и в предыдущем примере (вновь обратите внимание на рис. 17.31):
— (void)drawRect:(CGRect)rect{
/* Сначала создаем путь. Просто описатель пути. */
CGMutablePathRef path = CGPathCreateMutable();
/* Это границы прямоугольника. */
CGRect rectangle = CGRectMake(10.0f,
10.0f,
200.0f,
300.0f);
/* Добавляем прямоугольник к пути. */
CGPathAddRect(path,
NULL,
rectangle);
/* Получаем описатель текущего контекста. */
CGContextRef currentContext = UIGraphicsGetCurrentContext();
/* Масштабируем все фигуры, отрисованные в графическом контексте,
уменьшая их на половину. */
CGContextScaleCTM(currentContext,
0.5f,
0.5f);
/* Добавляем путь к контексту. */
CGContextAddPath(currentContext,
path);
/* Задаем голубой в качестве цвета заливки. */
[[UIColor colorWithRed:0.20f
green:0.60f
blue:0.80f
alpha:1.0f] setFill];
/* Задаем для обводки коричневый цвет. */
[[UIColor brownColor] setStroke];
/* Задаем для ширины (обводки) значение 5. */
CGContextSetLineWidth(currentContext,
5.0f);
/* Проводим путь в контексте и применяем к нему заливку. */
CGContextDrawPath(currentContext,
kCGPathFillStroke);
/* Избавляемся от пути. */