UIBezierPath繼承自NSObject晕拆,位于UIKit框架中材蹬,是Core Graphics框架中與路徑相關(guān)的功能的封裝实幕。在iOS3.2及更高版本中整吆,可以使用UIBezierPath創(chuàng)建基于向量的路徑勇哗、繪制路徑的輪廓咽安、填充路徑包圍的空間等妆棒。其中路徑可以定義簡(jiǎn)單的形狀澡腾,如矩形,橢圓或弧形糕珊,也可以定義復(fù)合多邊形动分,包含直線和曲線段的混合。
1 Bezier path 與 UIBezierPath
Bezier path源于法國(guó)數(shù)學(xué)家皮埃爾·貝齊耶(PierreBézier)研制的“貝塞爾曲線”(Bezier curves)的概念红选。簡(jiǎn)單來(lái)說(shuō)澜公,它是幾個(gè)點(diǎn)之間的曲線,如下圖纠脾。
關(guān)于貝塞爾曲線的深入學(xué)習(xí)可以參考Bezier path 資料玛瘸。
UIBezierPath對(duì)象是CGPathRef數(shù)據(jù)類型的封裝,用于創(chuàng)建基于向量的路徑苟蹈,而路徑是由線和曲線構(gòu)建的糊渊。每組連接的線和曲線段形成所謂的子路徑,共享相同的繪圖屬性慧脱,要繪制具有不同屬性的子路徑渺绒,必須將每個(gè)子路徑放在其自己的UIBezierPath對(duì)象中。
2 創(chuàng)建UIBezierPath對(duì)象
<a id = "2.1">
2.1 使用bezierPath
方法創(chuàng)建自定義路徑對(duì)象
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
大多數(shù)情況下,對(duì)于多邊形或自定義圖形宗兼,我們一般從“空路徑”開(kāi)始躏鱼,通過(guò)移動(dòng)位置到初始點(diǎn),然后向下一個(gè)點(diǎn)添加線和弧線的方法創(chuàng)建路徑內(nèi)容殷绍。
</a>
2.2 使用bezierPathWithRect:
方法創(chuàng)建矩形路徑
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 200, 100)];
2.3 使用bezierPathWithRoundedRect:cornerRadius:
方法創(chuàng)建圓角矩形路徑
UIBezierPath *roundedRectPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(100, 100, 200, 100) cornerRadius:10];
2.4 使用 bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:
方法創(chuàng)建自定義的圓角矩形路徑
UIBezierPath *roundedCornerPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(100, 100, 200, 100) byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(20, 20)];
該方法可以指定將哪個(gè)角設(shè)置為圓角:
UIRectCornerTopLeft
(左上角)
UIRectCornerTopRight
(右上角)
UIRectCornerBottomLeft
(左下角)
UIRectCornerBottomRight
(右下角)
UIRectCornerAllCorners
(所有角)
2.5 使用bezierPathWithOvalInRect:
方法創(chuàng)建橢圓或圓形路徑
UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 200, 100)];
<a id="2.6">
2.6 使用 bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:
方法創(chuàng)建弧形路徑
UIBezierPath *arcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(200, 200) radius:100 startAngle:0 endAngle:M_PI clockwise:NO];
該方法中的參數(shù):
cenrer
用于指定弧形的圓心染苛;radius
指定弧形的半徑;startAngle
指定弧形的起始角度主到;endAngle
指定弧形的終止角度茶行;clockwise
指定了弧形的繪制方向,是否為順時(shí)針登钥。上面的代碼中closewise
設(shè)置為NO
畔师,表示逆時(shí)針繪制。另外牧牢,值得注意的是看锉,這里指定的起始和終止角度都需要以弧度表示,在默認(rèn)的坐標(biāo)系中繪制時(shí)塔鳍,起始和終止角度的定義基于下圖所示的單位圓伯铣。
</a>
2.7 使用bezierPathByReversingPath
方法創(chuàng)建反向路徑
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 200, 100)];
UIBezierPath *reversedPath = [rectPath bezierPathByReversingPath];
需要注意的是,這是一個(gè)實(shí)例方法献幔,而上面其他幾個(gè)都是類方法懂傀。使用該方法繪制時(shí),它不一定會(huì)改變路徑的形狀蜡感,相反蹬蚁,它改變的是路徑的繪制方向(交換起點(diǎn)和終點(diǎn)),從而可能影響到路徑的填充效果郑兴。上面的代碼中犀斋,
rectPath
默認(rèn)是順時(shí)針繪制的(如左圖所示),對(duì)它調(diào)用該方法后情连,雖然路徑形狀沒(méi)有變化叽粹,但是是逆時(shí)針?lè)较蚶L制的(如右圖所示)。
3 構(gòu)建自定義路徑
構(gòu)建自定義路徑却舀,首先需要使用2.1介紹的方法創(chuàng)建一個(gè)新的空路徑對(duì)象虫几,通過(guò)選擇一個(gè)初始點(diǎn)并調(diào)用moveToPoint:
方法移動(dòng)到該點(diǎn),然后就可以開(kāi)始向該點(diǎn)添加線或曲線挽拔。添加新的線或曲線總是假定從當(dāng)前位置開(kāi)始辆脸,并以指定的某個(gè)點(diǎn)結(jié)束。每次添加后螃诅,新一段的終點(diǎn)自動(dòng)變成下一段的起點(diǎn)啡氢,也是當(dāng)前點(diǎn)(currentPoint
)状囱。結(jié)束時(shí),通過(guò)調(diào)用closePath
方法從當(dāng)前點(diǎn)向子路徑中的第一個(gè)點(diǎn)添加一條直線段來(lái)關(guān)閉路徑倘是。
3.1 使用addLineToPoint:
方法構(gòu)建直線路徑
// 1 創(chuàng)建路徑對(duì)象
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
// 2 移動(dòng)到起點(diǎn)
[bezierPath moveToPoint:CGPointMake(100, 100)];
// 3 添加直線
[bezierPath addLineToPoint:CGPointMake(300, 300)];
3.2 使用addArcWithCenter:radius:startAngle:endAngle:clockwise:
方法構(gòu)建弧形路徑
// 1 創(chuàng)建路徑對(duì)象
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
// 2 移動(dòng)到起點(diǎn)
[bezierPath moveToPoint:CGPointMake(100, 100)];
// 3 添加弧線
[bezierPath addArcWithCenter:CGPointMake(200, 200) radius:100 startAngle:0 endAngle:M_PI clockwise:YES];
這里定義弧線的方法與創(chuàng)建弧形路徑對(duì)象的方法類似亭枷,詳細(xì)參數(shù)可以參考2.6中的說(shuō)明。
3.3 使用addQuadCurveToPoint:controlPoint:
方法和addCurveToPoint:controlPoint1:controlPoint2:
方法構(gòu)建曲線路徑
我們常見(jiàn)的曲線形狀是使用起點(diǎn)和終點(diǎn)之間的切線以及一個(gè)或多個(gè)控制點(diǎn)進(jìn)行定義的搀崭。UIBezierPath為我們提供了二次貝塞爾曲線(只有一個(gè)控制點(diǎn))和三次貝塞爾曲線(有兩個(gè)控制點(diǎn))的繪制方法叨粘。下圖展示了這兩種曲線的區(qū)別。
3.3.1 二次曲線 (quadratic bezier curve)
// 1 創(chuàng)建路徑對(duì)象
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
// 2 移動(dòng)到起點(diǎn)
[bezierPath moveToPoint:CGPointMake(100, 200)];
// 3 添加二次曲線
[bezierPath addQuadCurveToPoint:CGPointMake(300, 200) controlPoint:CGPointMake(250, 100)];
3.3.2 三次曲線 (cubic bezier curve)
// 1 創(chuàng)建路徑對(duì)象
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
// 2 移動(dòng)到起點(diǎn)
[bezierPath moveToPoint:CGPointMake(100, 200)];
// 3 添加三次曲線
[bezierPath addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(200, 100) controlPoint2:CGPointMake(200, 300)];
3.4 使用closePath
方法關(guān)閉路徑
// 1 創(chuàng)建路徑對(duì)象
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
// 2 移動(dòng)到起點(diǎn)
[bezierPath moveToPoint:CGPointMake(200, 200)];
// 3 添加直線
[bezierPath addLineToPoint:CGPointMake(300, 400)];
[bezierPath addLineToPoint:CGPointMake(100, 400)];
// 4 關(guān)閉路徑
[bezierPath closePath];
使用該方法閉合路徑時(shí)瘤睹,會(huì)添加一條從當(dāng)前點(diǎn)到起點(diǎn)的直線宣鄙,因此在上面的代碼中,相當(dāng)于我們使用
[bezierPath closePath]
替代了[bezierPath addLineToPoint:CGPointMake(200, 200)]
默蚌。
3.5 使用appendPath:
方法追加路徑
// 1 創(chuàng)建路徑
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath addArcWithCenter:CGPointMake(200, 200) radius:110 startAngle:0 endAngle:2 * M_PI clockwise:YES];
// 2 追加一個(gè)圓形路徑
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(125, 125, 150, 150)];
[bezierPath appendPath:circle];
// 3 追加一個(gè)正方形路徑
UIBezierPath *square = [UIBezierPath bezierPathWithRect:CGRectMake(180, 180, 40, 40)];
[bezierPath appendPath:square];
3.6 使用removeAllPoints
方法刪除路徑
// 1 創(chuàng)建路徑
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath addArcWithCenter:CGPointMake(200, 200) radius:110 startAngle:0 endAngle:2 * M_PI clockwise:YES];
// 2 追加一個(gè)圓形路徑
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(125, 125, 150, 150)];
[bezierPath appendPath:circle];
// 3 創(chuàng)建一個(gè)正方形路徑
UIBezierPath *square = [UIBezierPath bezierPathWithRect:CGRectMake(180, 180, 40, 40)];
// 4 刪除該正方形路徑
[square removeAllPoints];
// 5 追加該正方形路徑
[bezierPath appendPath:square];
4 渲染路徑
4.1 設(shè)置渲染路徑的屬性
4.1.1 lineWidth
渲染路徑的線條寬度
默認(rèn)是1.0。
4.1.2 lineCapStyle
渲染路徑的線條端點(diǎn)的形狀:kCGLineCapButt
(默認(rèn))苇羡、kCGLineCapRound
绸吸、kCGLineCapSquare
。
4.1.3 lineJoinStyle
渲染路徑的線條接合點(diǎn)的形狀:kCGLineJoinMiter
(默認(rèn))设江、kCGLineJoinRound
锦茁、kCGLineJoinBevel
。
4.1.4 miterLimit
渲染路徑時(shí)用于避免線段連接處形成尖峰的斜角極限
默認(rèn)是10叉存。
4.1.5 flatness
渲染曲線路徑的平坦度
默認(rèn)平坦度值為0.6码俩。在大多數(shù)情況下,我們不需要更改平坦度歼捏。
4.1.6 usesEvenOddFillRule
渲染路徑時(shí)是否使用奇偶規(guī)則
如果為YES稿存,則使用奇偶規(guī)則填充路徑。如果為NO瞳秽,則使用非零規(guī)則填充瓣履。默認(rèn)是NO。
填充路徑時(shí)练俐,一般通過(guò)兩種方式計(jì)算填充區(qū)域:非零繞組數(shù)規(guī)則(nonzero winding number rule)和奇偶規(guī)則(even-odd rule)袖迎。
要確定哪些區(qū)域被填充,一般從給定區(qū)域內(nèi)的點(diǎn)開(kāi)始腺晾,向路徑邊界以外的任何點(diǎn)繪制一條射線燕锥,交叉路徑線的總數(shù)決定了填充區(qū)域。對(duì)于非零規(guī)則悯蝉,繪制路徑段的方向影響結(jié)果归形。從左到右路徑的交叉點(diǎn)計(jì)數(shù)為+1,從右到左路徑的交叉點(diǎn)計(jì)數(shù)為-1泉粉。如果交叉點(diǎn)的總和不為0连霉,則該點(diǎn)被認(rèn)為在路徑內(nèi)榴芳,并且對(duì)應(yīng)的區(qū)域被填充。如果交叉點(diǎn)的總和為0跺撼,則該點(diǎn)在路徑外部窟感,并且該區(qū)域不被填充。上圖左邊顯示了使用非零繞組數(shù)規(guī)則填充的兩組內(nèi)圈和外圈歉井。當(dāng)每個(gè)圓圈以相同方向繪制時(shí)柿祈,兩個(gè)圓圈都被填充。當(dāng)圓圈以相反方向繪制時(shí)哩至,內(nèi)圈未被填充躏嚎。
對(duì)于奇偶規(guī)則,計(jì)算路徑交叉的總數(shù)菩貌,如果結(jié)果為奇數(shù)卢佣,則該點(diǎn)被認(rèn)為在路徑內(nèi),并且對(duì)應(yīng)的區(qū)域被填充箭阶。如果結(jié)果為偶數(shù)虚茶,則該點(diǎn)被認(rèn)為是在路徑之外,并且該區(qū)域不被填充仇参。繪制路徑段的方向不影響結(jié)果嘹叫。如上圖右邊所示,繪制每個(gè)圓的方向并不重要诈乒,填充將始終如圖所示罩扇。
<a id="4.1.7">
4.1.7 setLineDash:count:phase:
設(shè)置渲染路徑的線條形狀
// 1 創(chuàng)建路徑對(duì)象
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 100, 100)];
// 2 定義一個(gè)浮點(diǎn)值的C風(fēng)格數(shù)組以指定線段的長(zhǎng)度及間隔
CGFloat pattern[4] = {8.0, 3.0, 20.0, 5.0};
// 3 設(shè)置線條形狀
[rectPath setLineDash:pattern count:4 phase:0];
// 4 設(shè)置線條寬度
[rectPath setLineWidth:4.0];
// 5 繪制路徑
[rectPath stroke];
該方法中怜奖,第一個(gè)參數(shù)
pattern
是浮點(diǎn)值的C風(fēng)格數(shù)組之景,指定了線段的長(zhǎng)度及間隔这橙,數(shù)組中的值交替定義媳拴,第一個(gè)是線段的長(zhǎng)度蜂科,后跟第一個(gè)線段間隔長(zhǎng)度涂召,再是第二個(gè)線段長(zhǎng)度瓶埋,再跟第二個(gè)線段間隔長(zhǎng)度篮愉,以此類推滩届。
第二個(gè)參數(shù)count
指定了pattern數(shù)組中的元素個(gè)數(shù)集侯。
第三個(gè)參數(shù)phase
指定畫線模式的起始點(diǎn),即開(kāi)始繪制時(shí)的偏移位置帜消,以畫線模式的點(diǎn)計(jì)算棠枉,正值向右偏移,負(fù)值向左偏移泡挺。
</a>
4.1.8 getLineDash:count:phase:
檢索渲染路徑的線條畫線模式
參數(shù)同上
4.2 渲染路徑
4.2.1 使用fill
方法填充由路徑包圍的區(qū)域
// 1 創(chuàng)建一個(gè)矩形路徑對(duì)象
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 100, 100)];
// 2 設(shè)置路徑填充顏色
[[UIColor redColor] setFill];
// 3 填充路徑
[rectPath fill];
<a id = "4.2.2">
4.2.2 使用fillWithBlendMode:alpha:
方法指定混合模式和透明度值來(lái)填充路徑
// 1 創(chuàng)建一個(gè)背景矩形對(duì)象
UIBezierPath *backRect = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 200, 200)];
// 2 設(shè)置背景矩形的填充顏色
[[UIColor greenColor] setFill];
// 3 填充背景矩形
[backRect fill];
// 4 創(chuàng)建一個(gè)前景矩形對(duì)象
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 100, 100)];
// 5 設(shè)置前景矩形的填充顏色
[[UIColor redColor] setFill];
// 6 使用指定的混合模式和透明度值填充前景路徑
[rectPath fillWithBlendMode:kCGBlendModeNormal alpha:0.5];
該方法中有兩個(gè)參數(shù)辈讶。
第一個(gè)參數(shù)blendMode
用于確定填充路徑如何與現(xiàn)有的渲染內(nèi)容合成,在上面的代碼中我們的使用正陈γǎ混合模式kCGBlendModeNormal
贱除,除此之外還有多重混合模式kCGBlendModeMultiply
和疊加混合模式kCGBlendModeOverlay
等生闲,詳細(xì)內(nèi)容可以參考文檔中關(guān)于CGBlendMode的介紹。
第二個(gè)參數(shù)alpha
用于確定填充路徑的透明度月幌,范圍在0.0(透明)和1.0(不透明)之間碍讯。
</a>
4.2.3 使用stroke
方法跟蹤并繪制路徑的輪廓
// 1 創(chuàng)建一個(gè)矩形路徑對(duì)象
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 100, 100)];
// 2 設(shè)置繪制路徑的畫筆顏色
[[UIColor redColor] setStroke];
// 3 設(shè)置繪制路徑的線條寬度
[rectPath setLineWidth:3.0];
// 4 繪制路徑
[rectPath stroke];
4.2.4 使用strokeWithBlendMode:alpha:
方法指定混合模式和透明度值來(lái)繪制路徑
// 1 創(chuàng)建背景矩形
UIBezierPath *backRect = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 200, 200)];
[[UIColor greenColor] setFill];
[backRect fill];
// 2 創(chuàng)建前景矩形
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 100, 100)];
// 2.1 設(shè)置繪制前景矩形的畫筆顏色
[[UIColor redColor] setStroke];
// 2.2 設(shè)置繪制路徑的線條寬度
[rectPath setLineWidth:10.0];
// 2.3 使用指定的混合模式和透明度值填充前景路徑
[rectPath strokeWithBlendMode:kCGBlendModeNormal alpha:0.5];
該方法中的參數(shù)說(shuō)明可以參考
fillWithBlendMode:alpha:
方法中的注釋。
5 剪切路徑
使用addClip
方法剪切路徑
// 1 創(chuàng)建兩個(gè)圓形路徑
UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 200) radius:100 startAngle:0 endAngle:2 * M_PI clockwise:YES];
UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(250, 200) radius:100 startAngle:0 endAngle:2 * M_PI clockwise:YES];
// 2 繪制路徑
[path1 stroke];
[path2 stroke];
// 3 剪切路徑
[path1 addClip];
// 4 填充路徑
[[UIColor redColor] setFill];
[path2 fill];
該方法會(huì)修改繪圖的可見(jiàn)區(qū)域扯躺,調(diào)用之后捉兴,隨后的繪圖操作只對(duì)指定路徑內(nèi)的區(qū)域有效。在上面的代碼中录语,
path1
剪切路徑后倍啥,path2
只保留與path1
相交的路徑區(qū)域,因此在對(duì)path2
進(jìn)行填充操作時(shí)澎埠,只有相交區(qū)域被填充虽缕。
6 轉(zhuǎn)換路徑
使用applyTransform:
方法轉(zhuǎn)換路徑
// 1 創(chuàng)建路徑對(duì)象
UIBezierPath *originPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 100)];
// 2 轉(zhuǎn)換路徑
[originPath applyTransform:CGAffineTransformMakeTranslation(100, 100)];
// 3 填充路徑
[[UIColor redColor] setFill];
[originPath fill];
該方法會(huì)使用指定的仿射變換矩陣來(lái)轉(zhuǎn)換路徑中的所有點(diǎn),這里的參數(shù)
transform
就是用于確定將哪種轉(zhuǎn)換矩陣應(yīng)用于路徑中彼宠,在上面的代碼中使用了CGAffineTransformMakeTranslation
,除此之外還有CGAffineTransformMakeScale
和CGAffineTransformMakeRotation
等,詳細(xì)內(nèi)容可以參考文檔中關(guān)于CGAffineTransform的介紹弟塞。
7 路徑的點(diǎn)擊測(cè)試
7.1 使用currentPoint
屬性表示路徑的當(dāng)前點(diǎn)
// 1 創(chuàng)建路徑
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(100, 100)];
[path addArcWithCenter:CGPointMake(200, 200) radius:50 startAngle:1.5 * M_PI endAngle:M_PI clockwise:NO];
// 2 繪制路徑
[path setLineWidth:2.0];
[path stroke];
// 3 打印路徑的當(dāng)前點(diǎn)
NSLog(@"The current point of the path is (%.2f, %.2f)", path.currentPoint.x, path.currentPoint.y);
該屬性中的值也表示新路徑段的起點(diǎn)。如果路徑當(dāng)前為空拙已,則該屬性包含
CGPointZero
决记。
7.2 使用bounds
屬性表示完全包圍路徑中所有點(diǎn)的最小矩形
// 1 創(chuàng)建一條直線路徑
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(100, 100)];
[path addLineToPoint:CGPointMake(300, 300)];
// 2 繪制直線路徑
[path setLineWidth:3.0];
[[UIColor redColor] setStroke];
[path stroke];
// 3 以直線路徑的邊界創(chuàng)建一個(gè)矩形路徑
UIBezierPath *boundsPath = [UIBezierPath bezierPathWithRect:path.bounds];
// 4 繪制矩形路徑
[boundsPath setLineWidth:5.0];
[[UIColor blueColor] setStroke];
[boundsPath stroke];
7.3 使用empty
屬性判斷路徑中是否具有任何有效路徑元素
// 1 創(chuàng)建路徑對(duì)象
UIBezierPath *path = [UIBezierPath bezierPath];
// 2 移動(dòng)路徑到指定點(diǎn)
[path moveToPoint:CGPointMake(100, 100)];
// 3 點(diǎn)擊測(cè)試
if (path.empty == YES) {
NSLog(@"The path has no valid elements");
}
else if (path.empty == NO) {
NSLog(@"The path has some valid elements");
}
這里所說(shuō)的有效路徑元素包括移動(dòng)到指定點(diǎn)的命令、繪制線或曲線的命令以及關(guān)閉路徑的命令倍踪。 因此系宫,即使上面的代碼只調(diào)用了
moveToPoint:
方法,路徑也不被視為空建车。
7.4 使用containsPoint:
方法判斷路徑包圍的區(qū)域是否包含指定的點(diǎn)扩借。
// 1 創(chuàng)建路徑
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(200, 200) radius:100 startAngle:0 endAngle:2 * M_PI clockwise:YES];
[path appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(200, 200) radius:75 startAngle:0 endAngle:2 * M_PI clockwise:NO]];
[path appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(200, 200) radius:50 startAngle:0 endAngle:2 * M_PI clockwise:YES]];
// 2 設(shè)置渲染路徑的屬性
[[UIColor redColor] set];
[path setLineWidth:3.0];
// 3 點(diǎn)擊測(cè)試
if ([path containsPoint:CGPointMake(260, 210)] == YES) {
[path fill];
}
else if ([path containsPoint:CGPointMake(260, 210)] == NO) {
[path stroke];
}
8 UIBezierPath的應(yīng)用
UIBezierPath常見(jiàn)的用途之一就是將圖像裁剪成自定義形狀,如用戶頭像等缤至。
這里我們所介紹的demo也將展示圖片裁剪的過(guò)程潮罪,除此之外,還展示了刮開(kāi)涂層(類似于刮刮樂(lè))的效果领斥。大致思路是嫉到,在視圖中的同一位置添加兩個(gè)大小相同的imageView使之重疊,然后分別將圖片裁剪為相同的圓形月洛,同樣是重疊的效果何恶,即上面一張圓形圖片(前景)完全覆蓋下面一張圓形圖片(背景),當(dāng)我們刮開(kāi)上面的圖片嚼黔,下面的圖片逐漸顯示细层,最終效果圖如下:
完整代碼可以參考UIBezierPathDemo。下面是詳細(xì)介紹疫赎。
8.1 創(chuàng)建項(xiàng)目
打開(kāi)Xcode撵彻,創(chuàng)建一個(gè)新的項(xiàng)目(File\New\Project...)陌僵,選擇iOS一欄下Application中的Single View Application模版,點(diǎn)擊Next偎谁。
在Product Name中填寫UIBezierPathDemo,點(diǎn)擊Next铐望。選擇文件位置正蛙,點(diǎn)擊Create創(chuàng)建工程。
8.2 構(gòu)建界面
在這里我們使用純代碼構(gòu)建锻全。打開(kāi)ViewController.m文件,定義兩個(gè)UIImageView對(duì)象的屬性部翘。
@interface ViewController ()
@property (nonatomic, strong) UIImageView *bgImageView; // 背景
@property (nonatomic, strong) UIImageView *fgImageView; // 前景
@end
打開(kāi)Assets.xcassets文件新思,添加兩張圖片(圖片資源)纵刘。
打開(kāi)ViewController.m文件,在viewDidLoad
方法中添加下面的代碼舵抹。
- (void)viewDidLoad
{
[super viewDidLoad];
// 創(chuàng)建背景imageView并添加到視圖中
self.bgImageView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 150, 300, 300)];
[self.view addSubview:self.bgImageView];
// 為背景imageView設(shè)置圖片
UIImage *bgImage = [UIImage imageNamed:@"bgImage"];
self.bgImageView.image = bgImage;
// 創(chuàng)建前景imageView并添加到視圖中
self.fgImageView = [[UIImageView alloc] initWithFrame:self.bgImageView.frame];
[self.view addSubview:self.fgImageView];
// 為前景imageView設(shè)置圖片
UIImage *fgImage = [UIImage imageNamed:@"fgImage"];
self.fgImageView.image = fgImage;
}
運(yùn)行一下,可以看到只顯示了前景圖片(背景圖片被覆蓋)香嗓。
8.3 裁剪圖片
在viewDidLoad
方法的下面,我們定義一個(gè)clipImage:
方法用于執(zhí)行裁剪圖片的操作饱岸,代碼如下:
- (UIImage *)clipImage:(UIImage *)image
{
// 開(kāi)啟一個(gè)與圖像大小一致的圖形上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 創(chuàng)建一個(gè)圓形的路徑
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
// 剪切圓形路徑
[path addClip];
// 將圖片繪制到圖形上下文中
[image drawAtPoint:CGPointZero];
// 從當(dāng)前圖形上下文中獲取被裁剪的圖片
UIImage *clippedImage = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉圖形上下文
UIGraphicsEndImageContext();
// 返回被裁剪的圖片
return clippedImage;
}
在這里我們沒(méi)有將繪制方法寫在
drawRect:
方法中汤锨,而是單獨(dú)寫在了自定義的裁剪方法里牍汹,所以我們沒(méi)有任何上下文可用慎菲,因此要生成一個(gè)新的圖像就需要開(kāi)啟一個(gè)新的圖形上下文,相關(guān)內(nèi)容可以參考Drawing and Creating Images文檔中的描述解幼。
裁剪圖片主要是通過(guò)對(duì)UIBezierPath對(duì)象調(diào)用addClip
方法來(lái)實(shí)現(xiàn)的底靠,上面的代碼中最后返回一個(gè)從圖形上下文中獲取到UIImage對(duì)象(即被裁剪后的圖像)暑中。
之后,在viewDidLoad
方法中通過(guò)調(diào)用clipImage:
方法严衬,將imageView的圖片重新設(shè)置為被裁剪的圖片请琳,修改后的代碼如下:
- (void)viewDidLoad
{
...
// 為背景imageView設(shè)置圖片
UIImage *bgImage = [UIImage imageNamed:@"bgImage"];
self.bgImageView.image = [self clipImage:bgImage];
...
// 為前景imageView設(shè)置圖片
UIImage *fgImage = [UIImage imageNamed:@"fgImage"];
self.fgImageView.image = [self clipImage:fgImage];
}
運(yùn)行一下榕堰,可以看到圖片被裁剪為圓形
8.4 實(shí)現(xiàn)刮開(kāi)涂層的效果
刮開(kāi)涂層的效果主要是通過(guò)重寫touchesMoved:withEvent:
方法實(shí)現(xiàn)的圾旨,詳細(xì)代碼如下:
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
// 創(chuàng)建一個(gè)UITouch對(duì)象
UITouch *touch = touches.anyObject;
// 設(shè)置觸摸位置在前景圖片上的坐標(biāo)
CGPoint touchPoint = [touch locationInView:self.fgImageView];
// 設(shè)置觸摸時(shí)清除點(diǎn)的大小
CGRect touchRect = CGRectMake(touchPoint.x, touchPoint.y, 20, 20);
// 開(kāi)啟一個(gè)與前景圖像大小一致的圖形上下文
UIGraphicsBeginImageContextWithOptions(self.fgImageView.bounds.size, NO, 0);
// 獲取當(dāng)前上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 將前景圖像視圖的圖層渲染到當(dāng)前上下文中
[self.fgImageView.layer renderInContext:context];
// 清除觸摸過(guò)的區(qū)域
CGContextClearRect(context, touchRect);
// 從當(dāng)前圖形上下文中獲取圖片(即刮開(kāi)的圖片)
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉圖形上下文
UIGraphicsEndImageContext();
// 設(shè)置前景imageView的圖片為刮開(kāi)的圖片
self.fgImageView.image = image;
}
運(yùn)行程序,用鼠標(biāo)在圖片上劃動(dòng)廓鞠,可以看到如下效果:
關(guān)于touchesMoved:withEvent:的詳細(xì)介紹可以參考文檔。
9 參考資料
1 UIBezierPath - UIKit | Apple Developer Documentation