瘋狂iOS講義瘋狂連載之在內存中繪圖


前面介紹的都是通過擴展UIView、重寫drawRect:方法進行繪圖,這種繪圖方式是直接在UIView控件上繪制所有的圖形――由於每次該控件顯示出來時,drawRect:方法都會被調用,這意味着每次該控件顯示出來時,程序都需要重繪所有的圖形,很明顯,這種方式的性能並不好。除此之外,總有些時候需要在內存中繪制圖片,這樣既可導出到手機本地,也可上傳到網絡上。


與直接在UIView控件上繪圖不同,在內存中繪圖時,需要開發者自己准備繪圖環境,Quartz 2D提供了一個非常便捷的函數:UIGraphicsBeginImageContext(CGSize size),該函數用於准備繪圖環境。當圖形繪制完成后,可調用UIGraphicsEndImageContext()函數結束繪圖和關閉繪圖環境。


總結來說,在內存中繪圖的步驟如下。




調用UIGraphicsBeginImageContext(CGSize size)函數准備繪圖環境。


調用UIGraphicsGetCurrentContext()函數獲取繪圖CGContextRef


用前面介紹的繪制集合圖形、使用路徑等方式進行繪圖。


調用UIGraphicsGetImageFromCurrentImageContext()函數獲取當前繪制的圖形,該方法返回一個UIImage對象。


調用UIGraphicsEndImageContext()函數結束繪圖,並關閉繪圖環境。


除了使用前面介紹需要CGContextRef參數的方法繪圖之外,還可調用如下函數進行繪圖。


UIRectFill(CGRect rect):向當前繪圖環境所創建的內存中的圖片上填充一個矩形。


UIRectFillUsingBlendMode(CGRect rect , CGBlendMode blendMode):向當前繪圖環境所創建的內存中的圖片上填充一個矩形,繪制使用指定的混合模式。


UIRectFrame(CGRect rect):向當前繪圖環境所創建的內存中的圖片上繪制一個矩形邊框。


UIRectFrameUsingBlendMode(CGRect rect , CGBlendMode blendMode):向當前繪圖環境所創建的內存中的圖片上繪制一個矩形邊框,繪制使用指定的混合模式。


上面4個方法都是直接繪制在當前繪圖環境所創建的內存中的圖片上,因此,這些方法都不需要傳入CGContextRef作為參數。


下面的程序示范了在內存中繪圖,並將圖片輸出到手機本地。首先創建一個Single View Application,該Application包含一個應用程序委托代理類、一個視圖控制


器和配套的Storyboard界面設計文件。本程序無須修改界面設計文件,也無須自定義UIView控件,該程序只是向該UIView上添加一個UIImageView,並控制該UIImageView顯示內存中繪制的圖片即可。因此,該程序只要修改視圖控制器類,該視圖控制器類的實現代碼如下。


程序清單:codes/12/12.2/DrawImage/DrawImage/FKViewController.m

wKiom1L5iUfzd_3vAANfrjqqbto100.jpg

wKioL1L5iSKSc67yAALB0z9bMtg880.jpg

程序中的第一行粗體字代碼用於創建內存中圖片的繪制環境,接着調用UIGraphics- GetCurrentContext()方法獲取繪圖的CGContextRef,剩下的繪圖操作與前面介紹的各種繪圖代碼完全相同。


圖形繪制完成后,第二行粗體字代碼調用UIGraphicsEndImageContext()方法結束繪圖,繪圖結束后,即可調用UIGraphicsGetImageFromCurrentImageContext()函數獲取當前繪制的圖片,程序既可使用UIImageView顯示該圖片(如程序的viewDidLoad方法所示),也可將圖片輸出到手機本地。


最后一行粗體字代碼調用了UIImagePNGRepresentation()函數獲取UIImage圖像的數據。該方法返回NSData對象,接下來就可利用NSData的方法執行輸出了。





提示:
UIImagePNGRepresentation()函數的作用是獲取UIImage圖片轉換為PNG格式的圖片數據,還有一個與之類似的UIImageJPEGRepresentation()函數,其作用是獲取UIImage圖片轉換為JPEG格式的圖片數據,UIImageJPEGRepresentation()函數多一個參數,用於指定圖片的壓縮質量。




