UIBezierPath là một lớp UIKit đóng gói đường dẫn vector cho đồ họa 2D trong iOS. Nó kết hợp mô tả hình học của đường viền với khả năng vẽ, cho phép tạo các đường thẳng, đường cong Bezier, cung tròn và hình chữ nhật. Không giống như CGPath, UIBezierPath chứa các phương thức vẽ riêng hoạt động trực tiếp trong ngữ cảnh đồ họa hiện tại. Theo Apple Developer Documentation, 2026, UIBezierPath hỗ trợ tất cả các thao tác Quartz 2D tiêu chuẩn trong một trình bao bọc hướng đối tượng.
Các điểm chính
UIBezierPath là một lớp của framework UIKit, kế thừa từ NSObject và triển khai NSCopying, NSSecureCoding. Nó cung cấp API Objective-C/Swift cấp cao để xây dựng và vẽ các đường dẫn vector. Bên trong, UIBezierPath lưu trữ một tham chiếu đến CGPathRef một đối tượng Core Graphics nhưng thêm các phương thức tiện lợi để vẽ, sửa đổi hình học và quản lý kiểu.
Lợi thế chính của UIBezierPath so với việc sử dụng trực tiếp CGPath là các phương thức vẽ tích hợp. Gọi fill() hoặc stroke() trên một thể hiện UIBezierPath tự động cấu hình ngữ cảnh và vẽ đường dẫn. Lớp này cũng hỗ trợ các thuộc tính lineWidth, lineCapStyle, lineJoinStyle, miterLimit và flatness, cho phép kiểm soát giao diện mà không cần đối tượng lưu trữ kiểu riêng.
UIBezierPath được sử dụng trong hầu hết các ứng dụng iOS để vẽ tùy chỉnh bên trong UIView: từ biểu tượng đơn giản đến biểu đồ hoạt hình phức tạp. Phương thức draw(_:) của lớp UIView là nơi chính mà các nhà phát triển tạo và vẽ UIBezierPath. Lớp này hoàn toàn tương thích với Core Animation và có thể được sử dụng để tạo hoạt ảnh đường dẫn thông qua CAShapeLayer và CABasicAnimation.
Phương thức move(to:) di chuyển điểm bắt đầu của đường dẫn đến tọa độ được chỉ định mà không vẽ. Đây là một lệnh gọi bắt buộc trước khi xây dựng bất kỳ đoạn nào. addLine(to:) thêm một đoạn thẳng từ vị trí hiện tại đến điểm được chỉ định. Phương thức close() đóng đường viền bằng cách vẽ một đường từ điểm cuối đến điểm đầu tiên của move(to:) cuối cùng.
UIBezierPath cung cấp một số bộ khởi tạo tiện lợi cho các hình dạng có sẵn. init(rect:) tạo hình chữ nhật, init(roundedRect:cornerRadius:) tạo hình chữ nhật với các góc bo tròn. Tham số UIRectCorner cho phép chỉ bo tròn các góc đã chọn, như topLeft và bottomRight, giữ các góc còn lại thẳng. Điều này được sử dụng rộng rãi để tạo nút và thẻ tùy chỉnh.
init(ovalIn:) tạo một hình elip nội tiếp trong hình chữ nhật được chỉ định. Nếu hình chữ nhật là hình vuông, hình elip trở thành hình tròn. init(roundedRect:byRoundingCorners:cornerRadii:) là tùy chọn linh hoạt nhất, cho phép đặt bán kính khác nhau cho mỗi góc. Bộ khởi tạo này xuất hiện trong iOS 6 và vẫn là tiêu chuẩn để tạo các vùng chứa bo tròn.
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:) thêm một đường cong Bezier bậc hai với một điểm điều khiển. Loại đường cong này được sử dụng cho các uốn cong mượt mà đơn giản khi cần giảm thiểu tính toán. Đường cong bậc hai luôn là một parabol và được đảm bảo nằm trong tam giác được tạo bởi các điểm bắt đầu, điều khiển và kết thúc.
addCurve(to:controlPoint1:controlPoint2:) thêm một đường cong Bezier bậc ba với hai điểm điều khiển. Đường cong bậc ba được sử dụng trong tất cả các trình chỉnh sửa vector hiện đại và phông chữ TrueType. Hai điểm điều khiển cung cấp nhiều bậc tự do hơn để kiểm soát chính xác hình dạng. Phương thức này được khuyến nghị để xây dựng các đường viền hữu cơ và phức tạp.
Khi xây dựng các đường cong, điều quan trọng là duy trì nguyên tắc liên tục độ mượt tại điểm kết nối của các đoạn. Đối với tính liên tục C1 (tiếp tuyến), điểm điều khiển của đường cong tiếp theo phải nằm trên cùng một đường thẳng với điểm điều khiển của đường cong trước đó. Đối với tính liên tục C2 (độ cong), cần có sự phối hợp phức tạp hơn, được tự động cung cấp trong CAShapeLayer khi tạo hoạt ảnh giữa hai UIBezierPath có cùng số điểm điều khiển.
| Phương thức | Loại | Điểm điều khiển | Bậc |
|---|---|---|---|
| addQuadCurve | Bezier bậc hai | 1 | 2 |
| addCurve | Bezier bậc ba | 2 | 3 |
| addArc | Cung tròn | — | — |
addArc(withCenter:radius:startAngle:endAngle:clockwise:) thêm một cung tròn với các tham số được chỉ định. Các góc được chỉ định bằng radian so với tâm của hình tròn. Góc không tương ứng với hướng sang phải (3 giờ trên mặt đồng hồ). Tham số clockwise xác định hướng: true theo chiều kim đồng hồ, false ngược chiều kim đồng hồ. Cung tròn rất cần thiết cho biểu đồ hình tròn, đồng hồ tốc độ và vòng tiến trình tương tác.
Để có một hình tròn hoàn chỉnh, sử dụng startAngle = 0 và endAngle = .pi * 2. Để tạo một khu vực (như một phần của biểu đồ hình tròn), sử dụng addArc theo sau là addLine(to:) đến tâm và close(). UIBezierPath không có phương thức addCircle tích hợp một hình tròn được tạo thông qua init(ovalIn:) với một hình chữ nhật vuông hoặc thông qua addArc với góc đầy đủ.
Phương thức addArc(withCenter:radius:startAngle:endAngle:clockwise:) tự động thực hiện move(to:) đến điểm bắt đầu của cung nếu vị trí hiện tại không khớp. Nếu vị trí hiện tại khác, một đường thẳng được vẽ giữa nó và điểm bắt đầu của cung. Hành vi này khác với Core Graphics, nơi cần một move(to:) rõ ràng trước mỗi đường viền mới.
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() tô màu vùng bên trong của đường dẫn với fillColor hiện tại hoặc màu được đặt trong ngữ cảnh đồ họa thông qua setFill(). Đối với các đường dẫn tự cắt nhau, quy tắc tô màu được xác định bởi thuộc tính usesEvenOddFillRule. Nếu là true, quy tắc chẵn-lẻ được sử dụng: một điểm được coi là bên trong hình dạng nếu một tia từ nó cắt đường viền một số lần lẻ.
stroke() vẽ đường viền của đường dẫn với các cài đặt lineWidth, lineCapStyle và lineJoinStyle hiện tại. lineCapStyle xác định hình dạng của các đầu đoạn mở: .butt (cắt thẳng), .round (nửa hình tròn), .square (hình vuông có phần nhô ra). lineJoinStyle xác định hình dạng của các góc nối: .miter (nhọn), .round (bo tròn), .bevel (vát).
Đối với các đường nét đứt, sử dụng thuộc tính setLineDash(_:count:phase:). Nó chấp nhận một mảng các độ dài nét đứt và khoảng trống xen kẽ, số lượng phần tử và pha ban đầu. Việc hiển thị nét đứt được thực hiện trong quá trình raster hóa đường dẫn các mẫu phức tạp không làm tăng độ phức tạp hình học của đường viền. CAShapeLayer hỗ trợ hoạt ảnh của thuộc tính strokeEnd cho hiệu ứng “vẽ” từ đầu đến cuối.
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()
}
Hãy tạo một chỉ báo tiến trình tùy chỉnh dưới dạng vòng tròn sử dụng UIBezierPath. Vòng tròn được xây dựng từ hai cung có cùng bán kính với màu viền khác nhau. Cung đầu tiên là một hình tròn đầy đủ màu xám (nền), cung thứ hai là một đoạn hiển thị tiến trình. Để tạo hoạt ảnh, CAShapeLayer và CABasicAnimation được sử dụng.
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
}
}
Trong ví dụ này, UIBezierPath được sử dụng để tạo một đường viền hình tròn, sau đó được chuyển đến CAShapeLayer thông qua thuộc tính cgPath. Hoạt ảnh strokeEnd từ 0 đến 1 hiển thị sự lấp đầy của vòng tròn. Cách tiếp cận này được sử dụng trong các chỉ báo tải, trình theo dõi thể dục và widget tiến trình. Phương thức layoutSubviews đảm bảo xây dựng lại đường dẫn khi kích thước View thay đổi.
lineCap = .round thêm các đầu tròn vào cung, mang lại cho chỉ báo một giao diện gọn gàng hơn. Thuộc tính strokeEnd là một thuộc tính có thể tạo hoạt ảnh của CAShapeLayer không yêu cầu vẽ lại toàn bộ đường viền mỗi khung hình. Hoạt ảnh chạy hoàn toàn trên GPU thông qua Core Animation, đảm bảo 60 FPS mà không tải CPU.
Câu hỏi thường gặp
UIBezierPath là một trình bao bọc hướng đối tượng xung quanh CGPath chứa các phương thức fill() và stroke() tích hợp. CGPath là một đối tượng C bất biến của Core Graphics yêu cầu vẽ rõ ràng thông qua CGContext. UIBezierPath thuận tiện hơn cho UIKit, trong khi CGPath hiệu quả hơn cho việc tái sử dụng.
Sử dụng init(roundedRect:byRoundingCorners:cornerRadii:) với các tham số .topLeft và .topRight cho roundedRect. Đây là phương thức duy nhất của UIBezierPath cho phép bo tròn góc chọn lọc mà không cần tạo đường dẫn tổng hợp phức tạp.
Thuộc tính cgPath trả về một CGPathRef bất biến có thể được chuyển đến CAShapeLayer hoặc sử dụng với các màu sắc và độ dày khác nhau. Các sửa đổi đối với UIBezierPath (di chuyển điểm) không ảnh hưởng đến cgPath đã thu được trước đó cho đến khi được cập nhật rõ ràng.
Kiểm tra usesEvenOddFillRule nếu đường viền có tự cắt nhau, quy tắc chẵn-lẻ có thể để lại các vùng chưa được tô. Đặt usesEvenOddFillRule = false cho quy tắc tô non-zero winding tiêu chuẩn.
Sử dụng CABasicAnimation với đường dẫn khóa “path” trên CAShapeLayer. Cả hai trạng thái đường dẫn phải chứa cùng số lượng đoạn và điểm điều khiển nếu không hoạt ảnh sẽ bị giật hoặc thất bại.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm