Core Graphics 是iOS上一個很酷的API。作爲開發者,你會使用它來個性化你的UI設計,用上一些很棒的效果 – 而且不需要一個設計師參與制作! 但是對於很多iOS開發者來說,Core Graphics 一開始會讓人恐懼,因爲它是一個很龐大的API,而且在開發過程中會遇到很多小困難。所以在本篇教程中,我們將解開Core Graphics的神祕面紗,並且用一系列的練習去一步步展示 – 現在我們從使用Core Graphics來美化一個table view開始吧!
我們將要製作的table view的效果和上面的截屏一樣。這麼特別的設計靈感來自Bills,一個由 PoweryBase開發的設計精美的app。這是個相當酷的app,你可以看下!在該教程系列的第一篇中,我們將使用Core Graphics去製作一個精美的table view cell。
我們將講解Core Graphics的入門知識,如何去填充繪製矩形,如何繪製顏色漸變效果,以及如何處理1像素寬的線問題。
在接下來的教程系列中,將繼續美化app – Table view的header,footer和收尾工作。現在讓我們開始接觸有趣的Core Graphics吧!
開始
開始前,讓我們先創建一個有table view模版的工程。
打開Xcode,選擇Navigation-based Application 模版,命名工程爲 “CoolTable”。編譯運行工程,確保一個空白的table view出現。
現在讓我們添加一些例子數據到table view中。打開RootViewController.h文件,根據以下內容做代碼修改:
|
我們在這裏添加了兩個數組,在接下來爲兩個table view section中的內容添加字符串。現在切換到RootViewController.m文件並根據以下內容做修改:
|
我們在這裏添加了兩個數組,接下來會爲兩個table view section中的內容添加字符串。現在切換到RootViewController.m文件並根據以下內容做修改:
很好 – 現在我們有一些例子數據了!編譯運行工程,你將看到以下畫面:
然而,當你上下滾動table view的整個section內容時,header會“浮”在上面:
這是一個標準的設定爲“plain”風格的table view行爲。然而,有了這個“plain”風格設定後,我們並不想讓header像這樣“浮”在上面 – 我們想讓它們像row(行)一樣是一個單元行。有“grouped”風格設定的table view就是我們想要的!
現在切換到RootViewController.xib文件,點擊xib中的Table View,設置Style參數爲“Grouped”:
很好!保存RootViewController.xib的設定,返回工程,現在我們會看到一個有很多內容項(只是看起來)的table view:
我們使用Core Graphics去美化它吧!但是在之前,我們還需要討論下我們想要的效果。
Table View 風格分析
爲了獲得我們想要的效果,我們將在table view的三個不同section中繪製:table header,cells和footer:
在本篇教程中,我們將開始繪製cells,現在在讓我們進一步看下想要的效果:
注意以下對上述效果圖的分析:
- cells具有從白色漸變到淺灰色的效果。
- 每一個cell的邊界周圍都有白色邊來突出它(除了最後的cell,只在兩邊有白色邊)。
- 每一個cell之間都有一條灰色的線來分隔它們(出了最後的cell)。
- 頁面在實際的cell邊界處會呈現出一點鋸齒狀,和來自header的“下襬”頁面對齊。
另外 – 它模擬的是當有光線以一定角度照射在iPhone頂部時的情形(一般房間裏面會有光線的)。要達到這種效果,頂部需要提高亮度(白色),底部需要有陰影(灰色)。你會在很多UI設計裏面看到這種效果,接下來的教程系列裏面也會看到!
所以要繪製cell,我們需要知道如何使用Core Graphics去繪製漸變效果和一些線條。應該會相當簡單,對吧?我們開始吧!
你好,Core Graphics!
無論什麼時候你想在iOS上做個性化繪製,你繪製的代碼需要放在UIView內部。有一個特殊的方法叫drawRect,你可以把所有的繪製代碼都放到裏面。
我們先創建一個“Hello,World”的紅色view,然後把它設置爲table view cell 的背景,確保正常運作。
現在先選擇 “Groups & Files”下面的”Classes”分組,前往菜單欄的”FileNew File…”,選擇 iOSCocoa Touch Class,Objective-C class,確保”Subclass of UIView”被選上,然後點擊下一步。
命名文件爲 “CustomCellBackground.m”,確保”Also create CustomCellBackground.h”被選中,然後點擊 “Finish”。
我們不需要對頭文件做修改,直接切換到CustomCellBackground.m文件,根據以下代碼做出修改:
|
好的,這裏有一些新內容,先來一點點解釋下。
在第一行,我們調用了叫做UIGraphicsGetCurrentContext()的方法來獲得Core Graphics Context,在接下來的方法中還會用到它。按照我的理解,context就是我們在上面繪製的“畫布”。
按照這種情況,我們的“畫布”就是view,但是你也可以獲得其他類型的context,比如一個屏幕以外的緩衝區,你可以在稍後把它轉變成圖像。
關於context有趣的一點是,他們是狀態性的。這表示當你調用了函數去改變一些屬性,比如改變填充顏色,填充顏色會一直維持那個顏色狀態,直到你改變了顏色爲止。
事實上,這就是我們在第三行代碼所作的 – 我們用CGContextSetFillColorWithColor函數去把填充顏色設置爲紅色,以供接下來填充形狀顏色的時候使用。
你也許會注意到當你調用這個方法時,我們不能提供UIColor給函數做參數 – 而是要使用CGColorRef。幸運的是,其實很容易把UIColor轉換成CGColor,只需訪問UIColor的CGColor屬性。
最後的一行代碼,我們調用了一個方法去用顏色填充提供的方框(使用之前在context中設定好的填充顏色)。對於方框,我們傳入了view的bounds值。
既然我們有一個紅色view了,讓我們把它設置爲table view cell的背景吧!根據以下代碼對RootViewController.m文件做修改:
|
我們在這裏做的是將每個cell的backgroundView和selectedBackgroundView在新的CustomCellBackground類中創建。我們還把cell的文本標籤text label的背景顏色設定爲clear,讓我們的背景可以顯露出來。
編譯運行程序,你將看到以下畫面:
太好了,我們可以用Core Graphics去繪製了!不管你信不信,我們已經學會了一些重要的技術 – 如何獲取一個context去繪製,如何改變填充顏色,如何用顏色去填充方框。你可以用這種方法去製作精美的UI了!
現在我們要進一步深入,學習其中一種最有用的技術去製作精美的UI:顏色漸變!
繪製漸變效果
我們將要在本工程中繪製很多漸變效果,所以讓我們添加顏色漸變代碼到一個輔助函數中。這樣我們就不需要在工程中重複編寫這部分的代碼了!確保你選中了”Groups & Files”下面的”Classes”分組,前往菜單項的”FileNew File…”,選擇 iOSCocoa Touch Class,Objective-C class,確保”Subclass of NSObject”選項被選中,然後點擊Next,命名文件爲“Common.m”,確保”Also create Common.h”選項被選中,然後點擊”Finish”。
現在使用以下代碼替換掉Common.h文件的內容:
|
我們在這裏不是定義一個類 – 我們只是定義一個公共方法。
現在切換到Common.m文件,使用以下代碼替換掉原來的內容:
|
這個函數裏面有很多技術點,現在分兩部分去解釋。我們先從剛纔寫的部分開始,它創建了接下來要繪製的漸變效果。
首先我們需要去做的是獲得將要繪製的漸變效果的color space。你可以用color space去做很多事情,但是99%的時間你只想要一個標準的基於設備的RGB color space,所以我們簡單的使用了CGColorSpaceCreateDeviceRGB函數去獲取需要的引用。
接下來,我們創建了一個數組去記錄漸變區域的每一種顏色。0數值可以表示漸變的開始,1表示漸變的結束。我們只有兩種顏色,然後我們想用第一種顏色作爲開始,第二種顏色作爲結束,所以傳入了0和1數組。
注意到你可以在顏色漸變中有三種甚至多種你想要的顏色,還可以設定哪種顏色會在漸變這裏開始。這在一些效果中將會很有用。之後,我們用傳入函數中的顏色去創建一個數組。在這裏爲了方便,我們使用了普通的NSArray。
我們接着用CGGradientCreateWithColors函數創建了漸變效果,傳入了顏色空間,顏色數組,還有之前定義的位置信息。注意到我們必須轉換NSArray爲一個CFArrayRef – 這相當簡單,我們可以用casting方式去做。
起作用的原因是因爲NSArray是CGArrayRef的“toll-free bridged” – 基本上是一種奇特的稱呼方式,Apple寫了所有魔法般的代碼去讓轉換像casting那樣簡單。
現在我們有一個漸變 的引用了,但是它還沒有繪製出任何東西 – 它只是一個指向我們稍後用來繪製它的信息的指針而已。現在我們開始動手吧!在drawLinearGradient方法的“More coming”註釋後面添加以下代碼:
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
|
首先我們要計算出我們要繪製的漸變效果的開始和結束點。我們從矩形的”頂部中間”到“底部中間”設置一條線。注意到這裏使用了來自CGGeometry.h的一些輔助函數(比如 CGRectGetMidX)去計算這些數據(可以讓我們的代碼更簡潔!)。
剩下的代碼幫助我們去在提供的矩形中繪製漸變 效果- 關鍵的函數是 CGContextDrawLinearGradient。關於這個函數比較詭異的一點是,它用漸變填充了整個繪製區域。沒有辦法讓漸變只填充在部分區域中。
好的…沒有了裁剪,就是這樣!裁剪是Core Graphics的一項出色的功能特性,讓你可以在任意形狀中限制繪製操作。你需要做的就是添加形狀到context上面,然後調用CGContextClip方法,而不是像之前那樣填充它。以後的繪製動作都會被限定在那個區域中!
這就是我們在這裏要做的。我們把矩形添加到context上面,裁剪它,然後調用CGContextDrawLinearGradient方法,傳入之前設定好的所有變量值。
CGContextSaveCGState/CGContextRestoreCGState是什麼呢?Core Graphics是一個狀態機,一旦你設定了一些操作,需要你修改它才能改變狀態。
好的,我們只是裁剪了一個區域,除非我們對裁剪區域做了修改,不然我們都不會在該區域範圍之外繪製了!
這就是 CGContextSaveCGState/CGContextRestoreCGState的用處。使用它,我們可以保存當前的context設置到棧中,然後當我們完成操作,回到之前的狀態時,讓它出棧即可。
最後需要做的 – 我們需要調用 CGGradientRelease方法去清空CGGradientCreateWithColor方法之前創建的內存空間(還有CGColorSpaceRelease方法,感謝@Jim!)。
就是這樣!讓我們在cell的背景中用上這個函數吧。打開CustomCellBackground.m文件,根據以下內容做修改:
|
編譯運行工程,你將看到以下畫面:
wow,簡單的顏色漸變做出來的效果真不錯!
繪製軌跡
到現在爲止,table view看起來挺不錯的了,但是我們還是要繼續做些修改,讓它稍微“突出”一點。我們會在邊界周圍繪製一個白色的矩形,還有cell之間的灰色分隔線。
我們已經知道如何去給矩形填充顏色了 – 同樣的,在方框周圍繪製線條同樣簡單!
根據以下代碼,對CustomCellBackground.m文件進行修改:
|
我們將要用紅色的線去繪製矩形,並把它放置在cell的中間,先讓它容易被看到。我們創建一種顏色,然後使用CGRectInset函數稍微縮小方框的尺寸。
CGRectInset方法要做的就是從方框的寬和高減少一定值,然後返回結果。
我們再把繪製顏色設置爲紅色,設置線的寬度爲一像素寬,然後調用CGContextStrokeRect方法去繪製矩形。
編譯運行工程,你將看到以下畫面:
看起來似乎還OK… 但是否會覺得有點模糊和奇怪?如果你放大它,你將看到一些古怪的現象:
我們用1像素點去繪製(與iPhone3GS的1像素點一樣),但是事實上它卻用幾個像素點去繪製… 怎麼會這樣子?
像素點線和像素邊界線
當你使用Core Graphics去繪製路徑時,它會剛好在軌跡邊的中間繪製。
我們的情況是,軌跡的邊是我們想要去填充的矩形。所以當我們沿着邊繪製1像素點的線時,有一半的線(1/2像素)會在矩形的內部,另一半線(1/2像素)會在矩形的外部。
當然,因爲沒有辦法去繪製1/2一個像素,Core Graphics使用了圖像保真的方法把兩個像素點吸到一起,但是有一個較淡的陰影讓它的外表看起來像只繪製了一個像素點。
但是我們不想要圖像保真,我們只想要一個像素點!這裏有幾種方式去修復它:
- 你可以使用裁剪去裁掉不想要的像素
- 你可以取消圖像保真並且修改矩形的邊界,確保是你想要繪製的線條。
- 你可以修改軌跡去繪製,這樣1/2像素的效果就可以考慮了
在本篇教程中,我們會用option #3,修改矩形,讓它具有筆畫的行爲。我們創建一個輔助函數去修改一個矩形爲1像素點筆畫。
打開Common.h文件,然後添加以下聲明到文件的底部:
|
添加以下代碼到Common.m文件中:
|
這裏我們修改了矩形,讓一半邊界進入到原來矩形的像素點中,讓筆畫符合預期效果。
在 CustomCellBackground.m文件中調用以下代碼:
|
很好。現在讓我們用正確的顏色和位置去完善它。使用以下代碼對 CustomCellBackground.m文件進行修改:
|
這裏我們減小了1像素點的頁面框高度,以便有空間可以放置分隔線,轉換它,使用白色去筆畫繪製。
編譯運行工程,現在應該有一條微小的白色邊界在cell的周圍。
接下來,我們要在cell之間添加淺灰色的分隔線!
繪製線條
因爲我們要在工程中繪製多條線,讓我們創建一個輔助函數吧。添加以下代碼到Common.h文件中:
|
把以下代碼添加到Common.m文件中:
|
好的,我們看下它的原理。在開始和結尾,我們保存和恢復了context,這樣我們就不會留下任何的更改操作。
然後設定線條的線帽。默認的設定是讓一條線有一個“圓”末端,表示線條剛好在最後的點處結束。
但是這個還不夠好,因爲我們要讓線以1/2點的長度從開始和結束位置縮進,來修正筆畫問題。所以我們讓線帽有一個“正方形”的末端,表示線條在末端伸長了1/2的線寬 – 就我們的1/2點情況而言 – 太完美了!
然後按照通常那樣設定顏色和線條寬度。
接着我們做線條的實際繪製。在Core Graphics中繪製線條,你首先要移動到點A(還沒有繪製任何東西),然後添加一條線到點B(在context中從點A添加一條線到點B)。你可以調用CGContextStrokePath方法去繪製線條。
就這樣!讓我們用它來繪製分隔線,添加以下代碼到 CustomCellBackground.m文件的drawRect方法中:
|
編譯運行工程,現在你在cell之間應該可以看到漂亮的分隔線了!
現在可以做什麼?
這個是上面的工程項目源代碼,你可以在這裏下載。
到現在你應該對Core Graphics又酷又強大的技術相當熟悉了 – 填充和繪製矩形, 繪製線條和漸變效果,還有裁剪軌跡!我們的table view看起來也挺酷的。
還有更多內容!我們還沒講解如何添加陰影效果,或者弧線,光澤效果,還有其他很酷的技術 – 在下一篇教程中,我們將會添加一個很酷的header到table view上!
同時,如果你有任何的問題,建議或者評論,請提出來!:]