本篇主要介紹CAShapeLayer以及與path的配合使用
一、CAShapeLayer
首先我們先介紹一下CAShapeLayer這個類:
1、CAShapeLayer繼承自CALayer
2撤蟆、CAShapeLayer需要與Path(不論是CGMutablePath還是UIBezierPath)一起使用
3郊霎、使用CAShapeLayer與Path可以實現(xiàn)不在view的drawRect方法中畫出有一些想要的圖形
4贼邓、CAShapeLayer屬于CoreAnimation框架,其動畫渲染直接提交到手機的GPU當中润绵,相較于view的drawRect方法使用CPU渲染而言,其效率極高该酗,能大大優(yōu)化內(nèi)存使用情況授药。
下面是CAShapeLayer的一些屬性
動畫路徑,由若干個點構(gòu)成
open var path: CGPath?
路徑填充的顏色,注意與邊框線的區(qū)分
open var fillColor: CGColor?
填充規(guī)則
open var fillRule: String
這個填充規(guī)則有下面兩個選項:
public let kCAFillRuleNonZero: String
public let kCAFillRuleEvenOdd: String
可能有同學對這兩個屬性有疑問,我來詳細解釋一下
比如說一個圖形a是一個圓,在a的內(nèi)部有一個三角形b
當fiiRule是默認屬性kCAFillRuleNonZero時,那就是正常顯示一個圓,內(nèi)部有一個三角形
而kCAFillRuleEvenOdd則會把屬于a內(nèi)部的點給扣掉,形成一個中空的部分
那為什么要叫做"奇偶"呢,這與判斷點是否屬于內(nèi)部的條件有關(guān):
選取一個點向任意方向畫一條射線,如果與兩條path的交點為偶數(shù),則屬于內(nèi)部,會被扣掉;與path的焦點為奇數(shù),則在中間部分
順帶解釋一下kCAFillRuleNonZero的判斷條件:
選取一個點向任意方向畫一條射線,當射線通過path時,path從左向右則加1,path從右向左則-1,當計數(shù)不為0時,這個點則在path內(nèi),所以叫做NonZero
邊線顏色
open var strokeColor: CGColor?
下面兩個屬性可以放在一起介紹,一個是開始點,一個是結(jié)束點,是CGFloat類型的百分比數(shù)值
可以通過設(shè)置這兩個屬性,對path進行分區(qū)顯示和動畫顯示
open var strokeStart: CGFloat
open var strokeEnd: CGFloat
邊線寬度
open var lineWidth: CGFloat
線端類型
open var lineCap: String
一圖秒懂系列
路徑相交樣式
open var lineJoin: String
/* `lineJoin' values. */
public let kCALineJoinMiter: String
public let kCALineJoinRound: String
public let kCALineJoinBevel: String
一圖秒懂系列
最大斜接長度(只有l(wèi)ineJoin設(shè)置為kCALineJoinMiter時有效)
open var miterLimit: CGFloat
那什么是斜街長度呢,當兩條不平行的線段相連時,較小角度的內(nèi)連點與較大角度的外連點之間的距離,就像上圖第一個線條上兩個直角的定點之間的距離,當超過最大斜接長度時,系統(tǒng)會默認使用kCALineJoinBevel來畫線
線性模版
open var lineDashPattern: [NSNumber]?
為了理解這個屬性,參考了官方例子進行了測試
let lineDashPatterns: [[NSNumber]?]? = [nil, [5,5,10], [10, 5, 5, 5]]
for (index, lineDashPattern) in lineDashPatterns.enumerated() {
? ? ? ? let shapeLayer = CAShapeLayer()
? ? ? ? shapeLayer.strokeColor = UIColor.black.cgColor
? ? ? ? shapeLayer.lineWidth = 5
? ? ? ? shapeLayer.lineDashPattern = lineDashPattern
? ? ? ? let path = CGMutablePath()
? ? ? ? let y = CGFloat(index * 50)
? ? ? ? path.addLines(between: [CGPoint(x: 0, y: y+50),
? ? ? ? CGPoint(x: 640, y: y+50)])
? ? ? ? shapeLayer.path = path?
? ? ? ? self.view.layer.addSublayer(shapeLayer)
}
給lineDashPattern傳入一個數(shù)組(默認為nil),奇數(shù)位的數(shù)字表示著色片段的長度,偶數(shù)位表示未著色片段的長度
如果數(shù)組只有奇數(shù)個成員,則最后一個數(shù)字也默認是最后一段未著色線段的長度(因為著色片段后必須有一段未著色片段)
線型模版的起始位置
open var lineDashPhase: CGFloat
舉上面這張圖第三條線的例子,如果lineDashPhase設(shè)置為10,則path會從第10個單位長度開始繪制,就是第一段長線段的末尾開始,先是長度為5的空白,然后長度為5的線段,長度為5的空白,長度為10的線段,最后依次.....
二、繪制波浪
不管你是用什么方法繪制path,無非是通過添加一個個點到path上,然后把path交給CAShapeLayer進行繪制
在這里,我使用正弦曲線進行繪制
mWavePathFir.removeAllPoints()
mWavePathFir.move(to: CGPoint.init(x: 0, y: self.frame.size.height))
//正弦坐標
for i in 0...Int(self.frame.size.width-1) {
let x = Double.pi*Double(i)/Double(self.frame.size.width)*2
let y = Double(offset)*Double.pi/Double(self.frame.size.width)
let z = waveHeight!*CGFloat(sinf(Float(x+y))) + waveHeightOffset!*(1-progress)
mWavePathFir.addLine(to: CGPoint(x: CGFloat(i), y: CGFloat(z)))
}
mWavePathFir.addLine(to: CGPoint.init(x: self.frame.size.width, y: self.frame.size.height))
mWaveLayerFir.path = mWavePathFir.cgPath
然后通過CADisplayLink設(shè)置YYJWaveProgress的progress來控制進度動畫
link = CADisplayLink.init(target: self, selector: #selector(self.setProgress))
link?.add(to: RunLoop.main, forMode: RunLoopMode.commonModes)
link?.isPaused = false
具體使用可參考我的github上的demo
喜歡這篇文章的小伙伴,歡迎點贊和小星星哦~