iOS 使用UIBezierPath 和 Core Graphics繪圖

首先我們通過圖-1了解iOS圖像編程的框架結(jié)構(gòu):

圖-1

UIBezierPath位于UIKit,絕大多數(shù)圖形界面都由UIKit完成。從圖中可以看出UIKit依賴于Core Graphics框架,也是基于Core Graphics框架實現(xiàn)的。如果想要完成某些更底層的功能或者追求極致的性能,那么就要用到Core Graphics去完成。UIBezierPath是對Core Graphics框架中的CGPath的封裝。兩者的本質(zhì)上都是一樣的,都是使用Quartz來繪畫。只不過把繪圖操作暴露在不同的API層面上,在具體實現(xiàn)上,當然也會有一些細小的差別。

我們用Core Graphics來繪圖的一個通常原因就是只是用圖片或是圖層效果不能輕易地繪制出矢量圖形。矢量繪圖包含一下這些:

  • 任意多邊形(不僅僅是一個矩形)
  • 斜線或曲線
  • 文本
  • 漸變

在我們繪制圖形之前,我們需要獲取到一個圖形上下文CGContext,可以把他形象的比喻成畫畫的畫板,試想如果我們沒有畫板是不是就不能繪畫,我們進行繪圖也是如此,UIKit提供一個C函數(shù)來幫組我們獲取圖形上下文:UIGraphicsGetCurrentContext()?,F(xiàn)在我們對UIBezierPath、Core Graphics以及CGPath之間的關(guān)系有了一個大致的了解,那么接下來我通過一個例子來展示它們是如何繪制圖形的。


效果圖

注意:如果不在drawRect:方法中調(diào)用UIGraphicsGetCurrentContext()函數(shù),其返回值為nil。當然我們也有其他方法來創(chuàng)建一個圖形上下文(譬如使用UIGraphicsBeginImageContext()),我會在代碼中為大家展示。

【按照文檔中的說法,系統(tǒng)會維護一個CGContextRef的棧,而UIGraphicsGetCurrentContext()會取棧頂?shù)腃GContextRef,正確的做法是只在drawRect里調(diào)用UIGraphicsGetCurrentContext(),因為在drawRect之前,系統(tǒng)會往棧里面壓入一個valid的CGContextRef,除非自己去維護一個CGContextRef,否則不應(yīng)該在其他地方取CGContextRef。】

code
自定義一個MyView使其繼承與UIView,重寫drawRect:方法:

//.h文件

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

//.m文件

#import "MyView.h"

@implementation MyView

- (instancetype)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];
    if (self) {
        
        self.backgroundColor = [UIColor grayColor];
    }
    return self;
}

//使用UIBezierPath繪圖
- (void)drawWithBezierPath {

    UIBezierPath *bPath = [UIBezierPath bezierPath];
    [bPath moveToPoint:CGPointMake(0, 0)];
    [bPath addCurveToPoint:CGPointMake(200, 200) controlPoint1:CGPointMake(50, 150) controlPoint2:CGPointMake(150, 50)];
    [[UIColor greenColor] setStroke];
    [bPath stroke];
}
//使用Graphics繪圖
- (void)drawWithGraphics {
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextMoveToPoint(context, 200, 0);
    CGContextAddCurveToPoint(context, 150, 150, 50, 50, 0, 200);
    [[UIColor redColor] setStroke];
    CGContextStrokePath(context);
}

//使用CGPath繪圖
- (void)drawWithCGPath {

    CGContextRef gc = UIGraphicsGetCurrentContext();
    //創(chuàng)建CGMutablePathRef
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddArc(path, NULL, 100, 100, 45, 0, M_PI*2, YES);
    //將CGMutablePathRef添加到當前Context內(nèi)
    CGContextAddPath(gc, path);
    //設(shè)置繪圖屬性
    [[UIColor redColor] setStroke];
    CGContextSetLineWidth(gc, 5);
    //執(zhí)行繪畫
    CGContextStrokePath(gc);
    CGPathRelease(path);
}