094318_FUru_262659.jpg




編譯、運行該程序,即可看到如圖12.13所示的效果。


該程序還將繪制的圖片輸出到該應用程序沙盒的Documents文件夾下。對模擬器而言,該Documents文件夾位於OS X系統的一個隱藏文件夾下。為了查看iOS應用程序沙盒的文件夾,請按如下步驟進行。



提示:
為了保證系統安全,iOS應用程序只能在系統為該應用所分配的文件區域下讀、寫文件,該文件區域被稱為該應用程序的沙盒。iOS應用的所有非代碼文件都要保存在此,例如,圖像、圖標、聲音、映像、屬性列表、文本文件等。iOS的每個應用程序都有自己獨立的沙盒,該應用程序不能訪問其他應用的沙盒。


094416_eJmK_262659.jpg





1)打開Mac OS X系統的Finder


2)單擊Finder應用中主菜單的“前往”→“前往文件夾”菜單項,或單擊command+Shift+G快捷鍵,系統彈出如圖12.14所示的對話框。


3)在圖12.14所示的對話框中輸入/users/登錄用戶名/library/application suport/iPhone Simulator,然后單擊“前往”按鈕進入該文件夾,即可看到5.05.16.06.17.0等文件夾,這就是該電腦上已經安裝過的iOS模擬器文件夾――不同的文件夾對應不同版本的iOS模擬器。



4)進入當前iOS模擬器版本對應的文件夾,比如當前使用iOS 7.0模擬器,則經過7.0文件夾進入該模擬器,接下來即可看到該模擬器目錄下包括ApplicationsMediaRoottmp、資源庫等文件夾,其中,Applications文件夾下保存了該模擬器上安裝的所有iOS應用。


5)進入Applications文件夾,即可在該文件夾下看到大量的子文件夾,每個子文件夾對應一個應用程序,找到本應用對應的子文件夾,並進入該子文件夾,即可看到包含DocumentsDrawImage.appLibrarytmp內容,在Documents文件夾下即可看到剛剛創建的newPng.png圖片,如圖12.15所示。



094604_qWqK_262659.jpg


12.15  查看模擬器沙盒下的文件


提示:
讀者可能會想,如果可以直接查看OS X系統的隱藏文件,是不是就可以直接通過Finder進入該模擬器沙盒目錄?實際上,OS X並沒有提供圖形化界面來設置顯示隱藏文件,不過可以通過命令行進行修改,在OS X系統命令行窗口輸入defaults write com.apple.finder AppleShowAllFiles -bool true,然后退出所有的Finder,重啟Finder程序,即可看到隱藏文件;如果希望恢復隱藏,在命令行窗口輸入defaults write com.apple.finder AppleShowAllFiles -bool false,然后退出所有的Finder,並重啟Finder程序即可


anchor.gif實例:繪圖板


該實例將會實現一個繪圖板,用戶可以根據喜好隨心所欲地在手機上“塗鴉”,塗鴉完成后,即可得到自己想要的圖片――這張圖片既可保存到手機本地,也可通過網絡分享。


為了實現這個應用,僅僅通過重寫UIViewdrawRect:方法並不適合――如果僅通過重寫UIViewdrawRect:方法來實現繪圖,用戶每次繪圖的時候就會丟失上一次繪圖的內容。這顯然不是本實例要實現的效果。


為了保證用戶每次繪圖的內容不會丟失,將會在內存中創建一張圖片,當用戶開始繪圖時,程序會通過重寫drawRect:方法進行實時繪制,當用戶想要繪制的圖形確定下來后,將該圖形繪制到內存中的圖片上。


舉例來說,當用戶想要在屏幕上繪制直線時,程序會把用戶開始觸碰屏幕的第一個點作為繪圖的起始點,當用戶手指不離開屏幕而是在屏幕上拖動時,程序會不斷地獲取拖動點的坐標,並調用該UIViewdrawRect:方法,該方法會從起始點繪制到當前拖動點――由於drawRect:每次重繪都只繪制起始點到當前拖動點的直線,因此,用戶可以實時看到拖動繪制的直線。當用戶松開手指時(表明用戶確定了最終繪制點),在內存中的圖片上繪制從起始點到手指松開點的直線即可。


