自適應界面能夠充分利用屏幕的可用空間疯潭,適應性意味著能夠調(diào)整內(nèi)容以便其能夠適合任何iOS設備嘱根。iOS中的自適應模式支持以簡單但是動態(tài)的方式來重新排列和調(diào)整內(nèi)容嚣潜,以響應更改。當使用這種模式時世囊,一個應用程序只需要很少的額外代碼就可以適應不同的屏幕尺寸(如圖12-1所示)别瞭。
Auto Layout是構建自適應界面的重要工具。使用Auto Layout株憾,我們可以定義管理視圖控制器視圖布局的規(guī)則(稱為約束)蝙寨。可以在Interface Builder中以可視化方式創(chuàng)建約束嗤瞎,也可以在代碼中編程創(chuàng)建約束墙歪。當父視圖的尺寸發(fā)生變化時,iOS系統(tǒng)將根據(jù)我們指定的約束條件自動調(diào)整視圖的尺寸并重新定位其余視圖贝奇。
特征(trait)是自適應模式的另一個重要組成部分虹菲。特征描述了視圖控制器和視圖必須運行的環(huán)境,特征可以幫助我們做出關于界面的高層決策掉瞳。
特征的作用
當約束不足以管理布局時毕源,視圖控制器有幾個機會去進行更改。視圖控制器陕习、視圖和一些其他對象管理著一個特征集合霎褐,這些特征指定了與該對象關聯(lián)的當前環(huán)境。下表描述了這些特征以及如何使用它們來影響用戶界面该镣。
特征 | 示例 | 描述 |
---|---|---|
horizontalSizeClass | UIUserInterfaceSizeClassCompact | 此特征表達了界面的一般寬度冻璃。使用它來做出粗略的布局決策,例如視圖是垂直堆疊损合、并排顯示省艳、全部隱藏還是以其他方法顯示。 |
verticalSizeClass | UIUserInterfaceSizeClassRegular | 此特征表達了界面的一般高度嫁审。如果界面設計要求所有內(nèi)容都適配在屏幕上而不滾動跋炕,請使用此特征來作出布局決策。 |
displayScale | 2.0 | 此特征表達內(nèi)容是顯示在Retina顯示屏上還是標準分辨率顯示屏上土居。使用它(根據(jù)需要)來做出像素級布局決策或者選擇要顯示的圖像版本。 |
userInterfaceIdiom | UIUserInterfaceIdiomPhone | 此特征提供了向后兼容性,并表達了運行應用程序的設備類型擦耀。盡可能避免使用這個特征棉圈。對于布局決策,請改為使用horizontal 和 vertical size classes 代替眷蜓。 |
使用特征來做出關于如何呈現(xiàn)用戶界面的決策分瘾。在Interface Builder中構建界面時,使用特征來更改顯示的視圖和圖像吁系,或者使用特征來應用不同的約束集合德召。許多UIKit類(如UIImageAsset)使用指定的特征來裁剪它們提供的信息。
以下是一些提示汽纤,可以幫助我們了解何時使用不同類型的特征:
- 使用size class對界面進行粗略更改上岗。size class更改是一個添加或刪除視圖,添加或刪除子視圖控制器或更改布局約束的合適時機蕴坪。我們也可以不做任何事情肴掷,并讓界面使用其現(xiàn)有布局約束去自動適應。
- 切勿假設size class與視圖的特定寬度或高度相符合背传。視圖控制器的size class可能會因多種原因而發(fā)生更改呆瞻。例如,iPhone上的容器視圖控制器可能會使其某個子視圖控制器在水平方向上固定來強制其以不同方式顯示其內(nèi)容径玖。
- 使用Interface Builder為每個size class指定不同的布局約束痴脾。使用Interface Builder來指定約束比編程添加和移除約束要簡單得多。視圖控制器通過應用其storyboard的對應的約束來自動處理size class更改梳星。
- 避免使用idiom信息來做出關于界面布局或界面內(nèi)容的決策赞赖。在iPad和iPhone上運行的應用程序通常應顯示相同的信息,并應該使用size class來進行布局決策丰泊。
特征和尺寸的改變會在何時發(fā)生?
特征的改變很少發(fā)生薯定,但確實會發(fā)生。UIKit根據(jù)底層環(huán)境的變化來更新視圖控制器的特征瞳购,size class特征比display scale特征更可能發(fā)生改變话侄,idiom特征很少會改變。size class會由于以下原因而發(fā)生改變:
- 通常情況下学赛,由于設備的旋轉(zhuǎn)年堆,視圖控制器窗口的垂直或者水平size class發(fā)生改變。
- 容器視圖控制器的垂直或者水平size class已改變盏浇。
- 當前視圖控制器的垂直或者水平size class由其容器顯式更改变丧。
視圖控制器層次結構中的size class更改會傳遞到任何子視圖控制器。window對象作為該層次結構的最底層绢掰,為其根視圖控制器提供baseline size class特征痒蓬。當設備方向在縱向和橫向之間變化時童擎,window會更新其自己的size class信息并沿著視圖控制器層次結構傳遞該信息。容器視圖控制器可以將size class更改傳遞給未經(jīng)修改的子視圖控制器攻晒,也可以覆蓋每個子視圖控制器的特征顾复。
在iOS 8以上系統(tǒng)版本中,window的原點(origin)始終位于左上角鲁捏,當設備在橫向和縱向之間旋轉(zhuǎn)時芯砸,window的bounds會發(fā)生改變。window的尺寸的變化會與任何相應的特征變化一起沿著視圖控制器層次結構向下傳遞给梅。對于層次結構中的每個視圖控制器假丧,UIKit會調(diào)用以下方法來報告這些更改:
-
willTransitionToTraitCollection:withTransitionCoordinator:
方法告知每個相關的視圖控制器其特征即將改變。 -
viewWillTransitionToSize:withTransitionCoordinator:
方法告知每個相關的視圖控制器其尺寸即將改變动羽。 -
traitCollectionDidChange:
方法告知每個相關的視圖控制器包帚,其特征現(xiàn)在已經(jīng)改變了。
在沿著視圖控制器層次結構傳遞信息時曹质,UIKit僅在有變化需要告知時才將變化告知給視圖控制器婴噩。如果容器視圖控制器覆蓋了其子視圖控制器的size class,則當容器的size class改變時羽德,不會告知其子視圖控制器几莽。同樣,如果視圖控制器的視圖具有固定的高度和寬度宅静,它也不會收到尺寸改變通知章蚣。
圖12-2顯示了在iPhone 6上發(fā)生旋轉(zhuǎn)時視圖控制器的特征和視圖尺寸是如何更新的。從縱向到橫向的旋轉(zhuǎn)將屏幕的垂直size class從常規(guī)變?yōu)榫o湊姨夹。size class更改后纤垂,相應視圖的尺寸也更改,然后沿著視圖控制器層次結構傳遞這些更改磷账。將視圖動畫為新尺寸后峭沦,UIKit在調(diào)用視圖控制器的traitCollectionDidChange:
方法之前會應用size class和視圖尺寸的更改。
不同設備的默認size class
每個iOS設備都有一組默認的size class逃糟,我們可以在設計界面時作為指導吼鱼。下表列出了設備在縱向和橫向上的size class,未列在表格中的設備與具有相同屏幕尺寸的設備具有相同的size class绰咽。
設備 | 縱向 | 橫向 |
---|---|---|
iPad(all) iPad Mini |
Vertical size class: Regular Horizontal size class: Regular |
Vertical size class: Regular Horizontal size class: Regular |
iPhone 6 Plus | Vertical size class: Regular Horizontal size class: Compact |
Vertical size class: Compact Horizontal size class: Regular |
iPhone 6 | Vertical size class: Regular Horizontal size class: Compact |
Vertical size class: Compact Horizontal size class: Compact |
iPhone 5s iPhone 5c iPhone 5 |
Vertical size class: Regular Horizontal size class: Compact |
Vertical size class: Compact Horizontal size class: Compact |
iPhone 4s | Vertical size class: Regular Horizontal size class: Compact |
Vertical size class: Compact Horizontal size class: Compact |
重要:切勿假設應用程序以特定的size class在設備上顯示菇肃。在決定如何配置某個對象時,應始終檢查從該對象的特征集合中得到的size class取募。
構建一個自適應界面
自適應界面應該對特征和尺寸的改變做出響應琐谤。在視圖控制器級別,可以使用特征對顯示的內(nèi)容和該內(nèi)容的布局進行粗略的確定玩敏。例如斗忌,在不同的size class之間切換時质礼,可以選擇更改視圖屬性,顯示或者隱藏視圖织阳,或者顯示完全不同的視圖集几苍。做出這些重大決策之后,可以選擇使用尺寸更改來微調(diào)內(nèi)容陈哑。
適應特征變化
特征為我們提供了一種在不同環(huán)境下配置應用程序使其有所不同的方式,我們可以使用它對界面進行粗略調(diào)整伸眶。對特征所做的大部分更改都可以直接在storyboard中完成惊窖,但有一些還需要額外的代碼。
配置storyboard以處理不同的size class
Interface Builder使我們可以輕松地將界面調(diào)整到不同的size class厘贼。storyboard編輯器支持配置不同size class的顯示界面界酒、在指定配置中刪除視圖以及指定不同的布局約束。 使用這些工具意味著不必在運行時以編程方式進行相同的更改嘴秸。相反毁欣,UIKit會在當前size class更改時自動更新界面。
圖13-1顯示了用于在Interface Builder中配置界面的工具岳掐。size class查看控件改變了界面的外觀凭疮。使用該控件查看給定size class的界面的外觀。對于單個視圖串述,使用安裝控件來查看對于給定的size class配置是否存在視圖执解。使用復選框左側的加號(+)按鈕添加新配置。
image asset是存儲應用程序圖片資源的首選方式纲酗。每個image asset包含同一圖像的多個版本衰腌,每個版本均為特定配置而設計。除了為標準分辨率顯示屏和Retina顯示屏指定不同的圖像外觅赊,還可以為不同的水平和垂直size class指定不同的圖像右蕊。使用image asset進行配置時,UIImageView
對象會自動選擇與當前size class和分辨率相關聯(lián)的圖像吮螺。
圖13-2顯示了image asset的屬性饶囚。更改寬度個高度屬性為目錄中的更多圖像添加插槽。用這些圖像填充這些插槽以用于每種size class組合规脸。
更改子視圖控制器的特征
子視圖控制器默認繼承父視圖控制器的特征坯约。對于size class特征,每個子視圖控制器都可能沒有與其父視圖控制器相同的特征莫鸭。例如闹丐,常規(guī)環(huán)境中的視圖控制器可能想要為其一個或者多個子視圖控制器配置一個緊湊的size class,來反映該子視圖控制器的空間減小被因。在實現(xiàn)容器視圖控制器時卿拴,可以通過調(diào)用容器視圖控制器的setOverrideTraitCollection:forChildViewController:
來修改子視圖控制器的特征衫仑。
以下代碼顯示了如何創(chuàng)建一組新特征并將他們與子視圖控制器關聯(lián)。只需在父視圖控制器執(zhí)行這段代碼一次堕花。
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從常規(guī)變?yōu)榫o湊時瞄崇,上面例子中的子視圖控制器保留其常規(guī)水平size class。然而壕曼,如果displayScale
特征發(fā)生改變苏研,則子視圖控制器會繼承新值。
將呈現(xiàn)的視圖控制器調(diào)整為新的樣式
呈現(xiàn)的視圖控制器可以在水平常規(guī)環(huán)境和水平緊湊環(huán)境之間自動調(diào)整腮郊。當從水平常規(guī)環(huán)境切換到水平緊湊環(huán)境時摹蘑,UIKit默認將內(nèi)置的呈現(xiàn)樣式更改為UIModalPresentationFullScreen
。對于自定義呈現(xiàn)樣式轧飞,presentation controller可以確定適應行為并相應地調(diào)整呈現(xiàn)內(nèi)容衅鹿。
對于某些應用程序,適應全屏樣式可能會出現(xiàn)問題过咬。例如大渤,Popover通常是通過點擊背景調(diào)光視圖來移除的,但在Popover覆蓋整個屏幕的緊湊環(huán)境中這樣做是不可能的掸绞,如圖13-3所示兼犯。當默認的自適應樣式不合適時,我們可以告訴UIKit使用不同的樣式或呈現(xiàn)一個更適合全屏樣式的完全不同的視圖控制器集漾。
要更改呈現(xiàn)樣式的默認自適應行為切黔,請分配一個委托對象給關聯(lián)的presentation controller。訪問呈現(xiàn)的視圖控制器的presentationController
屬性來獲取presentation controller具篇。在進行任何與適應性相關的更改之前纬霞,presentation controller都會詢問委托對象。委托對象可以返回與默認不容的呈現(xiàn)樣式驱显,并且可以為presentation controller提供可選的視圖控制器以進行顯示诗芜。
使用委托對象的adaptivePresentationStyleForPresentationController:
方法來指定與默認不同的呈現(xiàn)樣式。切換到緊湊環(huán)境時埃疫,唯一支持的樣式是兩種全屏樣式或者UIModalPresentationNone
伏恐。返回UIModalPresentationNone
會通知presentation controller忽略緊湊環(huán)境并繼續(xù)使用先前的呈現(xiàn)樣式。在呈現(xiàn)Popover時栓霜,忽略更改會為所有設備提供與iPad類似的彈出式行為翠桦。圖13-4顯示了默認的全屏適應并且沒有并排適應,可以比較下結果。
要完全替換視圖控制器销凑,請實現(xiàn)委托對象的presentationController:viewControllerForAdaptivePresentationStyle:
方法丛晌。在適應緊湊型環(huán)境時,可以使用該方法將導航控制器插入視圖層次結構中斗幼,或者加載專門為較小控件設計的視圖控制器澎蛛。
實現(xiàn)自適應Popover的技巧
從水平正常環(huán)境切換到水平緊湊環(huán)境時,Popover需要額外的修改蜕窿。水平緊湊環(huán)境的默認行為會將Popover改為全屏呈現(xiàn)谋逻。因為通常情況下,Popover是通過點擊其背景調(diào)光視圖來被移除的桐经,改為全屏呈現(xiàn)就廢除了移除Popover的主要方式斤贰。可以通過執(zhí)行以下任一操作來補償該行為:
- 將Popover的視圖控制器推入到現(xiàn)有導航堆棧上次询。當有父導航控制器可用時,請移除Popover并將其視圖控制器推入導航堆棧瓷叫。
-
添加控件以在全屏呈現(xiàn)時移除Popover屯吊。可以將控件添加到Popover的視圖控制器摹菠,但更好的選擇是使用
presentationController:viewControllerForAdaptivePresentationStyle:
方法為導航控制器換掉Popover盒卸。使用導航控制器提供了一個模態(tài)界面和添加完成按鈕或者其他控件(用來移除內(nèi)容)的空間。 -
使用presentation controller的委托對象去廢除任何適應性更改次氨。獲取Popover presentation controller并為其分配一個實現(xiàn)了
adaptivePresentationStyleForPresentationController:
方法的委托對象蔽介。從該方法返回UIModalPresentationNone
會導致Popover繼續(xù)使用之前的呈現(xiàn)樣式。
響應尺寸更改
尺寸更改的發(fā)生有很多原因煮寡,包括以下:
- 底層window的尺寸發(fā)生變化虹蓄,通常是由于設備方向改變。
- 父視圖控制器調(diào)整其子視圖控制器的尺寸幸撕。
- presentation controller更改其呈現(xiàn)的視圖控制器的尺寸薇组。
當尺寸發(fā)生改變時,UIKit會通過正常的布局過程自動更新可見的視圖控制器層次結構的尺寸和位置坐儿。如果使用Auto Layout約束來指定視圖的尺寸和位置律胀,則應用程序會自動適應任何更改并且應該在不同屏幕尺寸的設備上運行。
如果Auto Layout約束無法實現(xiàn)所需的外觀貌矿,則可以使用viewWillTransitionToSize:withTransitionCoordinator
方法來更改布局炭菌。還可以使用該方法創(chuàng)建其他動畫與尺寸更改動畫一起運行。例如逛漫,在界面旋轉(zhuǎn)期間黑低,可以使用轉(zhuǎn)場動畫協(xié)調(diào)器的targetTransform
屬性為界面的某些部分創(chuàng)建反向旋轉(zhuǎn)矩陣。