CAShapeLayer 是 CALayer 的子類,可是比 CALayer 更靈活,能夠畫出各類圖形,固然,你也能夠使用其餘方式來畫,隨你。git
在 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
)
|
而後你就會獲得如圖這樣的黑色矩形github
可是,CAShapeLayer 有一個神奇的屬性 path
用這個屬性配合上 UIBezierPath 這個類就能夠達到超神的效果。web
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
(
)
.
CGColor
layer
.
strokeColor
=
UIColor
.
blackColor
(
)
.
CGColor
|
能夠試一下把上面代碼設置顏色的部分改爲這個樣子,那麼運行程序的時候就會是這種樣子架構
在說回 UIBezierPath ,在 UIBezierPath 的 init 方法中,就有不少方便你畫各類圖形的方法,好比你能夠畫一個帶圓角的圖形socket
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 是個神奇的東西,給它一個path
它就能變成你想要的形狀,它還有不少能夠玩的地方。綜合使用能夠組合成不一樣的動畫,好比下面這樣:
這三個動畫就是使用了 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
89
90
91
|
-
(
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
9
|
-
(
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
來控制各類屬性,辦法較笨,但管用。
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
|
-
(
void
)
animationWith
:
(
CGFloat
)
y
{
CGFloat
flag
=
self
.
frame
.
origin
.
y
*
2.f
-
20.f
;
if
(
y
<
flag
)
{
if
(
self
.
eyeFirstLightLayer
.
lineWidth
<
5.f
)
{
self
.
eyeFirstLightLayer
.
lineWidth
+=
1.f
;
self
.
eyeSecondLightLayer
.
lineWidth
+=
1.f
;
}
}
if
(
y
<
flag
-
20
)
{
if
(
self
.
eyeballLayer
.
opacity
<=
1.0f
)
{
self
.
eyeballLayer
.
opacity
+=
0.1f
;
}
}
if
(
y
<
flag
-
40
)
{
if
(
self
.
topEyesocketLayer
.
strokeEnd
<
1.f
&&
self
.
topEyesocketLayer
.
strokeStart
>
0.f
)
{
self
.
topEyesocketLayer
.
strokeEnd
+=
0.1f
;
self
.
topEyesocketLayer
.
strokeStart
-=
0.1f
;
self
.
bottomEyesocketLayer
.
strokeEnd
+=
0.1f
;
self
.
bottomEyesocketLayer
.
strokeStart
-=
0.1f
;
}
}
if
(
y
>
flag
-
40
)
{
if
(
self
.
topEyesocketLayer
.
strokeEnd
>
0.5f
&&
self
.
topEyesocketLayer
.
strokeStart
<
0.5f
)
{
self
.
topEyesocketLayer
.
strokeEnd
-=
0.1f
;
self
.
topEyesocketLayer
.
strokeStart
+=
0.1f
;
self
.
bottomEyesocketLayer
.
strokeEnd
-=
0.1f
;
self
.
bottomEyesocketLayer
.
strokeStart
+=
0.1f
;
}
}
if
(
y
>
flag
-
20
)
{
if
(
self
.
eyeballLayer
.
opacity
>=
0.0f
)
{
self
.
eyeballLayer
.
opacity
-=
0.1f
;
}
}
if
(
y
>
flag
)
{
if
(
self
.
eyeFirstLightLayer
.
lineWidth
>
0.f
)
{
self
.
eyeFirstLightLayer
.
lineWidth
-=
1.f
;
self
.
eyeSecondLightLayer
.
lineWidth
-=
1.f
;
}
}
}
|
總之使用 UIbezierPath 和 CAShapeLayer 能夠畫出你想要的任何形狀,沒有它作不到,只有你想不到,搞定了它們你就能夠輕鬆定製你想要的任何控件了。
問啊-一鍵呼叫程序員答題神器,牛人一對一服務,開發者編程必備官方網站:www.wenaaa.com
QQ羣290551701 彙集不少互聯網精英,技術總監,架構師,項目經理!開源技術研究,歡迎業內人士,大牛及新手有志於從事IT行業人員進入!