iOS UIBezierPath使用——貝塞爾曲線

UIBezierPath用于定義一個直線/曲線組合而成的路徑,并且可以在自定義視圖中渲染該路徑。

注意:使用UIBezierPath繪畫的代碼寫在自定義視圖的drawRect:方法中。

一、創(chuàng)建UIBezierPath.

+ (instancetype)bezierPath;

初始化一個UIBezierPath對象。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

+ (instancetype)bezierPathWithRect:(CGRect)rect;

以CGRect為范圍,創(chuàng)建一個矩形路徑。

UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 200, 300)];

+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;

以CGRect為范圍,創(chuàng)建一個圓/橢圓。

UIBezierPath *bezierPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 200, 300)];

+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;

以CGRect為大小,以cornerRadius為圓角半徑,繪制一個圓角矩形。

UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(100, 100, 200, 300) cornerRadius:10.f];

+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;

繪制一個圓角矩形,通過UIRectCorner選擇圓弧的位置,cornerRadii為圓弧的大小。

注:UIRectCorner的值:

typedefNS_OPTIONS(NSUInteger, UIRectCorner) {

? ? UIRectCornerTopLeft? ? =1<<0, ? ? ? ? ? ? ? ? ? ?????//矩形左上角

? ? UIRectCornerTopRight? ? =1<<1, ? ? ? ? ? ? ? ? ? ? ? //矩形右上角

? ? UIRectCornerBottomLeft? =1<<2, ? ? ? ? ? ? ? ? ? ?//矩形左下角

? ? UIRectCornerBottomRight =1<<3, ? ? ? ? ? ? ? ? ? //矩形右下角

? ? UIRectCornerAllCorners? = ~0UL ? ? ? ? ? ? ? ? ? ? //矩形四個角都包括

};

注意:cornerRadii為圓弧半徑,圓弧以cornerRadii寬、高的值大的為準(zhǔn),如果超過其鄰近最短邊的一半,則已最短邊一半為準(zhǔn)。(自己試出來的)

UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(100, 100, 200, 300) byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft cornerRadii:CGSizeMake(20, 40)];

+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

繪制一個圓弧路徑。ArcCenter:圓弧圓心位置;radius:圓弧半徑;startAngle:開始的弧度(角度);endAngle:結(jié)束的弧度(角度);clockwise:是否為順時針。

注意:iPhone中,左上角為原點,x軸向右為正方向;y軸向下為正方向。

UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:self.view.center radius:100 startAngle:M_PI_2 endAngle: 2 * M_PI clockwise:YES];

+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;

根據(jù)CGPath創(chuàng)建一個新的UIBezierPath。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

UIBezierPath *newPath = [UIBezierPath bezierPathWithCGPath:bezierPath.CGPath];

二、路徑操作函數(shù)

- (void)moveToPoint:(CGPoint)point;

將當(dāng)前點移動到指定的位置。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100, 300)];

- (void)addLineToPoint:(CGPoint)point;

在路徑中增加一條直線(起點+終點=一條直線)

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100,300)];

[bezierPath addLineToPoint:CGPointMake(150, 150)];

- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;

繪制三階貝塞爾曲線。以endPoint為終點,以controlPoint1、controlPoint2兩個點為控制點,繪制貝塞爾曲線。

三階貝塞爾曲線原理圖

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100,300)];

[bezierPath addCurveToPoint:CGPointMake(300, 300) controlPoint1:CGPointMake(150, 150) controlPoint2:CGPointMake(220, 360)];

三階貝塞爾曲線

- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;

繪制二階貝塞爾曲線。以endPoint為終點,以controlPoint為控制點,繪制二階貝塞爾曲線。

二階貝塞爾曲線原理圖

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100,300)];

[bezierPath addQuadCurveToPoint:CGPointMake(300, 300) controlPoint:CGPointMake(150, 150)];

二階貝塞爾曲線

- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);

繪制一條圓弧。ArcCenter:圓弧圓心位置;radius:圓弧半徑;startAngle:開始的弧度(角度);endAngle:結(jié)束的弧度(角度);clockwise:是否為順時針。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100,300)];

