【翻譯】iOS 檢視的程式設計指導(一)

PJHubs_xx發表於2019-05-29

本翻譯不是意譯也不是直譯,因為意譯雜糅太多個人理解有時會曲解原文,直譯太硬,英文思路直譯成中文相當怪異。為保證原意,儘可能的直譯下進行意譯。

翻譯原文地址

在iOS中,我們會使用多個 window 和多個 View 在螢幕上展示你的應用程式內容。Window 本身不會包涵任何的可見內容,但提供了一個基礎容器給我們應用程式中的 view 使用。View 定義了一部分你想要填入 window 中的內容。舉個例子,你可以有用於顯示圖片、文字、形狀或者以上內容的組合 view,甚至你還可以使用 view 去組織和管理其它 view

概覽

每一個應用程式至少擁有一個 windowview 去展示它的內容。UIKit 框架和其它系統框架預先定義了一些 view 供你去展示你的內容。這些 view 涵蓋了簡單的 button 和文字 lable 甚至是複雜的 view,比如 table viewpicker viewscroll view。在這些預先定義好的 view 中如果沒有提供你想要的 view,你可以自定義 view 並自己管理繪製和事件處理。

view 管理你的應用程式可視內容

一個 viewUIView 類(或者它的一個子類)的例項並管理了一個應用程式 window 中的矩形區域。view 能夠進行內容的繪製,處理多點觸控事件和管理該 views 中子檢視的約束。在一個矩形 view 區域中繪圖會涉及到使用影像技術,比如 Core GraphicsOpenGL ES 或者 UIKit 去繪製形狀、圖片和文字。view 能夠直接響應手勢識別或觸控事件。在 view 的檢視層級中,父檢視負責其子檢視的位置和大小,且還能動態的完成。父檢視的這個動態修改的能力能夠讓你的 view 去適應條件的改變,比如說介面旋轉和動畫。

你可以認為 view 是構建你的使用者介面中的一個磚塊。你會經常的使用一系列的 view 去構建一個等級檢視去展示內容,而不是隻使用一個 view。每一個在等級檢視中的 view 都表現了使用者介面中的一部分,並且通常是用於優化一個具體的內容型別,例如,UIKit 有很多個用於優化影像、文字和其它型別內容的 view

相關章節:View and Window ArchitectureViews

配合 window 顯示你的 view

windowUIWindow 類的例項且能夠處理全部呈現在應用程式使用者介面上的內容。window 使用 view(及其檢視控制器)來管理檢視層級的互動和改變。在大部分的時候,你的應用程式 window 並不會發生改變。window 建立之後,它不會發生改變,只有通過它顯示view 才會發生改變。每一個應用程式都至少擁有一個 window 在裝置的主螢幕上去顯示這個應用程式的使用者介面。如果有外部顯示裝置連線到了這個裝置,應用程式能夠很好的在這個外部顯示裝置上建立第二個 window 去呈現內容。

相關章節:Windows

動畫提供了使用者可見介面改變的反饋

動畫提供了關於使用者可見檢視層級改變的反饋。系統定義了呈現模態檢視(或單一檢視)和位於不同組合檢視之間的標準過渡動畫。但是,view 的許多屬性可以進行動畫。例如,可以通過動畫改變 view 的透明度,在螢幕中的位置,大小,背景顏色或者其它的屬性。如果你直接使用底層的 Core Animation 層物件,你能夠作出很多不錯的動畫。

相關章節:Animations

Interface Builder 扮演的角色

Interface Builder 是一個圖形構造和配置你的應用程式 windowview 的工具。使用Interface Builder 能夠把 view 集中在 nib 檔案中進行處理,nib 是一個儲存了你可以自由操控版本的 view 或其它物件資源的檔案,當你在 runtime 中載入一個 nib 檔案時,你可以使用你的程式碼去操縱這些位於真實物件中的 view

當你不得不去進行建立應用程式介面的工作時,使用 Interface Builder 會變得非常簡單 。因為在 iOS 中已經整合進了對 Interface Buildernib 的支援,只需要一點時間就可以把你應用程式的設計工作合併到 nib 中。

關於如何使用 Interface Builder 的其它資訊,可檢視 Interface Builder User Guide。關於如何使用 view controller 管理包涵這些 viewnib 檔案,可檢視 View Controller Programming Guide for iOS 中的建立自定義檢視控制器部分。

檢視更多

