UIBezierPath

引言


記得剛接觸 iOS 的時(shí)候, 第一次聽(tīng)到貝塞爾曲線這個(gè)東西的時(shí)候, 當(dāng)時(shí)我只有一個(gè)感覺(jué), 那就是: 什么鬼? 我靠, 聽(tīng)起來(lái)好高大上的一個(gè)東西. 趁著最近這段時(shí)間不是很忙, 把 UIBezierPath 系統(tǒng)的學(xué)習(xí)了一下. 后來(lái)發(fā)現(xiàn), 其實(shí)也就那么回事兒, 哪兒有想象中的那么高大上! -.-!!! 先來(lái)介紹一下 UIBezierPath, 然后我們就直接從UIBezierPath.h 入手吧. 先看看這家伙都有什么好玩的東西.

UIBezierPath 概述


UIBezierPath API Reference
The UIBezierPath class lets you define a path consisting of straight and curved line segments and render that path in your custom views. You use this class initially to specify just the geometry for your path. Paths can define simple shapes such as rectangles, ovals, and arcs or they can define complex polygons that incorporate a mixture of straight and curved line segments. After defining the shape, you can use additional methods of this class to render the path in the current drawing context.

UIBezierPath 類允許你在自定義的 View 中繪制和渲染由直線和曲線組成的路徑. 你可以在初始化的時(shí)候, 直接為你的 UIBezierPath 指定一個(gè)幾何圖形. 路徑可以是簡(jiǎn)單的幾何圖形例如: 矩形、橢圓滚朵、弧線之類的, 也可以是相對(duì)比較復(fù)雜的由直線和曲線組成的多邊形. 當(dāng)你定義完圖形以后, 你可以使用額外的方法將你的路徑直接繪制在當(dāng)前的繪圖上下文中.
UIBezierPath 是由幾何路徑和屬性組成的, 屬性是用來(lái)在渲染階段描繪幾何路徑的, 比如線寬之類的東西. 路徑和屬性是完全獨(dú)立的, 他們并不互相依賴, 你可以分開(kāi)分開(kāi)去設(shè)置他們. 一旦你以自己喜歡的方式配置了 UIBezierPath 對(duì)象, 你就可以調(diào)用方法通知UIBezierPath 在當(dāng)前的繪圖上下文中繪制圖形了. 因?yàn)閯?chuàng)建爆哑、 配置钞艇、 渲染路徑等操作, 都是完全不同的步驟, 所以你可以在你的代碼中非常容易的對(duì)UIBezierPath 對(duì)象進(jìn)行復(fù)用. 你甚至可以使用同一個(gè) UIBezierPath 對(duì)象去渲染同一個(gè)圖形很多次, 你也可以再多次渲染的間隔中, 修改屬性來(lái)渲染出不同樣式的路徑.
當(dāng)你創(chuàng)建了一個(gè)空的UIBezierPath 對(duì)象時(shí), currentPoint 這個(gè)屬性是未定義的, 你需要手動(dòng)的去設(shè)置.currentPoint. 如果你希望在不繪制任何線條的情況下移動(dòng)currentPoint, 你可以使用 moveToPoint:方法. 其他的方法都會(huì)導(dǎo)致在你的路徑中添加額外的直線或曲線. 所有構(gòu)造路徑相關(guān)的方法, 都會(huì)以當(dāng)前路徑的currentPoint 為起點(diǎn), 以你指定的endPoint為終點(diǎn)進(jìn)行繪制. 當(dāng)完成繪制之后, 會(huì)自動(dòng)將新增的這條線的終點(diǎn)設(shè)置為UIBezierPath 對(duì)象的currentPoint.
一個(gè)簡(jiǎn)單的UIBezierPath 可以包含許多的開(kāi)放子路徑封閉子路徑. 調(diào)用closePath方法將會(huì)閉合路徑, 它將會(huì)從currentPoint 到 子路經(jīng)的 firstPoint繪制一條直線. 調(diào)用moveToPoint:方法將會(huì)結(jié)束當(dāng)前的子路徑, 但是并不會(huì)自動(dòng)閉合當(dāng)前的自路徑, 并且會(huì)將currentPoint 移動(dòng)到指定的點(diǎn), 也就是下一條繪制路徑的起始點(diǎn). UIBezierPath 中所有的自路徑都會(huì)共享同樣的繪圖屬性. 如果你希望繪制一些子路徑, 但是不適用相同的繪圖屬性, 那么你就只能創(chuàng)建很多的UIBezierPath 對(duì)象來(lái)承載每一條路徑.
當(dāng)你為UIBezierPath 對(duì)象配置完幾何路徑繪圖屬性之后, 你就可以使用strokefill 方法在當(dāng)前的繪圖上下文中進(jìn)行繪制了. stroke方法將會(huì)使用當(dāng)前的strokeColor繪圖屬性來(lái)描繪曲線的輪廓. 同樣的, fill 方法將會(huì)使用fillColor 來(lái)填充路徑所圍成的圖形(使用UIColor 類方法來(lái)設(shè)置strokeColorfillColor).

