設計和繪製貝塞爾曲線
此示例顯示了設計要在檢視上繪製它的形狀的過程。使用了特定的 shap,但你學到的概念可以應用於任何形狀。
如何在自定義檢視中繪製 Bézier 路徑
這些是主要步驟:
- 設計你想要的形狀輪廓。
- 將輪廓路徑分為線段,圓弧和曲線。
- 以程式設計方式構建該路徑。
- 在
drawRect
中繪製路徑或使用CAShapeLayer
繪製路徑。
設計形狀輪廓
你可以做任何事情,但作為一個例子,我選擇了下面的形狀。它可以是鍵盤上的彈出鍵。
將路徑劃分為多個部分
回顧一下你的形狀設計並將其分解成更簡單的線條元素(用於直線),弧形(用於圓形和圓角)和曲線(用於其他任何東西)。
這是我們的示例設計的樣子:
- 黑色是線段
- 淺藍色是弧段
- 紅色是曲線
- 橙色圓點是曲線的控制點
- 綠點是路徑段之間的點
- 虛線表示邊界矩形
- 深藍色數字是按程式設計方式新增的順序段
以程式設計方式構建路徑
我們將在左下角任意開始並按順時針方向工作。我將使用影象中的網格來獲取點的 x 和 y 值。我會在這裡對所有內容進行硬編碼,但當然你不會在一個真實的專案中做到這一點。
基本過程是:
- 建立一個新的
UIBezierPath
- 使用
moveToPoint
選擇路徑上的起點 - 將段新增到路徑中
- line:
addLineToPoint
- arc:
addArcWithCenter
- 曲線:
addCurveToPoint
- 用
closePath
關閉路徑
以下是在上圖中製作路徑的程式碼。
func createBezierPath() -> UIBezierPath {
// create a new path
let path = UIBezierPath()
// starting point for the path (bottom left)
path.moveToPoint(CGPoint(x: 2, y: 26))
// *********************
// ***** Left side *****
// *********************
// segment 1: line
path.addLineToPoint(CGPoint(x: 2, y: 15))
// segment 2: curve
path.addCurveToPoint(CGPoint(x: 0, y: 12), // ending point
controlPoint1: CGPoint(x: 2, y: 14),
controlPoint2: CGPoint(x: 0, y: 14))
// segment 3: line
path.addLineToPoint(CGPoint(x: 0, y: 2))
// *********************
// ****** Top side *****
// *********************
// segment 4: arc
path.addArcWithCenter(CGPoint(x: 2, y: 2), // center point of circle
radius: 2, // this will make it meet our path line
startAngle: CGFloat(M_PI), // π radians = 180 degrees = straight left
endAngle: CGFloat(3*M_PI_2), // 3π/2 radians = 270 degrees = straight up
clockwise: true) // startAngle to endAngle goes in a clockwise direction
// segment 5: line
path.addLineToPoint(CGPoint(x: 8, y: 0))
// segment 6: arc
path.addArcWithCenter(CGPoint(x: 8, y: 2),
radius: 2,
startAngle: CGFloat(3*M_PI_2), // straight up
endAngle: CGFloat(0), // 0 radians = straight right
clockwise: true)
// *********************
// ***** Right side ****
// *********************
// segment 7: line
path.addLineToPoint(CGPoint(x: 10, y: 12))
// segment 8: curve
path.addCurveToPoint(CGPoint(x: 8, y: 15), // ending point
controlPoint1: CGPoint(x: 10, y: 14),
controlPoint2: CGPoint(x: 8, y: 14))
// segment 9: line
path.addLineToPoint(CGPoint(x: 8, y: 26))
// *********************
// **** Bottom side ****
// *********************
// segment 10: line
path.closePath() // draws the final line to close the path
return path
}
注意:通過在單個命令中新增直線和圓弧可以減少上述程式碼中的一些(因為圓弧具有隱含的起點)。有關詳細資訊,請參見此處
畫出路徑
我們可以在層中或在 drawRect
中繪製路徑。
方法 1:在圖層中繪製路徑
我們的自定義類看起來像這樣。初始化檢視時,我們將 Bezier 路徑新增到新的 CAShapeLayer
。
import UIKit
class MyCustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
// Create a CAShapeLayer
let shapeLayer = CAShapeLayer()
// The Bezier path that we made needs to be converted to
// a CGPath before it can be used on a layer.
shapeLayer.path = createBezierPath().CGPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blueColor().CGColor
shapeLayer.fillColor = UIColor.whiteColor().CGColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = CGPoint(x: 10, y: 10)
// add the new layer to our custom view
self.layer.addSublayer(shapeLayer)
}
func createBezierPath() -> UIBezierPath {
// see previous code for creating the Bezier path
}
}
並在 View Controller 中建立我們的檢視
override func viewDidLoad() {
super.viewDidLoad()
// create a new UIView and add it to the view controller
let myView = MyCustomView()
myView.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
myView.backgroundColor = UIColor.yellowColor()
view.addSubview(myView)
}
我們得到……
嗯,這有點小,因為我硬編碼所有數字。我可以擴大路徑大小,但是,像這樣:
let path = createBezierPath()
let scale = CGAffineTransformMakeScale(2, 2)
path.applyTransform(scale)
shapeLayer.path = path.CGPath
方法 2:在 drawRect
中繪製路徑
使用 drawRect
比繪製到圖層要慢,因此如果你不需要,這不是推薦的方法。
以下是我們自定義檢視的修訂程式碼:
import UIKit
class MyCustomView: UIView {
override func drawRect(rect: CGRect) {
// create path (see previous code)
let path = createBezierPath()
// fill
let fillColor = UIColor.whiteColor()
fillColor.setFill()
// stroke
path.lineWidth = 1.0
let strokeColor = UIColor.blueColor()
strokeColor.setStroke()
// Move the path to a new location
path.applyTransform(CGAffineTransformMakeTranslation(10, 10))
// fill and stroke the path (always do these last)
path.fill()
path.stroke()
}
func createBezierPath() -> UIBezierPath {
// see previous code for creating the Bezier path
}
}
這給了我們相同的結果……
進一步研究
理解 Bezier 路徑的優秀文章。
- 像 Bézier 路徑一樣思考 (我從這位作者那裡讀到的一切都很好,上面例子的靈感來自這裡。)
- 編碼數學:第 19 集 - 貝塞爾曲線 (娛樂和良好的視覺插圖)
- Bezier 曲線 (它們如何用於圖形應用程式)
- Bezier 曲線 (很好地描述了數學公式是如何匯出的)
筆記
- 此示例最初來自此 Stack Overflow 答案 。
- 在你的實際專案中,你可能不應該使用硬編碼數字,而是從檢視的邊界獲取大小。