Рисование форм Используя пути Bézier

В iOS 3.2 и позже, можно использовать UIBezierPath класс для создания основанных на векторе путей. UIBezierPath класс является оберткой Objective C для связанных с путем функций в Базовой Графической платформе. Можно использовать этот класс для определения простых форм, таких как овалы и прямоугольники, а также сложные формы, включающие многократные прямые и изогнутые сегменты линии.

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

Основы пути Bézier

A UIBezierPath объект является оберткой для a CGPathRef тип данных. Пути являются основанными на векторе формами, создающимися с помощью линейных сегментов и сегментов кривой. Можно использовать линейные сегменты для создания прямоугольников и многоугольников, и можно использовать сегменты кривой, чтобы создать дуги, круги, и объединить искривленные формы. Каждый сегмент состоит из одной или более точек (в текущей системе координат) и команда рисования, определяющая, как интерпретируются те точки.

Каждый набор связанной строки и сегментов кривой формирует то, что упоминается как подпуть. Конец одного линейного сегмента или сегмента кривой по подпути определяет начало следующего. Сингл UIBezierPath объект может содержать один или несколько подпутей, определяющих полный путь, разделенный moveToPoint: команды, эффективно повышающие рейсфедер и перемещающие его в новое расположение.

Процессы для создания и использования объекта контуров являются отдельными. Создание пути является первым процессом и включает следующие шаги:

  1. Создайте объект контуров.

  2. Установите любые соответствующие атрибуты получения Вашего UIBezierPath объект, такой как lineWidth или lineJoinStyle свойства для перечеркиваемых путей или usesEvenOddFillRule свойство для заполненных путей. Эти атрибуты получения применяются ко всему пути.

  3. Установите начальную точку начального сегмента с помощью moveToPoint: метод.

  4. Добавьте линейные сегменты и сегменты кривой для определения подпути.

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

  6. Дополнительно, повторите шаги 3, 4, и 5 для определения дополнительных подпутей.

При создании пути необходимо расположить точки пути относительно точки источника (0, 0). Выполнение так упрощает перемещать путь позже. Во время получения точки Вашего пути применяются как есть к системе координат текущего графического контекста. Если Ваш путь ориентирован относительно источника, все, что необходимо сделать, чтобы изменить местоположение его, применяют аффинное преобразование с фактором перевода к текущему графическому контексту. Преимущество изменения графического контекста (в противоположность самому объекту контуров) состоит в том, что можно легко отменить трансформацию путем сохранения и восстановления состояния графики.

Для рисования объекта контуров Вы используете stroke и fill методы. Эти методы представляют линейные сегменты и сегменты кривой Вашего пути в текущем графическом контексте. Процесс рендеринга включает растеризацию линейных сегментов и сегментов кривой с помощью атрибутов объекта контуров. Процесс растеризации не изменяет сам объект контуров. В результате можно представить тот же объект контуров многократно в текущем контексте или в другом контексте.

Добавление строк и многоугольников к пути

Строки и многоугольники являются простыми формами, что Вы создаете детально использование moveToPoint: и addLineToPoint: методы. moveToPoint: метод устанавливает начальную точку формы, которую Вы хотите создать. От той точки Вы создаете строки формы с помощью addLineToPoint: метод. Вы создаете строки по очереди с каждой строкой, сформированной между предыдущей точкой и новой точкой, которую Вы указываете.

Перечисление 2-1 показывает, что код должен был создать пятигранную форму с помощью отдельных линейных сегментов. (Рисунок 2-1 показывает результат рисования этой формы с надлежащими настройками цвета обводки и цвета заливки, как описано в Рендеринге Содержания Объекта контуров Bézier.) Это кодовые наборы начальная точка формы и затем добавляет четыре связанных линейных сегмента. Пятый сегмент добавляется вызовом к closePath метод, соединяющий последнюю точку (0, 40) с первой точкой (100, 0).

Перечисление 2-1  , Создающее пятигранную форму

UIBezierPath *aPath = [UIBezierPath bezierPath];
 
// Set the starting point of the shape.
[aPath moveToPoint:CGPointMake(100.0, 0.0)];
 
// Draw the lines.
[aPath addLineToPoint:CGPointMake(200.0, 40.0)];
[aPath addLineToPoint:CGPointMake(160, 140)];
[aPath addLineToPoint:CGPointMake(40.0, 140)];
[aPath addLineToPoint:CGPointMake(0.0, 40.0)];
[aPath closePath];
  Форма рисунка 2-1, нарисованная с методами UIBezierPath класс

Используя closePath метод не только заканчивает подпуть, описывающий форму, это также чертит линию сегмент между первыми и последними точками. Это - удобный способ закончить многоугольник, не имея необходимость проводить заключительную линию.

Добавление дуг к пути