In addition to using a Bezier path object to draw shapes, you can also use it to define a new clipping region. The addClip
method intersects the shape represented by the path object with the current clipping region of the graphics context. During subsequent drawing, only content that lies within the new intersection region is actually rendered to the graphics context.

還有這么一段, 大家自己看, 這段我用語(yǔ)言組織不好. -.-!!! 求大神來(lái)個(gè)完美翻譯啊.

UIBezierPath.h


創(chuàng)建 UIBezierPath

+ (instancetype) bezierPath;
/**
  * 該方法將會(huì)創(chuàng)建一個(gè)閉合路徑, 起始點(diǎn)是 rect 參數(shù)的的 origin, 并且按照順時(shí)針?lè)较蛱砑又本€, 最終形成矩形
  * @param rect:   矩形路徑的 Frame
  */
+ (instancetype)bezierPathWithRect:(CGRect)rect;
/**
  * 該方法將會(huì)創(chuàng)建一個(gè)閉合路徑,  該方法會(huì)通過(guò)順時(shí)針的繪制貝塞爾曲線, 繪制出一個(gè)近似橢圓的形狀. 如果 rect 參數(shù)指定了一個(gè)矩形, 那么該 UIBezierPath 對(duì)象將會(huì)描述一個(gè)圓形.
  * @param rect:   矩形路徑的 Frame
  */
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
/**
  * 該方法將會(huì)創(chuàng)建一個(gè)閉合路徑,  該方法會(huì)順時(shí)針?lè)较蜻B續(xù)繪制直線和曲線.  當(dāng) rect 為正方形時(shí)且 cornerRadius 等于邊長(zhǎng)一半時(shí), 則該方法會(huì)描述一個(gè)圓形路徑.
  * @param rect:   矩形路徑的 Frame
  * @param cornerRadius:   矩形的圓角半徑
  */
+ (instancetype) bezierPathWithRoundedRect:(CGRect)rect 
                              cornerRadius:(CGFloat)cornerRadius;
/**
  * 該方法將會(huì)創(chuàng)建一個(gè)閉合路徑,  該方法會(huì)順時(shí)針?lè)较蜻B續(xù)繪制直線和曲線.  
  * @param rect:   矩形路徑的 Frame
  * @param corners:   UIRectCorner 枚舉類型, 指定矩形的哪個(gè)角變?yōu)閳A角
  * @param cornerRadii:   矩形的圓角半徑
  */
+ (instancetype) bezierPathWithRoundedRect:(CGRect)rect 
                         byRoundingCorners:(UIRectCorner)corners
                               cornerRadii:(CGSize)cornerRadii;
/**
  * 該方法會(huì)創(chuàng)建出一個(gè)開(kāi)放路徑, 創(chuàng)建出來(lái)的圓弧是圓的一部分. 在默認(rèn)的坐標(biāo)系統(tǒng)中, 開(kāi)始角度 和 結(jié)束角度 都是基于單位圓的(看下面這張圖). 調(diào)用這個(gè)方法之后, currentPoint 將會(huì)設(shè)置為圓弧的結(jié)束點(diǎn).
  * 舉例來(lái)說(shuō): 指定其實(shí)角度為0, 指定結(jié)束角度為π, 設(shè)置 clockwise 屬性為 YES, 將會(huì)繪制出圓的下半部分.
  * 然而當(dāng)我們不修改起始角度 和 結(jié)束角度, 我們僅僅將 clockwise 角度設(shè)置為 NO, 則會(huì)繪制出來(lái)一個(gè)圓的上半部分.
  * @param center:   圓心
  * @param radius: 半徑
  * @param startAngle:   起始角度
  * @param endAngle:   結(jié)束角度
  * @param clockwise:   是否順時(shí)針繪制
  */
+ (instancetype) bezierPathWithArcCenter:(CGPoint)center 
                                  radius:(CGFloat)radius 
                              startAngle:(CGFloat)startAngle 
                                endAngle:(CGFloat)endAngle 
                               clockwise:(BOOL)clockwise;
圖片摘自蘋(píng)果官網(wǎng)
+ (instancetype) bezierPathWithCGPath:(CGPathRef)CGPath;
/**
  * 通過(guò)該方法反轉(zhuǎn)一條路徑, 并不會(huì)修改該路徑的樣子. 它僅僅是修改了繪制的方向
  * @return: 返回一個(gè)新的 UIBezierPath 對(duì)象, 形狀和原來(lái)路徑的形狀一樣,
  *          但是繪制的方向相反.
  */
