UIBezierPath中文叫貝塞爾曲線,其作用是 UIBezierPath 類允許你在自定義的 View 中繪制和渲染由直線和曲線組成的路徑. 你可以在初始化的時(shí)候, 直接為你的 UIBezierPath 指定一個(gè)幾何圖形. 路徑可以是簡(jiǎn)單的幾何圖形例如: 矩形、橢圓、弧線之類的, 也可以是相對(duì)比較復(fù)雜的由直線和曲線組成的多邊形. 當(dāng)你定義完圖形以后, 你可以使用額外的方法將你的路徑直接繪制在當(dāng)前的繪圖上下文中.
具體的內(nèi)容和用法下面一一舉例
+(instancetype)bezierPath;
示例:
UIBezierPath *path0 = [UIBezierPath bezierPath];
這里只是簡(jiǎn)單的便利初始化方法,使用它還要設(shè)置其他屬性進(jìn)行操作
+ (instancetype)bezierPathWithRect:(CGRect)rect;
示例:
自定義View.m文件中drawRect中寫代碼
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.bounds];
path.lineWidth = 2;
[[UIColor orangeColor] set];
[path stroke];
}
在外面調(diào)用自定義View時(shí)的代碼
CustonView *vi = [[CustonView alloc] init];
vi.frame = CGRectMake(100, 100, 100, 100);
vi.backgroundColor = [UIColor yellowColor];
[self.view addSubview:vi];

矩形.png
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
示例:
自定義View.m文件中drawRect中寫代碼
- (void)drawRect:(CGRect)rect {
UIBezierPath *path1 = [UIBezierPath bezierPathWithOvalInRect:self.bounds];
// path1.lineWidth = 10;
[[UIColor orangeColor] set];
[path1 stroke];
}
在外面調(diào)用自定義View時(shí)的代碼
CustonView *vi = [[CustonView alloc] init];
vi.frame = CGRectMake(100, 100, 100, 50);
vi.backgroundColor = [UIColor yellowColor];
[self.view addSubview:vi];
//如果vi的寬高相等的話橢圓將變成○

橢圓
但更多情況下我們要的不是顯示的狀態(tài),而是要切割好的橢圓,怎么做?
這次不用在自定義View.m drawRect中寫代碼,可以直接寫在用的地方,這里我們用到了CAShapeLayer,是CALayer的子類,CALayer的屬性CAShapeLayer都可以用
示例:
UIView *vi = [[UIView alloc] initWithFrame:CGRectMake(200, 100, 100, 50)];
vi.backgroundColor = [UIColor yellowColor];
[self.view addSubview:vi];
//*****************關(guān)鍵代碼開始*********************
UIBezierPath *maskPath = [UIBezierPath bezierPathWithOvalInRect:vi1.bounds];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = vi.bounds;
maskLayer.path = maskPath.CGPath;
vi.layer.mask = maskLayer;
//*****************關(guān)鍵代碼結(jié)束*****************

矩形View切割后的橢圓.png
+ (instancetype) bezierPathWithRoundedRect:(CGRect)rect
cornerRadius:(CGFloat)cornerRadius;
示例:
//****************在drawRect中寫(顯示路徑)*************
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:25];
[path stroke];
}
//controller中調(diào)用
CustonView *vi = [[CustonView alloc] init];
vi.frame = CGRectMake(100, 100, 100, 50);
vi.backgroundColor = [UIColor yellowColor];
[self.view addSubview:vi];
//****************直接用(切割)*************
UIView *vi = [[UIView alloc] initWithFrame:CGRectMake(200, 100, 100, 50)];
vi.backgroundColor = [UIColor yellowColor];
[self.view addSubview:vi];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:vi.bounds cornerRadius:25];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = vi.bounds;
maskLayer.path = maskPath.CGPath;
vi.layer.mask = maskLayer;

顯示路徑.png

