UIBezierPath: что это, методы рисования и как работает

Автор: IT Sectr Опубликовано: 2026-07-21 Время чтения: 8 мин

UIBezierPath — класс из UIKit, инкапсулирующий векторный путь для 2D-графики в iOS. Он объединяет геометрическое описание контура с возможностями отрисовки, позволяя создавать линии, кривые Безье, дуги и прямоугольники. В отличие от CGPath, UIBezierPath содержит собственные методы рисования непосредственно в текущем графическом контексте. По данным Apple Developer Documentation, 2026, UIBezierPath поддерживает все стандартные операции Quartz 2D в объектно-ориентированной обёртке.

Главное

  • UIBezierPath — объектно-ориентированная обёртка CGPath для работы с векторными контурами в UIKit.
  • move(to:) и addLine(to:) задают прямые отрезки пути.
  • addQuadCurve(to:controlPoint:) и addCurve(to:controlPoint1:controlPoint2:) строят кривые Безье.
  • addArc(withCenter:radius:startAngle:endAngle:clockwise:) создаёт дуги и сектора.
  • fill и stroke — непосредственная отрисовка пути в текущем контексте без использования дополнительных объектов.

Что такое UIBezierPath?

UIBezierPath — класс фреймворка UIKit, наследующий NSObject и реализующий NSCopying, NSSecureCoding. Он предоставляет высокоуровневый Objective-C/Swift API для построения и отрисовки векторных контуров. Внутренне UIBezierPath хранит ссылку на CGPathRef — объект Core Graphics, но добавляет удобные методы для рисования, изменения геометрии и управления стилями.

Основное преимущество UIBezierPath перед прямым использованием CGPath — встроенные методы отрисовки. Вызов fill() или stroke() на экземпляре UIBezierPath автоматически настраивает контекст и рисует путь. Также класс поддерживает свойства lineWidth, lineCapStyle, lineJoinStyle, miterLimit и flatness, что позволяет управлять внешним видом без отдельного объекта для хранения стилей.

UIBezierPath используется в большинстве iOS-приложений для кастомной отрисовки внутри UIView: от простых иконок до сложных анимированных графиков. Метод draw(_:) класса UIView — основное место, где разработчики создают и рисуют UIBezierPath. Класс полностью совместим с Core Animation и может использоваться для анимации пути через CAShapeLayer и CABasicAnimation.

Прямые линии и прямоугольники

Метод move(to:) перемещает точку начала пути в заданные координаты без рисования. Это обязательный вызов перед построением любого сегмента. addLine(to:) добавляет прямой отрезок от текущей позиции до указанной точки. Для замыкания контура используется метод close(), который проводит линию от последней точки к первой точке последнего move(to:).

Прямоугольники со скруглениями

UIBezierPath предоставляет несколько удобных инициализаторов для готовых фигур. init(rect:) создаёт прямоугольник, init(roundedRect:cornerRadius:) — прямоугольник со скруглёнными углами. Параметр UIRectCorner позволяет скруглять только выбранные углы, например topLeft и bottomRight, оставляя остальные прямыми. Это широко применяется для создания кастомных кнопок и карточек.

init(ovalIn:) создаёт эллипс, вписанный в заданный прямоугольник. Если прямоугольник является квадратом, эллипс превращается в окружность. init(roundedRect:byRoundingCorners:cornerRadii:) — наиболее гибкий вариант, позволяющий задать разные радиусы для каждого угла. Этот инициализатор появился в iOS 6 и остаётся стандартом для создания закруглённых контейнеров.

swift
let path = UIBezierPath()
path.move(to: CGPoint(x: 50, y: 50))
path.addLine(to: CGPoint(x: 200, y: 50))
path.addLine(to: CGPoint(x: 200, y: 150))
path.addLine(to: CGPoint(x: 125, y: 200))
path.addLine(to: CGPoint(x: 50, y: 150))
path.close()

Кривые Безье: квадратичные и кубические

addQuadCurve(to:controlPoint:) добавляет квадратичную кривую Безье с одной контрольной точкой. Этот тип кривой используется для простых плавных изгибов, когда требуется минимизировать количество вычислений. Квадратичная кривая всегда является параболой и гарантированно лежит внутри треугольника, образованного начальной, контрольной и конечной точками.