[bezierPath addArcWithCenter:self.view.center radius:100 startAngle:M_PI_2 endAngle:2 * M_PI clockwise:YES];

注:因為圓弧的中心和起點不是一個位置,所以效果圖中多了一個從起點到圓弧開始點的直線。

效果圖

- (void)closePath;

使用一條直線閉合路徑的起點和終點, 該方法同時也會更新當(dāng)前點到新直線的終點(即路徑的起點)(使一個路徑變成閉合回路)。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100,300)];

[bezierPath addLineToPoint:CGPointMake(150, 50)];

[bezierPath addLineToPoint:CGPointMake(220, 80)];

[bezierPath closePath];

[bezierPath addLineToPoint:CGPointMake(250, 430)];

注:在效果圖中可以看出,在調(diào)用closePath方法之后,路徑形成了一個封閉的三角形,之后再添加直線,也是從起點開始,而不是上一個終點。

效果圖

- (void)removeAllPoints;

移除路徑中所有的點。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath removeAllPoints];

- (void)appendPath:(UIBezierPath *)bezierPath;

路徑中增加一個已有路徑。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

UIBezierPath *path2 = [UIBezierPath bezierPath];

[bezierPath appendPath:path2];

- (UIBezierPath *)bezierPathByReversingPath NS_AVAILABLE_IOS(6_0);

返回一個翻轉(zhuǎn)已有路徑的新路徑。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

UIBezierPath*path2 = [bezierPath bezierPathByReversingPath];

- (void)applyTransform:(CGAffineTransform)transform;

對路徑中的所有點進(jìn)行二維形變, 該變化立即生效, 且為永久性改變所有點。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath applyTransform:CGAffineTransformMakeTranslation(20, 20)];

三、路徑信息屬性

@property(nonatomic) CGPathRef CGPath;

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

CGPathRefpath = bezierPath.CGPath;

@property(readonly,getter=isEmpty) BOOL empty;

是否路徑信息為空, 即使通過moveToPoint:移動到指定的位置也算不為空。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

BOOLisEmpty = bezierPath.empty;

@property(nonatomic,readonly) CGRect bounds;

可以封閉所有路徑點的最小矩形范圍, 包括多次貝塞爾曲線的控制點在內(nèi)。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

CGRect bounds = bezierPath.bounds;

@property(nonatomic,readonly) CGPoint currentPoint;

路徑當(dāng)前所在點。

UIBezierPath*path = [UIBezierPathbezierPath];

CGPoint currentPoint = path.currentPoint;

- (BOOL)containsPoint:(CGPoint)point;

是否包含指定點。

UIBezierPath*path = [UIBezierPathbezierPath];

BOOL isContainsPoint = [path containsPoint:CGPointMake(20,30)];

四、繪圖相關(guān)方法和屬性

@property(nonatomic) CGFloat lineWidth;

路徑的線寬。

UIBezierPath*path = [UIBezierPath bezierPath];

path.lineWidth =10;

@property(nonatomic) CGLineCap lineCapStyle;

路徑的終點形狀, 該屬性適用于開放路徑的起點和終點。

注意:lineCapStyle的值:

typedefCF_ENUM(int32_t, CGLineCap) {

? ? kCGLineCapButt, ? ? ? ? ? ? ? ?//方形結(jié)束, 結(jié)束位置正好為精確位置?!J(rèn)值

? ? kCGLineCapRound, ? ? ? ? ? ?//圓形結(jié)束, 結(jié)束位置超過精確位置半個線寬。

? ? kCGLineCapSquare ? ? ? ? ? ?//方形結(jié)束, 結(jié)束位置超過精確位置半個線寬。

};

UIBezierPath*path = [UIBezierPath bezierPath];

path.lineCapStyle = kCGLineCapButt;

@property(nonatomic) CGLineJoin lineJoinStyle;

路徑的連接點(拐角)形狀。

注意:lineJoinStyle的值:

typedefCF_ENUM(int32_t, CGLineJoin) {

? ? kCGLineJoinMiter, ? ? ? ? ? ? ? ?//全部連接(尖角)?!J(rèn)值

? ? kCGLineJoinRound, ? ? ? ? ? ? ?//圓形連接。(圓角)

? ? kCGLineJoinBevel ? ? ? ? ? ? ? ?//斜角連接。(切角)

};

