UIBezierPath: co to jest, metody rysowania i jak działa

Autor: IT Sectr Opublikowano: 2026-07-21 Czas czytania: 8 min

UIBezierPath — klasa z UIKit, która enkapsuluje wektorową ścieżkę dla grafiki 2D w iOS. Łączy geometryczny opis konturu z możliwościami rysowania, umożliwiając tworzenie linii, krzywych Béziera, łuków i prostokątów. W przeciwieństwie do CGPath, UIBezierPath zawiera własne metody rysowania bezpośrednio w bieżącym kontekście graficznym. Według Apple Developer Documentation, 2026, UIBezierPath obsługuje wszystkie standardowe operacje Quartz 2D w obiektowym opakowaniu.

Najważniejsze

  • UIBezierPath — obiektowe opakowanie CGPath do pracy z konturami wektorowymi w UIKit.
  • move(to:) i addLine(to:) definiują proste odcinki ścieżki.
  • addQuadCurve(to:controlPoint:) i addCurve(to:controlPoint1:controlPoint2:) tworzą krzywe Béziera.
  • addArc(withCenter:radius:startAngle:endAngle:clockwise:) tworzy łuki i wycinki.
  • fill i stroke — bezpośrednie rysowanie ścieżki w bieżącym kontekście bez użycia dodatkowych obiektów.

Co to jest UIBezierPath?

UIBezierPath — klasa frameworku UIKit, dziedzicząca NSObject i implementująca NSCopying, NSSecureCoding. Zapewnia wysokopoziomowe Objective-C/Swift API do budowania i rysowania konturów wektorowych. Wewnętrznie UIBezierPath przechowuje referencję do CGPathRef — obiektu Core Graphics, ale dodaje wygodne metody rysowania, zmiany geometrii i zarządzania stylami.

Główna zaleta UIBezierPath w porównaniu z bezpośrednim użyciem CGPath to wbudowane metody rysowania. Wywołanie fill() lub stroke() na instancji UIBezierPath automatycznie konfiguruje kontekst i rysuje ścieżkę. Klasa obsługuje również właściwości lineWidth, lineCapStyle, lineJoinStyle, miterLimit i flatness, co pozwala zarządzać wyglądem bez osobnego obiektu do przechowywania stylów.

UIBezierPath jest używany w większości aplikacji iOS do niestandardowego rysowania wewnątrz UIView: od prostych ikon po złożone animowane wykresy. Metoda draw(_:) klasy UIView to główne miejsce, w którym programiści tworzą i rysują UIBezierPath. Klasa jest w pełni zgodna z Core Animation i może być używana do animacji ścieżki przez CAShapeLayer i CABasicAnimation.

Proste linie i prostokąty

Metoda move(to:) przesuwa punkt początkowy ścieżki do podanych współrzędnych bez rysowania. To obowiązkowe wywołanie przed zbudowaniem dowolnego segmentu. addLine(to:) dodaje prosty odcinek od bieżącej pozycji do wskazanego punktu. Do zamknięcia konturu służy metoda close(), która prowadzi linię od ostatniego punktu do pierwszego punktu ostatniego move(to:).

Prostokąty z zaokrągleniami

UIBezierPath udostępnia kilka wygodnych inicjalizatorów dla gotowych figur. init(rect:) tworzy prostokąt, init(roundedRect:cornerRadius:) — prostokąt z zaokrąglonymi rogami. Parametr UIRectCorner pozwala zaokrąglać tylko wybrane rogi, na przykład topLeft i bottomRight, pozostawiając pozostałe proste. Jest to szeroko stosowane do tworzenia niestandardowych przycisków i kart.

init(ovalIn:) tworzy elipsę wpisaną w zadany prostokąt. Jeśli prostokąt jest kwadratem, elipsa zamienia się w okrąg. init(roundedRect:byRoundingCorners:cornerRadii:) — najbardziej elastyczny wariant, pozwalający ustawić różne promienie dla każdego rogu. Ten inicjalizator pojawił się w iOS 6 i pozostaje standardem do tworzenia zaokrąglonych kontenerów.

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()

Krzywe Béziera: kwadratowe i sześcienne

