iOS Quart2D繪圖之UIGraphicsGetCurrentContext基礎。

背景:

某天,你正在上班ing……這個時候老大過來了,“小明啊,這個按鈕太丑了,客戶要求變成五角星形狀的,UI沒空給你切圖,你給我完成這個需求啊”,說完便頭也不回的走了,留下你一個人在冷風中搖曳,心里一萬只草泥馬在狂奔……

1.png

OK OK ,自己選的iOS開發,哭著也要完成…… 這里你可能就需要使用繪圖這一塊的知識了。如果你恰好這塊還是比較空白的,那么就是。。。

2.gif



沒關系,先讓我們重drawRect,這個方法開始吧。
首先了解一下這個東西干嘛的,怎么用,啥時候用。

  • 1 干嘛的:如果你要對View進行繪制,那么你就要到這里處理
  • 2 怎么用:那當然是在這里涂鴉
  • 3 啥時候用:代碼說明一切
-[DrawLineViewcontroller loadView]
-[DrawLineViewcontroller viewDidLoad]
-[DrawLineViewcontroller viewWillAppear:]
-[DrawLineView drawRect:]
-[DrawLineViewcontroller viewDidAppear:]

從上述代碼我們看到他是在視圖將要顯示之后和顯示之前調用的。


通常我們需要進行一下的步驟進行繪制。

  • 1 獲取當前的上下文(這里只能獲取一次,并且只能在drawRect方法中獲取)
  • 2 描述路徑、形狀(就是處理想要顯示的樣子)
  • 3 把描述好的路徑、形狀添加早上下文中
  • 4 顯示上下文內容


1 畫線

  • 第一步:先創建一個工程
  • 第二步:創建一個View的子類,因為要重寫drawRect方法
  • 第三步:重寫DrawRect
  • 第四步:先了解一下線段是如何繪制的,額……兩個點確定一條直線,這些東西大家都懂,所以我們需要一個起點和一個終點來確定一條直線。


    3.png

代碼如下:

- (void)drawRect:(CGRect)rect {
    // Drawing code
    NSLog(@"%s",__func__);
    //1.獲取上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    //2.描述路徑
    UIBezierPath * path = [UIBezierPath bezierPath];
    //起點
    [path moveToPoint:CGPointMake(10, 10)];
    //終點
    [path addLineToPoint:CGPointMake(100, 100)];
    //設置顏色
    [[UIColor whiteColor]setStroke];
    //3.添加路徑
    CGContextAddPath(contextRef, path.CGPath);
    //顯示路徑
    CGContextStrokePath(contextRef);
    
}
4.png

額外屬性:CGContextSetLineWidth(contextRef, 5); //設置線寬



1 畫矩形

  • 第一步:先創建一個工程
  • 第二步:創建一個View的子類,因為要重寫drawRect方法
  • 第三步:重寫DrawRect
  • 第四步:先了解一下矩形是如何繪制的,矩形是由四個邊組成,所以我們畫四條線段便可以畫一個矩形。


    5.png

方法一:

- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    //1.獲取上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    //2.描述路徑
    UIBezierPath * path = [UIBezierPath bezierPath];
    //起點
    [path moveToPoint:CGPointMake(10, 10)];
    //第二個點
    [path addLineToPoint:CGPointMake(100, 10)];
    //第三個點
    [path addLineToPoint:CGPointMake(100, 100)];
    //第四個點
    [path addLineToPoint:CGPointMake(10, 100)];
    //閉合路徑 也等于 [path addLineToPoint:CGPointMake(10, 10)];
    [path closePath];
    //設置顏色
    [[UIColor greenColor]setStroke];
    //3.添加路徑
    CGContextAddPath(contextRef, path.CGPath);
    //顯示路徑
    CGContextStrokePath(contextRef);
}



效果圖

方法一




方法二:通過一個起點和寬高,可計算出來矩形的大小位置

- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    //1.獲取上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    //2.描述路徑
    UIBezierPath * path = [UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 100, 100)];
    //設置顏色
    [[UIColor greenColor]set];
    //3.添加路徑
    CGContextAddPath(contextRef, path.CGPath);
    //顯示路徑
    CGContextFillPath(contextRef);
    
}




效果圖:

方法二



畫矩形總結:stroke:描邊 、 fill:填充

//設置描邊顏色
[[UIColor greenColor]setStroke];
//顯示描邊路徑
CGContextStrokePath(contextRef);
//設置填充顏色
[[UIColor greenColor]set];
//顯示填充路徑
CGContextFillPath(contextRef);



3 畫圓

  • 第一步:先創建一個工程
  • 第二步:創建一個View的子類,因為要重寫drawRect方法
  • 第三步:重寫DrawRect
  • 第四步:先了解一下圓形是如何繪制的,要確定圓心、半徑,以及旋轉的角度。



    方法一:
- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    //1、獲取當前上下文
    CGContextRef  contextRef = UIGraphicsGetCurrentContext();
    //2.描述路徑
    //ArcCenter:中心點
    //radius:半徑
    //startAngle:起始角度
    //endAngle:結束角度
    //clockwise:是否逆時針
    UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width*0.5, self.bounds.size.height*0.5) radius:self.bounds.size.width*0.4 startAngle:0 endAngle:M_PI*2 clockwise:NO];
    //3.添加路徑到上下文
    CGContextAddPath(contextRef, path.CGPath);
    //4.設置顏色
    [[UIColor brownColor]setFill];
    //4.顯示上下文 顯示一個實心圓
//    CGContextFillPath(contextRef);
    //顯示一個空心圓,描邊
    CGContextStrokePath(contextRef);
}



效果圖:

效果圖



方法二:通過話橢圓的方式去畫圓,大家都知道圓就是橢圓的一個特殊存在。寬高一致的時候就是圓形。

- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    //1、獲取當前上下文
    CGContextRef  contextRef = UIGraphicsGetCurrentContext();
    //2.描述路徑 這是畫橢圓的方法,大家都知道
    UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 100, 100)];
    //3.添加路徑到上下文
    CGContextAddPath(contextRef, path.CGPath);
    //4.設置顏色
    [[UIColor redColor]setFill];
    //4.顯示上下文
    CGContextFillPath(contextRef);
    
}



效果圖



4 畫文字

  • 第一步:先創建一個工程
  • 第二步:創建一個View的子類,因為要重寫drawRect方法
  • 第三步:重寫DrawRect
  • 第四步:寫一段文字,把文字繪制到上下文中

- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    //1.獲取當前上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    //2.創建文字
    NSString * str = @"紙巾藝術";
    //會知道上下文
    [str drawInRect:rect withAttributes:nil];
    CGContextStrokePath(contextRef);
}



基本版效果圖



復雜版:

- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    //1.獲取當前上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    //2.創建文字
    NSString * str = @"紙巾藝術";
    //設置字體樣式
    NSMutableDictionary * dict = [NSMutableDictionary dictionary];
    //NSFontAttributeName:字體大小
    dict[NSFontAttributeName] = [UIFont systemFontOfSize:25];
    //字體前景色
    dict[NSForegroundColorAttributeName] = [UIColor blueColor];
    //字體背景色
    dict[NSBackgroundColorAttributeName] = [UIColor redColor];
    //字體陰影
    NSShadow * shadow = [[NSShadow alloc]init];
    //陰影偏移量
    shadow.shadowOffset = CGSizeMake(2, 2);
    //陰影顏色
    shadow.shadowColor = [UIColor greenColor];
    //高斯模糊
    shadow.shadowBlurRadius = 5;
    dict[NSShadowAttributeName] = shadow;
    //字體間距
    dict[NSKernAttributeName] = @10;
    //繪制到上下文
    //從某一點開始繪制 默認 0 0點
//    [str drawAtPoint:CGPointMake(100, 100) withAttributes:nil];
    //繪制區域設置
    [str drawInRect:rect withAttributes:dict];
    //添加到上下文
    CGContextStrokePath(contextRef);
}



復雜版效果圖



5 畫圖片

  • 第一步:先創建一個工程
  • 第二步:創建一個View的子類,因為要重寫drawRect方法
  • 第三步:重寫DrawRect
  • 第四步:加載一張圖片,繪制到上下文中
<br>
- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    //1.獲取當前的上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    //2.加載圖片
    //這里順便咯嗦一句:使用imageNamed加載圖片是會有緩存的
    //我們這里只需要加載一次就夠了,不需要多次加載,所以不應該保存這個緩存
//    UIImage * image = [UIImage imageNamed:@"222"]; //所以可以換一種方式去加載
    UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"222.png" ofType:nil]];
    
    
//    //繪制的大小位置
//    [image drawInRect:rect];
    
    
//    //從某個點開始繪制
//    [image drawAtPoint:CGPointMake(0, 0)];
    
    
    //繪制一個多大的圖片,并且設置他的混合模式以及透明度
    //Rect:大小位置
    //blendModel:混合模式
    //alpha:透明度
    [image drawInRect:rect blendMode:kCGBlendModeNormal alpha:1];
    
    
    //從某一點開始繪制圖片,并設置混合模式以及透明度
    //point:開始位置
    //blendModel:混合模式
    //alpha:透明度
//    [image drawAtPoint:CGPointMake(0, 0) blendMode:kCGBlendModeNormal alpha:1];
    
    //添加到上下文
    CGContextFillPath(contextRef);
}