- (UIBezierPath *) bezierPathByReversingPath;

構(gòu)造路徑

/**
  * 如果當(dāng)前有正在繪制的子路徑, 該方法則會(huì)隱式的結(jié)束當(dāng)前路徑, 
  * 并將 currentPoint 設(shè)置為指定點(diǎn). 當(dāng)上一條子路徑被終止, 該方法
  * 實(shí)際上并不會(huì)去閉合上一條子路徑. 所以上一條自路徑的起始點(diǎn) 和
  * 結(jié)束點(diǎn)并沒(méi)有被鏈接.
  * 對(duì)于大多數(shù)構(gòu)造路徑相關(guān)的方法而言, 在你繪制直線或曲線之前, 需要先調(diào)用這個(gè)方法.
  * @param point:   當(dāng)前坐標(biāo)系統(tǒng)中的某一點(diǎn)
  */
- (void)moveToPoint:(CGPoint)point;
/**
  * 該方法將會(huì)從 currentPoint 到 指定點(diǎn) 鏈接一條直線. 
  * Note: 在追加完這條直線后, 該方法將會(huì)更新 currentPoint 為 指定點(diǎn)
  *       調(diào)用該方法之前, 你必須先設(shè)置 currentPoint. 如果當(dāng)前繪制路徑
  *       為空, 并且未設(shè)置 currentPoint, 那么調(diào)用該方法將不會(huì)產(chǎn)生任何
  *       效果.
  * @param point:   繪制直線的終點(diǎn)坐標(biāo), 當(dāng)前坐標(biāo)系統(tǒng)中的某一點(diǎn)
  */
- (void)addLineToPoint:(CGPoint)point;
/**
  * 該方法將會(huì)從 currentPoint 添加一條指定的圓弧.
  * 該方法的介紹和構(gòu)造方法中的一樣. 請(qǐng)前往上文查看
  * @param center: 圓心
  * @param radius: 半徑
  * @param startAngle: 起始角度
  * @param endAngle: 結(jié)束角度
  * @param clockwise: 是否順時(shí)針繪制
  */
- (void)addArcWithCenter:(CGPoint)center 
                  radius:(CGFloat)radius 
              startAngle:(CGFloat)startAngle 
                endAngle:(CGFloat)endAngle 
               clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
/**
  * 該方法將會(huì)從 currentPoint 到 指定的 endPoint 追加一條三次貝塞爾曲線.
  * 三次貝塞爾曲線的彎曲由兩個(gè)控制點(diǎn)來(lái)控制. 如下圖所示
  * Note: 調(diào)用該方法前, 你必須先設(shè)置 currentPoint, 如果路徑為空, 
  *       并且尚未設(shè)置 currentPoint, 調(diào)用該方法則不會(huì)產(chǎn)生任何效果. 
  *       當(dāng)添加完貝塞爾曲線后, 該方法將會(huì)自動(dòng)更新 currentPoint 為
  *       指定的結(jié)束點(diǎn)
  * @param endPoint: 終點(diǎn)
  * @param controlPoint1: 控制點(diǎn)1
  * @param controlPoint2: 控制點(diǎn)2
  */
- (void)addCurveToPoint:(CGPoint)endPoint 
          controlPoint1:(CGPoint)controlPoint1 
          controlPoint2:(CGPoint)controlPoint2;
圖片摘自蘋(píng)果官網(wǎng)
/**
  * 該方法將會(huì)從 currentPoint 到 指定的 endPoint 追加一條二次貝塞爾曲線.
  * currentPoint、endPoint居扒、controlPoint 三者的關(guān)系最終定義了二次貝塞爾曲線的形狀.
  * 二次貝塞爾曲線的彎曲由一個(gè)控制點(diǎn)來(lái)控制. 如下圖所示
  * Note: 調(diào)用該方法前, 你必須先設(shè)置 currentPoint, 如果路徑為空, 
  *       并且尚未設(shè)置 currentPoint, 調(diào)用該方法則不會(huì)產(chǎn)生任何效果. 
  *       當(dāng)添加完貝塞爾曲線后, 該方法將會(huì)自動(dòng)更新 currentPoint 為
  *       指定的結(jié)束點(diǎn)
  * @param endPoint: 終點(diǎn)
  * @param controlPoint: 控制點(diǎn)
  */
- (void)addQuadCurveToPoint:(CGPoint)endPoint 
               controlPoint:(CGPoint)controlPoint;