切割.png
+ (instancetype) bezierPathWithRoundedRect:(CGRect)rect
byRoundingCorners:(UIRectCorner)corners
cornerRadii:(CGSize)cornerRadii;
示例:
//****************在drawRect中寫(顯示路徑)*************
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(25, 25)];
[path stroke];
}
//controller中調(diào)用
CustonView *vi = [[CustonView alloc] init];
vi.frame = CGRectMake(100, 100, 100, 50);
vi.backgroundColor = [UIColor yellowColor];
[self.view addSubview:vi];
//****************直接用(切割)*************
UIView *vi = [[UIView alloc] initWithFrame:CGRectMake(200, 100, 100, 50)];
vi.backgroundColor = [UIColor yellowColor];
[self.view addSubview:vi];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:vi.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(25, 25)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = vi.bounds;
maskLayer.path = maskPath.CGPath;
vi.layer.mask = maskLayer;
這里用到了一個(gè)枚舉
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 << 0,
UIRectCornerTopRight = 1 << 1,
UIRectCornerBottomLeft = 1 << 2,
UIRectCornerBottomRight = 1 << 3,
UIRectCornerAllCorners = ~0UL
};
是哪個(gè)角需要設(shè)圓角,用 | 符號(hào)連接即可

顯示路徑.png

切割.png
+ (instancetype) bezierPathWithArcCenter:(CGPoint)center //圓心
radius:(CGFloat)radius //半徑
startAngle:(CGFloat)startAngle //開始角度
endAngle:(CGFloat)endAngle //結(jié)束角度
clockwise:(BOOL)clockwise; //順時(shí)針or逆時(shí)針

示例.jpg
+ (instancetype) bezierPathWithCGPath:(CGPathRef)CGPath;
CGPath是UIBezierPath的一個(gè)只讀屬性,通常獲取一個(gè)UIBezierPath對(duì)象的path可以給其他地方用比如上面例子中的 maskLayer.path = maskPath.CGPath;
- (UIBezierPath *) bezierPathByReversingPath;
這里的反方向指的是初始位置和末位置調(diào)換,而不是圖形反向,比如一條路徑是從(0,0)到(3,4),(0,0)是初始位置,(3,4)是末位置,如果使用此方法,則新的UIBezierPath對(duì)象的初始位置為(3,4),末位置是(0,0),但是兩條直線看上去是一模一樣的
除了一些常用的便利方法,還有一個(gè)對(duì)象方法用于自定義一些特殊的路徑
- (void)moveToPoint:(CGPoint)point;
- (void)addLineToPoint:(CGPoint)point;
- (void)addArcWithCenter:(CGPoint)center
radius:(CGFloat)radius
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngle
clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
- (void)addQuadCurveToPoint:(CGPoint)endPoint
controlPoint:(CGPoint)controlPoint;

二次貝塞爾曲線.jpg
- (void)addCurveToPoint:(CGPoint)endPoint
controlPoint1:(CGPoint)controlPoint1
controlPoint2:(CGPoint)controlPoint2;

三次貝塞爾曲線.jpg
//意思就是閉合路徑,初始位置和末位置點(diǎn)連成直線,形成閉合的狀態(tài)
- (void)closePath;
- (void)removeAllPoints;
//該方法將會(huì)在當(dāng)前 UIBezierPath 對(duì)象的路徑中追加
- (void)appendPath:(UIBezierPath *)bezierPath;
UIBezierPath的屬性
/**
* 獲取這個(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
};

曲線終點(diǎn)樣式.png
/**
* 默認(rèn)為: kCGLineJoinMiter.
*/
@property(nonatomic) CGLineJoin lineJoinStyle;
// CGPath.h
/* Line join styles. */
typedef CF_ENUM(int32_t, CGLineJoin) {
kCGLineJoinMiter,
kCGLineJoinRound,
kCGLineJoinBevel
};

曲線連接點(diǎn)樣式.png
/**
* 兩條線交匯處內(nèi)角和外角之間的最大距離, 只有當(dāng)連接點(diǎn)樣式為 kCGLineJoinMiter
* 時(shí)才會(huì)生效,最大限制為10
* 我們都知道, 兩條直線相交時(shí), 夾角越小, 斜接長(zhǎng)度就越大.
* 該屬性就是用來(lái)控制最大斜接長(zhǎng)度的.
* 當(dāng)我們?cè)O(shè)置了該屬性, 如果斜接長(zhǎng)度超過我們?cè)O(shè)置的范圍,
* 則連接處將會(huì)以 kCGLineJoinBevel 連接類型進(jìn)行顯示.
*/
@property(nonatomic) CGFloat miterLimit;

