iOS ~ 貝塞爾曲線各種方法:

image

TopicList

  • 一, UIBezierPath 簡介
  • 二, UIBezierPath 初始化方法
  • 三, UIBezierPath 常用屬性說明
  • 四, UIBezierPath 構建Path方法
  • 五, 圖形上下文中的路徑操作
  • 六, 由簡入繁繪制貝塞爾曲線
  • 七, 報錯等相關信息說明
  • 八, 引用文獻說明

話說,入坑得從官方文檔文檔開始:

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類允許您定義一個由直線和曲線組成的路徑在您的自定義視圖中森缠。你使用這個類的初始化方法指定的幾何路徑诵盼。路徑可以是簡單的幾何形狀,例如:矩形、橢圓恶座、弧,也可以定義為復雜的多邊形,或者說直線和曲線段的混合形狀。形狀定義之后,您可以使用這個類的其他方法來呈現(xiàn)當前繪圖的上下文路徑污尉。

我們可以使用直線段去創(chuàng)建矩形和多邊形,使用曲線段去創(chuàng)建弧(arc),圓或者其他復雜的曲線形狀.曲線定義: 起始點,終止點(錨點),控制點.通過調(diào)整控制點,貝塞爾曲線會發(fā)生變化.

一, UIBezierPath 簡介

UIBezierPath(貝塞爾曲線),位于 UIKit 框架,使用此類可以定義簡單的形狀,如橢圓或者矩形,或者有多個直線和曲線段組成的形狀.我們可以使用直線段去創(chuàng)建矩形和多邊形,使用曲線段去創(chuàng)建弧(arc),圓或者其他復雜的曲線形狀. 曲線定義: 起始點,終止點(錨點),控制點.通過調(diào)整控制點,貝塞爾曲線會發(fā)生變化.

貝塞爾曲線的數(shù)理知識
一次貝塞爾曲線

給定點P0,P1一次貝塞爾曲線只是一條兩點之間的直線鸣戴。這條線由下式給出:

image

一次貝塞爾曲線函數(shù)中的t會經(jīng)由起始點P0至終止點P1的B(t)所描述的曲線。例如當t=0.25時枪孩,B(t)即一條由點P0至P1路徑的四分之一處。就像由0至1的連續(xù)t藻肄,B(t)描述一條由P0至P1的直線.

image
二次貝塞爾曲線

二次方貝塞爾曲線的路徑由給定點P0蔑舞、P1、P2的函數(shù)B(t)追蹤:

image

二次貝塞爾曲線,為建構二次貝塞爾曲線,需要兩個控制點Q0和Q1作為由0至1的控制:
由P0至P1的控制點Q0,描述一條線性貝塞爾曲線;
由P1至P2的控制點Q1,描述一條線性貝塞爾曲線;
由Q0至Q1的連續(xù)點B(t),描述一條二次貝塞爾曲線;

image
三次貝塞爾曲線

對于三次曲線嘹屯,可由線性貝塞爾曲線描述的中介點Q0攻询、Q1、Q2州弟,和由二次曲線描述的點R0钧栖、R1所建構:

image
image

更多關于貝塞爾曲線的介紹,可參考維基百科的相關介紹