圖片摘自蘋(píng)果官網(wǎng)
/**
  * 該方法將會(huì)從 currentPoint 到子路經(jīng)的起點(diǎn) 繪制一條直線, 
  * 以此來(lái)關(guān)閉當(dāng)前的自路徑. 緊接著該方法將會(huì)更新 currentPoint
  * 為 剛添加的這條直線的終點(diǎn), 也就是當(dāng)前子路經(jīng)的起點(diǎn).
  */
- (void)closePath;
- (void)removeAllPoints;
/**
  * 該方法將會(huì)在當(dāng)前 UIBezierPath 對(duì)象的路徑中追加
  * 指定的 UIBezierPath 對(duì)象中的內(nèi)容. 
  */
- (void)appendPath:(UIBezierPath *)bezierPath;
/**
  * 獲取這個(gè)屬性, 你將會(huì)獲得一個(gè)不可變的 CGPathRef 對(duì)象,
  * 他可以傳入 CoreGraphics 提供的函數(shù)中
  * 你可以是用 CoreGraphics 框架提供的方法創(chuàng)建一個(gè)路徑, 
  * 并給這個(gè)屬性賦值, 當(dāng)時(shí)設(shè)置了一個(gè)新的路徑后, 
  * 這個(gè)將會(huì)對(duì)你給出的路徑對(duì)象進(jìn)行 Copy 操作
  */
@property(nonatomic) CGPathRef CGPath;
/**
  * 該屬性的值, 將會(huì)是下一條繪制的直線或曲線的起始點(diǎn).
  * 如果當(dāng)前路徑為空, 那么該屬性的值將會(huì)是 CGPointZero
  */
@property(nonatomic, readonly) CGPoint currentPoint;

繪圖屬性

/**
  * 線寬屬性定義了 `UIBezierPath` 對(duì)象中繪制的曲線規(guī)格. 默認(rèn)為: 1.0
  */
@property(nonatomic) CGFloat lineWidth;
/**
  * 該屬性應(yīng)用于曲線的終點(diǎn)和起點(diǎn). 該屬性在一個(gè)閉合子路經(jīng)中是無(wú)效果的. 默認(rèn)為: kCGLineCapButt
  */
@property(nonatomic) CGLineCap lineCapStyle;


// CGPath.h
/* Line cap styles. */
typedef CF_ENUM(int32_t, CGLineCap) {
    kCGLineCapButt,
    kCGLineCapRound,
    kCGLineCapSquare
};
Line Cap Style
/**
  * 默認(rèn)為: kCGLineJoinMiter.
  */
@property(nonatomic) CGLineJoin lineJoinStyle;


// CGPath.h
/* Line join styles. */
typedef CF_ENUM(int32_t, CGLineJoin) {
    kCGLineJoinMiter,
    kCGLineJoinRound,
    kCGLineJoinBevel
};
CGLineJoin
/**
  * 兩條線交匯處內(nèi)角和外角之間的最大距離, 只有當(dāng)連接點(diǎn)樣式為 kCGLineJoinMiter
  * 時(shí)才會(huì)生效,最大限制為10
  * 我們都知道, 兩條直線相交時(shí), 夾角越小, 斜接長(zhǎng)度就越大.
  * 該屬性就是用來(lái)控制最大斜接長(zhǎng)度的.
  * 當(dāng)我們?cè)O(shè)置了該屬性, 如果斜接長(zhǎng)度超過(guò)我們?cè)O(shè)置的范圍, 
  * 則連接處將會(huì)以 kCGLineJoinBevel 連接類型進(jìn)行顯示.
  */
@property(nonatomic) CGFloat miterLimit;
miterLimit
miterLimit = 3
/**
  * 該屬性用來(lái)確定渲染曲線路徑的精確度.
  * 該屬性的值用來(lái)測(cè)量真實(shí)曲線的點(diǎn)和渲染曲線的點(diǎn)的最大允許距離.
  * 值越小, 渲染精度越高, 會(huì)產(chǎn)生相對(duì)更平滑的曲線, 但是需要花費(fèi)更
  * 多的計(jì)算時(shí)間. 值越大導(dǎo)致則會(huì)降低渲染精度, 這會(huì)使得渲染的更迅
  * 速. flatness 的默認(rèn)值為 0.6.
  * Note: 大多數(shù)情況下, 我們都不需要修改這個(gè)屬性的值. 然而當(dāng)我們
  *       希望以最小的消耗去繪制一個(gè)臨時(shí)的曲線時(shí), 我們也許會(huì)臨時(shí)增
  *       大這個(gè)值, 來(lái)獲得更快的渲染速度.
  */

@property(nonatomic) CGFloat flatness;
/**
  * 設(shè)置為 YES, 則路徑將會(huì)使用 基偶規(guī)則 (even-odd) 進(jìn)行填充.
  * 設(shè)置為 NO,  則路徑將會(huì)使用 非零規(guī)則 (non-zero) 規(guī)則進(jìn)行填充.
  */
