UIBezierPath詳解

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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • UIBezierPath詳解 我在寫本篇文章之前,也沒有系統(tǒng)學(xué)習(xí)過貝塞爾曲線,只是曾經(jīng)某一次的需求需要使用到,才臨...
    白水灬煮一切閱讀 1,279評(píng)論 0 4
  • UIBezierPath Class Reference 譯:UIBezierPath類封裝了Core Graph...
    鋼鉄俠閱讀 1,939評(píng)論 0 3
  • 引言 記得剛接觸 iOS 的時(shí)候, 第一次聽到貝塞爾曲線這個(gè)東西的時(shí)候, 當(dāng)時(shí)我只有一個(gè)感覺, 那就是: 什么鬼?...
    李國(guó)安閱讀 23,427評(píng)論 17 151
  • 21- 《A GUIDE TO IOS ANIMATION 2.0》(楊騎滔)三章.CoreAnimation-學(xué)...
    春田花花幼兒園閱讀 1,921評(píng)論 22 16
  • 使用UIBezierPath類可以創(chuàng)建基于矢量的路徑,這個(gè)類在UIKit中。此類是Core Graphics框架關(guān)...
    Zhui_Do閱讀 711評(píng)論 0 4

友情鏈接更多精彩內(nèi)容