先看最終效果:
- - -
繪畫這個純屬周末雨天無聊,這里使用的都是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