addQuadCurve(to:controlPoint:) dodaje kwadratową krzywą Béziera z jednym punktem kontrolnym. Ten typ krzywej jest używany do prostych, płynnych zagięć, gdy wymagana jest minimalizacja liczby obliczeń. Krzywa kwadratowa jest zawsze parabolą i gwarantowanie leży wewnątrz trójkąta utworzonego przez punkt początkowy, kontrolny i końcowy.

Sześcienne krzywe Béziera

addCurve(to:controlPoint1:controlPoint2:) dodaje sześcienną krzywą Béziera z dwoma punktami kontrolnymi. Krzywe sześcienne są stosowane we wszystkich nowoczesnych edytorach wektorowych i czcionkach TrueType. Dwa punkty kontrolne dają więcej stopni swobody do precyzyjnego kształtowania krzywej. Ta metoda jest zalecana do budowania organicznych i złożonych konturów.

Przy budowaniu krzywych ważne jest przestrzeganie zasady ciągłości — gładkość krzywej w punkcie połączenia segmentów. Dla ciągłości C1 (styczna) punkt kontrolny następnej krzywej musi leżeć na tej samej prostej co punkt kontrolny poprzedniej. Dla ciągłości C2 (krzywizna) wymagane jest bardziej złożone dopasowanie, które jest automatycznie zapewniane w CAShapeLayer przy animacji między dwoma UIBezierPath z taką samą liczbą punktów kontrolnych.

MetodaTypPunktów kontrolnychStopień
addQuadCurveKwadratowa Béziera12
addCurveSześcienna Béziera23
addArcŁuk

Łuki i okręgi

addArc(withCenter:radius:startAngle:endAngle:clockwise:) dodaje łuk okręgu z podanymi parametrami. Kąty są podawane w radianach względem środka okręgu. Kąt zerowy odpowiada kierunkowi w prawo (godzina 3 na tarczy zegara). Parametr clockwise określa kierunek: true — zgodnie z ruchem wskazówek zegara, false — przeciwnie. Łuki są niezastąpione przy wykresach kołowych, prędkościomierzach i interaktywnych pierścieniach postępu.

Okręgi i wycinki

Dla pełnego okręgu wystarczy startAngle = 0 i endAngle = .pi * 2. Do utworzenia wycinka (jak część wykresu kołowego) użyj addArc z następującym addLine(to:) do środka i close(). UIBezierPath nie ma wbudowanej metody addCircle — okrąg tworzy się przez init(ovalIn:) z kwadratowym prostokątem lub przez addArc z pełnym kątem.

Metoda addArc(withCenter:radius:startAngle:endAngle:clockwise:) automatycznie wykonuje move(to:) do punktu początkowego łuku, jeśli bieżąca pozycja się z nim nie pokrywa. Jeśli bieżąca pozycja jest inna, między nią a początkiem łuku rysowana jest prosta linia. To zachowanie różni się od pracy z Core Graphics, gdzie wymagany jest jawny move(to:) przed każdym nowym konturem.

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()

Wypełnienie i obrys ścieżki

fill() wypełnia wewnętrzny obszar ścieżki bieżącym kolorem fillColor lub kolorem ustawionym w kontekście graficznym przez setFill(). Dla ścieżek z samoprzecięciami reguła wypełnienia jest określana przez właściwość usesEvenOddFillRule. Jeśli jest true, stosowana jest reguła even-odd: punkt jest uznawany za znajdujący się wewnątrz figury, jeśli promień z niego przecina kontur nieparzystą liczbę razy.

Konfiguracja obrysu

stroke() rysuje kontur ścieżki z bieżącymi ustawieniami lineWidth, lineCapStyle i lineJoinStyle. lineCapStyle określa kształt końców otwartych segmentów: .butt (proste cięcie), .round (półokrągły), .square (kwadratowy z występem). lineJoinStyle określa kształt narożników połączeń: .miter (ostry), .round (zaokrąglony), .bevel (ścięty).

