iPhone開發(fā)應(yīng)用之UIView開發(fā)流程下篇
iPhone開發(fā)應(yīng)用中的UIView你了解多少?繼續(xù)我們上篇iPhone開發(fā)應(yīng)用之UIView開發(fā)流程上篇的內(nèi)容開始講述。曾經(jīng)有人這么說過,在iphone里你看到的,摸到的,都是UIView,所以UIView在iphone開發(fā)里具有非常重要的作用。
坐標系統(tǒng)轉(zhuǎn)換矩陣
坐標系統(tǒng)轉(zhuǎn)換矩陣給改變視圖(或者是它的視圖)提供了一個輕松和簡易的方法。一個仿射轉(zhuǎn)換是一個數(shù)學矩陣,它指定了在坐標系統(tǒng)中的點是怎么被映射到另一個坐標系統(tǒng)中的點。你可以對整個視圖應(yīng)用仿射轉(zhuǎn)換,以基于其父視圖來改變視圖的尺寸,位置或者朝向。你也可以在你的繪制代碼中應(yīng)用仿射轉(zhuǎn)換,以對已解釋內(nèi)容的獨立部分實現(xiàn)相同的操控。如何應(yīng)用仿射轉(zhuǎn)換是基于這樣的上下文的:
為了修改整個視圖,可以修改視圖transform屬性的仿射轉(zhuǎn)換值。
為了在視圖中的drawRect:方法中修改內(nèi)容的指定部分,可以修改與當前圖形上下文相關(guān)的仿射轉(zhuǎn)換。
當你想實現(xiàn)動畫時,通??梢孕薷囊晥D的transform屬性值。例如,你可以使用這個屬性來制作一個視圖圍繞中心點翻轉(zhuǎn)的動畫。你不應(yīng)該在其父視圖的坐標空間中用這個屬性來永久的改變你的視圖,像修改它的位置和尺寸。對于這種類型的改變,你可以修改視圖的frame矩形。
注意:當修改視圖的transform屬性值時,所有的轉(zhuǎn)換都是基于視圖的中心點來實現(xiàn)的。
在視圖的drawRect:方法中,你可以使用仿射轉(zhuǎn)換來定位或者翻轉(zhuǎn)你想要繪制的項目。相對于在視圖某些部位中修正對象的位置,我們更傾向于相對于一個固定點去創(chuàng)建對象,通常是(0, 0),同時在繪制之前使用轉(zhuǎn)換來定位對象。這樣的話,如果在視圖中對象的位置改變了,你要做的只是修改轉(zhuǎn)換矩陣,這樣比為對象重新創(chuàng)建新的位置性能更好開銷更低。你可以通過使用CGContextGetCTM方法來獲取關(guān)于圖形上下文的仿射轉(zhuǎn)換,同時可以用Core Graphics的相關(guān)方法在繪制中來設(shè)置或者修改這個轉(zhuǎn)換矩陣。
當前轉(zhuǎn)換矩陣(CTM)是一個在任何時候都被使用的仿射矩陣。當操控整個視圖的幾何結(jié)構(gòu)時,CTM就是視圖transform屬性的值。在drawRect:方法中,CTM是關(guān)于圖形上下文的仿射矩陣。
每個子視圖的坐標系統(tǒng)都是構(gòu)建在其祖先的坐標系統(tǒng)之上的。所以當你修改一個視圖的transform屬性,這個改變會影響到視圖及其所有的子視圖。然而,這些改變只會影響到屏幕上視圖的最終解釋。因為每個視圖都負責繪制自己的內(nèi)容和對自己的子視圖進行布局,所以在繪制和布局的過程中它可以忽略父視圖的轉(zhuǎn)換。
Figure1- 6描述了在解釋的時候,兩個不同的轉(zhuǎn)換因子是如何在視覺上組合起來的。在視圖的drawRect:方法中,對一個形狀應(yīng)用一個45度的轉(zhuǎn)換因子會使該形狀翻轉(zhuǎn)指定的角度。另外加上一個45度的轉(zhuǎn)換因子會導致整個形狀翻轉(zhuǎn)90度。這個形狀對于繪制它的視圖來講仍然只是翻轉(zhuǎn)了45度,但是視圖自己的轉(zhuǎn)換讓它看起來像使翻轉(zhuǎn)了90度。
Figure 1-6 翻轉(zhuǎn)一個視圖和它的內(nèi)容
重要:如果一個視圖的transform屬性不是其定義時轉(zhuǎn)換矩陣,那么視圖的frame屬性是未定義的而且必須被忽略。當對視圖應(yīng)用轉(zhuǎn)換時,你必須使用視圖的bounds和center屬性來獲取視圖的位置和尺寸。子視圖的frame矩形仍然是有效的,因為它們與視圖的bounds相關(guān)。
獲取更多關(guān)于在運行時修改視圖的transform屬性,查看 “Translating, Scaling, and Rotating Views.”獲取更多如何在繪制過程中使用轉(zhuǎn)換來定位內(nèi)容,查看 Drawing and Printing Guide for iOS.
點與像素
在iOS中,所有的坐標值和距離都被指定為使用浮點數(shù),其單元值稱為點。點的數(shù)量隨著設(shè)備的不同而不同,而且彼此不相關(guān)。要明白關(guān)于點的最主要一點是它們提供了一個繪制用的固定框架。
Table 1-1 列出了不同iOS設(shè)備的分辨率(點度量)。前為寬后為長。只要你依照這些屏幕的尺寸來設(shè)計用戶界面,你的視圖就回被相應(yīng)的設(shè)備正確顯示。
Table 1-1
每一種使用基于點度量系統(tǒng)的設(shè)備都定義了一個用戶坐標空間。這是幾乎在你所有的代碼都會用到的標準坐標空間。例如,當你要操控視圖的幾何結(jié)構(gòu)或者調(diào)用Core Graphics方法來繪制內(nèi)容時會用到點和用戶坐標空間。即使有時用戶坐標空間里的坐標時直接映射到設(shè)備屏幕的像素,你還是永遠不應(yīng)該假設(shè)這是永遠不變的。相反,你應(yīng)該記?。阂粋€點并不一定對應(yīng)著屏幕上的一個像素
在設(shè)備層面,所有由你指定的視圖上的坐標在某些點上必須被轉(zhuǎn)化成像素。然而,從用戶坐標空間上的點到設(shè)備坐標空間上的像素通常由系統(tǒng)來處理。UIKit和Core Graphics都主要使用基于向量的繪制模型,所有的坐標值都被指定為使用點。這樣,如果你用Core Graphics畫了一條曲線,你會用一些值來指定這條曲線,而不管底層屏幕使用怎樣的解決方法。
當你需要處理圖像或者其他基于像素的技術(shù),像OpenGL ES時,iOS會幫你管理這些像素。對于存儲為應(yīng)用程序的束中的資源的靜態(tài)圖像文件,iOS定義了一些約定,可以指定不同像素密度的圖像,也可以在加載圖像時最大限度的適應(yīng)當前屏幕的解決方案。視圖也提供了關(guān)于當前放縮因子的信息,以便你可以適當?shù)恼{(diào)整任何基于像素的繪制代碼來適應(yīng)有更高級解決方案的屏幕。在不同屏幕的解決方案中處理基于像素內(nèi)容的技術(shù)可以在"Supporting High-Resolution Screens"和"Drawing and Printing Guide for iOS"找到描述。
視圖的運行時交互模型
當用戶和界面進行交互時,或者由代碼程序性的改變一些東西時,一系列復(fù)雜的事件就會發(fā)生在UIKit的內(nèi)部來處理這些交互。在這個系列中的某些點,UIKit喚出你的視圖類,同時給它們一個機會去響應(yīng)程序的行為。理解這些喚出點對于理解視圖在哪里融入系統(tǒng)很重要。Figure 1-7 展示了這些事件的基本序列,從用戶觸屏開始到圖形系統(tǒng)更新屏幕內(nèi)容來響應(yīng)結(jié)束。同樣的事件序列也會發(fā)生在任何程序性啟動的動作。
Figure 1-7 UIKit 與視圖對象進行交互
以下的步驟分解了圖1-7中的事件序列,既解釋了在每一步發(fā)生了什么,也解釋了應(yīng)用如何響應(yīng)
1、用戶觸屏
2、硬件報告觸摸事件給UIKit框架
3、UIKit框架將觸摸事件打包成UIEvent對象,同時分發(fā)給適合的視圖。(對于UIKit框架如何提交事件給視圖的詳細解釋,查看 Event Handing Guide for iOS)
4、視圖中的事件處理代碼可能進行以下的動作來響應(yīng):
改變視圖或者其子視圖的屬性(frame, bounds, alpha, 等等)
調(diào)用setNeedsLayout方法以標記該視圖(或者它的子視圖)為需要進行布局更新
調(diào)用setNeedsDisplay或者setNeedsDisplayInRect:方法以標記該視圖(或者它的子視圖)需要進行重畫
通知一個控制器關(guān)于一些數(shù)據(jù)的更新
當然,哪些事情要做,哪些方法要被調(diào)用是由視圖來決定的。
5、如果一個視圖的幾何結(jié)構(gòu)改變了,UIKit會根據(jù)以下幾條規(guī)則來更新它的子視圖:
a、如果自動重設(shè)尺寸的規(guī)則在發(fā)生作用,UIKit會根據(jù)這些規(guī)則來調(diào)整視圖。獲取更多關(guān)于自動重設(shè)尺寸規(guī)則如何工作,查看
- "Handling Layout Changes Automatically Using Autoresizing Rules."
b、如果視圖實現(xiàn)了layoutSubviews方法,UIKit會調(diào)用它。你可以在你的定制視圖中覆蓋這個方法同時用它來調(diào)整任何子視圖的位置和大小。例如,一個提供了巨大滾動區(qū)域的視圖會需要使用幾個子視圖作為“瓦塊”而不是創(chuàng)建一個不太可能放進內(nèi)存的巨大視圖。在這個方法的實現(xiàn)中,視圖會隱藏任何屏幕外的子視圖,或者重定位它們?nèi)缓笥脕砝L制新的可視內(nèi)容。作為這個流程的一部分,視圖的布局代碼也可以廢止任何需要被重畫的視圖。
6、如果任何視圖的任何部分被標記為需要重畫,UIKit會要求視圖重畫自身。
對于顯式的定義了drawRect:方法的定制視圖,UIKit會調(diào)用這個方法。這方法的實現(xiàn)應(yīng)該盡快重畫視圖的指定區(qū)域,并且不應(yīng)該再做其他事。不要在這個點上做額外的布局,也不要改變應(yīng)用的數(shù)據(jù)模型。提供這個方法僅僅是為了更新視圖的可視內(nèi)容。
標準的系統(tǒng)視圖通常不會實現(xiàn)drawRect:方法,但是也會在這個時候管理它們的繪制。
7、任何已經(jīng)更新的視圖會與應(yīng)用余下的可視內(nèi)容組合在一起,同時被發(fā)送到圖形硬件去顯示。
8、圖形硬件將已解釋內(nèi)容轉(zhuǎn)化到屏幕上。
注意:上面的更新模型主要應(yīng)用于使用標準系統(tǒng)視圖和繪制技術(shù)的應(yīng)用。使用OpenGL ES來繪制的應(yīng)用通常會配置一個單一的全屏視圖和直接繪制相關(guān)的OpenGL圖像上下文。你的視圖還是應(yīng)該處理觸屏事件,但是它是全屏的,毋需給子視圖布局或者實現(xiàn)drawRect:方法。獲取更多關(guān)于使用OpenGL ES的信息,查看 OpenGL ES Programming Guide for iOS.
給定之前的一系列步驟,將自己的定制視圖整合進去的方法包括:
事件處理方法:
- touchesBegan:withEvent:
- touchesMoved:withEvent:
- touchesEnded:withEvent:
- touchesCancelled:withEvent:
- layoutSubviews方法
- drawRect:方法
這些是視圖的最常用的覆蓋方法,但是你可能不需要覆蓋全部。如果你使用手勢識別來處理事件,你不需要覆蓋事件處理方法。相似的,如果你的視圖沒有包含子視圖或者它的尺寸不會改變,那就沒有理由去覆蓋layoutSubviews方法。最后,只有當視圖內(nèi)容會在運行時改變,同時你要用UIKit或者Core Graphics等本地技術(shù)來繪制時才需要用到drawRect。
要記住這些是主要的整合點,但是不僅僅只有這些。UIView類中有些方法是專門設(shè)計來給子類覆蓋的。你應(yīng)該到UIView Class Reference中查看這些方法的描述,以便在定制時清楚哪些方法適合給你覆蓋。
有效使用視圖的提示
當你需要繪制一些標準系統(tǒng)視圖不能提供的內(nèi)容時,定制視圖是很有用的。但是你要負責保證視圖的性能要足夠的高。UIKit會盡可能的優(yōu)化視圖相關(guān)的行為,也會幫助你提高性能。然而,考慮一些提示可以幫助到UIKit。
重要:在調(diào)整繪制代碼之前,你應(yīng)該一直收集與你視圖當前性能有關(guān)的數(shù)據(jù)。估量當前性能讓你可以確定是否真的有問題,同時如果真的有問題,它也提供一個基線,讓你在未來的優(yōu)化中可以比較。
視圖不會總是有一個相應(yīng)的視圖控制器
在應(yīng)用中,視圖和視圖控制器之間的一對一關(guān)系是很少見的。視圖控制器的工作是管理一個視圖層次,而視圖層次經(jīng)常是包含了多個視圖,它們都有自包含特性。對于iPhone應(yīng)用,每個視圖層次通常都填滿了整個屏幕,盡管對于iPad應(yīng)用來說不是。
當你設(shè)計用戶界面的時候,考慮到視圖控制器的所扮演的角色是很重要的。視圖控制器提供了很多重要的行為,像協(xié)調(diào)視圖的展示,協(xié)調(diào)視圖的剔除,釋放內(nèi)存以響應(yīng)低內(nèi)存警告,還有翻轉(zhuǎn)視圖以響應(yīng)界面的方向變更。逃避這些行為會導致應(yīng)用發(fā)生錯誤。
獲取更多關(guān)于視圖控制器的信息,查看 View Controller Programming Guide for iOS
最小化定制的繪畫
盡管定制的繪畫有時是需要的,但是你也應(yīng)該盡量避免它。真正需要定制繪畫的時候是已有的視圖類無法提供足夠的表現(xiàn)和能力時。任何時候你的內(nèi)容都應(yīng)該可以被組裝到其他視圖,最好結(jié)果時組合那些視圖對象到定制的視圖層次
利用內(nèi)容模式
內(nèi)容模式可以最小化重畫視圖要花費的時間。默認的,視圖使用UIViewContentModeScaleToFill 內(nèi)容模式,這個模式會放縮視圖的已有內(nèi)容來填充視圖的frame矩形。需要時你可以改變這個模式來調(diào)整你的內(nèi)容,但是應(yīng)該避免使用UIViewContentModeRedraw內(nèi)容模式。不管哪個內(nèi)容模式發(fā)生作用,你都可以調(diào)用setNeedsDisplay或者setNeedsDisplayInRect:方法來強制視圖重畫它的內(nèi)容。
可能的話將視圖聲明為不透明
UIKit使用opaque屬性來決定它是否可以優(yōu)化組合操作。將一個定制視圖的這個屬性設(shè)置為YES會告訴UIKit不需要解釋任何在該視圖后的內(nèi)容。這樣可以為你的繪制代碼提高性能并且是推薦的。當然,如果你將這個屬性設(shè)置為YES,你的視圖一定要用不透明的內(nèi)容完全填充它的bounds矩形。
滾動時調(diào)整視圖的繪制行為
滾動會導致數(shù)個視圖在短時間內(nèi)更新。如果視圖的繪制代碼沒有被適當?shù)恼{(diào)整,滾動的性能會非常的緩慢。相對于總是保證視圖內(nèi)容的平庸,我們更傾向于考慮滾動操作開始時改變視圖行為。例如,你可以暫時減少已解釋的內(nèi)容,或者在滾動的時候改變內(nèi)容模式。當滾動停止時,你可以將視圖返回到前一狀態(tài),同時需要時更新內(nèi)容。
不要嵌入子視圖來定制控制
盡管在技術(shù)上增加子視圖到標準系統(tǒng)控制對象-繼承自UIControl的類-是可行的,你還是永遠不應(yīng)該用這種方法來定制它們??刂茖ο笾С侄ㄖ?,它們有顯式并且良好歸檔的接口。例如,UIButton類包含了設(shè)置標題和背景圖片的方法。使用已定義好的定制點意味著你的代碼總是會正確的工作。不用這些方法,而嵌入一個定制的圖像視圖或者標簽到按鈕中去會導致應(yīng)用出現(xiàn)未預(yù)期的結(jié)果。
小結(jié):關(guān)于iPhone開發(fā)應(yīng)用之UIView開發(fā)流程 下篇的內(nèi)容介紹完了,希望本文對你有所幫助!如果你對iphone開發(fā)很有興趣的話,請看iphone開發(fā)頻道,更多文章可以讓你參考。