1.初覽
在正式開(kāi)始前先簡(jiǎn)單介紹下CoreGraphics擒悬,我會(huì)把CoreGraphics分解成概念和套路兩個(gè)部分模她,具體框架設(shè)計(jì)思路和API用法不會(huì)涉及,可移步參考Quartz 2D Programming Guide
- 概念
概念好比武學(xué)中的內(nèi)功心法懂牧,理解了這些侈净,才能發(fā)揮出招式真正的威力,CoreGraphics中比較核心的概念是Graphics Contexts僧凤,這將是本文引入的唯一的一個(gè)概念畜侦。
- Graphics Contexts(圖形上下文)
Context是個(gè)比較抽象的東西,它不僅僅是一個(gè)可以繪制的圖層拼弃,還包含為當(dāng)前圖層設(shè)置的參數(shù)夏伊,如陰影,線條粗細(xì)吻氧,繪制模式等∮搅可以類(lèi)比成一個(gè)新建的Photoshop圖層以及當(dāng)前筆觸盯孙,顏色等配置。對(duì)于移動(dòng)平臺(tái)祟滴,有三種常見(jiàn)的Context
1.View Graphics Context: 由UIView自動(dòng)創(chuàng)建振惰,你重寫(xiě)UIView drawRect方法時(shí),你的內(nèi)容會(huì)畫(huà)在這個(gè)上下文上垄懂。
2.Bitmap Graphics Context: 繪制在該上下文的內(nèi)容會(huì)以點(diǎn)陣形式存儲(chǔ)在一塊內(nèi)存中骑晶。簡(jiǎn)單說(shuō),就是為圖片開(kāi)辟一塊內(nèi)存草慧,然后在里面畫(huà)東西桶蛔,上下文幫你把圖片內(nèi)存抽象成一個(gè)Context(圖層)了。
3.PDF Graphics Context:顧名思義漫谷,跟PDF文件相關(guān)仔雷,本文不會(huì)涉及。
- 套路
就是慣用套路,這相當(dāng)于武學(xué)中的招式碟婆。我一直有個(gè)疑問(wèn)电抚,如果一門(mén)武學(xué)沒(méi)有招式,怎么判斷這是哪個(gè)門(mén)子的武學(xué)竖共? CoreGraphics里面就有相關(guān)的招式蝙叛,這里將帶入幾個(gè)具備代表性的招式,以后看到別人用CoreGraphics寫(xiě)的看似無(wú)招的代碼公给,其實(shí)仔細(xì)品讀后會(huì)發(fā)現(xiàn)所謂的無(wú)招只是沒(méi)有固定套路借帘,招式還是在的。
第一招:拿取當(dāng)前Graphics Context妓布。
CGContextRef context = UIGraphicsGetCurrentContext();
通常是起始招式姻蚓,接下來(lái)一般會(huì)用來(lái)為上下文設(shè)置參數(shù)比如說(shuō)設(shè)置畫(huà)線時(shí)的寬度CGContextSetLineWidth(context, 1),把上下文內(nèi)容截取成一個(gè)位圖CGBitmapContextCreateImage(context)等等匣沼。
第二招:開(kāi)辟Bitmap Graphics Context
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0);
draw something ...
UIGraphicsEndImageContext();
注意上下構(gòu)成一套組合狰挡,是開(kāi)啟然后關(guān)閉一個(gè)Bitmap Graphics Context,在這區(qū)域內(nèi)的所有繪制操作都是對(duì)于該Bitmap Context的释涛,在代碼塊里面使用第一招UIGraphicsGetCurrentContext()拿到的就是這個(gè)Bitmap Graphics Context加叁。
第三招: 保存和恢復(fù)當(dāng)前Context狀態(tài)
Set line width 5, black hair color ...
Draw hair...
CGContextSaveGState(context);//save line width 5, black color
Set line width 8, red color...
Draw hair ornaments...
CGContextRestoreGState(context);//restore line width 5, black color
Continue to draw hair...
這招又是一個(gè)組合塊,會(huì)產(chǎn)生什么效果呢唇撬?舉個(gè)栗子它匕,你正在描繪一個(gè)人物的頭部畫(huà)像,畫(huà)頭發(fā)=>畫(huà)裝飾物=>修飾頭發(fā)=>修飾裝飾物...這樣需要來(lái)回切換著畫(huà)筆狀態(tài)窖认,實(shí)際過(guò)程中會(huì)有很多參數(shù)需要配置豫柬,這個(gè)招式讓我們能保存恢復(fù)某些狀態(tài)。當(dāng)你使用CGContextSaveGState后接下來(lái)你更改畫(huà)筆狀態(tài)扑浸,畫(huà)完后再使用CGContextRestoreGState可以將狀態(tài)恢復(fù)到使用Save方法之前烧给。關(guān)于哪些狀態(tài)可以保存,請(qǐng)參考CGContextSaveGState Discussion部分
最后一招:扭轉(zhuǎn)乾坤???
CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, CGRectGetHeight(rect)));
這是你熟悉又陌生的線性變換操作喝噪,因?yàn)镃ore Graphics(原點(diǎn)左下角础嫡,y軸向上為正)使用的坐標(biāo)系和UIKit(原點(diǎn)左上角,y軸向下為正)的坐標(biāo)系是不一樣的酝惧,在重寫(xiě)UIView drawRect的時(shí)候直接畫(huà)上去的內(nèi)容是一個(gè)相對(duì)x軸的鏡像榴鼎。因此需要做一次線性變換來(lái)得到正確的方位,該操作是將y變成-1*y 然后沿y軸平移晚唇,平移距離為CGRectGetHeight(rect)巫财。
好了招式都介紹完了。
2.繪制
接下來(lái)要把學(xué)到的內(nèi)容用于實(shí)戰(zhàn)了缺亮,希望通過(guò)實(shí)戰(zhàn)演練翁涤,大家能加深理解桥言,逐漸達(dá)到無(wú)招的狀態(tài)。代碼長(zhǎng)度不一葵礼,只列出關(guān)鍵部分号阿,文底有全套實(shí)現(xiàn)地址。
-
發(fā)光
效果圖:
實(shí)現(xiàn):
//為文字設(shè)置陰影
CGContextSetShadowWithColor(context, CGSizeZero, self.glowSize, self.glowColor.CGColor);
第一招拿到context后鸳粉,這個(gè)效果的核心代碼就只有1句了扔涧,我都不好意思做解釋。你甚至可以直接修改UILabel自帶的陰影屬性來(lái)達(dá)到這個(gè)效果届谈。
-
描邊
效果圖:
實(shí)現(xiàn):
//設(shè)置邊線寬度
CGContextSetLineWidth(context, self.outlineWidth);
//設(shè)置線條轉(zhuǎn)角樣式
CGContextSetLineJoin(context, kCGLineJoinRound);
//設(shè)置繪圖模式為描線
CGContextSetTextDrawingMode(context, kCGTextStroke);
這個(gè)效果的核心只有3行枯夜,通過(guò)第一招拿到context,然后配置context艰山。接下來(lái)調(diào)用super的draw方法湖雹,這時(shí)候就畫(huà)了字的描邊。如果要如圖一樣的黑色填充曙搬,把DrawingMode改成Fill的模式摔吏,再調(diào)用一遍super的draw方法即可。
-
漸變
效果圖:
實(shí)現(xiàn):
//>>>第一部分
//第二招開(kāi)始
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0);
//把當(dāng)前內(nèi)容繪制在Bitmap Context上
[super drawTextInRect:rect];
//第一招
CGContextRef context = UIGraphicsGetCurrentContext();
//以當(dāng)前context內(nèi)容生成一張圖片
CGImageRef mask = CGBitmapContextCreateImage(context);
//第二招結(jié)束
UIGraphicsEndImageContext();
//>>>第二部分
//第一招纵装,此時(shí)是View Graphics context了
context = UIGraphicsGetCurrentContext();
//最后一招征讲,扭轉(zhuǎn)乾坤
CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, CGRectGetHeight(rect)));
//注意是ClipTo,因此只有mask部分能被繪制上內(nèi)容
CGContextClipToMask(context, rect, mask);
//>>>第三部分
//創(chuàng)建漸變
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)color, NULL);
//繪制漸變橡娄,漸變只顯示mask部分
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation|kCGGradientDrawsAfterEndLocation);
這個(gè)效果大體分為3個(gè)部分诗箍,第一部分把原來(lái)的字形畫(huà)在一張圖片里用作mask。第二部分使用mask裁剪當(dāng)前view的context挽唉。第三部分在當(dāng)前view的context上繪制一個(gè)線性漸變滤祖。除線性漸變外,還有徑向漸變瓶籽,具體可以參考結(jié)尾的Git代碼氨距。
-
鏤空
效果圖:
實(shí)現(xiàn):
//第一部分
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0);
[super drawTextInRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGImageRef image = CGBitmapContextCreateImage(context);
UIGraphicsEndImageContext();
//第二部分
context = UIGraphicsGetCurrentContext();
CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, CGRectGetHeight(rect)));
//使用第一部分得到的image創(chuàng)建一個(gè)mask,這里得到的是一個(gè)反向的遮罩
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerComponent(image), CGImageGetBitsPerPixel(image), CGImageGetBytesPerRow(image), CGImageGetDataProvider(image), CGImageGetDecode(image), CGImageGetShouldInterpolate(image));
CGContextClipToMask(context, rect, mask);
//第三部分
//設(shè)置為填充色
[self.maskColor set];
//使用顏色填充區(qū)域
CGContextFillRect(context, rect);
該實(shí)現(xiàn)跟漸變大體相似棘劣,不過(guò)這里創(chuàng)建了一個(gè)反向的mask,把字體區(qū)域給鏤空了楞遏。然后其他部分填充為一個(gè)純色茬暇,當(dāng)然也可以用圖片填充這個(gè)區(qū)域。還有一種鏤空的實(shí)現(xiàn)是通過(guò)修改BlendMode剔除像素寡喝,這里不贅述了糙俗。
-
3D
效果圖:
實(shí)現(xiàn):
//循環(huán)繪制text到context,每次偏移幾個(gè)像素
[self.text drawInRect:CGRectMake(rect.origin.x + i, rect.origin.y + i, rect.size.width, rect.size.height) withAttributes:attrs];
從動(dòng)畫(huà)中應(yīng)該也可以看出端倪,所謂的3D效果其實(shí)是很多圖層疊加起來(lái)的预鬓,因此真正的核心代碼只有這一句巧骚。但相應(yīng)的為了實(shí)現(xiàn)這種透視感,每一層的顏色和陰影都有些許變化。
-
涂層
效果圖:
實(shí)現(xiàn):
//循環(huán)繪制圖片劈彪,圖片偏移i = i + randomValue
[self.strokeTexture drawInRect:CGRectMake(i, midY - self.strokeWidth/2.f, self.strokeWidth, self.strokeWidth) blendMode:kCGBlendModeNormal alpha:self.maskAlpha];
這個(gè)效果的核心代碼也只有這一句竣蹦,基本思想是用一張筆刷灰度圖,修改該灰度圖的TintColor沧奴,然后繪制在context上痘括,通過(guò)隨機(jī)調(diào)整間隔,就達(dá)到了深淺相間的效果滔吠「倬控制文字和圖片的繪制順序,就形成了上下效果疮绷。
-
故障
效果圖:
實(shí)現(xiàn):
這個(gè)效果的實(shí)現(xiàn)沒(méi)有引入什么新的操作翰舌,是一些基本操作的組合。
1.用3中顏色先把文字畫(huà)3遍
CGRect bottomRect = CGRectMake(rect.origin.x + self.bottomOffset.x, rect.origin.y + self.bottomOffset.y, rect.size.width, rect.size.height);
CGRect middleRect = CGRectMake(rect.origin.x + self.middleOffset.x, rect.origin.y + self.middleOffset.y, rect.size.width, rect.size.height);
self.textColor = self.bottomColor;
[super drawTextInRect:bottomRect];
self.textColor = self.middleColor;
[super drawTextInRect:middleRect];
self.textColor = self.topColor;
[super drawTextInRect:rect];
得到下面的效果
2.為圖片添加切片
//得到切片圖片
CGImageRef sliceRef = CGImageCreateWithImageInRect(contentImage, imageSlice);
//把原上下文切片部分內(nèi)容剔除
CGContextClearRect(context, contentSlice);
//把切片畫(huà)到原上下文被剔除部分冬骚,左右隨機(jī)平移一定距離
CGContextDrawImage(context, translatedRect, rotateRef);
3.添加隨機(jī)的線段
代碼還有很大優(yōu)化空間椅贱,不列舉了,說(shuō)下基本實(shí)現(xiàn)思路吧
- 構(gòu)建一個(gè)循環(huán)體
- 循環(huán)體內(nèi)隨機(jī)生成CGRect
- 過(guò)濾掉重疊的Rect
-
材質(zhì)
效果圖:
實(shí)現(xiàn):
這不是蒙圖實(shí)現(xiàn)唉韭!這不是蒙圖實(shí)現(xiàn)夜涕!這不是蒙圖實(shí)現(xiàn)!蒙圖很難實(shí)現(xiàn)這種有高低落差的光影效果属愤。
//使用CIHeightFieldFromMask生成高低落差圖
CIImage *inputImage = [CIImage imageWithCGImage:imageRef];
CIFilter *filter = [CIFilter filterWithName:@"CIHeightFieldFromMask"];
[filter setValue:inputImage forKey:kCIInputImageKey];
CIImage *outputImage = filter.outputImage;
CGImageRelease(imageRef); imageRef = NULL;
//使用CIShadedMaterial拼接材質(zhì)
CIImage *materia = [CIImage imageWithCGImage:self.materiaImage.CGImage];
CIFilter *filterMaterial = [CIFilter filterWithName:@"CIShadedMaterial"];
[filterMaterial setValue:outputImage forKey:kCIInputImageKey];
[filterMaterial setValue:materia forKey:kCIInputShadingImageKey];
CIImage *finalEffect = filterMaterial.outputImage;
UIImage *finalImage = [UIImage imageWithCIImage:finalEffect];
[finalImage drawInRect:rect];
通過(guò)使用不同的材質(zhì)球女器,來(lái)顯示不同的材質(zhì)效果,上圖使用的材質(zhì)圖
這個(gè)效果主要用到的是Core Image里面的filter住诸,目的是引入實(shí)現(xiàn)特殊字體的另一個(gè)思路驾胆,通過(guò)第二招將文字變成圖片,然后就可以組合使用Core Image Filter來(lái)實(shí)現(xiàn)更加復(fù)雜的效果贱呐。CoreImageFilterReference
附上代碼地址ArtFontDemo