UIBezierPath класс предоставляет поддержку для инициализации нового объекта контуров с сегментом дуги. Параметры bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise: метод определяет круг, содержащий желаемую дугу и запуск и конечные точки самой дуги. Рисунок 2-2 показывает компоненты, входящие в создание дуги, включая круг, определяющий дугу, и угловые измерения раньше указывали его. В этом случае дуга создается в направлении по часовой стрелке. (Рисование дуги в направлении против часовой стрелки нарисовало бы штриховую часть круга вместо этого.) Код для создания этой дуги показан в Перечислении 2-2.

Рисунок 2-2  дуга в системе координат по умолчанию

Перечисление 2-2  , Создающее новый путь дуги

// pi is approximately equal to 3.14159265359.
#define   DEGREES_TO_RADIANS(degrees)  ((pi * degrees)/ 180)
 
- (UIBezierPath *)createArcPath
{
   UIBezierPath *aPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150)
                           radius:75
                           startAngle:0
                           endAngle:DEGREES_TO_RADIANS(135)
                           clockwise:YES];
   return aPath;
}

Если Вы хотите включить сегмент дуги в середину пути, необходимо изменить объект контуров CGPathRef тип данных непосредственно. Для получения дополнительной информации об изменении пути с помощью Базовых Графических функций, посмотрите Изменение Пути Используя Базовые Графические Функции.

Добавление кривых к пути

UIBezierPath класс предоставляет поддержку для добавления кубических и квадратичных кривых Bézier к пути. Сегменты кривой запускаются в текущей точке и конце в точке, которую Вы указываете. Форма кривой определяется с помощью строк касательной между запуском и конечными точками и одной или более контрольными точками. Рисунок 2-3 показывает приближения обоих типов кривой и отношения между контрольными точками и формой кривой. Точное искривление каждого сегмента включает сложное математическое отношение между всеми точками и хорошо документируется онлайн и в Википедии.

  Сегменты кривой рисунка 2-3 по пути

Для добавления кривых к пути Вы используете следующие методы:

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

Создание овальных и прямоугольных контуров

Овалы и прямоугольники являются общими типами путей, создающихся с помощью комбинации кривой и линейных сегментов. UIBezierPath класс включает bezierPathWithRect: и bezierPathWithOvalInRect: удобные методы для создания путей с овальными или прямоугольными формами. Оба из этих методов создают новый объект контуров и инициализируют его с указанной формой. Можно использовать возвращенный объект контуров сразу же или добавить больше форм к нему по мере необходимости.

Если Вы хотите добавить прямоугольник к существующему объекту контуров, необходимо сделать настолько использующий moveToPoint:, addLineToPoint:, и closePath методы, как Вы были бы для любого другого многоугольника. Используя closePath метод для заключительной стороны прямоугольника является удобным способом добавить заключительную строку пути и также отметить конец прямоугольного подпути.

Если Вы хотите добавить овал к существующему пути, самый простой способ сделать так состоит в том, чтобы использовать Базовую Графику. Несмотря на то, что можно использовать addQuadCurveToPoint:controlPoint: приблизить овальную поверхность, CGPathAddEllipseInRect функция намного более проста использовать и более точный. Для получения дополнительной информации посмотрите Изменение Пути Используя Базовые Графические Функции.

Изменение пути Используя базовые графические функции

UIBezierPath класс является действительно просто оберткой для a CGPathRef тип данных и атрибуты получения связались с тем путем. Несмотря на то, что Вы обычно добавляете линейные сегменты и сегменты кривой с помощью методов UIBezierPath класс, класс также представляет a CGPath свойство то, что можно использовать для изменения базового типа данных пути непосредственно. Когда Вы предпочли бы создавать свой путь с помощью функций Базовой Графической платформы, можно использовать это свойство.

Существует два способа изменить путь, связанный с a UIBezierPath объект. Можно изменить путь полностью с помощью Базовых Графических функций, или можно использовать смесь Базовых Графических функций и UIBezierPath методы. Изменение пути полностью с помощью Базовых Графических вызовов проще до некоторой степени. Вы создаете непостоянное CGPathRef тип данных и вызывает любые функции, необходимо изменить его информацию о пути. Когда Вы сделаны, Вы присваиваете свой объект контуров соответствию UIBezierPath объект, как показано в Перечислении 2-3.

Перечисление 2-3  , Присваивающее новое CGPathRef к a UIBezierPath объект

// Create the path data.
CGMutablePathRef cgPath = CGPathCreateMutable();
CGPathAddEllipseInRect(cgPath, NULL, CGRectMake(0, 0, 300, 300));
CGPathAddEllipseInRect(cgPath, NULL, CGRectMake(50, 50, 200, 200));
 
// Now create the UIBezierPath object.
UIBezierPath *aPath = [UIBezierPath bezierPath];
aPath.CGPath = cgPath;
aPath.usesEvenOddFillRule = YES;
 
// After assigning it to the UIBezierPath object, you can release
// your CGPathRef data type safely.
CGPathRelease(cgPath);