@property(nonatomic) BOOL usesEvenOddFillRule;
/**
  * @param pattern: 該屬性是一個(gè) C 語(yǔ)言的數(shù)組, 其中每一個(gè)元素都是 CGFloat
  *                 數(shù)組中的元素代表著線段每一部分的長(zhǎng)度, 第一個(gè)元素代表線段的第一條線,
  *                 第二個(gè)元素代表線段中的第一個(gè)間隙. 這個(gè)數(shù)組中的值是輪流的. 來(lái)解釋一下
  *                 什么叫輪流的. 
  *                 舉個(gè)例子: 聲明一個(gè)數(shù)組 CGFloat dash[] = @{3.0, 1.0}; 
  *                 這意味著繪制的虛線的第一部分長(zhǎng)度為3.0, 第一個(gè)間隙長(zhǎng)度為1.0, 虛線的
  *                 第二部分長(zhǎng)度為3.0, 第二個(gè)間隙長(zhǎng)度為1.0. 以此類推.

  * @param count: 這個(gè)參數(shù)是 pattern 數(shù)組的個(gè)數(shù)
  * @param phase: 這個(gè)參數(shù)代表著, 虛線從哪里開(kāi)始繪制.
  *                 舉個(gè)例子: 這是 phase 為 6. pattern[] = @{5, 2, 3, 2}; 那么虛線將會(huì)
  *                 第一個(gè)間隙的中間部分開(kāi)始繪制, 如果不是很明白就請(qǐng)繼續(xù)往下看,
  *                 下文實(shí)戰(zhàn)部分會(huì)對(duì)虛線進(jìn)行講解.
  */
- (void)setLineDash:(const CGFloat *)pattern
              count:(NSInteger)count
              phase:(CGFloat)phase;
/**
  * 該方法可以重新獲取之前設(shè)置過(guò)的虛線樣式.
  *  Note:  pattern 這個(gè)參數(shù)的容量必須大于該方法返回?cái)?shù)組的容量.
  *         如果無(wú)法確定數(shù)組的容量, 那么可以調(diào)用兩次該方法, 第一次
  *         調(diào)用該方法的時(shí)候, 傳入 count 參數(shù), 然后在用 count 參數(shù)
  *         來(lái)申請(qǐng) pattern 數(shù)組的內(nèi)存空間. 然后再第二次正常的調(diào)用該方法
  */
- (void)getLineDash:(CGFloat *)pattern 
              count:(NSInteger *)count
              phase:(CGFloat *)phase;

繪制路徑

/**
  * 該方法當(dāng)前的填充顏色 和 繪圖屬性對(duì)路徑的封閉區(qū)域進(jìn)行填充.
  * 如果當(dāng)前路徑是一條開(kāi)放路徑, 該方法將會(huì)隱式的將路徑進(jìn)行關(guān)閉后進(jìn)行填充
  * 該方法在進(jìn)行填充操作之前, 會(huì)自動(dòng)保存當(dāng)前繪圖的狀態(tài), 所以我們不需要
  * 自己手動(dòng)的去保存繪圖狀態(tài)了. 
  */
- (void)fill;
/**
  * 該方法當(dāng)前的填充顏色 和 繪圖屬性 (外加指定的混合模式 和 透明度) 
  * 對(duì)路徑的封閉區(qū)域進(jìn)行填充. 如果當(dāng)前路徑是一條開(kāi)放路徑, 該方法將
  * 會(huì)隱式的將路徑進(jìn)行關(guān)閉后進(jìn)行填充
  * 該方法在進(jìn)行填充操作之前, 會(huì)自動(dòng)保存當(dāng)前繪圖的狀態(tài), 所以我們不需要
  * 自己手動(dòng)的去保存繪圖狀態(tài)了. 
  *
  * @param blendMode: 混合模式?jīng)Q定了如何和已經(jīng)存在的被渲染過(guò)的內(nèi)容進(jìn)行合成
  * @param alpha: 填充路徑時(shí)的透明度
  */
- (void)fillWithBlendMode:(CGBlendMode)blendMode 
                    alpha:(CGFloat)alpha;
- (void)stroke;
/**
  * @param blendMode: 混合模式?jīng)Q定了如何和已經(jīng)存在的被渲染過(guò)的內(nèi)容進(jìn)行合成
  * @param alpha: 填充路徑時(shí)的透明度
  */
- (void)strokeWithBlendMode:(CGBlendMode)blendMode
                      alpha:(CGFloat)alpha;

剪切路徑

