《View Programming Guide for iOS》閱讀筆記

文檔地址: 《View Programming Guide for iOS》

View and Window Architecture


  • 視圖繪制周期

UIView 類使用了請求式繪制模型來展示內容。當一個視圖第一次出現在屏幕上時,系統要求它繪制自己的內容。系統截取視圖內容的一個快照,并且將這個快照用于視圖的可視化呈現。如果視圖內容永遠不改變,那么這個視圖的繪圖代碼可能永遠都不會再次調用。這個快照的圖片在大部分涉及到該視圖的操作中被重復使用。如果改變了視圖內容,則需要通知系統視圖發生了改變。之后視圖會重復繪制過程并且為新的繪制結果截取一個快照。

當視圖內容發生改變時,不需要直接重繪這些改變。相反,通過調用函數 setNeedsDisplay 或者 setNeedsDisplayInRect: 來使當前視圖無效。這些函數會告訴系統視圖的內容發生改變并且需要在下次時機到來時重繪。系統會一直等到當前的 run loop 結束后,才會開始任何繪制操作。這個延遲,給了你一個機會去廢止多個視圖,從當前視圖層級中添加或者刪除視圖,隱藏視圖,重設視圖大小,和重定位視圖。所有的這些改變稍后會再同一時間呈現。

備注:改變視圖的幾何結構并不會讓系統自動重繪視圖內容。視圖的 contentMode 屬性決定了視圖幾何結構的改變該如何解析。大部分的 content modes 只是在視圖的邊界中拉伸或者重定位已經存在的視圖快照而不需要重新創建一個快照。

當繪制視圖內容的時刻到來時,真正的繪制過程會根據視圖和它的配置而有所不同。系統視圖通常是實現自己的私有繪圖函數來重繪內容。這些一樣的系統視圖通常會暴露一些接口,以便能用來配置視圖實際的外觀。對于自定義的 UIView 的子類,典型的應該重寫視圖的 drawRect: 函數,使用它來繪制視圖內容。當然也存在一些其他的方法去提供視圖的內容,比如直接設置內容下的圖層。但是重寫 drawRect: 函數是使用最多的技術。

  • UIKit 框架的坐標原點位于左上角,x 軸向右延伸,y 軸向下延伸。而 Core Graphics 和 OpenGL ES 的坐標系統原點則在左下角,y 軸向上延伸,x 軸向右延伸。

  • UIView 屬性中的 frame和 center 是相對于父視圖的坐標系統的。而 bounds屬性相對于自身的坐標系統,故 bounds 默認的 point 位置是(0,0),大小與 frame 相同

  • 改變視圖的 transform 屬性時,所有變形都是相對于視圖中心點也就是 center 屬性的。

  • 在視圖的 drawRect: 方法中,可以使用仿射變換來定位和確定需要繪制的元素。相比于在視圖的某個地點固定一個對象的位置,相對于一個固定點(通常是(0,0))來創建每個對象是更為簡單的。在繪制之前使用 transform 就能做到這點。在這種情況下,如果視圖中的對象位置發生改變,只需要修改這個 transform 即可,這比在新的位置重新創建對象要快速并且花銷更小。可以使用 CGContextGetCTM 函數來檢索圖形上下文的仿射變換矩陣,在繪制過程中也可以使用 Core Graphics 的相關函數來設置 CTM。

    CTM(current transformation matrix) 是任何時候都被使用的仿射變換,當操作的是整個視圖時,CTM 就是視圖的 transform 屬性。在 drawRect: 方法中, CTM 與當前活動的圖形上下文有關

  • 當一個視圖的 transform 屬性不是 identity transform 時,這個視圖的 frame 屬性就是未定義并且必須被忽視的。此時,你必須使用視圖的 bounds 和 center 屬性來獲得視圖的大小和位置。該視圖的任何子視圖的 frame 矩形依然是有效的,因為它們是基于父視圖的 bounds 屬性的。

  • 一個點并不一定對應著屏幕上的一個像素

  • 對于顯式定義了 drawRect: 方法的視圖來說,UIKit 負責調用這個方法。這個方法中的實現應該盡可能快地重繪視圖的指定區域并且不應該做別的任何事情。不要在這里做額外的布局,也不要改變應用的數據模型。這個方法的唯一目的就是更新視圖的可視內容。

  • 自定義視圖需要重寫的事件處理函數有touchesBegan:withEvent:touchesMoved:withEvent:touchesEnded:withEvent:touchesCancelled:withEvent: 如果使用了手勢識別來處理事件,則不需要重寫這些函數。如果視圖不包含任何子視圖或者它的尺寸不發生改變,也不需要重寫 layoutSubviews 函數。最后,當視圖內容在運行時發生改變,同時使用了 UIKit 或者 Core Graphics 來繪制圖形,則需要重寫drawRect:函數。