內(nèi)角和外角距離.png
/**
* 該屬性用來(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ù)代表著, 虛線從哪里開始繪制.
* 舉個(gè)例子: 這是 phase 為 7. pattern[] = @{8.0,3.0,16.0,7.0}; 那么虛線將會(huì)從第7個(gè)像素開始,由于第一個(gè)實(shí)線的長(zhǎng)度是8像素,所以會(huì)先顯示1像素實(shí)線,然后3像素空白,16像素實(shí)線,7像素空白,8像素實(shí)線,3像素空白……
*/
- (void)setLineDash:(const CGFloat *)pattern
count:(NSInteger)count
phase:(CGFloat)phase;
示例:
示例:
//****************在drawRect中寫(顯示路徑)*************
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, 10)];
[path addLineToPoint:CGPointMake(100, 80)];
path.lineWidth = 2;
[[UIColor orangeColor] set];
CGFloat dash[] = {8.0,3.0,16.0,7.0};
[path setLineDash:dash count:4 phase:7];
[path stroke];
}
//controller中調(diào)用
CustonView *vi = [[CustonView alloc] init];
vi.frame = CGRectMake(100, 100, 100, 100);
[self.view addSubview:vi];
下圖中黑色為vi,虛線的兩端位置是相對(duì)于vi的位置定的(0,10)(100,80)

虛線.png
/**
* 該方法可以重新獲取之前設(shè)置過的虛線樣式.
* 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)前路徑是一條開放路徑, 該方法將會(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)前路徑是一條開放路徑, 該方法將
* 會(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)存在的被渲染過的內(nèi)容進(jìn)行合成
* @param alpha: 填充路徑時(shí)的透明度
*/
- (void)fillWithBlendMode:(CGBlendMode)blendMode
alpha:(CGFloat)alpha;
- (void)stroke;
/**
* @param blendMode: 混合模式?jīng)Q定了如何和已經(jīng)存在的被渲染過的內(nèi)容進(jìn)行合成
* @param alpha: 填充路徑時(shí)的透明度
*/
- (void)strokeWithBlendMode:(CGBlendMode)blendMode
alpha:(CGFloat)alpha;
/**
* 該方法返回一個(gè)布爾值, 當(dāng)曲線的覆蓋區(qū)域包含
* 指定的點(diǎn)(內(nèi)部點(diǎn)), 則返回 YES, 否則返回 NO.
* Note: 如果當(dāng)前的路徑是一個(gè)開放的路徑, 那么
* 就算指定點(diǎn)在路徑覆蓋范圍內(nèi), 該方法仍然會(huì)
* 返回 NO, 所以如果你想判斷一個(gè)點(diǎn)是否在一個(gè)
* 開放路徑的范圍內(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)前路徑是否繪制過直線或曲線.
* 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;
該方法將會(huì)直接對(duì)路徑中的所有點(diǎn)進(jìn)行指定的放射
/**
* 該方法將會(huì)直接對(duì)路徑中的所有點(diǎn)進(jìn)行指定的放射
* 變換操作.
*/
- (void)applyTransform:(CGAffineTransform)transform;
示例:
- (void) drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint: CGPointMake(50, 50)];
[path addArcWithCenter:CGPointMake(50, 50) radius:30 startAngle:0 endAngle:M_PI clockwise:YES];
path.lineWidth = 3;
UIBezierPath *reversingPath = [path bezierPathByReversingPath];
reversingPath.lineWidth = 3;
CGAffineTransform transform = CGAffineTransformMakeTranslation(100, 0);
[path applyTransform: transform];
[[UIColor redColor] set];
[path stroke];
[[UIColor greenColor] set];
[reversingPath stroke];
}
//controller中調(diào)用
custonView *vi = [[custonView alloc] init];
vi.frame = CGRectMake(100, 100, 300, 300);
[self.view addSubview:vi];

平移.png