放肆地使用UIBezierPath和CAShapeLayer画各种图形
CAShapeLayer 是 CALayer 的子類,但是比 CALayer 更靈活,可以畫出各種圖形,當然,你也可以使用其他方式來畫,隨你。
雜談
在 CAShapeLayer 中,也可以像 CALayer 一樣指定它的 frame 來畫,就像這樣:
| 1 2 3 4 5 | let?layer?=?CAShapeLayer() layer.frame?=?CGRectMake(110,?100,?150,?100) layer.backgroundColor?=?UIColor.blackColor().CGColor view.layer.addSublayer(layer) |
然后你就會得到如圖這樣的黑色矩形
但是,CAShapeLayer 有一個神奇的屬性?path?用這個屬性配合上 UIBezierPath 這個類就可以達到超神的效果。
UIBezierPath 顧名思義,這是用貝塞爾曲線的方式來構建一段弧線,你可以用任意條弧線來組成你想要的形狀,比如,你想用它來和上面一樣畫一個矩形,那就可以這樣子來做:
| 1 2 3 4 5 | let?path?=?UIBezierPath(rect:?CGRectMake(110,?100,?150,?100)) let?layer?=?CAShapeLayer() layer.path?=?path.CGPath layer.fillColor?=?UIColor.blackColor().CGColor view.layer.addSublayer(layer) |
要注意的是,這里就不要用backgroundColor?這個屬性了,而要使用?fillColor?和?strokeColor?,前者代表設置這個 Layer 的填充色,后者代表設置它的邊框色
| 1 2 | layer.fillColor?=?UIColor.clearColor().CGCo lorlayer.strokeColor?=?UIColor.blackColor().CGColor |
可以試一下把上面代碼設置顏色的部分改成這個樣子,那么運行程序的時候就會是這種樣子
玩一下UIBezierPath
在說回 UIBezierPath ,在 UIBezierPath 的 init 方法中,就有很多方便你畫各種圖形的方法,比如你可以畫一個帶圓角的圖形
| 1 2 3 4 5 | let?path?=?UIBezierPath(roundedRect:?CGRectMake(110,?100,?150,?100),?cornerRadius:?50) let?layer?=?CAShapeLayer() layer.path?=?path.CGPath layer.fillColor?=?UIColor.clearColor().CGColor layer.strokeColor?=?UIColor.blackColor().CGColor |
還可以指定起始角和半徑畫圓
| 1 2 3 4 5 6 7 8 | let?radius:?CGFloat?=?60.0 let?startAngle:?CGFloat?=?0.0 let?endAngle:?CGFloat?=?CGFloat(M_PI?*?2) let?path?=?UIBezierPath(arcCenter:?view.center,?radius:?radius,?startAngle:?startAngle,?endAngle:?endAngle,?clockwise:?true) let?layer?=?CAShapeLayer() layer.path?=?path.CGPath layer.fillColor?=?UIColor.clearColor().CGColor layer.strokeColor?=?UIColor.blackColor().CGColor |
在這里涉及到角度的問題,起始角和結束角,這里的角度使使用弧度制來表示,這里我收藏了一張圖片,以方便參考。
怎么畫曲線
貝塞爾曲線的畫法是由起點、終點、控制點三個參數來畫的,為了解釋清楚這個點,我寫了幾行代碼來解釋它
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | let?startPoint?=?CGPointMake(50,?300) let?endPoint?=?CGPointMake(300,?300) let?controlPoint?=?CGPointMake(170,?200) let?layer1?=?CALayer() layer1.frame?=?CGRectMake(startPoint.x,?startPoint.y,?5,?5) layer1.backgroundColor?=?UIColor.redColor().CGColor let?layer2?=?CALayer() layer2.frame?=?CGRectMake(endPoint.x,?endPoint.y,?5,?5) layer2.backgroundColor?=?UIColor.redColor().CGColor let?layer3?=?CALayer() layer3.frame?=?CGRectMake(controlPoint.x,?controlPoint.y,?5,?5) layer3.backgroundColor?=?UIColor.redColor().CGColor let?path?=?UIBezierPath() let?layer?=?CAShapeLayer() path.moveToPoint(startPoint) path.addQuadCurveToPoint(endPoint,?controlPoint:?controlPoint) layer.path?=?path.CGPath layer.fillColor?=?UIColor.clearColor().CGColor layer.strokeColor?=?UIColor.blackColor().CGColor view.layer.addSublayer(layer) view.layer.addSublayer(layer1) view.layer.addSublayer(layer2) view.layer.addSublayer(layer3) |
我很隨意的定義了三個點,為了清楚顯示它們的位置,我放了三個矩形在上面以便觀察,然后調用?path.moveToPoint(startPoint)?讓它移動到起始點,然后調用path.addQuadCurveToPoint(endPoint, controlPoint: controlPoint)?這個方法告訴它結束點和控制點,這樣它就能畫出一條有弧度的線條了,如果把fillColor設置一個顏色,那么它就會變成一個很丑的形狀了,示例圖如下
控制點決定了它的曲率,曲線的頂點不等于控制點的位置,具體可以看一下貝塞爾曲線的定義,你還可以使用兩個控制點來畫,兩個控制點可以使用方法?path.addCurveToPoint(endPoint, controlPoint1: controlPoint, controlPoint2: controlPoint2)來搞定
這樣它會是這個樣子
再來說說 CAShapeLayer
CAShapeLayer 是個神奇的東西,給它一個path它就能變成你想要的形狀,它還有很多可以玩的地方。綜合使用可以組合成不同的動畫,比如下面這樣
demo1.gif
demo2.gif
demo3.gif
這三個動畫就是使用了?strokeEnd、strokeStart和lineWidth?三個屬性,第一個動畫用了strokeEnd這個屬性的值范圍是0-1,動畫顯示了從0到1之間每一個值對這條曲線的影響,strokeStart的方法則是相反的,如果把這兩個值首先都設置成0.5然后慢慢改變成0和1的時候就會變成第二個動畫,配合lineWidth則曲線會慢慢變粗,這里的很多屬性都是支持動畫的。
示例代碼
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | private?func?animation1()?{ ????????let?animation?=?CABasicAnimation(keyPath:?"strokeEnd") ????????animation.fromValue?=?0 ????????animation.toValue?=?1 ????????animation.duration?=?2 ????????layer.addAnimation(animation,?forKey:?"") ????} ????? ????private?func?animation2()?{ ????????layer.strokeStart?=?0.5 ????????layer.strokeEnd?=?0.5 ????????? ????????let?animation?=?CABasicAnimation(keyPath:?"strokeStart") ????????animation.fromValue?=?0.5 ????????animation.toValue?=?0 ????????animation.duration?=?2 ????????? ????????let?animation2?=?CABasicAnimation(keyPath:?"strokeEnd") ????????animation2.fromValue?=?0.5 ????????animation2.toValue?=?1 ????????animation2.duration?=?2 ????????? ????????layer.addAnimation(animation,?forKey:?"") ????????layer.addAnimation(animation2,?forKey:?"") ????} ????? ????private?func?animation3()?{ ????????let?animation?=?CABasicAnimation(keyPath:?"lineWidth") ????????animation.fromValue?=?1 ????????animation.toValue?=?10 ????????animation.duration?=?2 ????????layer.addAnimation(animation,?forKey:?"") ????} |
應用一下
前一陣子在仿時光網這個APP,其中有一個Layer的形狀很怪異,是這樣的
很明顯它可以用 CAShapeLayer + UIBezierPath 來做,思路大概是這樣,先移動到左上方的位置,然后向下劃線,然后往右劃線,然后往上劃線,還剩一個蓋子,這個蓋子就用一個控制點控制曲率,非常簡單,代碼如下
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | let?finalSize?=?CGSizeMake(CGRectGetWidth(view.frame),?400) let?layerHeight?=?finalSize.height?*?0.2 let?layer?=?CAShapeLayer() let?bezier?=?UIBezierPath() bezier.moveToPoint(CGPointMake(0,?finalSize.height?-?layerHeight)) bezier.addLineToPoint(CGPointMake(0,?finalSize.height?-?1)) bezier.addLineToPoint(CGPointMake(finalSize.width,?finalSize.height?-?1)) bezier.addLineToPoint(CGPointMake(finalSize.width,?finalSize.height?-?layerHeight)) bezier.addQuadCurveToPoint(CGPointMake(0,finalSize.height?-?layerHeight), ????controlPoint:?CGPointMake(finalSize.width?/?2,?(finalSize.height?-?layerHeight)?-?40)) layer.path?=?bezier.CGPath layer.fillColor?=?UIColor.blackColor().CGColor view.layer.addSublayer(layer) |
就能畫出這樣的形狀來
再來一個復雜點的,微信下拉拍小視頻的那只眼睛很有趣,來模仿一下那個效果吧,它是這樣的
首先你得畫出這只眼睛,這是眼睛包括5個部分組成(這個是用OC寫的)
| 1 2 3 4 5 | @property?(strong,?nonatomic)?CAShapeLayer?*eyeFirstLightLayer; @property?(strong,?nonatomic)?CAShapeLayer?*eyeSecondLightLayer; @property?(strong,?nonatomic)?CAShapeLayer?*eyeballLayer; @property?(strong,?nonatomic)?CAShapeLayer?*topEyesocketLayer; @property?(strong,?nonatomic)?CAShapeLayer?*bottomEyesocketLayer; |
然后,還是通過 UIBezierPath 和 CAShapeLayer 這樣的老套路來畫,代碼較多
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | -?(CAShapeLayer?*)eyeFirstLightLayer?{ ????if?(!_eyeFirstLightLayer)?{ ????????_eyeFirstLightLayer?=?[CAShapeLayer?layer]; ????????CGPoint?center?=?CGPointMake(CGRectGetWidth(self.frame)?/?2,?CGRectGetHeight(self.frame)?/?2); ????????UIBezierPath?*path?=?[UIBezierPath?bezierPathWithArcCenter:center ????????????????????????????????????????????????????????????radius:CGRectGetWidth(self.frame)?*?0.2 ????????????????????????????????????????????????????????startAngle:(230.f?/?180.f)?*?M_PI ??????????????????????????????????????????????????????????endAngle:(265.f?/?180.f)?*?M_PI ?????????????????????????????????????????????????????????clockwise:YES]; ????????_eyeFirstLightLayer.borderColor?=?[UIColor?blackColor].CGColor; ????????_eyeFirstLightLayer.lineWidth?=?5.f; ????????_eyeFirstLightLayer.path?=?path.CGPath; ????????_eyeFirstLightLayer.fillColor?=?[UIColor?clearColor].CGColor; ????????_eyeFirstLightLayer.strokeColor?=?[UIColor?whiteColor].CGColor; ????} ????return?_eyeFirstLightLayer; } -?(CAShapeLayer?*)eyeSecondLightLayer?{ ????if?(!_eyeSecondLightLayer)?{ ????????_eyeSecondLightLayer?=?[CAShapeLayer?layer]; ????????CGPoint?center?=?CGPointMake(CGRectGetWidth(self.frame)?/?2,?CGRectGetHeight(self.frame)?/?2); ????????UIBezierPath?*path?=?[UIBezierPath?bezierPathWithArcCenter:center ????????????????????????????????????????????????????????????radius:CGRectGetWidth(self.frame)?*?0.2 ????????????????????????????????????????????????????????startAngle:(211.f?/?180.f)?*?M_PI ??????????????????????????????????????????????????????????endAngle:(220.f?/?180.f)?*?M_PI ?????????????????????????????????????????????????????????clockwise:YES]; ????????_eyeSecondLightLayer.borderColor?=?[UIColor?blackColor].CGColor; ????????_eyeSecondLightLayer.lineWidth?=?5.f; ????????_eyeSecondLightLayer.path?=?path.CGPath; ????????_eyeSecondLightLayer.fillColor?=?[UIColor?clearColor].CGColor; ????????_eyeSecondLightLayer.strokeColor?=?[UIColor?whiteColor].CGColor; ????} ????return?_eyeSecondLightLayer; } -?(CAShapeLayer?*)eyeballLayer?{ ????if?(!_eyeballLayer)?{ ????????_eyeballLayer?=?[CAShapeLayer?layer]; ????????CGPoint?center?=?CGPointMake(CGRectGetWidth(self.frame)?/?2,?CGRectGetHeight(self.frame)?/?2); ????????UIBezierPath?*path?=?[UIBezierPath?bezierPathWithArcCenter:center ????????????????????????????????????????????????????????????radius:CGRectGetWidth(self.frame)?*?0.3 ????????????????????????????????????????????????????????startAngle:(0.f?/?180.f)?*?M_PI ??????????????????????????????????????????????????????????endAngle:(360.f?/?180.f)?*?M_PI ?????????????????????????????????????????????????????????clockwise:YES]; ????????_eyeballLayer.borderColor?=?[UIColor?blackColor].CGColor; ????????_eyeballLayer.lineWidth?=?1.f; ????????_eyeballLayer.path?=?path.CGPath; ????????_eyeballLayer.fillColor?=?[UIColor?clearColor].CGColor; ????????_eyeballLayer.strokeColor?=?[UIColor?whiteColor].CGColor; ????????_eyeballLayer.anchorPoint?=?CGPointMake(0.5,?0.5); ????} ????return?_eyeballLayer; } -?(CAShapeLayer?*)topEyesocketLayer?{ ????if?(!_topEyesocketLayer)?{ ????????_topEyesocketLayer?=?[CAShapeLayer?layer]; ????????CGPoint?center?=?CGPointMake(CGRectGetWidth(self.frame)?/?2,?CGRectGetHeight(self.frame)?/?2); ????????UIBezierPath?*path?=?[UIBezierPath?bezierPath]; ????????[path?moveToPoint:CGPointMake(0,?CGRectGetHeight(self.frame)?/?2)]; ????????[path?addQuadCurveToPoint:CGPointMake(CGRectGetWidth(self.frame),?CGRectGetHeight(self.frame)?/?2) ?????????????????????controlPoint:CGPointMake(CGRectGetWidth(self.frame)?/?2,?center.y?-?center.y?-?20)]; ????????_topEyesocketLayer.borderColor?=?[UIColor?blackColor].CGColor; ????????_topEyesocketLayer.lineWidth?=?1.f; ????????_topEyesocketLayer.path?=?path.CGPath; ????????_topEyesocketLayer.fillColor?=?[UIColor?clearColor].CGColor; ????????_topEyesocketLayer.strokeColor?=?[UIColor?whiteColor].CGColor; ????} ????return?_topEyesocketLayer; } -?(CAShapeLayer?*)bottomEyesocketLayer?{ ????if?(!_bottomEyesocketLayer)?{ ????????_bottomEyesocketLayer?=?[CAShapeLayer?layer]; ????????CGPoint?center?=?CGPointMake(CGRectGetWidth(self.frame)?/?2,?CGRectGetHeight(self.frame)?/?2); ????????UIBezierPath?*path?=?[UIBezierPath?bezierPath]; ????????[path?moveToPoint:CGPointMake(0,?CGRectGetHeight(self.frame)?/?2)]; ????????[path?addQuadCurveToPoint:CGPointMake(CGRectGetWidth(self.frame),?CGRectGetHeight(self.frame)?/?2) ?????????????????????controlPoint:CGPointMake(CGRectGetWidth(self.frame)?/?2,?center.y?+?center.y?+?20)]; ????????_bottomEyesocketLayer.borderColor?=?[UIColor?blackColor].CGColor; ????????_bottomEyesocketLayer.lineWidth?=?1.f; ????????_bottomEyesocketLayer.path?=?path.CGPath; ????????_bottomEyesocketLayer.fillColor?=?[UIColor?clearColor].CGColor; ????????_bottomEyesocketLayer.strokeColor?=?[UIColor?whiteColor].CGColor; ????} ????return?_bottomEyesocketLayer; } |
然后更改一下某些屬性的值,方便稍后的動畫
| 1 2 3 4 5 6 7 8 | -?(void)setupAnimation?{ ????self.eyeFirstLightLayer.lineWidth?=?0.f; ????self.eyeSecondLightLayer.lineWidth?=?0.f; ????self.eyeballLayer.opacity?=?0.f; ????_bottomEyesocketLayer.strokeStart?=?0.5f; ????_bottomEyesocketLayer.strokeEnd?=?0.5f; ????_topEyesocketLayer.strokeStart?=?0.5f; ????_topEyesocketLayer.strokeEnd?=?0.5f;} |
最后根據 UIScrollView 的?contentOffset?來控制各種屬性,辦法較笨,但管用。
最后
總之使用 UIbezierPath 和 CAShapeLayer 可以畫出你想要的任何形狀,沒有它做不到,只有你想不到,搞定了它們你就可以輕松定制你想要的任何控件了。
轉載于:https://www.cnblogs.com/code4better/p/5537210.html
總結
以上是生活随笔為你收集整理的放肆地使用UIBezierPath和CAShapeLayer画各种图形的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对于java反射的理解
- 下一篇: IE中透明度的读写