二, 初始化方法
 // 初始化方法,需要用實例方法添加線條.使用比較多,可以根據(jù)需要任意定制樣式,畫任何我們想畫的圖形.
 + (instancetype)bezierPath;

 // 返回一個矩形 path
 + (instancetype)bezierPathWithRect:(CGRect)rect;

 // 返回一個圓形或者橢圓形 path
 + (instancetype)bezierPathWithOvalInRect:(CGRect)rect;

 // 返回一個帶圓角的矩形 path ,矩形的四個角都是圓角;
 + (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;

 // 返回一個帶圓角的矩形 path , UIRectCorner 枚舉值可以設置只繪制某個圓角;
 + (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;

 // 返回一段圓弧,參數(shù)說明: center: 弧線中心點的坐標 radius: 弧線所在圓的半徑 startAngle: 弧線開始的角度值 endAngle: 弧線結束的角度值 clockwise: 是否順時針畫弧線.
 + (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

 // 用一條 CGpath 初始化
 + (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;

 // 返回一個反轉(zhuǎn)當前路徑的路徑對象.(反方向繪制path)
 - (UIBezierPath *)bezierPathByReversingPath;

三, UIBezierPath 常用屬性

1.CGPath: 將UIBezierPath類轉(zhuǎn)換成CGPath
2.currentPoint: 當前path的位置,可以理解為path的終點
3.lineWidth: 線條寬度
4.lineCapStyle: 端點樣式
5.lineJoinStyle: 連接類型
6.flatness: 繪線的精細程度婆翔,默認為0.6拯杠,數(shù)值越大,需要處理的時間越長
7.usesEvenOddFillRule: 判斷奇偶數(shù)組的規(guī)則繪制圖像,圖形復雜時填充顏色的一種規(guī)則啃奴。類似棋盤
8.miterLimit: 最大斜接長度(只有在使用kCGLineJoinMiter是才有效,最大限制為10)潭陪, 邊角的角度越小,斜接長度就會越大最蕾,為了避免斜接長度過長依溯,使用lineLimit屬性限制,如果斜接長度超過miterLimit瘟则,邊角就會以KCALineJoinBevel類型來顯示
9.- setLineDash:count:phase:為path繪制虛線黎炉,dash數(shù)組存放各段虛線的長度,count是數(shù)組元素數(shù)量醋拧,phase是起始位置

 // lineCapStyle     // 端點類型
 kCGLineCapButt,     // 無端點
 kCGLineCapRound,    // 圓形端點
 kCGLineCapSquare    // 方形端點(樣式上和kCGLineCapButt是一樣的慷嗜,但是比kCGLineCapButt長一點)

 // lineJoinStyle     // 交叉點的類型
 kCGLineJoinMiter,    // 尖角銜接
 kCGLineJoinRound,    // 圓角銜接
 kCGLineJoinBevel     // 斜角銜接

四, UIBezierPath 構建Path
 - (void)moveToPoint:(CGPoint)point;
 // 以 point點 開始作為起點, 一般用`+ (instancetype)bezierPath`創(chuàng)建的貝塞爾曲線宿百,先用該方法標注一個起點,再調(diào)用其他的創(chuàng)建線條的方法來繪制曲線

 // 繪制二次貝塞爾曲線的關鍵方法,即從path的最后一點開始添加一條線到point點
 - (void)addLineToPoint:(CGPoint)point;

 // 繪制二次貝塞爾曲線的關鍵方法,和`-moveToPoint:`配合使用. endPoint為終止點,controlPoint為控制點.
 - (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;

 // 繪制三次貝塞爾曲線的關鍵方法,以三個點畫一段曲線. 一般和moveToPoint:配合使用.
 // 其中,起始點由`-moveToPoint:`設置,終止點位為`endPoint:`, 控制點1的坐標controlPoint1,控制點2的坐標是controlPoint2.
 - (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;

 // 繪制一段圓弧, center:原點坐標,radius:半徑,startAngle:起始角度,endAngle:終止角度,clockwise順時針/逆時針方向繪制
 - (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

 // 閉合線
 - (void)closePath;

 // 移除所有的點,從而有效地刪除所有子路徑
 - (void)removeAllPoints;

 // 追加指定的bezierPath到路徑上
 - (void)appendPath:(UIBezierPath *)bezierPath;

 // 用仿射變換矩陣變換路徑的所有點
 - (void)applyTransform:(CGAffineTransform)transform;

五, 圖形上下文中的路徑操作
 // 填充路徑
 - (void)fill;

 // 各個點連線
 - (void)stroke;

 // 填充模式, alpha 設置
 // blendMode : https://onevcat.com/2013/04/using-blending-in-ios/
 - (void)fillWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;

 // 鏈接模式, alpha 設置
 - (void)strokeWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;

 // 圖形繪制超出當前路徑范圍,則不可見
 - (void)addClip;

六, 由簡入繁繪制貝塞爾曲線
  1. 初始化一個 UIBezierPath 對象
  2. 設置相關的屬性;
  3. 調(diào)用 -moveToPoint: 方法設置線段的起點;
  4. 添加線段或者曲線段去構建一個或者多個子路徑;

代碼示例:

#define kLineW ScreenWidth-10*2
#define kMargin 10

// 重寫 drawRect 方法
- (void)drawRect:(CGRect)rect {

    //由于UIBezierPath繪制出來的是矢量圖形(即layer路徑)并不能真正的展示出來,因此,想讓它顯示在圖層上,需要設置線條顏色
    [[UIColor orangeColor] set];

    // 示例代碼:
    // 1\. 繪制一條直線,即一次貝塞爾曲線
    UIBezierPath *path = [[UIBezierPath alloc] init];
    path.lineWidth = 1.f;
    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineCapRound;
    path.miterLimit = 10.f;
    path.flatness = 10.f;
    path.usesEvenOddFillRule = YES;
    // 設置起始點
    [path moveToPoint:CGPointMake(kMargin, kMargin)];
    // 添加子路徑
    [path addLineToPoint:CGPointMake(kLineW, kMargin)];//添加一條子路徑
    // 根據(jù)坐標點連線
    [path stroke];

    // 2.繪制一條折線,其實就是增加一個端點
    UIBezierPath *path1 = [UIBezierPath bezierPath];
    [path1 moveToPoint:CGPointMake(kMargin, kMargin*2)];
    [path1 addLineToPoint:CGPointMake(kLineW, kMargin*2)];//添加一條子路徑
    [path1 addLineToPoint:CGPointMake(kLineW, kMargin*3)];//添加兩條子路徑
    [path1 closePath];//當構建子路徑數(shù)>=2條時,可以調(diào)用`closePath`方法來閉合路徑.
    [path1 stroke];

    // 3.繪制一個矩形
    // 方法1: 類似上面,用點去繪制;
    UIBezierPath *path2 = [UIBezierPath bezierPath];
    [path2 moveToPoint:CGPointMake(kMargin, kMargin*4)];
    [path2 addLineToPoint:CGPointMake(kLineW, kMargin*4)];
    [path2 addLineToPoint:CGPointMake(kLineW, kMargin*5)];
    [path2 addLineToPoint:CGPointMake(kMargin, kMargin*5)];
    [path2 stroke];

    // 方法2: 初始化方法直接繪制
    UIBezierPath *path3 = [UIBezierPath bezierPathWithRect:CGRectMake(kMargin, kMargin*6, kLineW, kMargin*5)];
    [path3 fill];// 設置填充
    [path3 stroke];

    // 4\. 繪制帶圓角的矩形
    UIBezierPath *path4 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(kMargin, kMargin*13, kLineW, kMargin*5) byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(kMargin, kMargin)];
    [path4 fillWithBlendMode:kCGBlendModeMultiply alpha:0.3];
    [path4 stroke];

    // 5\. 繪制橢圓
    UIBezierPath *path5 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(self.center.x-100, kMargin*20, 200, 50)];
    [path5 fillWithBlendMode:kCGBlendModeOverlay alpha:0.5];
    [path5 stroke];

    // 6\. 繪制圓形
    UIBezierPath *path6 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(kMargin*5, kMargin*26, 100, 100)];
    [path6 stroke];

    // 7\. 繪制一段圓弧
    UIBezierPath *path7 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(kMargin*30, kMargin*31) radius:50 startAngle:1.5*3.1415926 endAngle:3.1415926 clockwise:YES];
    [path7 stroke];

    // 8.繪制扇形
    UIBezierPath * path8 = [UIBezierPath bezierPath]; // 創(chuàng)建路徑
    [path8 moveToPoint:CGPointMake(100, kMargin*45)]; // 設置起始點
    [path8 addArcWithCenter:CGPointMake(100, kMargin*45) radius:50 startAngle:0 endAngle:3.14159/2 clockwise:NO];
    //[[UIColor lightGrayColor] setStroke];
    //[[UIColor lightGrayColor] setFill];
    [path8 closePath];
    [path8 stroke];

    // 9\. 繪制豎直虛線
    UIBezierPath *verticalLinePath = [UIBezierPath bezierPath];
    CGFloat dash[] = {3.0, 3.0};
    [verticalLinePath setLineDash:dash count:2 phase:0.0];
    [verticalLinePath moveToPoint: CGPointMake(5, 0)];
    [verticalLinePath addLineToPoint: CGPointMake(5, ScreenHeight*2)];
    [verticalLinePath stroke];
    [verticalLinePath fill];

    // 10.繪制二次貝塞爾曲線
    UIBezierPath *path9 = [UIBezierPath bezierPath];
    [path9 moveToPoint:CGPointMake(250, 450)];
    [path9 addQuadCurveToPoint:CGPointMake(350, 450) controlPoint:CGPointMake(300, 550)];
    [path9 stroke];

    // 11.繪制三次貝塞爾曲線
    UIBezierPath *path10 = [UIBezierPath bezierPath];
    [path10 moveToPoint:CGPointMake(50, 550)];
    [path10 addCurveToPoint:CGPointMake(300, 550) controlPoint1:CGPointMake(150, 450) controlPoint2:CGPointMake(250, 600)];
    [path10 stroke];
}

效果預覽:

image
七, 報錯等相關信息說明

1.關于drawRext: 如果你是在控制器的 viewDidLoad 方法中繪制的,那么將得到如下的報錯信息:
打印報錯:CGContextSaveGState:invalid context 0x0.CGContextDrawPath: invalid context 0x0.

image

原因:沒有在view-drawRext:方法中進行相關的操作:
例如 UIBezierPath- (void)setFill, - (void)setStroke 方法, UIColor- (void)setFill, - (void)setStroke方法等

解決辦法:

  • 把有關 view layer 層的操作單獨封裝到一個 view 類的 -drawRect: 方法中
  • 不要調(diào)用相關的方法,一般官方文檔會給予提示:
    Set the fill or stroke colors individually. These should be implemented by subclassers.

2.報錯信息

image

解決辦法: H含金量 iOS繪圖及貝塞爾曲線關鍵知識

3.角度控制

image

4.關于顏色
由于UIBezierPath繪制出來的是矢量圖形(即layer路徑)并不能真正的展示出來,因此,想讓它顯示在圖層上,需要設置線條顏色,有以下方法:

1.直接設置 color
[[UIColor orangeColor] set];

2. 和 CAShapeLayer 搭配使用, shape外形,顧名思義就是用來呈現(xiàn)layer外形的.
UIBezierPath *pathA = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 100, 100)];
CAShapeLayer *layerA = [[CAShapeLayer alloc] init];
// 設置線條寬度
layerA.lineWidth = 1.f;
// 線條填充色
layerA.strokeColor = [UIColor orangeColor].CGColor;
// 背景填充色
layerA.fillColor = [UIColor whiteColor].CGColor;
// 設置路徑
layerA.path = path.CGPath;
[self.layer addSublayer:layerA];

3.常用代碼段

/// 畫圓點
CGPoint point = [self.pointArray.firstObject CGPointValue];
CGRect frame = CGRectMake(point.x-4, point.y-4, 8, 8);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:frame];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = path.CGPath;
shapeLayer.fillColor = RGB(8, 202, 203).CGColor;
shapeLayer.shadowOffset = CGSizeMake(0,4);
shapeLayer.shadowOpacity = 0.3;
[self.layer addSublayer:shapeLayer];