Кубические кривые Безье

addCurve(to:controlPoint1:controlPoint2:) добавляет кубическую кривую Безье с двумя контрольными точками. Кубические кривые применяются во всех современных векторных редакторах и шрифтах TrueType. Две контрольные точки дают больше степеней свободы для точного управления формой кривой. Этот метод рекомендуется для построения органических и сложных контуров.

При построении кривых важно соблюдать принцип непрерывности — гладкость кривой в точке соединения сегментов. Для непрерывности C1 (касательная) контрольная точка следующей кривой должна лежать на одной прямой с контрольной точкой предыдущей. Для непрерывности C2 (кривизна) требуется более сложное согласование, которое автоматически обеспечивается в CAShapeLayer при анимации между двумя UIBezierPath с одинаковым количеством контрольных точек.

МетодТипКонтрольных точекСтепень
addQuadCurveКвадратичная Безье12
addCurveКубическая Безье23
addArcДуга

Дуги и окружности

addArc(withCenter:radius:startAngle:endAngle:clockwise:) добавляет дугу окружности с заданными параметрами. Углы указываются в радианах относительно центра окружности. Нулевой угол соответствует направлению вправо (3 часа на циферблате). Параметр clockwise определяет направление обхода: true — по часовой стрелке, false — против. Дуги незаменимы для круговых диаграмм, спидометров и интерактивных колец прогресса.

Окружности и сектора

Для полной окружности достаточно startAngle = 0 и endAngle = .pi * 2. Для создания сектора (как часть круговой диаграммы) используйте addArc с последующим addLine(to:) к центру и close(). UIBezierPath не имеет встроенного метода addCircle — окружность создаётся через init(ovalIn:) с квадратным прямоугольником или через addArc с полным углом.

Метод addArc(withCenter:radius:startAngle:endAngle:clockwise:) автоматически выполняет move(to:) к начальной точке дуги, если текущая позиция не совпадает с ней. Если текущая позиция отличается, между ней и началом дуги проводится прямая линия. Это поведение отличается от работы с Core Graphics, где требуется явный move(to:) перед каждым новым контуром.

swift
let center = CGPoint(x: 150, y: 150)
let slice = UIBezierPath()
slice.move(to: center)
slice.addArc(
    withCenter: center,
    radius: 100,
    startAngle: 0,
    endAngle: .pi * 0.75,
    clockwise: true
)
slice.close()

Заливка и обводка пути

fill() заливает внутреннюю область пути текущим цветом fillColor или цветом, установленным в графическом контексте через setFill(). Для пути с самопересечениями правило заливки определяется свойством usesEvenOddFillRule. Если оно равно true, используется even-odd правило: точка считается внутри фигуры, если луч из неё пересекает контур нечётное число раз.

Настройка обводки

stroke() рисует контур пути с текущими настройками lineWidth, lineCapStyle и lineJoinStyle. lineCapStyle определяет форму концов открытых сегментов: .butt (прямой срез), .round (полукруглый), .square (квадратный с выступом). lineJoinStyle задаёт форму углов соединений: .miter (острый), .round (скруглённый), .bevel (срезанный).

Для штриховых линий используется свойство setLineDash(_:count:phase:). Оно принимает массив длин чередующихся штрихов и пропусков, количество элементов и начальную фазу. Визуализация пунктира выполняется на этапе растеризации пути — сложные паттерны не увеличивают геометрическую сложность контура. CAShapeLayer поддерживает анимацию свойства strokeEnd для эффекта «рисования» линии от начала до конца.

swift
override func draw(_ rect: CGRect) {
    let path = UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 200, height: 200))
    UIColor.blue.setFill()
    UIColor.black.setStroke()
    path.lineWidth = 4
    path.fill()
    path.stroke()
}

Пример создания сложной фигуры

Создадим кастомный индикатор прогресса в виде кольца с помощью UIBezierPath. Кольцо строится из двух дуг одинакового радиуса с разным цветом обводки. Первая дуга — полная окружность серого цвета (фон), вторая — сегмент, отображающий прогресс. Для анимации используется CAShapeLayer и CABasicAnimation.

