設計和繪製貝塞爾曲線

此示例顯示了設計要在檢視上繪製它的形狀的過程。使用了特定的 shap,但你學到的概念可以應用於任何形狀。

如何在自定義檢視中繪製 Bézier 路徑

這些是主要步驟:

  1. 設計你想要的形狀輪廓。
  2. 將輪廓路徑分為線段,圓弧和曲線。
  3. 以程式設計方式構建該路徑。
  4. drawRect 中繪製路徑或使用 CAShapeLayer 繪製路徑。

設計形狀輪廓

你可以做任何事情,但作為一個例子,我選擇了下面的形狀。它可以是鍵盤上的彈出鍵。

StackOverflow 文件

將路徑劃分為多個部分

回顧一下你的形狀設計並將其分解成更簡單的線條元素(用於直線),弧形(用於圓形和圓角)和曲線(用於其他任何東西)。

這是我們的示例設計的樣子:

StackOverflow 文件

  • 黑色是線段
  • 淺藍色是弧段
  • 紅色是曲線
  • 橙色圓點是曲線的控制點
  • 綠點是路徑段之間的點
  • 虛線表示邊界矩形
  • 深藍色數字是按程式設計方式新增的順序段

以程式設計方式構建路徑

我們將在左下角任意開始並按順時針方向工作。我將使用影象中的網格來獲取點的 x 和 y 值。我會在這裡對所有內容進行硬編碼,但當然你不會在一個真實的專案中做到這一點。

基本過程是:

  1. 建立一個新的 UIBezierPath
  2. 使用 moveToPoint 選擇路徑上的起點
  3. 將段新增到路徑中
  • line:addLineToPoint
  • arc:addArcWithCenter
  • 曲線:addCurveToPoint
  1. 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)
    
}

我們得到……

StackOverflow 文件

嗯,這有點小,因為我硬編碼所有數字。我可以擴大路徑大小,但是,像這樣:

let path = createBezierPath()
let scale = CGAffineTransformMakeScale(2, 2)
path.applyTransform(scale)
shapeLayer.path = path.CGPath

StackOverflow 文件

方法 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
    }
}

這給了我們相同的結果……

StackOverflow 文件

進一步研究

理解 Bezier 路徑的優秀文章。

筆記

  • 此示例最初來自此 Stack Overflow 答案
  • 在你的實際專案中,你可能不應該使用硬編碼數字,而是從檢視的邊界獲取大小。