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 — 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.
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:).
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.
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:) 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.
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.
| Metoda | Typ | Punktów kontrolnych | Stopień |
|---|---|---|---|
| addQuadCurve | Kwadratowa Béziera | 1 | 2 |
| addCurve | Sześcienna Béziera | 2 | 3 |
| addArc | Łuk | — | — |
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.
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.
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() 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.
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.
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()
}
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.
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
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.
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.
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.
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.
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
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.
Przeczytaj również