需要指出的是,為了保證用戶能看到之前繪制的圖形,重寫UIViewdrawRect:方法時一定要先把內存中的圖片繪制到UIView上。





首先創建一個Single View Application,該Application包含一個應用程序委托代理類、一個視圖控制器和配套的Storyboard界面設計文件。在Interface Builder中打開界面設計文件,將該界面設計文件中最大的UIView改為使用自定義的FKDrawView類。在界面上方放置一個UISegmentedControl控件,該控件用於控制繪圖顏色;在界面下方放置一個工具條,並向工具條中添加一個UISegmentedControl控件,該控件用於控制繪圖形狀。本應用的界面設計如圖12.16所示。



095701_e883_262659.jpg
為了讓界面上的兩個UISegmentedControl控件能控制用戶繪制的顏色和形狀,需要在Interface Builder中為這兩個UISegmentedControl控件分別綁定changeColor:changeShape:兩個IBAction方法。


為了能記錄該應用當前需要繪制的圖形,本程序先創建一個頭文件,該文件中僅定義一個枚舉類型,代碼如下。


程序清單:codes/12/12.2/HandDraw/HandDraw/Constant.h

wKioL1L5iVbTKTDSAADAf3b2p2k525.jpg

本應用的視圖控制器類比較簡單,主要就是實現changeColor:changeShape:兩個IBAction方法。下面是該視圖控制器類的實現代碼。
程序清單:codes/12/12.2/HandDraw/HandDraw/FKViewController.m

wKioL1L5iXSQySS8AAMG25-wKZg393.jpg

從上面程序中的兩行粗體字代碼可以看出,該視圖控制器類的view並不是UIView,而是自定義的FKDrawView,這個FKDrawView就是實現本應用的關鍵,FKDrawView不僅要重寫drawRect:方法,該方法還完成兩件事情:將內存中的圖片繪制出來;用戶手指拖動進行“實時”繪制。


FKDrawView類的接口代碼比較簡單,只是定義currentColorshape兩個屬性即可,這兩個屬性用於接收控制器傳入的繪制顏色和繪制形狀。FKDrawView類的實現代碼如下。


程序清單:codes/12/12.2/HandDraw/HandDraw/FKDrawView.

wKioL1L5iaqwI-qmAAUF41-W1To278.jpgwKiom1L5id3C5Dh9AAPppKRjeK4758.jpgwKioL1L5icmQF21LAAWaJ2ZNAPY999.jpg

wKiom1L5ib2Qlc7AAABTn-nw9cY470.jpg

該程序的關鍵是上面的粗體字draw:方法,該方法會根據想要繪制的圖形類型,繪制不同的形狀。需要讀者留意的是,該方法在兩個地方被調用過――當用戶拖動手指時,激發touchesMoved: withEvent:方法,該方法中會通知該控件重繪自己,該控件會調用drawRect:方法進行重繪,drawRect:方法中調用了這個粗體字draw:方法執行實時繪制。除此之外,當用戶手指結束觸碰時,激發touchesEnded: withEvent:方法,該方法中調用了[self draw:buffCtx];代碼,這行代碼將會把起始點到結束觸碰點的形狀繪制在內存中的圖片上。


程序中還有一個稍微有點復雜的地方,就是所謂的“自由繪制”。即當用戶手指在屏幕上拖動時,程序需要繪制手指拖動的軌跡,這條軌跡表面上看是一條不規則的曲線,但實際上它由很多很短的線段組成――當用戶手指在屏幕上拖動時,程序會不斷地從上一個觸碰點繪制到當前觸碰點,由於這些線段都非常短,因此,用戶會以為我們繪制了一條光滑的曲線。程序為了完成這個繪制過程,使用prevPoint保存上一個觸碰點的坐標,並不斷地繪制從prevPointlastPoint的線段,每次繪制完成后,使用prevPoint保存當前觸碰點的坐標,這對下一次繪制而言,當前觸碰點就變成了上一個觸碰點。


編譯、運行該程序,用戶即可在屏幕上繪制任意的形狀,繪制效果如圖12.17所示。
095925_eDtJ_262659.jpg


――――――本文節選自《瘋狂ios講義(上)》
095954_7cDA_262659.jpg



注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com