IOS13 APP『Swift5』實例說明 —使用 UIBezierPath來繪製圖案

在swift中有提供一些畫線條與曲線的方式繪製路徑,然後再利用CAShapeLayer的方式建立圖層,之後在View中顯示出你所畫出的圖案,下面就來說明一下如何用UIBezierPath畫出Tsum Tsum的Miki圖案吧。

步驟如下 :

Step 1 : 要用UIBezierPath來畫圖,當然最重要的事先宣告一個UIBezierPath()。

let path = UIBezierPath()

Step 2 : 設定起點,UIBezierPath有一個法方是.move,可以將畫筆移到起點。

let startPoint = CGPoint(x: 256, y: 256)path.move(to: startPoint)

Stpe 3 : 移到起點後,來介紹一下幾種UIBezierPath畫線的方法:

  • .addLine : 這是畫直線的方法,就是從現在的位置,畫直線到目的地的位置,使用這方式最少需要加兩個點,若是只有起點與一個點,並不會畫出直線,最後path.close()會從目前所在的 (256, 768) 連線回起點(256, 256),這也可以省略,因為會自行連回起點。如要畫一條直線,則要使用別的方式CAShapeLayer()的屬性.strokeColor來做出來。
path.addLine(to: CGPoint(x: 768, y: 256))path.addLine(to: CGPoint(x: 768, y: 768))path.addLine(to: CGPoint(x: 256, y: 768))path.close()
  • .addQuadCurve : 這是畫曲線的方法,利用控制點(controlPoint)改變直線的弧度,將目前現在的位置到目的地的位置之間的直線,使用控制點的離直線的遠近改變成曲線。
facePath.addQuadCurve(to: CGPoint(x: 101, y: 54), controlPoint: CGPoint(x: 91, y: 53))

這樣說起來,你可能還是搞不懂,那就看看下面的幾張圖,其實就是移動controlPoint的座標讓曲線的位置改變。

  • .addCurve : 這也是畫曲線的方法,但是控制點(controlPoint)有兩個,所以可以畫出S型的曲線。
let path = UIBezierPath()
let startPoint = CGPoint(x: 0, y: 512)
path.move(to: startPoint)
path.addCurve(to: CGPoint(x: 1023, y: 512), controlPoint1: CGPoint(x: 256, y: 256), controlPoint2: CGPoint(x: 768, y: 768))
  • .addCurve : 這是畫圓弧的方法。利用中心點與半徑,畫出圓弧,這邊要注意的是startAngle與endAngle的角度,這裡是需要使用弧度,而 180 度等於弧度 pi ,因此 CGFloat.pi / 180 等於1度的弧度。clockwise為true時則是順時針畫圓,false則是逆時針畫圓。所以簡單說就是360度,圓弧就是起點的度數到終點的度數,看是順時針畫圓還是逆時針畫圓,將兩個度數中間用圓弧連在一起。
let path = UIBezierPath()let startPoint = CGPoint(x: 0, y: 512)path.move(to: startPoint)path.addArc(withCenter: CGPoint(x: 512, y: 512), radius: 512, startAngle: CGFloat.pi / 180 , endAngle: CGFloat.pi / 180 * 270, clockwise: false)

上面說明了UIBezierPath()的畫線方法,但是其實像話矩形或是圓形,不需要一條一條線的畫,UIBezierPath()也提供了不同的參數可以直接畫出這些形狀,下面介紹一下:

  • 矩形 : rect只要提供左上角位置與寬高,就會畫出矩形,寬高一樣時就會畫出正方形。
let path = UIBezierPath(rect: CGRect(x: 256, y: 256, width: 512, height: 512))
  • 圓形 : ovalIn只要提供左上角位置與寬高,就可以畫出圓形,所以寬高不同時,就會變橢圓形
let path = UIBezierPath(ovalIn: CGRect(x: 448, y: 384, width: 128, height: 256))
  • 圓弧 : arcCenter其實與.addCurve一樣,可以畫出圓弧。
let path = UIBezierPath(arcCenter: CGPoint(x: 512, y: 512), radius: 512, startAngle: CGFloat.pi / 180 * 0, endAngle: CGFloat.pi / 180 * 180, clockwise: true)

Step 4 : 當畫完之後,就是要使用CAShapeLayer()來建立圖層。

let shape = CAShapeLayer()

Step 5 : 將剛剛畫好的圖形的路徑加入CAShapeLayer()的路徑屬性.path。

shape.path = path.cgPath

Step 6 : 剛剛畫的圖想要顯示什麼顏色,那就使用CAShapeLayer()的顏色屬性.fillColor。

shape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor

Step 7: 最後就是將這圖層加入View。如果最後view是用.layer.mask的方式顯示那就是使用mask方式顯示,mask 中文意思是遮罩,如設定 CALayer 元件的 mask,那 View 只會顯示 mask 裡非透明的區塊,也就是剛剛畫的圖形,而其他圖形以外的部分則會被遮蔽。如果是用.layer.addSublayer,就是在 View 上顯示額外顏色的形狀,使用這方式比較適合顯示較複雜的UIBezierPath,所以範例中使用.layer.addSublayer。

