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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

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