//CGPath和UIBezierPath混合使用繪圖
- (void)drawWithBezierPathAndGraphics {

    UIBezierPath*    aPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:100 startAngle:0 endAngle:M_PI*2 clockwise:YES];
    CGPathRef cgPath = aPath.CGPath;
    CGMutablePathRef  mutablePath = CGPathCreateMutableCopy(cgPath);
    CGPathAddRect(mutablePath, NULL, CGRectMake(50, 50, 100, 100));
    aPath.CGPath = mutablePath;
    aPath.lineWidth = 5;
    [[UIColor greenColor] setStroke];
    [aPath stroke];
    CGPathRelease(mutablePath);
}

- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    [self drawWithBezierPath];
    
    [self drawWithGraphics];
    
    [self drawWithCGPath];
    
    [self drawWithBezierPathAndGraphics];

}

以上代碼只是簡單的演示如何用UIBezierPath和Core Graphics進行繪圖,大家可以細心觀察彼此之間的差別和聯(lián)系,也可以大膽的嘗試使用其他的繪圖API也繪制自己想繪制的圖形,譬如復雜的圖表、人物肖像、餅狀圖等等只要是基于矢量圖的都能實現(xiàn)。通過以上代碼我們可以發(fā)現(xiàn),如果我們需要繪制一張圖形,就必須自定義一個view并重寫其DrawRect:方法,哪怕只是一個簡單的矩形。這樣是不是未免有點麻煩,難道就沒有其他方法嗎?of course,當然有了,我就不賣關(guān)子了,直接上代碼:

//開始圖像繪圖
    UIGraphicsBeginImageContext(self.view.bounds.size);
    //獲取當前CGContextRef
    CGContextRef gc = UIGraphicsGetCurrentContext();
    
    //創(chuàng)建用于轉(zhuǎn)移坐標的Transform,這樣我們不用按照實際顯示做坐標計算
    CGAffineTransform transform = CGAffineTransformMakeTranslation(50, 50);
    //創(chuàng)建CGMutablePathRef
    CGMutablePathRef path = CGPathCreateMutable();
    //左眼
    CGPathAddEllipseInRect(path, &transform, CGRectMake(0, 0, 20, 20));
    //右眼
    CGPathAddEllipseInRect(path, &transform, CGRectMake(80, 0, 20, 20));
    //笑
    CGPathMoveToPoint(path, &transform, 100, 50);
    CGPathAddArc(path, &transform, 50, 50, 50, 0, M_PI, NO);
    //將CGMutablePathRef添加到當前Context內(nèi)
    CGContextAddPath(gc, path);
    //設(shè)置繪圖屬性
    [[UIColor blueColor] setStroke];
    CGContextSetLineWidth(gc, 2);
    //執(zhí)行繪畫
    CGContextStrokePath(gc);
    
    //從Context中獲取圖像,并顯示在界面上
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    UIImageView *imgView = [[UIImageView alloc] initWithImage:img];
    [self.view addSubview:imgView];

最后提醒一下大家,使用CoreFoundation框架創(chuàng)建對象,例如代碼中出現(xiàn)的:CGMutablePathRef path = CGPathCreateMutable();不要忘了在繪制完之后使用相應(yīng)的方法去釋放它們:CGPathRelease(path);。否則會造成內(nèi)存泄漏,ARC是不會幫組我們?nèi)ス芾鞢oreFoundation創(chuàng)建出來的對象。

最后編輯于
?著作權(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)容

  • Core Graphics Framework是一套基于C的API框架,使用了Quartz作為繪圖引擎。它提供了低...
    ShanJiJi閱讀 1,721評論 0 20
  • 原文地址:http://www.cocoachina.com/industry/20140115/7703.htm...
    默默_David閱讀 6,252評論 0 1
  • 我用的okhttp,所以直接用的同一個client,在網(wǎng)上找的直接用OkhttpDownload不行,看原因是因為...
    世外大帝閱讀 1,168評論 0 2
  • 安裝samba /etc/samba/smb.conf 末尾加上 修改文件夾權(quán)限 重啟 samba 服務(wù)
    huarda閱讀 398評論 1 2
  • 如果你最近升級了Xcode8,一跑Swift2.3的項目,毫無意外地,都是錯誤。希望看完這篇文章,可以幫你順利跑起...
    清風沐沐閱讀 1,405評論 0 1

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