都寫一起了!!!




6 綜合實例:

1 重繪

  • 實現一個下載進度條
    • 第一步:先創建一個工程
    • 第二步:創建一個View的子類,因為要重寫drawRect方法
    • 第三步:重寫DrawRect
    • 第四步:加載一張圖片,繪制到上下文中


- (void)drawRect:(CGRect)rect {
    
    CGFloat startA = - M_PI_2;
    CGFloat endA =  - M_PI_2 + self.progress * M_PI * 2;
    NSLog(@"%f - %f - %f",self.progress,(3.14159265359*self.progress)/180,endA);
    UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width*0.5, self.bounds.size.height*0.5) radius:self.bounds.size.width*0.5-10 startAngle:startA endAngle:endA clockwise:YES];
    [path stroke];
    
}



這里我們可以看到我并沒有獲取當前的上下文,這里是因為[path stroke]; 已經幫我們完成了其余的操作。
其實內部實現還是一樣的!!
基本版效果:

基本版效果

高能版:上面的只是完成了功能,但是卻不好看!!然后我們稍稍的美化一下

- (void)drawRect:(CGRect)rect {
    
    CGFloat startA = - M_PI_2;
    CGFloat endA =  - M_PI_2 + self.progress * M_PI * 2;
    NSLog(@"%f - %f - %f",self.progress,(3.14159265359*self.progress)/180,endA);
    UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width*0.5, self.bounds.size.height*0.5) radius:self.bounds.size.width*0.5-10 startAngle:startA endAngle:endA clockwise:YES];
    [[UIColor colorWithRed:self.progress green:(1-self.progress) blue:0 alpha:1]setStroke];
    path.lineWidth = 5;
    [path stroke];
    
}



動畫版



2 不同顏色的線

先跑出問題!



這種線怎么處理!我們先實現三條線

- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    UIBezierPath * path = [UIBezierPath bezierPath];
    //設置線寬
    path.lineWidth = 5;
    
    //第一條線
    [[UIColor purpleColor]setStroke];
    [path moveToPoint:CGPointMake(10, 10)];
    [path addLineToPoint:CGPointMake(10, 100)];
    
    //第二條線
    [[UIColor orangeColor]setStroke];
    [path moveToPoint:CGPointMake(30, 10)];
    [path addLineToPoint:CGPointMake(30, 100)];
    
    //第三條線
    [[UIColor greenColor]setStroke];
    [path moveToPoint:CGPointMake(50, 10)];
    [path addLineToPoint:CGPointMake(50, 100)];
    
    //使用描邊方式添加到上下文中
    [path stroke];
}



你可能想當然的就這么做了,可是你發現效果是這樣子的!!!

哎呀我去,為什么

其實是這樣子的


先設置了紫色

設置了橙色



設置了綠色



所以,最后顯示的就是綠色!!!!

這里我們還要了解一個東西!
上下文的狀態棧。



21.png



方法一:

- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    
    //1 獲取上下文
    //分別設置線段的顏色
    CGContextRef purple = UIGraphicsGetCurrentContext();
    [[UIColor purpleColor]setStroke];
    CGContextSaveGState(purple);
    
    CGContextRef orange = UIGraphicsGetCurrentContext();
    [[UIColor orangeColor]setStroke];
    CGContextSaveGState(orange);
    
    CGContextRef green = UIGraphicsGetCurrentContext();
    [[UIColor greenColor]setStroke];
    CGContextSaveGState(green);
    
    
    UIBezierPath * path = [UIBezierPath bezierPath];
    //設置線寬
    path.lineWidth = 5;
    
    //把紫色的上下文從棧中取出來
    CGContextRestoreGState(purple);
    //第一條線
    [[UIColor purpleColor]setStroke];
    [path moveToPoint:CGPointMake(10, 10)];
    [path addLineToPoint:CGPointMake(10, 100)];
    [path stroke];
    
    
    //把紫色的上下文從棧中取出來
    CGContextRestoreGState(orange);
    path = [UIBezierPath bezierPath];
    //設置線寬
    path.lineWidth = 9;
    //第二條線
    [[UIColor orangeColor]setStroke];
    [path moveToPoint:CGPointMake(30, 10)];
    [path addLineToPoint:CGPointMake(30, 100)];
    [path stroke];
    
    //把紫色的上下文從棧中取出來
    CGContextRestoreGState(green);
    path = [UIBezierPath bezierPath];
    //設置線寬
    path.lineWidth = 3;
    //第三條線
    [[UIColor greenColor]setStroke];
    [path moveToPoint:CGPointMake(50, 10)];
    [path addLineToPoint:CGPointMake(50, 100)];
    [path stroke];
}



方法二:

- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    [[self bezierPathWithPoint:CGPointMake(10, 10) endPoint:CGPointMake(10, 180) lineColor:[UIColor purpleColor] lineWidth:6] stroke];
    [[self bezierPathWithPoint:CGPointMake(50, 10) endPoint:CGPointMake(50, 180) lineColor:[UIColor greenColor] lineWidth:6] stroke];
    [[self bezierPathWithPoint:CGPointMake(90, 10) endPoint:CGPointMake(90, 180) lineColor:[UIColor orangeColor] lineWidth:6] stroke];
    
}

- (UIBezierPath *)bezierPathWithPoint:(CGPoint)startPoint endPoint:(CGPoint) endPoint lineColor:(UIColor*)lineColor lineWidth:(CGFloat)lineWidth{
    UIBezierPath * path = [UIBezierPath bezierPath];
    [lineColor setStroke];
    path.lineWidth = lineWidth;
    [path moveToPoint:startPoint];
    [path addLineToPoint:endPoint];
    
    return path;
}



了解了原理之后就這樣子寫快點啦



2017年12月11日

新增漸變色的繪制:方案一使用CGContextDrawLinearGradient去做



新建一個View在Controller中加載他,然后實現如下代碼,注釋比較詳細,就不在說明了。

- (void)drawRect:(CGRect)rect {
    
    //畫線
    [self drawLine];
    
    //畫一個漸變色的線
    [self drawLineWithStartColor:[UIColor redColor] endColor:[UIColor greenColor]];
}

- (void)drawLineWithStartColor:(UIColor *)startColor endColor:(UIColor *)endColor{
    //1、獲取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    //3、創建一個顏色空間
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    //4、設置顏色的location
    CGFloat locations[] = { 0.0, 1.0 };
    //5、創建一個顏色數組,由于NSArray里面只能存對象,所以需要橋接
    NSArray *colors = @[(__bridge id)startColor.CGColor,(__bridge id)endColor.CGColor];
    
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
    //釋放
    CFRelease(colorSpace);
    
    //保存一份當前上下文,壓棧
    CGContextSaveGState(ctx);
    
    //繪制一個矩形,矩形足夠小,也可以是一條線
    CGFloat width = self.frame.size.width;
    CGContextMoveToPoint(ctx, 0, 120);
    CGContextAddLineToPoint(ctx, width, 120);
    CGContextAddLineToPoint(ctx, width, 123);
    CGContextAddLineToPoint(ctx, 0, 123);
    //裁剪
    CGContextEOClip(ctx);
    
    //繪制漸變
    CGContextDrawLinearGradient(ctx, gradient, CGPointMake(0, 100), CGPointMake(self.frame.size.width, 100), kCGGradientDrawsBeforeStartLocation);
    
    //恢復一份當前的上下文,出棧
    CGContextRestoreGState(ctx);
    
    //釋放內存
    CGColorSpaceRelease(colorSpace);
    CGGradientRelease(gradient);
}

- (void)drawLine{
    //1、獲取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    //2畫一條直線的需要知道的就是兩個點,也就是起點和終點
    //2.1、選擇一個點最為畫線的起點
    CGContextMoveToPoint(ctx, 0, 100);
    //2.2、設置線的終點
    CGContextAddLineToPoint(ctx, 300, 100);
    
    CGContextSetLineWidth(ctx, 3);
    
    //3、填充路徑 填充路徑的方法有兩個 一個是 stroke(描邊) 一個 fill(填充)
    CGContextStrokePath(ctx);// 類似于描邊
    
    //4、釋放內存
    CGContextRelease(ctx);
}




方案二使用CAGradientLayer去做

    CAGradientLayer * layer = [CAGradientLayer layer];
    //設置大小
    layer.frame = CGRectMake(0, 400, 300, 3);
    //設置顏色
    layer.colors = @[(__bridge id)[UIColor colorWithRed:204.0 / 255.0 green:224.0 / 255.0 blue:244.0 / 255.0 alpha:1].CGColor,
                        (__bridge id)[UIColor colorWithRed:29.0 / 255.0 green:156.0 / 255.0 blue:215.0 / 255.0 alpha:1].CGColor,
                        (__bridge id)[UIColor colorWithRed:0.0 / 255.0 green:50.0 / 255.0 blue:126.0 / 255.0 alpha:1].CGColor];
    //漸變層的相對位置,起始點為0,終止點為1,中間點為 (point-startpoint)/(endpoint-startpoint)
    layer.locations = @[@0,@.5,@1];
    //漸變方向
    layer.startPoint = CGPointMake(0, 1);
    layer.endPoint = CGPointMake(1, 1);
    [self.view.layer addSublayer:layer];
最終效果


demo地址

下一篇:圖形上下文

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容