backgroundView.layer.addSublayer(shape)

至於圖形的座標怎麼知道呢?有幾種方式,例如使用windoes的小畫家,開啟圖片,也可以看到圖形的x與y的座標,或是使用網路上的一些免費的網站提供的功能,如使用iview、GeoGebra等等,若是你是專業的美工,那你就可以直接使用photoshop等美工軟體看。

https://yangcha.github.io/iview/iview.html

最後就補充如何畫出一條直線,可以使用CAShapeLayer()的屬性來設定,.strokeColor 設定邊框的顏色,其實就當作畫筆的顏色就可,.lineWidth 設定邊框的寬度,然後再用 CALayer 的 function addSublayer 加入特別形狀的 layer,這樣就可以顯示一條直線。

Playground的程式碼:

let backgroundView = UIView(frame: CGRect(x: 0, y: 0, width: 512, height: 512))
backgroundView.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
let path = UIBezierPath()
path.move(to: CGPoint(x: 100, y: 256))
path.addLine(to:CGPoint(x: 400, y: 256))
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(shapeLayer)
backgroundView

Xcode內的程式碼:

let path = UIBezierPath()path.move(to: CGPoint(x: 100, y: 256))path.addLine(to: CGPoint(x: 400, y: 256))let shapeLayer = CAShapeLayer()shapeLayer.path = path.cgPathshapeLayer.strokeColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor

view.layer.addSublayer(shapeLayer)

Playground的程式碼:

import UIKit
import PlaygroundSupport