Если Вы принимаете решение использовать смесь Базовых функций Графики и UIBezierPath методы, необходимо тщательно переместить информацию о пути назад и вперед между двумя. Поскольку a UIBezierPath объекту принадлежит его базовое CGPathRef тип данных, Вы не можете просто получить тот тип и изменить его непосредственно. Вместо этого необходимо сделать непостоянную копию, изменить копию, и затем присвоить копию назад CGPath свойство как показано в Перечислении 2-4.

Перечисление 2-4  , смешивающее базовую графику и UIBezierPath вызовы

UIBezierPath *aPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 300, 300)];
 
// Get the CGPathRef and create a mutable version.
CGPathRef cgPath = aPath.CGPath;
CGMutablePathRef  mutablePath = CGPathCreateMutableCopy(cgPath);
 
// Modify the path and assign it back to the UIBezierPath object.
CGPathAddEllipseInRect(mutablePath, NULL, CGRectMake(50, 50, 200, 200));
aPath.CGPath = mutablePath;
 
// Release both the mutable copy of the path.
CGPathRelease(mutablePath);

Рендеринг содержания объекта контуров Bézier

После создания a UIBezierPath объект, можно представить его в текущем графическом контексте с помощью stroke и fill методы. Перед вызовом этих методов, тем не менее, обычно существует несколько других задач выполнить, чтобы гарантировать, что путь нарисован правильно:

Перечисление 2-5 показывает демонстрационную реализацию a drawRect: метод, рисующий овал в пользовательском представлении. Верхний левый угол ограничительного прямоугольника овала расположен в точке (50, 50) в системе координат представления. Поскольку операции заполнения красят прямо до границы пути, этот метод заполняет путь прежде, чем перечеркнуть ее. Это препятствует тому, чтобы цвет заливки затенил половину перечеркиваемой строки.

Перечисление 2-5  , Получающее путь в представлении

- (void)drawRect:(CGRect)rect
{
    // Create an oval shape to draw.
    UIBezierPath *aPath = [UIBezierPath bezierPathWithOvalInRect:
                                CGRectMake(0, 0, 200, 100)];
 
    // Set the render colors.
    [[UIColor blackColor] setStroke];
    [[UIColor redColor] setFill];
 
    CGContextRef aRef = UIGraphicsGetCurrentContext();
 
    // If you have content to draw after the shape,
    // save the current state before changing the transform.
    //CGContextSaveGState(aRef);
 
    // Adjust the view's origin temporarily. The oval is
    // now drawn relative to the new origin point.
    CGContextTranslateCTM(aRef, 50, 50);
 
    // Adjust the drawing options as needed.
    aPath.lineWidth = 5;
 
    // Fill the path before stroking it so that the fill
    // color does not obscure the stroked line.
    [aPath fill];
    [aPath stroke];
 
    // Restore the graphics state before drawing any other content.
    //CGContextRestoreGState(aRef);
}

Выполнение обнаружения хита на пути

Чтобы определить, имело ли сенсорное событие место на заполненной части пути, можно использовать containsPoint: метод UIBezierPath. Этот метод тестирует указанную точку против всех закрытых подпутей в объекте контуров и возвратах YES если это находится на или в каком-либо из тех подпутей.

Если Вы хотите сделать тестирование хита на перечеркиваемой части пути (вместо закрашенной области), необходимо использовать Базовую Графику. CGContextPathContainsPoint функция позволяет Вам контрольные точки или на заливке или на штриховой части пути, в настоящее время присваиваемого графическому контексту. Перечисление 2-6 показывает метод, тестирующий, чтобы видеть, пересекает ли указанная точка указанный путь. Параметр заполнения позволяет вызывающей стороне указать, должна ли точка быть протестирована против заполненной или перечеркиваемой части пути. Путь, переданный в вызывающей стороной, должен содержать один или несколько закрытые подпути для обнаружения хита для следования.

Перечисление 2-6  , Тестирующее точки против объекта контуров

- (BOOL)containsPoint:(CGPoint)point onPath:(UIBezierPath *)path inFillArea:(BOOL)inFill
{
   CGContextRef context = UIGraphicsGetCurrentContext();
   CGPathRef cgPath = path.CGPath;
   BOOL    isHit = NO;
 
   // Determine the drawing mode to use. Default to
   // detecting hits on the stroked portion of the path.
   CGPathDrawingMode mode = kCGPathStroke;
   if (inFill)
   {
      // Look for hits in the fill area of the path instead.
      if (path.usesEvenOddFillRule)
         mode = kCGPathEOFill;
      else
         mode = kCGPathFill;
   }
 
   // Save the graphics state so that the path can be
   // removed later.
   CGContextSaveGState(context);
   CGContextAddPath(context, cgPath);
 
   // Do the hit detection.
   isHit = CGContextPathContainsPoint(context, point, mode);
 
   CGContextRestoreGState(context);
 
   return isHit;
}