Dla linii przerywanych używana jest właściwość setLineDash(_:count:phase:). Przyjmuje tablicę długości naprzemiennych kresek i przerw, liczbę elementów i początkową fazę. Wizualizacja przerywanej linii odbywa się na etapie rasteryzacji ścieżki — złożone wzory nie zwiększają złożoności geometrycznej konturu. CAShapeLayer obsługuje animację właściwości strokeEnd dla efektu „rysowania“ linii od początku do końca.

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()
}

Przykład tworzenia złożonej figury

Stwórzmy niestandardowy wskaźnik postępu w postaci pierścienia za pomocą UIBezierPath. Pierścień buduje się z dwóch łuków o tym samym promieniu, ale różnym kolorze obrysu. Pierwszy łuk to pełny okrąg w kolorze szarym (tło), drugi — segment pokazujący postęp. Do animacji używane są CAShapeLayer i 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
    }
}

W tym przykładzie UIBezierPath jest używany do utworzenia okrągłego konturu, który następnie jest przekazywany do CAShapeLayer przez właściwość cgPath. Animacja strokeEnd od 0 do 1 wizualizuje wypełnianie pierścienia. Takie podejście jest stosowane we wskaźnikach ładowania, trackerach fitness i widgetach postępu. Metoda layoutSubviews gwarantuje przebudowę ścieżki przy zmianie rozmiarów View.

lineCap = .round dodaje zaokrąglone końce łuku, co nadaje wskaźnikowi bardziej estetyczny wygląd. Właściwość strokeEnd to animowalna właściwość CAShapeLayer, która nie wymaga przerysowywania całego konturu w każdej klatce. Animacja jest wykonywana w całości na GPU przez Core Animation, co zapewnia 60 FPS bez obciążania CPU.

Często zadawane pytania

Czym różni się UIBezierPath od CGPath?

UIBezierPath to obiektowe opakowanie CGPath zawierające wbudowane metody fill() i stroke(). CGPath to niemodyfikowalny obiekt C Core Graphics wymagający jawnego rysowania przez CGContext. UIBezierPath jest wygodniejszy dla UIKit, CGPath wydajniejszy przy wielokrotnym użyciu.

Jak zaokrąglić tylko górne rogi prostokąta?

Użyj init(roundedRect:byRoundingCorners:cornerRadii:) z parametrem .topLeft i .topRight dla roundedRect. To jedyna metoda UIBezierPath pozwalająca selektywnie zaokrąglać rogi bez tworzenia złożonej ścieżki złożonej.

Jak ponownie użyć UIBezierPath z różnymi stylami?

Właściwość cgPath zwraca niezmienny CGPathRef, który można przekazywać do CAShapeLayer lub używać z różnymi kolorami i grubościami. Modyfikacje UIBezierPath (przesuwanie punktów) nie wpływają na wcześniej uzyskany cgPath do czasu jawnej aktualizacji.

Dlaczego fill() nie wypełnia całej ścieżki?

Sprawdź usesEvenOddFillRule — jeśli kontur ma samoprzecięcia, reguła even-odd może pozostawić obszary niewypełnione. Ustaw usesEvenOddFillRule = false dla standardowej reguły wypełniania non-zero winding.

Jak animować kształt UIBezierPath?

Użyj CABasicAnimation ze ścieżką kluczową „path” na CAShapeLayer. Oba stany ścieżki muszą zawierać taką samą liczbę segmentów i punktów kontrolnych — w przeciwnym razie animacja będzie szarpana lub nie zadziała.

Podsumowanie

  • UIBezierPath — obiektowa klasa UIKit do pracy z konturami wektorowymi i grafiką 2D.
  • move(to:) i addLine(to:) — podstawowe metody budowania prostych linii i wielokątów.
  • addQuadCurve i addCurve tworzą kwadratowe i sześcienne krzywe Béziera z jednym i dwoma punktami kontrolnymi.
  • addArc rysuje łuki okręgów z elastycznym zarządzaniem kątami i kierunkiem.
  • fill() i stroke() wykonują rysowanie bezpośrednio w bieżącym kontekście graficznym.
  • Inicjalizatory init(roundedRect:) i init(ovalIn:) przyspieszają tworzenie standardowych figur.
  • Właściwość cgPath umożliwia integrację UIBezierPath z Core Animation i CAShapeLayer do animacji.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również