使用Core Graphics繪畫一個山寨微信icon

先看最終效果:

- - -

繪畫這個純屬周末雨天無聊,這里使用的都是Core Graphics上很基本的幾個方法,對新手(我也是新手)來說還是有幫助的。下面說下這個繪制過程。

0. 設(shè)置背景色

1. 繪制綠色橢圓

2. 繪制綠色三角形

3. 繪制眼睛

4. 繪制白色橢圓

5. 繪制白色三角形

6. 繪制眼睛

7. 就這么簡單

_ _ _

那么,省去創(chuàng)建project的步驟之后我們要做的就是自定義個UIView子類,我們就將它命名為`WechatView`,然后在storyboard上拖動一個`UIView`到視圖控制器上。

接著,為了在繪制過程中能不需要每次都編譯,我們可以使用iOS7之后的一個新特性 `IB_DESIGNABLE` 來時時看到我們的繪畫過程。說到 `IB_DESIGNABLE` 就自然的會聯(lián)想到 `IBInspectable`,先淺陋的介紹一下這兩個的作用。首先是 `IB_DESIGNABLE`,如剛才所說,它可以讓我們時時的看到我們的繪制過程,大大的省去了我們編譯的時間,是iOS7上一個超贊的新特性。然后是 `IBInspectable`,它是添加在屬性上的,在屬性上添加上它我們就可以在Xcode的右邊的*Show the Attributes inspector*上看到,并可以自定義這些值(當(dāng)然,并不是所有的類習(xí)慣都支持的,具體有哪些,需要的時候試試便知哈)。

回到正題,現(xiàn)在我們只需要將 `IB_DESIGNABLE` 添加到`WechatView` 的頭文件上即可(當(dāng)然,你想添加到.m文件上編譯器也不反對)。然后,在這里就添加一個用處不大的屬性bkColor作為背景顏色,如下代碼:

IB_DESIGNABLE

@interface WechatView : UIView

@property(nonatomic, assign)IBInspectable UIColor *bkColor;

@end

這樣,我們就可以在點(diǎn)擊storyboard后,選擇*Show the Identity inspector*上看到下圖:


在*Show the Attributes inspector*上看到下圖:


現(xiàn)在改變一下bkColor,將它改為灰色。


改后當(dāng)然是看不到視圖改變的,我們得真正的為 `backgoundColor` 這個屬性賦值;或者使用Core Graphices來填充背景。方法一中如果想時時看到改變效果,我們可以添加如下代碼:

- (void)prepareForInterfaceBuilder {

[super prepareForInterfaceBuilder];

self.backgroundColor = self.bkColor;

}

代碼剛上,效果即可見哈,爽

`prepareForInterfaceBuilder` 這個方法是iOS8之后才有的,它就是提供給我們試試查看效果的,但是它只是在當(dāng)你的view準(zhǔn)備被繪制在*Interface Builder*時被執(zhí)行,換句話說就是程序運(yùn)行時它是不被調(diào)用的。==command+r==運(yùn)行下程序我們就可以發(fā)現(xiàn),這是的view背景并不是剛才所看到的灰色。說明`prepareForInterfaceBuilder`確實(shí)沒有被調(diào)用。(我們可以通過這個方法,省去編譯時間來看我們的繪制效果,然后再將代碼移到我們所要的位置)。這個方法就說到這。

填充背景

還有方法二,也是接下來我們要用到的方法:使用Core Graphices來填充背景。將前面的代碼注釋掉,然后添加如下代碼:

- (void)drawRect:(CGRect)rect {

CGContextRef context =UIGraphicsGetCurrentContext();

/*0. 填充背景*/

//將當(dāng)前context的顏色填充為bkColor

[self.bkColor setFill];

//填充顏色到當(dāng)前的context上,大小為rect

CGContextFillRect(context, rect);

}

同樣,可以看到`WechatView`的背景變成了灰色。這樣就算開始了第0步繪畫。

繪制綠色橢圓

第1步是開始繪制綠色橢圓,在以上的代碼基礎(chǔ)上繼續(xù)添加如下代碼:

//繪制的最小寬度

CGFloat minWidth = MAX(160, rect.size.width);

//定義綠色橢圓位置和大小

CGFloat greenX = 10;

CGFloat greenY = 10;

CGFloat greenCircleWidth = minWidth/1.5;

CGFloat greenCircleHeight = 21.0/24 * greenCircleWidth;

CGFloat gcW = greenCircleWidth;

CGFloat gcH = greenCircleHeight;

//1. 繪制綠色橢圓

UIColor *greenColor = RGB(125, 225, 73, 1);

//將當(dāng)前context的顏色填充為greenColor

[greenColor setFill];

//繪制并填充橢圓

CGContextFillEllipseInRect(context, CGRectMake(greenX, greenY, greenCircleWidth, greenCircleHeight));