因為檢視是非常複雜和靈活的物件,在一個文件中完全講解是很困難的。但是,下面的這些文件能夠很好的幫助你去學習關於如何管理檢視互動和使用者介面的內容。

  • 檢視控制器是管理應用中的檢視很重要的一部分。檢視控制器控制著在其單一檢視層級之中的所有檢視,並協助這些檢視在螢幕上的顯示。檢視 View Controller Programming Guide for iOS 來獲得更多關於檢視控制器的內容以及它執行規則的資訊。
  • 檢視是應用的手勢和觸控事件的關鍵接收者。檢視 [Event Handling Guide for iOS]() 來獲得更多關於如何使用手勢識別器和處理觸控事件。
  • 自定義檢視必須要使用可信賴的繪製技術去渲染其內容。檢視 Drawing and Printing Guide for iOS 來獲得更多關於使用這些技術去繪製你的檢視內容。
  • 在一些標準檢視動畫無法滿足的部分,你可以使用核心動畫庫 Core Animation。檢視 Core Animation Programming Guide 來獲得更多關於使用核心動畫庫 Core Animation 實現動畫的內容。

viewwindow 的結構

viewwindow 展示應用程式使用者介面和處理介面上的互動。UIKit 和其它系統框架提供了一些只需要做一點修改或完全不修改就可以使用的 view。你還可以自定義檢視去展示標準檢視不允許的位置內容。

檢視結構的基礎知識

我們想要通過視覺化完成的事情大多都是基於 view 物件的,也就是 UIView 類的例項。view 物件在螢幕上定義了一個矩形區域,該區域能夠繪製並和響應觸控事件。view 能夠作為其它 view 的父容器,管理它們的位置和大小。UIView 類本身管理了這些工作的大部分,但我們也可以根據需要自定義檢視之間的預設行為。

viewCore Animation 層共同處理內容的渲染和動畫。每一個 view 都擁有一個 layer 物件(通常是 CALayer 的例項),它負責 view 的渲染內容和與 view 相關的動畫。我們大多數情況下的操作應該通過 UIView 提供的介面進行,但在某些需要對渲染或動畫行為進行更多的控制時,我們可以替換為對 layer 進行操作。