/**
  *  該方法將會(huì)修改當(dāng)前繪圖上下文的可視區(qū)域.
  *  當(dāng)調(diào)用這個(gè)方法之后, 會(huì)導(dǎo)致接下來(lái)所有的渲染
  *  操作, 只會(huì)在剪切下來(lái)的區(qū)域內(nèi)進(jìn)行, 區(qū)域外的
  *  內(nèi)容將不會(huì)被渲染.
  *  如果你希望執(zhí)行接下來(lái)的繪圖時(shí), 刪除剪切區(qū)域,
  *  那么你必須在調(diào)用該方法前, 先使用 CGContextSaveGState 方法
  *  保存當(dāng)前的繪圖狀態(tài), 當(dāng)你不再需要這個(gè)剪切區(qū)域
  *  的時(shí)候, 你只需要使用 CGContextRestoreGState 方法
  *  來(lái)恢復(fù)之前保存的繪圖狀態(tài)就可以了.
  * @param blendMode: 混合模式?jīng)Q定了如何和
  *                   已經(jīng)存在的被渲染過(guò)的內(nèi)容進(jìn)行合成
  * @param alpha: 填充路徑時(shí)的透明度
  */
- (void)addClip;

Hit Detection

/**
  *  該方法返回一個(gè)布爾值, 當(dāng)曲線的覆蓋區(qū)域包含
  * 指定的點(diǎn)(內(nèi)部點(diǎn)), 則返回 YES, 否則返回 NO. 
  * Note: 如果當(dāng)前的路徑是一個(gè)開(kāi)放的路徑, 那么
  *       就算指定點(diǎn)在路徑覆蓋范圍內(nèi), 該方法仍然會(huì)
  *       返回 NO, 所以如果你想判斷一個(gè)點(diǎn)是否在一個(gè)
  *       開(kāi)放路徑的范圍內(nèi)時(shí), 你需要先Copy一份路徑,
  *       并調(diào)用 -(void)closePath; 將路徑封閉, 然后
  *       再調(diào)用此方法來(lái)判斷指定點(diǎn)是否是內(nèi)部點(diǎn).
  * @param point: 指定點(diǎn).
  */
- (BOOL) containsPoint:(CGPoint)point;
/**
  * 檢測(cè)當(dāng)前路徑是否繪制過(guò)直線或曲線.
  * Note: 記住, 就算你僅僅調(diào)用了 moveToPoint 方法
  *       那么當(dāng)前路徑也被看做不為空.
  */
@property (readonly, getter=isEmpty) BOOL empty;
/**
  * 該屬性描述的是一個(gè)能夠完全包含路徑中所有點(diǎn)
  *  的一個(gè)最小的矩形區(qū)域. 該區(qū)域包含二次貝塞爾
  *  曲線和三次貝塞爾曲線的控制點(diǎn).
  */
@property (nonatomic, readonly) CGRect bounds;

Apply Transform

/**
  * 該方法將會(huì)直接對(duì)路徑中的所有點(diǎn)進(jìn)行指定的放射
  * 變換操作. 
  */
- (void)applyTransform:(CGAffineTransform)transform;

實(shí)戰(zhàn)


UIBezierPath.h 文件已經(jīng)帶領(lǐng)大家完全的過(guò)了一遍了, 其實(shí)看上去還是蠻簡(jiǎn)單過(guò)的昂. 不過(guò)俗話說(shuō): 光說(shuō)不練假把式. 接下來(lái)就到了激動(dòng)人心的實(shí)戰(zhàn)環(huán)節(jié)了. 我們來(lái)簡(jiǎn)單的寫(xiě)幾句代碼, 看看效果吧. Note: 簡(jiǎn)單的繪制直線甲棍、繪制矩形之類的在這里我就不做演示了, 我覺(jué)得演示這種東西簡(jiǎn)直就是在侮辱大家的智商. -.-!!!

bezierPathByReversingPath

- (UIBezierPath *) bezierPathByReversingPath;

把這個(gè)方法拿出來(lái)和大家分享, 是因?yàn)槲易畛蹩吹竭@個(gè)方法的介紹之后, 有了一個(gè)錯(cuò)誤的理解. 開(kāi)始我認(rèn)為調(diào)用這個(gè)方法, 會(huì)產(chǎn)生一個(gè)鏡像的路徑出來(lái)(就像照鏡子一樣的那種反轉(zhuǎn)). 其實(shí)并不是這樣的, 這個(gè)方法并不會(huì)去修改一條路徑的形狀, 僅僅是改變了繪制路徑的方向. 先來(lái)看代碼:

