[TOC=5]
* * * * *
>原文鏈接 :[Building an Adaptive Interface](https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/BuildinganAdaptiveInterface.html#//apple_ref/doc/uid/TP40007457-CH32-SW1)
自適應界面應該對特征和 Size Class 的變化做出反應。在視圖控制器級別,可以使用特征對顯示的內容和該內容的布局進行粗略確定。例如,在 Size Class 之間切換時,可以選擇更改視圖屬性,顯示或隱藏視圖,或者顯示完全不同的視圖集。做出這些重大決定之后,可以使用 Size Class 更改來微調內容。
### 第一節 適應特征變化
特征使您可以針對不同環境對應用程序進行不同的配置,并使用它們對界面進行粗略調整。 對特征所作的大部分改變可以直接在故事板文件中完成,但有些需要額外的代碼。
#### 1.1 配置故事板處理不同 Size Class
Interface Builder 可以輕松地將界面調整到適應不同的 Size Class 。故事板編輯器支持在不同 Size Class 配置中顯示界面,在特定配置中刪除視圖,以及指定不同的布局約束。還可以創建為不同 Size Class 提供不同圖像的圖像資源。使用這些工具意味著不必在運行時以編程方式進行相同的更改。相反,UIKit 會在當前 Size Class 更改時自動更新界面。
圖 13-1 顯示了用于在 Interface Builder 中配置界面的工具。Size Class 查看控件改變了界面的外觀。使用該控件來查看界面將如何查找給定的 Size Class。對于單個視圖,請使用安裝控件來配置對于給定的 Size Class 配置是否存在視圖。使用復選框左側的加號(+)按鈕添加新的配置。
###### 圖 13-1 為不同 Size Class 定制界面

> 注意:
卸載視圖保留在您的視圖層次結構中,并且可以正常操作,但不會顯示在屏幕上。
圖片資源是存儲應用的圖片的首選方式。每個圖像資源包含同一圖像的多個版本,每個版本都為特定的配置而設計。除了為標準和視網膜顯示指定不同的圖像之外,還可以為不同的水平和垂直 Size Class 指定不同的圖像。使用圖像資源進行配置時,[UIImageView](https://developer.apple.com/documentation/uikit/uiimageview) 對象將自動選擇與當前 Size Class 和分辨率關聯的圖像。
圖 13-2 顯示了圖像資源屬性。在目錄中更改寬度和高度屬性添加更多圖像插槽。用圖像填充這些插槽以用于每個 Size Class 組合。
###### 圖13-2 為不同 Size Class 配置圖像資產

#### 1.2 更改子視圖控制器的特征
子視圖控制器默認繼承父視圖控制器的特征。對于尺寸類別這樣的特征來說,每個子節點與父節點具有相同的特征是沒有意義的。例如,常規環境中的視圖控制器可能希望為其一個或多個子節點分配緊湊 Size Class 以反映該子節點的空間量減少。在實現容器視圖控制器時,通過調用容器視圖控制器的 [setOverrideTraitCollection:forChildViewController:](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621406-setoverridetraitcollection) 方法來修改子節點特征。
清單13-1顯示了如何創建一組新的特征,并將它們與一個子視圖控制器相關聯。從父視圖控制器執行這個代碼,只需要一次。被覆蓋的特征與子節點保持一致,直到你再次改變它們,或者直到從視圖控制器層次結構中移除子節點。
###### 清單 13-1 更改子視圖控制器的特性
~~~
UITraitCollection* horizTrait = [UITraitCollection
traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
UITraitCollection* vertTrait = [UITraitCollection
traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassCompact];
UITraitCollection* childTraits = [UITraitCollection
traitCollectionWithTraitsFromCollections:@[horizTrait, vertTrait]];
[self setOverrideTraitCollection:childTraits forChildViewController:self.childViewControllers[0]];
~~~
當父視圖控制器的特征發生變化時,子節點繼承任何未被父代明確覆蓋的特征。 例如,當父級的水平 Size Class 從常規變為緊湊時,上例中的子節點保留其常規水平 Size Class 。 但是,如果 [displayScale](https://developer.apple.com/documentation/uikit/uitraitcollection/1623519-displayscale) 特征發生更改,則子節點繼承新值。
#### 1.3 將呈現的視圖控制器調整為新的樣式
被呈現視圖控制器自動適應水平常規和緊湊的環境。從水平常規環境轉換到水平緊湊環境時,UIKit 默認將內置呈現樣式更改為 [UIModalPresentationFullScreen](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/1621361-fullscreen) 。對于自定義呈現樣式,呈現控制器可以決定適應行為并相應地調整呈現效果。
對于一些應用來說,適應全屏模式可能會帶來問題。例如,彈窗通常通過在其邊界以外的地方點擊來關閉,但是在彈出窗口覆蓋整個屏幕的緊湊環境中這樣做是不可能的,如圖 13-3 所示。當默認的適應風格不合適時,可以告訴UIKit使用不同的樣式,或者呈現完全不同的但是更適合于全屏樣式視圖控制器。
###### 圖 13-3 常規和緊湊環境中的彈出窗口

要更改呈現樣式的默認自適應行為,請將代理賦值給關聯的呈現控制器。可以使用被呈現視圖控制器的 [presentationController](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621426-presentationcontroller) 屬性訪問呈現控制器。在進行任何與適應性相關的更改之前,呈現控制器會訪問代理對象。代理可以返回與默認不同的演呈現樣式,并且可以向呈現控制器提供可選的視圖控制器進行顯示。
使用代理的 adaptivePresentationStyleForPresentationController: 方法指定與默認不同的演呈現樣式。轉換到緊湊型環境時,唯一受支持的樣式是兩個全屏樣式或UIModalPresentationNone。返回 UIModalPresentationNone 會通知呈現控制器忽略緊湊環境,并繼續使用先前的呈現樣式。在彈出窗口的情況下,忽略更改會為所有設備提供與 iPad 類似的彈出窗口行為。 圖 13-4 顯示了默認的全屏適應和非全屏,并且并排顯示,因此可以比較呈現結果。
###### 圖片 13-4 更改被呈現視圖控制器的自適應行為

要完全替換視圖控制器,請實現委托的 [presentationController:viewControllerForAdaptivePresentationStyle:](https://developer.apple.com/documentation/uikit/uiadaptivepresentationcontrollerdelegate/1618326-presentationcontroller) 方法。在適應緊湊的環境時,可以使用該方法將導航控制器插入到視圖層次結構中,或者加載專為較小空間設計的視圖控制器。
#### 1.4 實現自適應彈窗的技巧
從水平常規變為水平緊湊時,窗口需要額外的修改。 水平緊湊的默認行為將它變成全呈現。因為彈出式窗口通常是通過在彈出式界面之外點擊來關閉的,所以轉換到全屏呈現則消除了關閉彈出窗口的操作。 可以通過執行以下操作之一來彌補該行為:
* **將彈窗的視圖控制器推到現有的導航堆棧上**。 當有導航控制器可用時,關閉彈出窗口并將其視圖控制器推入導航堆棧。
* **添加控件以在全屏顯示時關閉彈出窗口**。可以將控件添加到彈出窗口的視圖控制器,但更好的選擇是使用 [presentationController:viewControllerForAdaptivePresentationStyle:](https://developer.apple.com/documentation/uikit/uiadaptivepresentationcontrollerdelegate/1618326-presentationcontroller) 方法將導航控制器的彈出窗口替換掉。使用導航控制器提供一個模板界面和空間來添加一個完成按鈕或其他控件來關閉內容。
* **使用呈現控制器代理來消除任何適應性更改**。獲取彈窗視圖控制器并為其賦值一個實現 [adaptivePresentationStyleForPresentationController:](https://developer.apple.com/documentation/uikit/uiadaptivepresentationcontrollerdelegate/1618343-adaptivepresentationstyleforpres)方法的代理。從該方法返回 [UIModalPresentationNone](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/1621386-none) 會導致彈出窗口繼續顯示為彈出窗口。有關更多信息,請參閱 [Adapting Presented View Controllers to a New Style](https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/BuildinganAdaptiveInterface.html#//apple_ref/doc/uid/TP40007457-CH32-SW6)。
### 第二節 響應 Size 變化
由于許多原因,可能會發生 Size 變化,包括以下內容:
* 底層窗口的 Size 變化,通常是因為方向變化。
* 父視圖控制器調整它的一個子節點。
* 一個呈現控制器改變被呈現視圖控制器的 Size 。
當 Size 發生變化時,UIKit 通過正常的布局過程自動更新可見視圖控制器層次結構的大小和位置。如果您使用“自動布局”約束來指定視圖的大小和位置,則應用程序會自動適應任何大小的更改,并且應該在不同屏幕大小的設備上運行。
如果自動布局約束不足以實現所需外觀,則可以使用 [viewWillTransitionToSize:withTransitionCoordinator:](https://developer.apple.com/documentation/uikit/uicontentcontainer/1621466-viewwilltransitiontosize) 方法更改布局。還可以使用該方法創建額外的動畫,以便與 Size 更改動畫一起運行。例如,在界面旋轉過程中,可以使用轉換協調器的 [targetTransform](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioncoordinatorcontext/1619289-targettransform) 屬性為界面的某些部分創建反向旋轉矩陣。