為了便於我們理解 viewlayer 的關係,下面這個例子可以提供幫助。圖 1-1 展示了來自於 ViewTransitions 這個簡單應用的檢視結構和它與底層 Core Animation 層的關係。這個應用程式的檢視包括一個 window(本質上是個 UIView),它是一個繼承於 UIView 類且作為 view 容器的物件,一個影像 view,一個用於顯示按鈕的 toolbar 以及一個 bar button item(它不是 view,但其內部有一個可共使用的 view)。(實際上 ViewTransitions 這個簡單應用還包括了一個用於實現變換的影像 view,但為了保證簡單,在圖 1-1 中並沒有描述出來)。每個 view 都有與之匹配的 layer 物件,可以通過 viewlayer 屬性來訪問它。(因為 bar buttom item 不是 view,所以你不能直接訪問它的 layerlayer 層物件的背後是 Core Animation 渲染物件,並且最終用於管理硬體緩衝區中顯示在螢幕上的實際位元組。

圖 1-1 簡單應用中的檢視結構

使用 Core Animation layer 物件對效能提升有很大的幫助。要儘可能的少呼叫檢視物件的繪製部分程式碼,當這部分程式碼被呼叫時,其結果會被 Core Animation 進行快取,並在未來進行多次重用。在需要更新檢視時重用渲染出內容的會消耗大量的繪製週期。在動畫中重用這部分快取的內容是非常重要的,因為這些內容是可被操控的。使用這些快取的內容會比建立新內容成本小很多。

view 的檢視層級以及管理子檢視

除了提供自身顯示的內容,view 還可以作為其它 view 的容器。當一個 view 包含了另外一個 view,父子關係就在其之中建立了。在這個父子關係中,子檢視通常被稱為 subView 父檢視被稱為 superView。這種關係型別的建立對應用程式的視覺化內容和行為都造成了影響。

看上去子檢視中的內容會遮擋其父檢視的部分或全部內容,當子檢視是非透明時,則其所佔據的區域將會完全遮擋父檢視。如果子檢視是部分透明的,則父子檢視中的內容會先進行融合再顯示於螢幕上。每個 superView 都一個儲存 subView 的陣列,陣列中元素的位置會影響每個 subview 的可見性。 當同為一個 superView 的兩個 subView 互相重疊在了一起,最後新增進 superView 中的(或移動到 subView 陣列最後的) subView 將會顯示在在上面。

superViewsubView 的關係還影響到了一系列的檢視行為。改變一個父檢視的大小時會波及到其子檢視的大小和位置。當改變父檢視的大小時,我們可以適當的通過一些配置方法來重新設定每個子檢視大小。例如隱藏父檢視,修改父檢視的透明度或者對父檢視的座標系統做數學變換(3D)等這些事件同樣會影響子檢視。

對檢視層級的排布還意味著應用程式如何響應各種事件。當一個特殊的檢視發生了觸控,系統立即將這個觸控資訊傳送給這個檢視進行處理。但是如果該檢視沒有對這個事件進行單獨處理,則系統將會把該事件物件傳遞給父檢視。如果父檢視同樣沒有處理這個事件,系統將會把該事件繼續傳遞給父檢視,在整個響應鏈上以此類推。

更多關於如何建立檢視層級的內容,可檢視 Creating and Managing a View Hierarchy

檢視繪製週期

UIView 類使用了“按需變化”的繪製模型去展示內容。當檢視第一次呈現在螢幕上時,系統要求其進行內容繪製。系統對渲染內容進行快照捕獲並且使用該快照作為該檢視的視覺呈現。如果你從未對檢視內容進行修改,執行檢視內容繪製的程式碼將不會再執行。對檢視進行的大多數操作會重複使用快照圖片替代。如果你對內容進行了修改,需要通知系統檢視已經修改。檢視將會重複進行繪製檢視內容的過程,並捕獲新的影像內容為快照。

當你的檢視內容修改時,你不必立即重繪這些修改的內容。相反,你可以使用 setNeedsDisplaysetNeedsDisplayInRect 方法使檢視內容失效。這些方法將會告訴系統檢視的內容已經修改,並且需要在下個時機中進行重繪。系統將會等待到當前 runloop 結束後才進行初始化任何的繪圖操作。這個延遲給你一個機會去使多個檢視內容失效,從檢視層級中新增或者移除檢視,隱藏檢視,調整檢視大小以及調整檢視位置。你對檢視進行所有修改都會在同一時間進行調整。

注意:修改檢視的幾何形狀並不會自動觸發系統對檢視內容的重繪。檢視的 contentMode 屬性決定了如何解釋檢視的幾何形狀修改。大多數 contentMode 在延伸和重定位已經存在快照的邊界,而不是建立一個新的快照。獲取更多 contentMode 是怎麼影響你的檢視繪製週期的,可以檢視 Content Modes

當渲染檢視內容時,實際的繪製過程的變化會依賴於檢視及其配置。系統檢視一般通過實現私有繪圖方法來完成檢視內容的渲染。這些相同的系統檢視經常會暴露出介面供我們配置檢視的實際外觀。自定義 UIView 型別的子類,通常你要針對你的檢視去過載 drawRect: 方法去繪製你檢視的內容。這還有一些其它方法提供完成內容的繪製,例如直接設定底層的內容,但是過載 drawRect 方法是目前最常用的方法。

獲取更多關於如何繪製自定義檢視的內容,可見 Implementing Your Drawing Code

內容模式(content modes

每個檢視都有一個內容模式,其控制瞭如何根據檢視的幾何尺寸發生變化時回收檢視內容,以及是否回收檢視內容。當檢視第一次顯示時,按照流程繪製其內容並捕獲其內容於底層點陣圖中。接下來改變檢視幾何尺寸並不會導致點陣圖的重新建立。反而,contentMode 屬性目的在於點陣圖是否縮放以進行適配新的界限,還是僅簡單的貼在一個檢視的角或者邊上。

檢視的內容模式在你做以下操作時生效:

  • 改變檢視的 framebounds 矩形區域的寬或高。
  • 賦值帶有縮放功能的 transform 變換給檢視的 transform 屬性。

預設情況下大多數檢視的 contentMode 屬性是 UIViewContentModeScaleToFill,它能夠讓檢視內容縮放適配至新的 frame 大小。圖 1-2 展示了一些可用的內容模式結果。從圖中可以看到,不是所有的內容模式都會完全充滿檢視界限,並且有一些還會導致檢視內容的失真。

Figure 1-2  Content mode comparisons

內容模式對於檢視回收非常適合,但如果你特別想在縮放或者重設尺寸操作時自定義檢視重繪它,還可以使用 UIViewContentModeRedraw 該內容模式。給檢視模式設定為該值能夠當幾何尺寸改變時強制讓系統呼叫 drawRect: 方法。一般情況下,應該儘可能的避免在任何時候使用該值,除非你非常確定你不會使用標誌系統檢視。

檢視更多關於可用的內容模型內容,可看 UIView Class Reference

可伸縮的檢視

你可以指定檢視的一部分作為可伸縮區域,以至於當檢視改變其大小時只修改了可伸縮區域。通常會在按鈕或者其它檢視中使用可伸縮區域,其中檢視的部分割槽域定義了一個可重複模式。檢視中指定的可伸縮區域允許在單獨一個軸或者兩個軸上進行伸縮。需要注意的是,當對兩個軸進行伸縮,對檢視的邊來說還要定義一個重複模式避免任何變形。圖 1-3 清晰的展示了失真是怎麼在檢視中展示出來的。來自原圖中每一個畫素點的顏色重複的通過一致的排列充滿了大圖。

Figure 1-3  Stretching the background of a button

使用 contentStretch 屬性指定檢視的可伸縮區域。這個屬性接受一個矩形區域,該值是 0.0 到 1.0 範圍內的標準值。當伸縮檢視時,系統將會對檢視當前的 bounds 值和縮放值與該標準值進行乘積,以達到確定對檢視一個畫素或多個畫素的伸縮。使用標準值能夠降低當檢視界限改變時更新 contentStretch 屬性值的次數。

檢視的內容模式還扮演瞭如何使用檢視的伸縮區域決定者的角色。當內容模式導致內容區域的縮放時才會使用可伸縮的區域。這意味著可伸縮檢視只支援 UIViewContentModeScaleToFillUIViewContentModeScaleAspectFitUIViewContentModeScaleAspectFill 內容模式。如果你指定了內容模式對內容進行了貼邊或貼角(因此實際上導致內容並不能縮放),該檢視將會忽略可伸縮區域。

注意:指定背景檢視時,建議建立可伸縮的 UIImage 物件時使用 contentStretch 屬性。可伸縮檢視可以在 Core Animation Layer 中完全處理,其通常提供更好的效能。

內建動畫支援

每一個檢視後都有一個層物件的好處之一是你可以對與檢視相關的更改進行動畫化。動畫是向使用者傳遞資訊交流的有效方法,在設計應用程時應該一直思考這個問題。許多 UIView 的屬性都是可動畫化的,也就是說支援半自動的從一個值到另外一個值。提高這些其中一個可動畫化屬性的效能,我們只需要做:

  1. 告訴 UIKit 你想要一個高效能動畫。
  2. 改變屬性值。

你可以對 UIView 物件中執行動畫的屬性如下:

  • frame -- 使用這個屬效能夠對檢視位置和大小的改變進行動畫變化。
  • bounds -- 使用這個屬效能對檢視大小的改變進行動畫變化。
  • center -- 使用這個屬效能對檢視位置的改變進行動畫變化。
  • transform -- 使用這個屬效能夠對檢視選擇和縮放。
  • alpha -- 使用這個屬效能夠修改檢視進行透明度的。
  • backgroundColor -- 使用這個屬效能夠修改檢視的背景顏色。
  • contentStrech -- 使用這個屬效能夠修改檢視的中心延伸。

從一組檢視變換到另外一組檢視是動畫非常重要的一點。通常情況下會使用檢視控制器通過動畫去管理使用者介面各部分中主要修改的部分。舉個例子,展示從高階別到低階別檢視的過渡資訊,通常情況下會使用導航控制器管理每一個成功顯示的檢視資料。甚至你還可以建立兩個檢視集合之間的動畫過渡來替代檢視控制器。在使用標準控制器動畫達不到你想要的結果時,可以通過這個方法做到。

除了使用 UIKit 進行動畫的建立外,還可以使用 Core Animation layer 完成。下降到圖層級別能夠對動畫的時間和屬性進行更多的控制。

檢視更多關於如果提升基於檢視的動畫效能可看 Animations。檢視更多關於如何使用 Core Animation 建立動畫可看Core Animation Programming GuideCore Animation Cookbook.

歡迎關注 PJ 的 iOS 開發日常

優秀的人遵守規則,頂尖的人創造規則

相關文章