- (void) drawRect:(CGRect)rect {
  
  // 1. 隨便畫(huà)一個(gè)路徑出來(lái).
  UIBezierPath *path = [UIBezierPath bezierPath];
  [path moveToPoint: CGPointMake(10, 10)];
  [path addLineToPoint: CGPointMake(80, 40)];
  [path addLineToPoint: CGPointMake( 40,  80)];
  [path addLineToPoint: CGPointMake(40, 40)];
  path.lineWidth = 3;

  // 2. 為這條路徑制作一個(gè)反轉(zhuǎn)路徑
  UIBezierPath *reversingPath = [path bezierPathByReversingPath];
  reversingPath.lineWidth = 3;

  // 3. 為了避免兩條路徑混淆在一起, 我們?yōu)榈谝粭l路徑做一個(gè)位移
  CGAffineTransform transform = CGAffineTransformMakeTranslation(200, 0);
  [path applyTransform: transform];

  // 4. 設(shè)置顏色, 并繪制路徑
  [[UIColor redColor] set];
  [path stroke];
    
  [[UIColor greenColor] set];
  [reversingPath stroke];
}

command+R跑一下程序看看效果. 效果如下圖:

路徑反轉(zhuǎn)

左側(cè)綠色路徑就是我們調(diào)用 - (UIBezierPath *) bezierPathByReversingPath; 方法反轉(zhuǎn)出來(lái)的路徑, 右側(cè)則是原路徑, 我們可以看到, 兩條路徑在形狀上并沒(méi)有任何的變化. 通過(guò)實(shí)踐證明了我最初的理解是錯(cuò)誤的. 那么我們現(xiàn)在來(lái)證明一下路徑的繪制方向發(fā)生了改變. 前文中提到, 當(dāng)繪制一條直線之后, currentPoint 將會(huì)自動(dòng)更新為這條直線的endPoint. 我們嘗試一下分別使用兩條路徑的currentPointself.center進(jìn)行連接, 將剛才的代碼修改為下面這個(gè)樣子:

- (void) drawRect:(CGRect)rect {
  
  // 1. 隨便畫(huà)一個(gè)路徑出來(lái).
  UIBezierPath *path = [UIBezierPath bezierPath];
  [path moveToPoint: CGPointMake(10, 10)];
  [path addLineToPoint: CGPointMake(80, 40)];
  [path addLineToPoint: CGPointMake( 40,  80)];
  [path addLineToPoint: CGPointMake(40, 40)];
  path.lineWidth = 3;

  // 2. 為這條路徑制作一個(gè)反轉(zhuǎn)路徑
  UIBezierPath *reversingPath = [path bezierPathByReversingPath];
  reversingPath.lineWidth = 3;

  // 3. 為了避免兩條路徑混淆在一起, 我們?yōu)榈谝粭l路徑做一個(gè)位移
  CGAffineTransform transform = CGAffineTransformMakeTranslation(200, 0);
  [path applyTransform: transform];

  // 4. 兩條路徑分別添加一條直接到 self.center
  [path addLineToPoint: CGPointMake(self.frame.size.width*0.5, self.frame.size.height*0.5)];
  [reversingPath addLineToPoint: CGPointMake(self.frame.size.width*0.5, self.frame.size.height*0.5)];

  // 5. 設(shè)置顏色, 并繪制路徑
  [[UIColor redColor] set];
  [path stroke];
    
  [[UIColor greenColor] set];
  [reversingPath stroke];
}

把程序跑起來(lái), 看看效果, 如下圖:

路徑反轉(zhuǎn)

從圖中我們可以清晰的看到, 路徑反轉(zhuǎn)以后, 原路徑的beginPoint 現(xiàn)在變成了endPoint, 現(xiàn)在大家是否明白了- (UIBezierPath *) bezierPathByReversingPath;方法的作用了呢? 是不是很清晰.

usesEvenOddFillRule

這個(gè)屬性是一個(gè)布爾值, 意思是: 是否使用奇偶填充規(guī)則, 該屬性默認(rèn)為 No, 默認(rèn)的填充規(guī)則是 非零環(huán)繞數(shù)原則. 最開(kāi)始我也不懂具體的規(guī)則代表的意思是什么, 后來(lái)調(diào)研了一下, 總算搞懂了這兩種FillMode 的區(qū)別. 詳細(xì)的介紹介紹和Demo 點(diǎn)這里.

setLineDash

虛線這東西, 猛地一看確實(shí)有點(diǎn)兒懵逼, 其實(shí)很簡(jiǎn)單, 先介紹一下這三個(gè)參數(shù).
@param pattern: 這個(gè)參數(shù)說(shuō)白了就是虛線的規(guī)格, 你讓他多長(zhǎng)他就多長(zhǎng), 你讓他有多大的間距他就有多大的間距. 是一個(gè) C 語(yǔ)言的數(shù)組.
@param count: 官方文檔上說(shuō)就是 pattern 數(shù)組的個(gè)數(shù). 那你就直接給一個(gè)數(shù)組的個(gè)數(shù)就完了, 何必給自己添麻煩呢.
@param phase: 從哪個(gè)位置開(kāi)始繪制虛線, 也就是偏移量, 你給個(gè)20, 那他就從20開(kāi)始繪制虛線.
其實(shí)這三個(gè)參數(shù)上文中已經(jīng)都介紹過(guò)了, 這里在簡(jiǎn)單的提一嘴, 省的你往上翻了. 文字表達(dá)不出來(lái), 還是看代碼和效果吧.