完成了第1步,效果見圖:

繪制綠色三角形

第2步:繪制綠色三角形。通過觀察微信的icon,計(jì)算,微調(diào),我們可以添加以下的代碼來繪制這個三角形:

//2. 畫三角形

//橢圓左邊焦點(diǎn)

CGFloat greenCircleFocusLeft = gcW/2-sqrt(pow(gcW/2, 2)-pow(gcH/2, 2));

//橢圓右邊焦點(diǎn)

CGFloat greenCircleFocusRight = gcW/2+sqrt(pow(gcW/2, 2)-pow(gcH/2, 2));

CGFloat gcFL = greenCircleFocusLeft;

CGFloat gcFR = greenCircleFocusRight;

//眼睛大小

CGFloat eyesWidth1 = gcW/7.5;

//2. 畫三角形

CGPoint points[] = {

CGPointMake(gcFL-eyesWidth1, greenY + gcH + (gcFL-eyesWidth1)/12-10),

CGPointMake(greenX+gcW/2+40, 1.2*(greenX+gcW/2) + greenY),

CGPointMake(gcFL+10, 1.8*gcFL + greenY)};

CGContextAddLines(context, points, 3);

CGContextClosePath(context);

//CGContextStrokePath(context);

CGContextFillPath(context);

三角形的三個點(diǎn)的計(jì)算花了我不少時間,但是這不是重點(diǎn),重點(diǎn)是其實(shí)很簡單的問題我卻傻X的花了很長時間哈,而且最終還只是湊合的,不是最優(yōu)解。取消`CGContextStrokePath(context);`這句的注釋,我們可以看到下圖:

注釋掉后即可看到:


繪制眼睛

第3步:繪制眼睛。眼睛的位置取的是橢圓焦點(diǎn)的位置,而且繪制后看起來還挺對的。眼睛無非是兩個黑色的圓形。如下:

//3. 畫眼睛(圓)

[RGB(45, 49, 32, 1) setFill];

CGContextFillEllipseInRect(context, CGRectMake(greenX + gcFL, greenY+gcH/4, eyesWidth1, eyesWidth1));

CGContextFillEllipseInRect(context, CGRectMake(greenX + gcFR-eyesWidth1, greenY+gcH/4, eyesWidth1, eyesWidth1));

然后就是這樣了:


繪制白色icon

第4,5,6步跟1,2,3步基本是一樣的,只是位置不同而已。

//4. 畫白色橢圓

CGFloat wcX = greenX + gcW / 2;

CGFloat wcY = greenY + gcH / 2;

CGFloat wcW = gcW * 0.8;

CGFloat wcH = gcH * 0.8;

CGFloat whiteCircleFocusLeft = wcW/2-sqrt(pow(wcW/2, 2)-pow(wcH/2, 2));

CGFloat whiteCircleFocusRight = wcW/2+sqrt(pow(wcW/2, 2)-pow(wcH/2, 2));

CGFloat wcFL = whiteCircleFocusLeft;

CGFloat wcFR = whiteCircleFocusRight;

CGFloat eyesWidth2 = wcW/7.5;

UIColor *whiteColor = RGB(251, 251, 251, 1);

[whiteColor setFill];

CGContextFillEllipseInRect(context, CGRectMake(wcX, wcY, wcW, wcH));

//5. 畫白色三角形

CGPoint whiteTrianglePoints[] = {

CGPointMake(wcFR+eyesWidth2/4+wcX, wcY + wcH + (wcFL-eyesWidth2/4)/4),

CGPointMake(wcX+wcW/2, 0.9*(wcX+wcW/2)),

CGPointMake(wcFR+eyesWidth2, 0.9*wcFR + wcY)};

CGContextAddLines(context, whiteTrianglePoints, 3);

CGContextClosePath(context);

CGContextFillPath(context);

//6. 畫眼睛(圓)

[RGB(60, 64, 49, 1) setFill];

CGContextFillEllipseInRect(context, CGRectMake(wcX + wcFL, wcY + wcH/4, eyesWidth2, eyesWidth2));

CGContextFillEllipseInRect(context, CGRectMake(wcX + wcFR - eyesWidth2, wcY + wcH/4, eyesWidth2, eyesWidth2));

OK,現(xiàn)在就長這樣了,咋一看還是挺像的哈。

添加陰影

下面我們再添加一些細(xì)節(jié)上的東西。比如我們發(fā)現(xiàn)少了陰影,還有微信的icon上的三角形是圓角的。首先是陰影:在`CGFloat minWidth = MAX(160, rect.size.width);`這句之上加上下面的代碼:

//陰影

CGContextSetShadowWithColor(context, CGSizeMake(-0.5, 0.5), 6, [UIColor blackColor].CGColor);

