提高繪畫性能
繪圖是比較耗性能的, 這對任何平臺都是這樣. 優(yōu)化代碼性能是開發(fā)當(dāng)中重要的一環(huán). 表A-1列舉了幾個優(yōu)化繪圖代碼的幾個建議, 你應(yīng)該使用性能工具來測試你的代碼, 并移除污點代碼和冗余代碼.
表A-1 幾個關(guān)于繪圖性能的提示
| Tip | Action |
|---|---|
| 較少繪制 | 在每次界面刷新周期內(nèi), 你應(yīng)該只更新需要重繪的部分. 如果你使用drawRect:方法來繪制view, 那么應(yīng)該制定更新特定rect. 如果使用OpenGL, 那么你必須自己去跟蹤更新操作. |
謹慎地調(diào)用setNeedsDisplay:方法 |
調(diào)用setNeedsDisplay方法是需要花費時間和資源去計算實際需要更新的區(qū)域. 不要傳一個包含整個view的rect參數(shù)進去. 也不要沒事就調(diào)用該方法, 而是按需調(diào)用. |
| 盡可能多地將view設(shè)置為不透明(opaque==YES) | 當(dāng)view時不透明時, 組合多個view的消耗比view透明時少的多. 所以當(dāng)不需要透明的view時, 盡量將view設(shè)置為不透明. |
| 當(dāng)滾動視圖時,復(fù)用其中的cell或內(nèi)容view | 當(dāng)滾動視圖時, 重復(fù)創(chuàng)建新的view會到時滾動不平滑, 會卡頓. |
| 通過修改transformation matrix來復(fù)用path | 通過修改當(dāng)前的變換矩陣, 你可以使用同一個path來繪制不同內(nèi)容. |
| 在滾動時, 避免清除之前的內(nèi)容 | 默認情況下, UIKit在調(diào)用drawRect:方法進行重繪時,會將之前的內(nèi)容清除掉. 如果某個view要響應(yīng)滾動事件, 那么該view會在滾動更新期間重復(fù)清除同一區(qū)域, 這是比較耗性能的. 所以你可以通過將屬性clearsContextBeforeDrawing設(shè)置為NO,來禁用該行為. |
| 在繪圖時減少改變繪圖狀態(tài)(graphic state)的次數(shù) | 修改graphic state需要底層繪圖子系統(tǒng)來完成. 如果你的好幾個內(nèi)容需要相同的graphic state的話, 盡量將它們放在一塊完成, 避免多出改變graphic state |
| 使用Instrument工具來調(diào)試你的代碼性能 | Core Animation instrument工具能夠幫助發(fā)現(xiàn)代碼中的性能問題, 尤其是: ①Flash Updated region可以讓你很容易的知道你是視圖的那部分內(nèi)容需要更新 ②色彩錯位圖像可以幫你看到排列不好的圖片, 這會導(dǎo)致模糊圖片和較差的性能. 更多內(nèi)容請看Instruments User Guide的Measuring Graphics Performance in Your iOS Device部分 |
讓視圖支持高分率屏幕
在iOS4.0后, 屏幕就分Retina(高分辨率)和普通. 為了適配高分辨率屏幕, 大部分工作有系統(tǒng)完成, 你需要的事情比較少, 比如適配基于光柵化的圖片等.
為支持高分辨率的工作清單
為了支持高分辨率屏幕, 你需要干如下事情:
- 提供高分辨率的版本的圖片
- 提供高分辨率版本的icon
- 對于基于矢量的形狀和內(nèi)容, 請繼續(xù)使用自定義Core Graphic和UIKit繪圖代碼。如果您想為繪制的內(nèi)容添加額外的東西, 請參關(guān)于iOS繪圖的一些概念中
點(point) VS 像素(pixel)的內(nèi)容,以了解如何做到這一點。 - 如果使用OpenGL ES進行繪圖,請決定是否要選擇高分辨率繪圖,并相應(yīng)地設(shè)置layer的比例因子(scale)
- 對于您創(chuàng)建的自定義圖像,修改圖像創(chuàng)建代碼以考慮當(dāng)前的比例因子
- 如果應(yīng)用程序使用核心動畫,根據(jù)需要調(diào)整代碼以補償scale差異
充分利用iOS平臺優(yōu)勢
iOS為繪圖提供了多種技術(shù)支持, 來幫助你打造更好的APP, 而不需要考慮屏幕的分辨率:
- 標(biāo)準(zhǔn)的UIKit視圖(文本框, 按鈕, TableView等)在各種分辨率下都能很好的顯示
- 基于矢量的內(nèi)容(UIBezierPath, CGPathRef, PDF)會自動利用多余像素來清晰的渲染線條.
- 文本也會自動利用高分辨率來清晰顯示.
- UIKit能夠自動為你的屏幕適配各種圖片(@2x, @3x)
如果你的APP使用原生繪圖技術(shù)來進行渲染, 那么你只需提供高分辨率版本的圖片即可適配高分辨率屏幕.
使用OpenGL ES或者GLKit技術(shù)在高分辨率屏幕上繪制
如果APP使用OpenGL ES或者GLKit技術(shù)來繪制內(nèi)容, 那么之前的繪圖代碼在不需要改動的情況, 應(yīng)該能夠繼續(xù)工作. 當(dāng)你在高分辨率屏幕上繪制內(nèi)容時, 你的內(nèi)容會進行收縮從而變得塊狀化. 之所以會這樣, 是因為底層支持OpenGL的是CAEAGLLayer類, 該類的默認行為與Core Animation類似. 也就是說, scale factor默認設(shè)置為1.0, 這會使得Core Animation合成器壓縮內(nèi)容, 從而使得在高分辨率屏幕上顯示的塊狀化. 為了避免塊狀化, 你需要手動增加OpenGL渲染緩沖區(qū)(renderbuffers)的大小以匹配屏幕(有多更多像素, 內(nèi)容可的繪制可以更加細節(jié)化)大小. 因為想渲染緩沖區(qū)添加更多像素會影響性能, 你必須顯示的選擇支持高分辨率屏幕.
如若要啟用高分辨率繪圖, 那么必須手動改變view的scale來顯示OpenGL或GLKit繪制的內(nèi)容. 將view的contentScaleFactor屬性從1.0到2.0, 將觸發(fā)對底層CAEAGLLayer對象改變scale. renderbufferStorage:fromDrawable:方法用于將layer對象綁定到renderbuffers中, 通過將scale來同比縮放layer的bounds后, 得出渲染緩沖區(qū)的size. 因此, 將比例因子加倍(scale), 那么渲染緩沖區(qū)的大小也需要加倍, 從而能夠容納更多的像素. 之后將使用這些額外的像素來繪制內(nèi)容.
代碼B-1顯示了將layer綁定到渲染緩沖區(qū)的正確姿勢(方法). 如果你創(chuàng)建的是OpenGL ES類型APP, 那么這個步驟xcode已經(jīng)幫你完成了. 那么你只需為view設(shè)置合適的scale就行. 如果不是, 那么就需要使用如下代碼來獲取渲染緩沖區(qū)的size. 對于給定類型的設(shè)備, 您不應(yīng)該假定渲染緩沖區(qū)大小是固定的.
代碼清單B-1 初始化渲染緩沖區(qū), 并檢索其實際尺寸
GLuint colorRenderbuffer;
glGenRenderbuffersOES(1, &colorRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);
[myContext renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:myEAGLLayer];
// Get the renderbuffer size.
GLint width;
GLint height;
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &width);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &height);
重要: 底層有
CAEAGLLayer支持的view不應(yīng)該實現(xiàn)自定義的drawRect:方法. 實現(xiàn)了drawRect:方法會使得系統(tǒng)更改scale以便適配屏幕的scale. 如果你的繪圖代碼不希望出現(xiàn)這種情況, 那么APP的內(nèi)容將會渲染的不正確
圖片加載
出于功能和美學(xué)的原因,圖像是App用戶界面的普遍元素。它們可能是應(yīng)用程序的一個關(guān)鍵區(qū)別因素。APP的許多地方需要用到image, 從APP icon,到launchImage, 還有按鈕, 背景等等.
此外,IOS支持使用UIKit和Core Graphic框架來加載和顯示圖像。確定使用哪個框架和函數(shù)取決于如何使用它們。不過,只要有可能,建議使用UIKit類來展示代碼中的圖像。表C-1列出了一些使用場景和處理它們的推薦選項。
表C-1 image的使用場景
| 場景 | 推薦使用方法 |
|---|---|
| image作為view的內(nèi)容呈現(xiàn) | 使用類UIImageView來展示image. 假設(shè)你view的唯一內(nèi)容時image, 那么你仍然可在imageView的上面添加其他view, 比如控制或其他內(nèi)容 |
| image作為view的裝飾,并成為view的一部分 | 使用UIImage來加載image并繪制到view中 |
| 將bitmap數(shù)據(jù)保存到image對象中 | 可以使用UIKit或Core Graphic函數(shù)來操作, 前面知識有覆蓋 |
| 將image保存為JPEG或PNG格式 | 使用image的原始數(shù)據(jù)創(chuàng)建一個UIImage對象. 使用UIImageJPEGRepresentation或UIImagePNGRepresentation函數(shù)來獲取NSData對象, 然后是NSData實例方法將數(shù)據(jù)保存到文件中. |
系統(tǒng)支持的圖片
UIKit框架以及iOS的底層系統(tǒng)框架為您提供了創(chuàng)建、訪問、繪制、寫入和操作圖像的廣泛可能性。
UIKit中的Image類和函數(shù)
UIKit框架有三個類和一個protocol用來進行image相關(guān)的操作:
UIImage
該類表示UIKit中的圖像. 你可以從不同的source創(chuàng)建UIImage對象, 保存file和Quartz圖像. 該類的類方法可以讓你使用不同的blend mode和opacity來將圖像繪制到當(dāng)前繪圖上下文中.
UIImage類會自動為你處理相應(yīng)的適配操作, 比如scale(考慮到高分辨率屏幕的顯示). 并且在使用Quartz圖像是, 你需要手動修改坐標(biāo)系.
UIImageView
該類的實例是顯示單個圖像或動畫展示一系列圖像的view。如果圖像是view中的唯一內(nèi)容,則使用UIImageView類而不是繪制圖像。
UIImagePickerController和UIImagePickerControllerDelegate
該類可以讓APP訪問用戶相冊中的圖片和電影, 或者拍攝一張照片. 該類負責(zé)present一個界面, 讓用戶可以從相冊中選擇照片. 當(dāng)用戶選擇了一張照片, 它會將照片以一個UIImage實例傳給delegate, 該delegate必須實現(xiàn)protocol中的方法.
除了這些類, UIKit還聲明了多個函數(shù)用來執(zhí)行不同的image相關(guān)的任務(wù):
-
獲取bitmap圖像
UIGraphicsBeginImageContext函數(shù)可以創(chuàng)建一個離屏的bitmap圖像上下文. 你可以在該上下文中繪制內(nèi)容, 在從上下文中獲取UIImage對象. -
獲取或者緩存image數(shù)據(jù) 每個UIIMage對象背后是一個
CGImageRef對象在支撐, 并可以直接訪問這個背后的對象. 然后,可以將core graphic對象傳遞給圖像I/O框架來保存數(shù)據(jù)。還可以通過調(diào)用UIImagePNGRepresentation或UIImageJPEGRepresentation函數(shù)將UIImage對象中的圖像數(shù)據(jù)轉(zhuǎn)換為PNG或JPEG格式。然后,可以訪問數(shù)據(jù)對象中的字節(jié),并且可以將圖像數(shù)據(jù)寫入文件。 -
將image寫入相冊中 調(diào)用
UIImageWriteToSavePhotosAlbum函數(shù), 并傳入UIImage對象, 可以將圖像寫入相冊.
其他和圖片相關(guān)的框架
除了UIKit之外,還可以使用多個系統(tǒng)框架來創(chuàng)建、訪問、修改和寫入圖像。如果您發(fā)現(xiàn)無法使用UIKit方法或函數(shù)完成某個與圖像相關(guān)的任務(wù),那么這些底層框架之一的函數(shù)可能能夠執(zhí)行您想要的操作。這些函數(shù)中的一些可能需要一個CoreGraphic圖像對象(CGImageRef)。您可以通過CGImage屬性訪問底層支持UIImage對象的CGImageRef對象。
注意:如果存在UIKit方法或函數(shù)來完成給定的與圖像相關(guān)的任務(wù),則應(yīng)該使用UIKit,而不是使用任何相應(yīng)的底層函數(shù)
Quartz中的Core Graphic框架是最重要的底層系統(tǒng)庫. 該框架的一些函數(shù)和UIKit中的方法和函數(shù)一一對應(yīng); 比如, 有些CoreGraphic函數(shù)可以讓你創(chuàng)建或繪制bitmap, 而其他可以讓你從不同的source創(chuàng)建image. CoreGraphic還提供了UIKit中沒有的功能, 比如已存在圖像中選取部分來創(chuàng)建圖片, 使用色彩空間, 獲取其他有趣的圖片特征信息, 比如每行多少字節(jié), 一個點又多少像素, 和渲染意圖等等.
圖像I/O框架與CoreGraphic密切相關(guān)。它允許一個應(yīng)用程序讀寫的大多數(shù)圖像文件格式,包括標(biāo)準(zhǔn)的網(wǎng)絡(luò)格式,高動態(tài)范圍圖像,和原始相機數(shù)據(jù)。它具有快速的圖像編碼和解碼、圖像元數(shù)據(jù)和圖像緩存特征。
資產(chǎn)庫(Assets Library)是一個允許APP訪問照片APP資產(chǎn)管理的框架。您可以通過representation(例如PNG或JPEG)或URL獲得資產(chǎn)(asset)。通過representation或URL,您可以獲得CoreGraphic圖像對象或原始圖像數(shù)據(jù)。該框架還允許您將圖片寫入保存的照片相冊
系統(tǒng)支持的圖片格式
表C-2列舉了iOS所支持的圖片格式, png格式iOS最喜歡的. 通常UIKit支持的格式, 其他圖像I/O框架也支持.
表C-2 iOS支持的圖片格式
| 格式 | 擴展名 |
|---|---|
| 便攜式網(wǎng)絡(luò)圖形(PNG) | .png |
| 標(biāo)記圖像文件格式(TIFF) | .tiff或.tif |
| 聯(lián)合攝影專家組(JPEG) | .jpeg 或 .jpg |
| 圖形交互格式(GIF) | .gif |
| 窗口圖片格式 | .ico |
| 窗口光標(biāo) | .cur |
| XWindow位圖 | .xbm |
維持圖片的質(zhì)量
為您的用戶界面提供高質(zhì)量的圖像應(yīng)該是您的設(shè)計中的優(yōu)先考慮的事情。圖像提供了一種合理有效的方式來顯示復(fù)雜的圖形,并且應(yīng)該在適當(dāng)?shù)牡胤绞褂?。在為?yīng)用程序創(chuàng)建圖像時,請記住以下指南:
- 使用png格式的圖片 png格式提供了無損的圖像內(nèi)容, 這意味著將圖像數(shù)據(jù)格式保存為png, 然后將其讀回會得到前后一致的圖像. png還是優(yōu)化的存儲格式, 用于更快的讀取圖像數(shù)據(jù), 它是iOS的首選圖像格式.
- 創(chuàng)建Image以使不需要重新調(diào)整大小 如果計劃使用特定大小的圖片, 請確保創(chuàng)建相應(yīng)大小的的圖片資源. 不要創(chuàng)建更大的圖像, 然后在縮小. 因為縮放需要消耗CPU資源. 如果顯示的圖像需要改變大小, 那么請求創(chuàng)建多個版本(大小不一)的圖片, 并從相對接近目標(biāo)大小的地方縮放大小.
- 從不透明的png文件中刪除alpha通道 如果png圖像的每個像素都是不透明的, 那么刪除alpha通道避免圖片混合后的合成步驟. 這樣就簡化了顯示, 提高了性能.