let backgroundView = UIView(frame: CGRect(x: 0, y: 0, width: 1024, height: 1024))
backgroundView.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
let degree : CGFloat = CGFloat.pi / 180let leftEarPath = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 60, height: 60))
let leftEarShape = CAShapeLayer()
leftEarShape.path = leftEarPath.cgPath
leftEarShape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(leftEarShape)
let rightEarPath = UIBezierPath(ovalIn: CGRect(x: 105, y: 0, width: 60, height: 60))
let rightEarShape = CAShapeLayer()
rightEarShape.path = rightEarPath.cgPath
rightEarShape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(rightEarShape)
let leftHandPath = UIBezierPath(ovalIn: CGRect(x: 110, y: 130, width: 20, height: 20))
let leftHandShape = CAShapeLayer()
leftHandShape.path = leftHandPath.cgPath
leftHandShape.fillColor = UIColor(red: 174/255, green: 218/255, blue: 244/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(leftHandShape)
let rightHandPath = UIBezierPath(ovalIn: CGRect(x: 32, y: 130, width: 20, height: 20))
let rightHandShape = CAShapeLayer()
rightHandShape.path = rightHandPath.cgPath
rightHandShape.fillColor = UIColor(red: 174/255, green: 218/255, blue: 244/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(rightHandShape)
let upHeadPath = UIBezierPath(arcCenter: CGPoint(x: 81, y: 100), radius: 65, startAngle: degree * 0, endAngle: degree * 180, clockwise: false)
let upHeadShape = CAShapeLayer()
upHeadShape.path = upHeadPath.cgPath
upHeadShape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(upHeadShape)
let downHeadPath = UIBezierPath()
let downHeadPoint = CGPoint(x: 16, y: 100)
downHeadPath.move(to: downHeadPoint)
downHeadPath.addQuadCurve(to: CGPoint(x: 81, y: 150), controlPoint: CGPoint(x: 23, y: 150))
downHeadPath.addQuadCurve(to: CGPoint(x: 146, y: 100), controlPoint: CGPoint(x: 142, y: 150))
downHeadPath.close()
let downHeadShape = CAShapeLayer()
downHeadShape.path = downHeadPath.cgPath
downHeadShape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(downHeadShape)
let facePath = UIBezierPath()
let facePoint = CGPoint(x: 83, y: 61)
facePath.move(to: facePoint)
facePath.addQuadCurve(to: CGPoint(x: 101, y: 54), controlPoint: CGPoint(x: 91, y: 53))
facePath.addQuadCurve(to: CGPoint(x: 129, y: 87), controlPoint: CGPoint(x: 126, y: 56))
facePath.addLine(to: CGPoint(x: 129, y: 106))
facePath.addQuadCurve(to: CGPoint(x: 129, y: 130), controlPoint: CGPoint(x: 141, y: 114))
facePath.addQuadCurve(to: CGPoint(x: 34, y: 130), controlPoint: CGPoint(x: 85, y: 168))
facePath.addQuadCurve(to: CGPoint(x: 37, y: 106), controlPoint: CGPoint(x: 24, y: 114))
facePath.addLine(to: CGPoint(x: 37, y: 87))
facePath.addQuadCurve(to: CGPoint(x: 65, y: 54), controlPoint: CGPoint(x: 39, y: 56))
facePath.addQuadCurve(to: CGPoint(x: 83, y: 61), controlPoint: CGPoint(x: 73, y: 53))
//facePath.close()
let faceShape = CAShapeLayer()
faceShape.path = facePath.cgPath
faceShape.fillColor = UIColor(red: 251/255, green: 203/255, blue: 165/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(faceShape)
let nosePath = UIBezierPath(ovalIn: CGRect(x: 74, y: 111, width: 17, height: 13))
let noseShape = CAShapeLayer()
noseShape.path = nosePath.cgPath
noseShape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(noseShape)
let rightEyePath = UIBezierPath(ovalIn: CGRect(x: 52, y: 98, width: 15, height: 20))
let rightEyeShape = CAShapeLayer()
rightEyeShape.path = rightEyePath.cgPath
rightEyeShape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(rightEyeShape)
let leftEyePath = UIBezierPath(ovalIn: CGRect(x: 97, y: 98, width: 15, height: 20))
let leftEyeShape = CAShapeLayer()
leftEyeShape.path = leftEyePath.cgPath
leftEyeShape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(leftEyeShape)
backgroundView

Xcode的程式碼:

在Xcode中可以用.append的方法,將多個UIBezierPath路徑結合。

override func viewDidLoad() {
super.viewDidLoad()

let backgroundView = UIView(frame: CGRect(x: 0, y: 0, width: 180, height: 180))
backgroundView.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1)

let leftHandPath = UIBezierPath(ovalIn: CGRect(x: 110, y: 130, width: 20, height: 20))
let rightHandPath = UIBezierPath(ovalIn: CGRect(x: 32, y: 130, width: 20, height: 20))
let handShape = CAShapeLayer()
leftHandPath.append(rightHandPath)
handShape.path = leftHandPath.cgPath
handShape.fillColor = UIColor(red: 174/255, green: 218/255, blue: 244/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(handShape)

let degree : CGFloat = CGFloat.pi / 180
let leftEarPath = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 60, height: 60))
let rightEarPath = UIBezierPath(ovalIn: CGRect(x: 105, y: 0, width: 60, height: 60))
let earShape = CAShapeLayer()
leftEarPath.append(rightEarPath)
earShape.path = leftEarPath.cgPath
earShape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(earShape)

let upHeadPath = UIBezierPath(arcCenter: CGPoint(x: 81, y: 100), radius: 65, startAngle: degree * 0, endAngle: degree * 180, clockwise: false)
let downHeadPath = UIBezierPath()
let downHeadPoint = CGPoint(x: 16, y: 100)
downHeadPath.move(to: downHeadPoint)
downHeadPath.addQuadCurve(to: CGPoint(x: 81, y: 150), controlPoint: CGPoint(x: 23, y: 150))
downHeadPath.addQuadCurve(to: CGPoint(x: 146, y: 100), controlPoint: CGPoint(x: 142, y: 150))

let headShape = CAShapeLayer()
upHeadPath.append(downHeadPath)
headShape.path = upHeadPath.cgPath
headShape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(headShape)

let facePath = UIBezierPath()
let facePoint = CGPoint(x: 83, y: 61)
facePath.move(to: facePoint)
facePath.addQuadCurve(to: CGPoint(x: 101, y: 54), controlPoint: CGPoint(x: 91, y: 53))
facePath.addQuadCurve(to: CGPoint(x: 129, y: 87), controlPoint: CGPoint(x: 126, y: 56))
facePath.addLine(to: CGPoint(x: 129, y: 106))
facePath.addQuadCurve(to: CGPoint(x: 129, y: 130), controlPoint: CGPoint(x: 141, y: 114))
facePath.addQuadCurve(to: CGPoint(x: 34, y: 130), controlPoint: CGPoint(x: 85, y: 168))
facePath.addQuadCurve(to: CGPoint(x: 37, y: 106), controlPoint: CGPoint(x: 24, y: 114))
facePath.addLine(to: CGPoint(x: 37, y: 87))
facePath.addQuadCurve(to: CGPoint(x: 65, y: 54), controlPoint: CGPoint(x: 39, y: 56))
facePath.addQuadCurve(to: CGPoint(x: 83, y: 61), controlPoint: CGPoint(x: 73, y: 53))

let faceShape = CAShapeLayer()
faceShape.path = facePath.cgPath
faceShape.fillColor = UIColor(red: 251/255, green: 203/255, blue: 165/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(faceShape)

let nosePath = UIBezierPath(ovalIn: CGRect(x: 74, y: 111, width: 17, height: 13))
let rightEyePath = UIBezierPath(ovalIn: CGRect(x: 52, y: 98, width: 15, height: 20))
let leftEyePath = UIBezierPath(ovalIn: CGRect(x: 97, y: 98, width: 15, height: 20))
let eyeShape = CAShapeLayer()
nosePath.append(rightEyePath)
nosePath.append(leftEyePath)
eyeShape.path = nosePath.cgPath
eyeShape.fillColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1).cgColor
backgroundView.layer.addSublayer(eyeShape)

backgroundView.center = view.center
view.addSubview(backgroundView)
// Do any additional setup after loading the view.
}

--

--