八, 引用文獻說明

wikiwand
UIBezierPath 學習筆記
UIBezierPath 通過貝塞爾曲線畫圓環(huán) 創(chuàng)建一個環(huán)形進度指示器
CAShapeLayer

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末洪添,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子雀费,更是在濱河造成了極大的恐慌干奢,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盏袄,死亡現(xiàn)場離奇詭異忿峻,居然都是意外死亡,警方通過查閱死者的電腦和手機辕羽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門逛尚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刁愿,你說我怎么就攤上這事绰寞。” “怎么了铣口?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵滤钱,是天一觀的道長。 經(jīng)常有香客問我脑题,道長件缸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任叔遂,我火速辦了婚禮他炊,結果婚禮上,老公的妹妹穿的比我還像新娘已艰。我一直安慰自己痊末,他們只是感情好,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布哩掺。 她就那樣靜靜地躺著舌胶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疮丛。 梳的紋絲不亂的頭發(fā)上幔嫂,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機與錄音誊薄,去河邊找鬼履恩。 笑死,一個胖子當著我的面吹牛呢蔫,可吹牛的內(nèi)容都是我干的切心。 我是一名探鬼主播飒筑,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绽昏!你這毒婦竟也來了协屡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤全谤,失蹤者是張志新(化名)和其女友劉穎肤晓,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體认然,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡补憾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了卷员。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盈匾。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖毕骡,靈堂內(nèi)的尸體忽然破棺而出削饵,到底是詐尸還是另有隱情,我是刑警寧澤未巫,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布葵孤,位于F島的核電站,受9級特大地震影響橱赠,放射性物質(zhì)發(fā)生泄漏尤仍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一狭姨、第九天 我趴在偏房一處隱蔽的房頂上張望宰啦。 院中可真熱鬧,春花似錦饼拍、人聲如沸赡模。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漓柑。三九已至,卻和暖如春叨吮,著一層夾襖步出監(jiān)牢的瞬間辆布,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工茶鉴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锋玲,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓涵叮,卻偏偏與公主長得像惭蹂,于是被迫代替她去往敵國和親伞插。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

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