拐角樣式

UIBezierPath*path = [UIBezierPath bezierPath];

path.lineJoinStyle = kCGLineJoinMiter;

@property(nonatomic) CGFloat miterLimit; // Used when lineJoinStyle is kCGLineJoinMiter

最大斜接長度,怎么理解呢?就是上圖中拐角處外面尖角和內(nèi)部尖角的距離。但是這個只有在kCGLineJoinMiter情況下使用才有效果,如果這個miterLimit小于斜接長度,就成為了kCGLineJoinBevel類型。

UIBezierPath*path = [UIBezierPath bezierPath];

path.lineJoinStyle = kCGLineJoinMiter;

path.miterLimit = 1; ? ? ? ? ? ? ? ?////這里設(shè)為1 因為斜接長度超過了1 所以就自動轉(zhuǎn)化為了kCGLineJoinBevel類型。

@property(nonatomic) CGFloat flatness;

確定彎曲路徑短的繪制精度的因素。

@property(nonatomic) BOOL usesEvenOddFillRule; // Default is NO. When YES, the even-odd fill rule is used for drawing, clipping, and hit testing.

一個bool值指定even-odd規(guī)則是否在path可用。

- (void)setLineDash:(nullableconstCGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase;

設(shè)置線型 可設(shè)置成虛線。

注:pattern 線段數(shù)組 如:CGFloat dash[] = {1,1}; 代表實線和空白交替的長度 及先繪制1個長度再空1個,再繪制一個.....;

????????count數(shù)組長度 count值小于數(shù)組實際長度時,方法就會對相應(yīng)長度的數(shù)組元素進(jìn)行循環(huán),而大于的時候 會有警告,沒有效果; ? ? ? ? ? ? ? ? ? ? ? ? ??

????????phase 循環(huán)數(shù)組時跳過的長度,如pattern為{2,6},phase為1,則第一個元素畫1的時候就跳過直接畫6

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100,300)];

[bezierPath addLineToPoint:CGPointMake(150, 50)];

CGFloat patten[] = {4,6};

[bezierPath setLineDash:pattencount:2phase:1];

效果圖

- (void)getLineDash:(nullableCGFloat *)pattern count:(nullableNSInteger *)count phase:(nullableCGFloat *)phase;

檢索線型。

- (void)fill;

利用當(dāng)前繪畫屬性填充路徑封閉范圍, 該方法在繪畫之前會自動將開放子路徑封閉, 填充部分不包含路徑本身, 所以對于線寬較大的路徑, 填充部分會跟部分路徑重合。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100,300)];

[bezierPath addLineToPoint:CGPointMake(150, 50)];

[bezierPath addLineToPoint:CGPointMake(220, 80)];

bezierPath.lineWidth=5.0;

[bezierPath fill];

[bezierPath stroke];

- (void)stroke;

利用當(dāng)前繪畫屬性沿著路徑畫線。

UIBezierPath*path = [UIBezierPathbezierPath];

// do something....

[path stroke];

- (void)fillWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;

利用指定模式填充路徑封閉范圍, 該方法在繪畫之前會自動將開放子路徑封閉, 填充部分不包含路徑本身, 所以對于線寬較大的路徑, 填充部分會跟部分路徑重合。

注意:CGBlendMode的值很多,大家有興趣的可以自己挨個試試。