Windows


  • 每個 iOS 應用程序至少包含一個窗口。窗口通常座位一個或者多個視圖的空白容器。同時,應用程序也不通過展示新的窗口來改變內容。如果想要這么做,改變窗口最前面的視圖來完成。

  • 當創建窗口時,應該總是將窗口的大小設置為充滿屏幕的邊界。不應該為了容納狀態欄或者其他元素而減去窗口大小。無論何時,狀態欄總是浮在窗口的上面的。所以應該是放入到窗口中的視圖來縮減大小去適應狀態欄。如果是使用視圖控制器,則視圖控制器應該自動處理視圖大小。

  • 窗口有等級概念,每個 UIWindow 對象都有一個可配置的 windowLevel 屬性。通常不需要改變應用程序的窗口等級。新的窗口在創建時,會自動指派為正常窗口等級。高窗口等級是出現在應用程序內容之上的必要信息,比如系統狀態欄和 alert 消息。雖然可以手動將窗口設置為這樣的等級,但當使用到特殊接口時,通常系統會做好這些事情。舉例來說,當顯示隱藏狀態欄,或者顯示一個 alert 視圖時,系統會自動創建必要的窗口去顯示這些內容。

  • 當應用程序進入到后臺時,窗口改變通知并不會被傳遞。因為當程序進入后臺時盡管窗口不在屏幕上顯示了,但在應用程序環境中,窗口依然被認為是可見的。

  • retina 屏的 iOS 設備可以外接顯示設備。

Views


  • 使用編程方法來創建視圖時,視圖創建代碼一般放在視圖控制器的 loadView 函數中。無論是使用編程或者 nib 文件來創建視圖,都可以在 viewDidLoad 函數中添加視圖的配置代碼。
  • 父視圖會自動 retain 子視圖,所以當添加了一個子視圖后,release 子視圖的操作是安全的。事實上,推薦這么做,因為它能防止應用程序保持太多的視圖而導致的內存泄露。記住,如果從父視圖中移除了子視圖后,還想繼續使用子視圖,必須對子視圖做 retain 操作。removeFromSuperview 函數會在子視圖從父視圖中移除后,自動釋放子視圖。如果沒有在下一個時間循環周期前做 retain 操作,這個視圖將會被釋放。
  • UIView的 window 屬性代表當前正在顯示的視圖所在的窗口。對于當前在屏幕上顯示的視圖來說,窗口對象就是它們所在視圖層次的根視圖。
  • 如果隱藏的視圖是 first responder,這個視圖不會自動的取消自己 first responder 的狀態。以 first responder 為目標的事件依然會被傳遞到這個隱藏的視圖。為了防止這種情況發生,應該在隱藏視圖時,強制使其取消 first responder 狀態。
  • 當包含了旋轉因子的視圖做矩形轉換時,看如下的圖:
uiview_convert_rotated.jpg
  • 如果一個視圖的 transform 屬性不是 identity transform,那么它的 frame 和 autoresizing 行為結果都是未定義的。

  • 視圖在初始化過程之前調用 UIView 類方法 layerClass,并且使用返回的類來創建 layer 對象。此外,視圖總是會指定自己本身作為 layer 對象的代理。在這點上,視圖擁有著圖層,并且視圖和圖層之前的關系必須不能改變。也就是說,不能指定相同的視圖作為另一個圖層對象的代理。改變這種所屬關系或者代理關系,都可能會導致視圖繪制的錯誤,并且應用程序存在潛在的 crash 問題。

  • 通過創建 UIView 的子類,重寫 layerClass 類函數可以改變創建圖層時的默認的 CALayer 類。

  • 自定義的 layer 不接收事件,也不參與到 responder chain 中,但是它們繪制自身,并且根據 Core Animation 的規則,在他們的父視圖或者父圖層中響應大小變化。

  • CGRectGetMidX(),CGRectGetMidY() 兩個函數可以分別得到一個 frame 的中心點的 x 坐標和 y 坐標。

  • CALayer 的屬性 position 就是中心點,和 UIView 的屬性 center 效果相同。

  • 自定義的視圖類,如果是通過代碼來創建,則需要重寫 initWithFrame: 初始化函數。而若是從 nib 文件中加載,則需要重寫 initWithCoder: 函數。注意:nib 加載的視圖并不會調用 initWithFrame: 函數。

  • 視圖默認的行為是一次只響應一個 touch。如果用戶按下了第二個手指,系統會忽視這個 touch 事件并且不會將它報告給視圖。如果希望在視圖的事件處理函數中跟蹤多手指手勢,需要設置視圖的 multipleTouchEnabled 屬性為 YES 來使多點觸摸事件生效。


轉自:WebFrogs 的博客

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容