swift
class RingView: UIView {

    private let progressLayer = CAShapeLayer()

    override func layoutSubviews() {
        super.layoutSubviews()
        let center = CGPoint(x: bounds.midX, y: bounds.midY)
        let radius = bounds.width * 0.4
        let lineWidth: CGFloat = 12

        let backgroundRing = UIBezierPath()
        backgroundRing.addArc(withCenter: center, radius: radius,
                          startAngle: -.pi / 2, endAngle: .pi * 1.5, clockwise: true)

        progressLayer.path = backgroundRing.cgPath
        progressLayer.strokeColor = UIColor.systemBlue.cgColor
        progressLayer.lineWidth = lineWidth
        progressLayer.lineCap = .round
        progressLayer.fillColor = UIColor.clear.cgColor
        progressLayer.strokeEnd = 0
        layer.addSublayer(progressLayer)
    }

    func animateProgress(to value: CGFloat) {
        CABasicAnimation(keyPath: "strokeEnd").apply {
            $0.toValue = value
            $0.duration = 1.5
            $0.timingFunction = CAMediaTimingFunction(name: .easeOut)
            progressLayer.add($0, forKey: nil)
        }
        progressLayer.strokeEnd = value
    }
}

В этом примере UIBezierPath используется для создания кругового контура, который затем передаётся в CAShapeLayer через свойство cgPath. Анимация strokeEnd от 0 до 1 визуализирует заполнение кольца. Такой подход применяется в индикаторах загрузки, фитнес-трекерах и виджетах прогресса. Метод layoutSubviews гарантирует перестроение пути при изменении размеров View.

lineCap = .round добавляет скруглённые концы дуги, что придаёт индикатору более аккуратный вид. Свойство strokeEnd — это анимируемое свойство CAShapeLayer, которое не требует перерисовки всего контура в каждом кадре. Анимация выполняется полностью на GPU через Core Animation, что обеспечивает 60 FPS без нагрузки на CPU.

Часто задаваемые вопросы

Чем UIBezierPath отличается от CGPath?

UIBezierPath — объектно-ориентированная обёртка над CGPath, содержащая встроенные методы fill() и stroke(). CGPath — это немодифицируемый C-объект Core Graphics, требующий явного рисования через CGContext. UIBezierPath удобнее для UIKit, CGPath производительнее для повторного использования.

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

Используйте init(roundedRect:byRoundingCorners:cornerRadii:) с параметром .topLeft и .topRight для roundedRect. Это единственный метод UIBezierPath, позволяющий выборочно скруглять углы без создания сложного составного пути.

Как переиспользовать UIBezierPath с разными стилями?

Свойство cgPath возвращает неизменяемый CGPathRef, который можно передавать в CAShapeLayer или использовать с разными цветами и толщинами. Модификации UIBezierPath (движение точек) не влияют на ранее полученный cgPath до явного обновления.

Почему fill() не закрашивает весь путь?

Проверьте usesEvenOddFillRule — если контур имеет самопересечения, even-odd правило может оставить области незаполненными. Установите usesEvenOddFillRule = false для стандартного non-zero winding правила заливки.

Как анимировать форму UIBezierPath?

Используйте CABasicAnimation с ключевым путём «path» на CAShapeLayer. Оба состояния пути должны содержать одинаковое количество сегментов и контрольных точек — в противном случае анимация будет дёрганой или не сработает.

Итоги

  • UIBezierPath — объектно-ориентированный класс UIKit для работы с векторными контурами и 2D-графикой.
  • move(to:) и addLine(to:) — базовые методы построения прямых линий и многоугольников.
  • addQuadCurve и addCurve создают квадратичные и кубические кривые Безье с одной и двумя контрольными точками.
  • addArc рисует дуги окружности с гибким управлением углами и направлением обхода.
  • fill() и stroke() выполняют отрисовку прямо в текущем графическом контексте.
  • Инициализаторы init(roundedRect:) и init(ovalIn:) ускоряют создание стандартных фигур.
  • Свойство cgPath позволяет интегрировать UIBezierPath с Core Animation и CAShapeLayer для анимации.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также