這是不久前我甚至不知道如何發音貝塞爾,更不用說知道如何使用Bézier路徑來製作自定義形狀。以下是我所學到的。事實證明,他們並不像起初看起來那樣可怕。
如何在自定義視圖
繪製Bézier path這些是主要的步驟:
- 設計你想要的形狀的輪廓。
- 將輪廓路徑劃分爲直線,圓弧和曲線的線段。
- 以編程方式構建該路徑。
- 在
drawRect
或使用CAShapeLayer
繪製路徑。
設計形狀輪廓
你可以做任何事情,但作爲一個例子,我選擇了下面的形狀。它可以是鍵盤上的彈出鍵。

分成段的路徑
回頭看你的外形設計,並把它分解成線(用於直線),弧線的簡單元素(圓和圓角),和曲線(對於其他任何東西)。
這裏是我們的榜樣設計會是什麼樣子:

- 黑色是線段
- 淡藍色的弧段
- 紅色的曲線
- 橙色小點是控制點的曲線
- 綠點是路徑之間的點s egments
- 虛線顯示邊框的順序段,他們將被添加
- 深藍色的數字編程
構建路徑編程
我們將在底部任意啓動左角並順時針旋轉。我將使用圖像中的網格來獲取點的x和y值。我會在這裏硬編碼一切,但當然你不會在真正的項目中這樣做。
的基本過程是:
- 創建一個新的
UIBezierPath
- 選擇路徑上的起點與
moveToPoint
- 段添加到路徑
- 行:
addLineToPoint
- 弧:
addArcWithCenter
- 曲線:
addCurveToPoint
- 關閉與
closePath
這裏的路徑是使上述圖像中的路徑的代碼。
func createBezierPath() -> UIBezierPath {
// create a new path
let path = UIBezierPath()
// starting point for the path (bottom left)
path.move(to: CGPoint(x: 2, y: 26))
// *********************
// ***** Left side *****
// *********************
// segment 1: line
path.addLine(to: CGPoint(x: 2, y: 15))
// segment 2: curve
path.addCurve(to: CGPoint(x: 0, y: 12), // ending point
controlPoint1: CGPoint(x: 2, y: 14),
controlPoint2: CGPoint(x: 0, y: 14))
// segment 3: line
path.addLine(to: CGPoint(x: 0, y: 2))
// *********************
// ****** Top side *****
// *********************
// segment 4: arc
path.addArc(withCenter: 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.addLine(to: CGPoint(x: 8, y: 0))
// segment 6: arc
path.addArc(withCenter: 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.addLine(to: CGPoint(x: 10, y: 12))
// segment 8: curve
path.addCurve(to: CGPoint(x: 8, y: 15), // ending point
controlPoint1: CGPoint(x: 10, y: 14),
controlPoint2: CGPoint(x: 8, y: 14))
// segment 9: line
path.addLine(to: CGPoint(x: 8, y: 26))
// *********************
// **** Bottom side ****
// *********************
// segment 10: line
path.close() // draws the final line to close the path
return path
}
注意:某些上面的代碼可以通過加入在一個命令的線和弧被減小(因爲電弧具有一個隱含的起點)。有關更多詳細信息,請參閱here。
繪製路徑
我們可以借鑑的路徑或者在一個層或drawRect
。
方法1:在層
我們的定製類看起來像這樣繪製路徑。我們當視圖初始化添加我們的貝塞爾曲線路徑到一個新的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.blue.cgColor
shapeLayer.fillColor = UIColor.white.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
}
}
,創造我們的視圖控制器視圖這樣
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.yellow
view.addSubview(myView)
}
我們得到...

嗯,這是一個有點小,因爲我存的所有號碼在我可以擴展的路徑大小了,雖然,這樣的:
let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath

方法2:draw
繪製路徑使用draw
比繪圖層慢,所以這不是推薦的方法,如果你不需要它。
這裏是我們的自定義視圖修改後的代碼:
import UIKit
class MyCustomView: UIView {
override func draw(_ rect: CGRect) {
// create path (see previous code)
let path = createBezierPath()
// fill
let fillColor = UIColor.white
fillColor.setFill()
// stroke
path.lineWidth = 1.0
let strokeColor = UIColor.blue
strokeColor.setStroke()
// Move the path to a new location
path.apply(CGAffineTransform(translationX: 10, y: 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
}
}
這給了我們同樣的結果...

進一步研究
我真的建議看着下面的材料。他們最終使Bézier路徑對我來說是可以理解的。 (教我如何發音:/bɛ紫eɪ/)
如果視圖的「框架」發生變化,該怎麼辦?發生方向變化時,我們如何調整形狀? – ozgur
@ozgur,至少有兩個選項。我會在上面的示例中展示一個縮放(可能是翻譯)轉換。另一種選擇是根據新框架重新計算貝塞爾路徑。在上面的例子中,我將所有數字硬編碼到Bezier路徑中。但是,當我在實際項目中使用貝塞爾路徑時,我根據幀大小確定貝塞爾值。當框架(或更可能的邊界)改變時,我重新計算貝塞爾路徑。 – Suragch
我之前選擇了後者。我重新計算了bezier路徑並使用'layoutSubviews'方法更新了有問題的圖層,但我不確定這是否是正確的位置。當視圖的框架發生變化時,你會在哪裏更新路徑? – ozgur