本文參考此文參考bash
咱們在項目中會接觸到一些曲線的繪製,最初接觸這個概念是由於有一個在屏幕右邊的按鈕,由於貼着屏幕,按鈕右邊是沒有圓角的,而左上和左下是圓角,相似:app
還沒知道貝塞爾曲線繪製以前,我是直接用 _laborExplainButton.layer.cornerRadius = 33/2;
繪製一個四個角都是圓角的按鈕,而後給出文字靠左屬性 _laborExplainButton.titleLabel.textAlignment = NSTextAlignmentLeft;
設定按鈕寬度時,多給出一段寬度,在用Masonary佈局時讓右邊超出父視圖,再慢慢調數值,讓文字接近居中。佈局
方法很笨,可是還算是實現了圖片的效果(笑)。學習
可是日後出現的一些相似於tableView的緊密相連的cell的第一個cell左上和右上爲圓角的需求,總不能再這樣「投機」了,就開始學習到了用貝塞爾曲線實現。動畫
本筆記將從蘋果官方API:UIBezierPath.h文件的各類方法解釋、介紹出發,介紹經常使用的一些方法和流程,再對具體案例作實現介紹,作一個從學到用的總結,不能作到全部方法方面都顧及,更深的更復雜的繪製實現還待後續學習補充。ui
UIBezierPath是在iOS開發中繪製矢量圖或者路徑的時候會常用的一個部分,在UIKit裏面是CoreGraphics對path的封裝,使用UIBezierPath能夠繪製直線、矩形、橢圓、不規則圖形、多邊形和貝塞爾曲線等,只要是能想到的線條都能畫出來。atom
// 用來對某(幾)個角進行貝塞爾繪製
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 << 0,
UIRectCornerTopRight = 1 << 1,
UIRectCornerBottomLeft = 1 << 2,
UIRectCornerBottomRight = 1 << 3,
UIRectCornerAllCorners = ~0UL
};
// 初始化無形裝的貝塞爾曲線
+ (instancetype)bezierPath;
// 初始化矩形貝塞爾曲線
+ (instancetype)bezierPathWithRect:(CGRect)rect;
// 繪製橢圓(圓形)貝塞爾曲線
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
// 繪製含有圓角的貝塞爾曲線
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; // rounds all corners with the same horizontal and vertical radius
// 繪製可選擇圓角方位的貝塞爾曲線
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
// 繪製圓弧曲線
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
// 根據CGPathRef繪製貝塞爾曲線
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
// CGPath能夠理解爲圖形的路徑,拿到CGPath
- (CGPathRef)CGPath NS_RETURNS_INNER_POINTER CF_RETURNS_NOT_RETAINED;
// Path construction
// 貝塞爾曲線開始的點
- (void)moveToPoint:(CGPoint)point;
// 添加直線到該點
- (void)addLineToPoint:(CGPoint)point;
// 添加二次曲線到該點
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
// 添加曲線到該點
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
// 添加圓弧
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
// 閉合曲線
- (void)closePath;
// 移除全部曲線的點
- (void)removeAllPoints;
// 路徑拼接
- (void)appendPath:(UIBezierPath *)bezierPath;
// 返回一個與當前路徑相反的新的貝塞爾路徑對象
- (UIBezierPath *)bezierPathByReversingPath NS_AVAILABLE_IOS(6_0);
// 路徑進行仿射變換
- (void)applyTransform:(CGAffineTransform)transform;
// Path info
// 只讀類型,路徑上是否有有效的元素
@property(readonly,getter=isEmpty) BOOL empty;
// 和view的bounds是不同的,它獲取path的X座標、Y座標、寬度,可是高度爲0
@property(nonatomic,readonly) CGRect bounds;
// 當前path的位置,能夠理解爲path的終點
@property(nonatomic,readonly) CGPoint currentPoint;
// 路徑是否包含點point
- (BOOL)containsPoint:(CGPoint)point;
// Drawing properties
// 邊框高度
@property(nonatomic) CGFloat lineWidth;
// 端點類型
@property(nonatomic) CGLineCap lineCapStyle;
// 線條鏈接類型
@property(nonatomic) CGLineJoin lineJoinStyle;
// 線條最大寬度最大限制
@property(nonatomic) CGFloat miterLimit; // Used when lineJoinStyle is kCGLineJoinMiter
// 繪製的精度,默認爲0.6,精度越大須要處理的時間越長
@property(nonatomic) CGFloat flatness;
// 單雙數圈規則是否用於繪製路徑,默認是NO
@property(nonatomic) BOOL usesEvenOddFillRule; // Default is NO. When YES, the even-odd fill rule is used for drawing, clipping, and hit testing.
// 設置線型
- (void)setLineDash:(nullable const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase;
// 檢索線型
- (void)getLineDash:(nullable CGFloat *)pattern count:(nullable NSInteger *)count phase:(nullable CGFloat *)phase;
// Path operations on the current graphics context
// 填充貝塞爾曲線內部
- (void)fill;
// 繪製貝塞爾曲線邊框
- (void)stroke;
// These methods do not affect the blend mode or alpha of the current graphics context
// 過於複雜
- (void)fillWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
- (void)strokeWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
// 修改當前圖形上下文的繪圖區域可見,隨後的繪圖操做致使呈現內容只有發生在指定路徑的填充區域
- (void)addClip;
複製代碼
UIBezierPath是對CGPathRef的封裝,它提供了CGPath屬性使咱們在開發過程當中獲取底層的path,在建立矢量圖形的時候,把圖形拆解成一條或者多條線段,而後拼接在一塊兒,每條線段的終點都是下一條線段的起點,這就是大概的實現思路。具體步驟以下:spa
一、建立一個UIBezierPath對象;.net
二、用moveToPoint:方法設置初始線段的起點;code
三、添加線段,定義一個或者多個子路徑;
四、修改UIBezierPathUIBezierPath的繪圖部分的相關屬性;
效果圖:
圖片是個UIImageView,起初對背景的白色view切了四個角的圓角,發現這個UIImageView仍是四角尖尖,再對UIImageView切圓角,變成了四角圓圓....因此,在對背景view切好四個角圓角後,咱們還要用貝塞爾,切UIImageView的上兩個角圓角:
可是,我用的是Masonary佈局,在懶加載中寫貝塞爾相關屬性,並不能實現,由於一開始不能獲得UIImageView的佈局,因此研究了一番,咱們在- (void)layoutSubviews;
方法中才去寫切圓角操做,這個方法是在佈局完以後會走一次的,很好的解決了取不到範圍的問題。
具體:
- (void)layoutSubviews{
[super layoutSubviews];
// 切上上兩角圓角
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.coverImageView.bounds byRoundingCorners: UIRectCornerTopLeft|UIRectCornerTopRight cornerRadii:CGSizeMake(5, 5)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
// frame爲UIImageView的bounds
maskLayer.frame = self.coverImageView.bounds;
maskLayer.path = maskPath.CGPath;
self.coverImageView.layer.mask = maskLayer;
}
複製代碼
項目中其餘相似的狀況都舉一反三,使用此方法解決。
這裏實際上是UIBezierPath結合其餘layer使用。 原則上使用UIBezierPath主要只是畫出形狀或畫出一個圖形的路徑path,可是它也能夠配合其餘的layer使用(CAShapeLayer,CAGradientLayer等),layer能夠添加動畫,因此UIBezierPath結合layer使用效果會更棒。