陰影是加上了,但是并不是我們想要的效果。怎樣才能讓它只是在邊緣上加陰影呢?也很簡單,繼續(xù)在剛才添加的語句下添加:

CGContextBeginTransparencyLayer(context, nil);


搞定,感覺還行哈,不過眼睛的陰影好像也沒了。試試用同樣的方法看能不能解決:在第3步畫眼睛之前添加:

CGContextEndTransparencyLayer(context);

//為綠色橢圓的眼睛添加陰影

CGContextSetShadowWithColor(context, CGSizeMake(-0.5, 0.5), 2, [UIColor blackColor].CGColor);

然后在第3步畫完眼睛之后添加:

CGContextBeginTransparencyLayer(context, nil);

再然后在第6步畫眼睛之前添加上:

//為白色橢圓的眼睛添加陰影

CGContextSetShadowWithColor(context, CGSizeMake(-0.5, 0.5), 2, [UIColor blackColor].CGColor);

這樣說有點(diǎn)亂,注意到了:`CGContextBeginTransparencyLayer(context, nil)` 和 `CGContextEndTransparencyLayer(context)` 是成對出現(xiàn)的,從函數(shù)名稱可以知道它們的作用分別是開始一個透明的layer和結(jié)束一個透明的layer。而夾在它們中間的繪圖操作在指定的context上被合成到一個完全透明的背景(在context中作為一個分離的目標(biāo)緩沖區(qū))。這個操作會保持context原有的裁剪區(qū)域。調(diào)用了`Begin`之后除了1.全局的透明度被設(shè)置為1;2.陰影被屏蔽外,其他的都不變。這樣解釋后,上面的代碼應(yīng)該可以理解了。現(xiàn)在我們的icon看起來是這樣的:

圓角三角形

最后,解決下圓角三角形的問題:回到第2步,將`CGContextFillPath(context);`注釋,并添加以下代碼:

CGContextSetLineJoin(context, kCGLineJoinRound);

CGContextSetLineWidth(context, 4);

CGContextSetStrokeColorWithColor(context, greenColor.CGColor);

CGContextDrawPath(context, kCGPathFillStroke);

然后同理在第5步畫白色三角形上注釋`CGContextFillPath(context);`,并添加以下代碼:

CGContextSetLineJoin(context, kCGLineJoinRound);

CGContextSetLineWidth(context, 3);

CGContextSetStrokeColorWithColor(context, whiteColor.CGColor);

CGContextDrawPath(context, kCGPathFillStroke);

好了,廢話說完了,也算是完成了,最終效果:


實(shí)際上,微信的icon的顏色還是一個漸變的,在這里就不繼續(xù)研究了,要實(shí)現(xiàn)漸變可以用`CGContextDrawLinearGradient(CGContextRef context, CGGradientRef gradient, CGPoint startPoint, CGPoint endPoint, CGGradientDrawingOptions options)`或`CGContextDrawRadialGradient(CGContextRef context, CGGradientRef gradient, CGPoint startCenter, CGFloat startRadius, CGPoint endCenter, CGFloat endRadius, CGGradientDrawingOptions options)`這兩個函數(shù)。

bug 修復(fù)

command+r 編譯+運(yùn)行以下(現(xiàn)在才需要編譯,這也太好了吧),然后發(fā)現(xiàn)成功的crash了。


什么原因?好吧,我把屬性定義成 `assign` 了(低級錯誤),改成`strong` ,問題解決。

結(jié)束語

以上就是我繪制的整個編碼過程,繪制微信icon實(shí)際上就用了core graphics的幾個基本功能,希望能對初學(xué)者有所幫助,說錯的地方也希望能得到指點(diǎn)哈~~

源碼地址:https://github.com/linshaolie/blog/tree/master/DrawWechatIcon

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 之前文章格式有問題,更新一下,原文在這[http://www.lxweimin.com/p/1008f980375...
    木木烈少閱讀 469評論 0 0
  • 簡介: Core Graphics 和Quartz 2D的區(qū)別 quartz是一個通用的術(shù)語,用于描述在iOS和M...
    made_China閱讀 1,361評論 0 1
  • --繪圖與濾鏡全面解析 概述 在iOS中可以很容易的開發(fā)出絢麗的界面效果,一方面得益于成功系統(tǒng)的設(shè)計(jì),另一方面得益...
    韓七夏閱讀 2,796評論 2 10
  • 轉(zhuǎn)載:http://www.lxweimin.com/p/32fcadd12108 每個UIView有一個伙伴稱為l...
    F麥子閱讀 6,312評論 0 13
  • 一直在尋找心中那個愛自己的人,適合自己的人,如果用一個神圣的詞也許就是:真愛。 但是回顧過往的兩段愛情以及幾段從未...
    大顱不大閱讀 360評論 0 0