- (void) typeDashLine {

    // 1. 先創(chuàng)建三條路徑, 有對(duì)比更有助于理解
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint: CGPointMake(80, 40)];
    [path addLineToPoint: CGPointMake(self.frame.size.width - 40, 40)];
    path.lineWidth = 2;
    
    
    UIBezierPath *path1 = [UIBezierPath bezierPath];
    [path1 moveToPoint: CGPointMake(80, 80)];
    [path1 addLineToPoint: CGPointMake(self.frame.size.width - 40, 80)];
    path1.lineWidth = 2;
    
    
    UIBezierPath *path2 = [UIBezierPath bezierPath];
    [path2 moveToPoint: CGPointMake(80, 120)];
    [path2 addLineToPoint: CGPointMake(self.frame.size.width - 40, 120)];
    path2.lineWidth = 2;
    
    // 2.  這部分是配置三條路徑虛線的規(guī)格, 重點(diǎn)主要是這部分.
    CGFloat dashLineConfig[] = {8.0, 4.0};
    [path setLineDash: dashLineConfig
                           count: 2
                          phase: 0];
    
    
    CGFloat dashLineConfig1[] = {8.0, 4.0, 16.0, 8.0};
    [path1 setLineDash: dashLineConfig1
                count: 4
                phase: 0];
    
    
    CGFloat dashLineConfig2[] = {8.0, 4.0, 16.0, 8.0};
    [path2 setLineDash: dashLineConfig2
                count: 4
                phase: 12];
    
    // 3. 繪制
    [[UIColor orangeColor] set];
    [path stroke];
    [path1 stroke];
    [path2 stroke];
}

代碼看完了, 別的就不多說(shuō)了, 主要看效果:

Dash Line

我覺(jué)得看完代碼和效果圖之后, 我已經(jīng)不需要多說(shuō)什么了. 我相信大家都已經(jīng)看明白了. (原諒我, 其實(shí)我真的不知道該怎么用語(yǔ)言表達(dá)了, 寫(xiě)幾筆測(cè)試代碼, 全都一目了然了! -.-!!!)

結(jié)語(yǔ)


猛然間抬頭看看表, 已經(jīng)2:30了. 我說(shuō)怎么那么困呢! 睡了!

文獻(xiàn)



Lemon龍說(shuō):

如果您在文章中看到了錯(cuò)誤 或 誤導(dǎo)大家的地方, 請(qǐng)您幫我指出, 我會(huì)盡快更改

如果您有什么疑問(wèn)或者不懂的地方, 請(qǐng)留言給我, 我會(huì)盡快回復(fù)您

如果您覺(jué)得本文對(duì)您有所幫助, 您的喜歡是對(duì)我最大的鼓勵(lì)

如果您有好的文章, 可以投稿給我, 讓更多的 iOS Developer 在簡(jiǎn)書(shū)這個(gè)平臺(tái)能夠更快速的成長(zhǎng)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赶掖,隨后出現(xiàn)的幾起案子感猛,更是在濱河造成了極大的恐慌,老刑警劉巖奢赂,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陪白,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡膳灶,警方通過(guò)查閱死者的電腦和手機(jī)咱士,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)轧钓,“玉大人序厉,你說(shuō)我怎么就攤上這事”瞎浚” “怎么了弛房?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)而柑。 經(jīng)常有香客問(wèn)我文捶,道長(zhǎng)荷逞,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任粹排,我火速辦了婚禮颅围,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘恨搓。我一直安慰自己院促,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布斧抱。 她就那樣靜靜地躺著常拓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辉浦。 梳的紋絲不亂的頭發(fā)上弄抬,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音宪郊,去河邊找鬼掂恕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛弛槐,可吹牛的內(nèi)容都是我干的懊亡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼乎串,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼店枣!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起叹誉,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鸯两,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后长豁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體钧唐,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年匠襟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了钝侠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宅此,死狀恐怖机错,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情父腕,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布青瀑,位于F島的核電站璧亮,受9級(jí)特大地震影響萧诫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枝嘶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一帘饶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧群扶,春花似錦及刻、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至骆莹,卻和暖如春颗搂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背幕垦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工丢氢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人先改。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓疚察,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親仇奶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稍浆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容