typedefCF_ENUM (int32_t, CGBlendMode) {

? ? /* Available in Mac OS X 10.4 & later. */

? ? kCGBlendModeNormal,

? ? kCGBlendModeMultiply,

? ? kCGBlendModeScreen,

? ? kCGBlendModeOverlay,

? ? kCGBlendModeDarken,

? ? kCGBlendModeLighten,

? ? kCGBlendModeColorDodge,

? ? kCGBlendModeColorBurn,

? ? kCGBlendModeSoftLight,

? ? kCGBlendModeHardLight,

? ? kCGBlendModeDifference,

? ? kCGBlendModeExclusion,

? ? kCGBlendModeHue,

? ? kCGBlendModeSaturation,

? ? kCGBlendModeColor,

? ? kCGBlendModeLuminosity,

? ? /* Available in Mac OS X 10.5 & later. R, S, and D are, respectively,

?? ? ? premultiplied result, source, and destination colors with alpha; Ra,

?? ? ? Sa, and Da are the alpha components of these colors.

?? ? ? The Porter-Duff "source over" mode is called `kCGBlendModeNormal':

?? ? ? ? R = S + D*(1 - Sa)

?? ? ? Note that the Porter-Duff "XOR" mode is only titularly related to the

?? ? ? classical bitmap XOR operation (which is unsupported by

?? ? ? CoreGraphics). */

? ? kCGBlendModeClear,? ? ? ? ? ? ? ? ? /* R = 0 */

? ? kCGBlendModeCopy,? ? ? ? ? ? ? ? ? /* R = S */

? ? kCGBlendModeSourceIn,? ? ? ? ? ? ? /* R = S*Da */

? ? kCGBlendModeSourceOut,? ? ? ? ? ? ? /* R = S*(1 - Da) */

? ? kCGBlendModeSourceAtop,? ? ? ? ? ? /* R = S*Da + D*(1 - Sa) */

? ? kCGBlendModeDestinationOver,? ? ? ? /* R = S*(1 - Da) + D */

? ? kCGBlendModeDestinationIn,? ? ? ? ? /* R = D*Sa */

? ? kCGBlendModeDestinationOut,? ? ? ? /* R = D*(1 - Sa) */

? ? kCGBlendModeDestinationAtop,? ? ? ? /* R = S*(1 - Da) + D*Sa */

? ? kCGBlendModeXOR,? ? ? ? ? ? ? ? ? ? /* R = S*(1 - Da) + D*(1 - Sa) */

? ? kCGBlendModePlusDarker,? ? ? ? ? ? /* R = MAX(0, (1 - D) + (1 - S)) */

? ? kCGBlendModePlusLighter? ? ? ? ? ? /* R = MIN(1, S + D) */

};

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100,300)];

[bezierPath addLineToPoint:CGPointMake(150, 50)];

[bezierPath addLineToPoint:CGPointMake(220, 80)];

bezierPath.lineWidth=5.0;

[bezierPath fillWithBlendMode:kCGBlendModeNormal alpha:0.4];

[bezierPath stroke];

效果圖

- (void)strokeWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;

利用指定模式沿著路徑畫線。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100,300)];

[bezierPath addLineToPoint:CGPointMake(150, 50)];

[bezierPath addLineToPoint:CGPointMake(220, 80)];

bezierPath.lineWidth=5.0;

[bezierPath strokeWithBlendMode:kCGBlendModeNormal alpha:0.4];

效果圖

- (void)addClip;

剪切被接收者路徑包圍的區(qū)域該路徑是帶有剪切路徑的當(dāng)前繪圖上下文。使得其成為我們當(dāng)前的剪切路徑。簡單的說,就是一個path調(diào)用addClip之后,它所在的context的可見區(qū)域就變成了它的“fill area”,接下來的繪制,如果在這個區(qū)域外都會被無視。

//首先畫一個三角形

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

[bezierPath moveToPoint:CGPointMake(100,300)];

[bezierPath addLineToPoint:CGPointMake(150, 50)];

[bezierPath addLineToPoint:CGPointMake(220, 80)];

bezierPath.lineWidth=5.0;

[bezierPath fillWithBlendMode:kCGBlendModeNormal alpha:0.4];

//然后調(diào)用addClip(裁剪)方法。

[bezierPath addClip];

//之后,在繪制一個圓弧。

[bezierPath addArcWithCenter:bezierPath.currentPoint radius:100 startAngle:M_PI_2 endAngle:2 * M_PI clockwise:YES];

[bezierPath stroke];

效果圖上可以看出圓弧只顯示了在三角形上的一部分。

效果圖

- (void)setFill;

路徑的填充顏色。

[[UIColor orangeColor] setFill];

- (void)setStroke;

路徑的畫線顏色。

[[UIColor orangeColor] setStroke];

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

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

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