(本文翻譯自AppCoda創建者Simon Ng出版的書籍《iOS開發中級教程》,詳見:Intermediate iOS Programming with Swift)
起初,iPhone只有一種3.5英寸的屏幕,那時的軟件界面很容易搭建,開發者只需要處理兩種不同方向上的界面展示(水平和豎直方向)。之后,蘋果發布了9.7英寸屏幕的iPad,這個時候的開發者必須為他們的應用創建兩個不同的界面(比如創建兩個storyboard或者xib文件),一個用來搭建iPhone版的界面,另一個用來搭建iPad版的。
好日子一去不復返!從2014年開始,蘋果公司的iPhone和iPad產品線變化很大。隨著iPhone 6和iPhone 6 Plus的發布,現在的應用需要適配多種不同屏幕尺寸的設備,主要有如下幾種:
- iPhone 4/4s(3.5英寸)
- iPhone 5/5c/5s(4英寸)
- iPhone 6(4.7英寸)
- iPhone 6 Plus(5.5英寸)
- iPad / iPad 2 / iPad Air / iPad Air 2(9.7英寸)
- iPad mini / iPad mini 2 / iPad mini 3(7.9英寸)
對開發者來說,創建一個能夠適配多種屏幕尺寸和方向的通用型界面是一個非常大的挑戰。那么,我們如何來創建一個像素不被拉伸、界面完美的應用呢?
在iOS 8中,蘋果推出了一種新技術叫做Adaptive User Interfaces (自適應界面)
,它能夠支持不同尺寸、不同方向屏幕的適配,使用這種技術,我們的應用就可以適配以上多種iPhone和iPad設備了。
實現自適應界面,需要了解一個新概念——Adaptive Layout (自適應布局)
。在Xcode 6中,開發者可以通過使用a universal storyboard來搭建能夠適配所有設備的自適應界面。現在,你可以只使用一個storyboard文件來布局你所有的視圖控件,包括iPhone和iPad版本。
為了實現自適應布局,蘋果在iOS 8中推出了Size Classes
技術,它是實現自適應布局中最重要的一步。Size Classes
是對不同尺寸、不同方向的設備的一種抽象,將Size Classes
和Auto Layout (自動布局)
技術結合起來使用,就能夠搭建出自適應界面。在iOS 8中,實現自適應布局需要如下幾個步驟:
- 先使用自動布局來搭建你的界面,自動布局已經能夠處理大多數尺寸和方向的適配任務。
- 針對不同的size classes添加特定的布局約束。比如說,當設備橫屏時增加兩個標簽之間的間距。
在本文中,我將介紹如何使用各種自適應界面技術來創建一個通用型的應用,它可以適配各種不同尺寸和方向的設備。
自適應界面示例
本次示例工程不需要編寫代碼,我們主要使用storyboard來布局我們的視圖組件,并學習如何使用自動布局和Size Classes技術來實現自適應界面。通過本文的學習,你將會構建一個具有單一視圖控制器的應用,該應用可以適配任何不同尺寸和方向的設備。
創建Xcode工程
首先,啟動Xcode,創建一個Single View Application
的項目,項目名稱填入AdaptiveUIDemo
,并確保Devices
選項選擇的是Universal
。
一旦創建完項目,打開Main.storyboard
文件,你會看見一個600×600尺寸的方形控制器視圖,因為使用了Size Classes
功能,控制器的視圖表示的是一個通用的設備,而不是像以前那樣表示的是一個具體的設備(例如4英寸的iPhone)。
你可以使用自動布局對視圖組件(比如標簽)進行約束,同以前舊版本的Xcode或者關閉了Size Classes
功能的項目一樣。
接下來,下載圖片資源并將其添加到項目的images.xcassets
里面。鏈接地址點擊這里。(譯注:原文資源文件地址需要FQ訪問,本人已轉存到這里:圖片1、圖片2、圖片3)
接下來,回到storyboard文件,往控制器中拖入一個UIImageView
,設置它的寬度為600、高度為390,在屬性選項卡中,設置image為tshirt
,Mode選擇Aspect Fill
。在本文接下來的內容中,我將把這個UIImageView
叫做產品圖片視圖。
然后,再往控制器中拖入一個UIView
,放在產品圖片視圖的下面,設置它的寬度為600、高度為210。這個UIView
將作為一個容器,用來裝我們的其他視圖控件(比如標簽),把一些相關聯的組件包裝在一個父視圖內,將更有利于我們對其進行布局。在本文接下來的內容中,我將把這個UIView
叫做產品簡介視圖。
接下來,往產品簡介視圖中拖入一個UILable
,將其文本內容設置為PSD T-Shirt Mockup Template,將字體設置為Avenir Next,字體大小設置為32,在尺寸選項卡中,將X改為22、Y改為15、寬度改為556、高度改為44。在本文接下來的內容中,我將把這個UILabel
叫做標題標簽。
再拖入一個UILabel
放在標題標簽的下面,將其文本內容設置為This is a free psd T-shirt mockup provided by pixeden.com. The PSD comes with a plain simple tee-shirt mockup template. You can edit the t-shirt color and use the smart layer to apply your designs. The high-resolution makes it easy to frame specific details with close-ups.,同樣將字體設置為Avenir Next,字體大小設為18,Lines設為0。在尺寸選項卡中,將X改為22、Y改為58、寬度改為556、高度改為123。在本文接下來的內容中,我將把這個UILabel
叫做產品描述標簽。
要注意這兩個標簽是放在產品簡介視圖里面的,你可以打開大綱視圖來確認它們的層級關系是否正確。如果你做的正確,你的界面應該和下圖所示一樣。
如果你的界面跟上圖在位置上不完全一致也沒關系,因為接下來要對這些控件添加約束進行自動布局。
現在先讓我們來看一下我們的界面在不同的設備上表現如何。在Xcode 6中,你可以不通過啟動模擬器運行程序就能看到你的界面如何展示。最新版本的Xcode提供了一個預覽功能,可以使開發者快速方便地查看界面在不同尺寸設備上的樣子。
如下圖所示操作,點擊助手彈出菜單選擇預覽,然后按住shift和option鍵點擊Main.storyboard
。
你可以選擇在哪個區域顯示預覽窗口。雙擊main旁邊的加號按鈕,Xcode會自動打開助手編輯視圖并將應用界面的預覽顯示出來。默認顯示出的是4英寸iPhone上應用界面的樣子,你可以點擊左下角的加號按鈕來添加不同尺寸的設備,從而來查看應用界面在不同屏幕尺寸上的顯示。如果你添加了所有尺寸的設備,包括iPad設備,你的屏幕現在應該跟下圖所示一樣。你會發現,我們現在的界面不能很好的適應所有的設備,這是因為我們還沒有使用自動布局來給控件添加約束。
添加自動布局約束
現在我們來為視圖組件添加布局約束。首先從我們的產品圖片視圖開始,有些開發者對自動布局感到恐懼,我的習慣是先用普通的語言將約束描述一遍,以我們的產品圖片視圖為例,我認為對它的約束應該是下面這個樣子:
- 在產品圖片視圖和主視圖之間,它們的上邊、左邊和右邊的間距應該為0。
- 產品圖片視圖大約占主視圖的65%~70%左右
- 產品圖片視圖和它下邊的產品簡介視圖之間的間距也應該為0。
把這些描述轉換成自動布局的約束,看上去應該如下所示:
- 約束產品圖片視圖的上邊、左邊和右邊的間距,值設為0。
- 給產品圖片視圖增加一個相對于主視圖的高度約束,將相乘的系數設為0.65。
- 添加產品圖片視圖和產品簡介視圖之間的間距約束,值設為0。
現在我們開始設置這些約束,選擇產品圖片視圖,點擊自動布局菜單中的Pin按鈕,選中上邊、左邊、右邊的約束并設置為0,注意要確保Constrain to margin
沒有被選中,然后點擊下邊的按鈕添加三個約束。
接下來,打開左側的大綱視圖,按住Control鍵從產品圖片視圖拖到主視圖,在彈出的菜單中選擇等高約束。
添加完等高約束后,該約束就會出現在大綱視圖上。
選中該約束,點擊右側的尺寸選項卡,在這里你可以對該約束進行自定義的設置。
在設置之前,先查看一下first item
中是否是tshirt.Height、second item
中是否是SuperView.Height,如果不是,可以點擊下拉菜單然后選擇Reverse First and Second item。
默認情況下,兩個高度值之間的相乘系數是1,代表著產品圖片視圖占據了主視圖的100%,我們希望產品圖片視圖只是占據主視圖的65%,所以將相乘的系數改為0.65。
接下來,選擇產品簡介視圖,點擊Pin按鈕,選中左邊、右邊和下邊的約束,設置為0。同樣要確保Constrain to margin
選項沒有被選中,然后點擊下邊按鈕添加三個約束。
接下來,我們要為產品圖片視圖和產品簡介視圖添加間距約束。在大綱視圖中,按住Control鍵從產品圖片視圖拖到產品簡介視圖。
在彈出的菜單中選擇豎向間距約束,這樣我們就添加了產品圖片視圖的底部和產品簡介視圖的頂部之間的間距約束。
現在再預覽一下我們的界面,你會發現在不同尺寸的設備上它看上去稍微好一些了,但是我們還可以做很多事情來讓它變得更好。
現在,我們來為產品簡介視圖中的兩個標簽設置約束。
先選擇標題標簽,就是看上去字體更大的那個,點擊自動布局的Pin按鈕,設置它上邊約束15、左邊約束22、右邊約束22,記住確保Constrain to margin
選項沒有被選中,然后點擊下邊的按鈕添加三個約束。
接下來,選擇產品描述標簽,用同樣的辦法設置它的下邊約束29、左邊約束22、右邊約束22。
最后,給兩個標簽添加間距約束,按住Control鍵從標題標簽拖到產品描述標簽,同樣,在彈出的菜單中選擇豎向間距約束。
當你添加完這個約束后,你會在storyboard上發現很多黃色和紅色的線,這表示我們的布局現在存在問題,當約束不明確或有沖突時就會產生布局問題。
要解決布局問題,可以點擊大綱視圖中的紅色箭頭,這樣會列出現在存在所有的布局問題。
Xcode非常智能可以幫助我們很容易地解決布局問題,通過點擊紅色指示器按鈕,就會彈出多種可能的解決辦法,我們選擇Change Priority來解決我們之前的布局問題。
非常棒!我們已經完成了所有視圖控件的自動布局,現在重新在預覽視圖中查看我們的界面在不同尺寸設備上的展示。
界面看上去比之前要好多了,產品圖片已經能正確的顯示。然而,我們的界面還有以下幾處問題需要解決:
- 產品描述標簽在iPad上被豎向居中顯示了
- 標題標簽不能被完整地顯示,尤其是在iPhone設備上
- 產品描述標簽在iPhone上也只是部分顯示
我們先來解決第一條問題,很明顯,問題應該出在我們為產品描述標簽添加的約束上。你可以在尺寸選項卡里面查看已經添加的約束。選擇產品描述標簽,然后選擇尺寸選項卡。
你會發現如下圖所示的一系列約束。
我們給產品描述標簽添加了四個約束,很明顯,我們的問題應該與上邊和下邊的間距約束有關。
在自動布局中,每一個約束都有一個優先級的概念,高優先級的約束會比低優先級的約束優先被執行。所有新建的約束其優先級都會被設為一個相同的值(值為1000),意味著所有約束都同等重要、都需要被執行。我們產品描述標簽的上邊和下邊約束的優先級相同,但是看上去好像下邊的約束更勝一籌,導致我們標題標簽和產品描述標簽中間出現了一個很大的間距。
要解決這個問題,應該把產品描述標簽的下邊間距約束的優先級降低。點擊下邊間距約束上的Edit按鈕,將priority的值改為250。
這樣完成之后,我們的第一條界面問題就解決了。
Size Classes
不要忘了,我們之前提到過自適應界面技術需要兩個步驟,到目前為止我們只是完成了界面的自動布局,自動布局能夠滿足大多數情況下的界面適配,接下來的第二步我們將使用Size Classes
技術讓我們的界面看上去更加完美。
一個size class
代表了一種或多種屏幕尺寸的抽象,包括豎直方向(高度)和水平方向(寬度)的尺寸。在iOS 8中有兩種類型的size class
:regular(整齊的)和compact(緊湊的)。regular類型的size class
表示一個比較大的屏幕空間,compact類型的size class
則表示一個比較小的屏幕空間。
使用size classes
來抽象地描述屏幕的尺寸,會有以下四種結果:Regular width-Regular Height
、Regular width-Compact Height
、Compact width-Regular Height
和Compact width-Compact Height
。
下圖中的表格展示了不同iOS設備對應的size classes
模型。
為了描述真實的顯示環境,你必須指定水平和豎直兩個方向上的size class
類型,以iPad為例,你應該指定它的size class
為Regular width-Regular Height
。
在自動布局的基礎上,你可以為不同的size classes
指定特定的布局約束,比如說,你可以為size class
設為compact height-regular width
的屏幕上的標簽指定特定的字體大小,你也可以專門為regular-regular
尺寸的屏幕改變一個按鈕的位置。
需要注意的是,所有iPhone設備的豎向屏幕的size class
都為compact width-regular height
,這意味著你的界面在iPhone 4s和iPhone 6上豎屏展示的樣子是幾乎一樣的。而對于iPhone 6 Plus的橫屏來說,它的size class
應該是regular width-compact height
,這表示你的界面與在iPhone 4/5/6上的展示是完全不一樣的。
對Size Classes
的基本概念有過了解之后,我們接下來看看如何解決我們應用界面剩下的兩個問題:
- 標題標簽不能被完整地顯示,尤其是在iPhone設備上
- 產品描述標簽在iPhone上也只是部分顯示
使用Size Classes自定義字體大小
我們想讓標題標簽和描述標簽能在iPhone上完美顯示,問題是現在字體的大小對于iPad來說很合適,但是對于iPhone來說它太大了。
使用Size Classes
技術,你可以為不同尺寸的屏幕指定特定的字體大小。接下來,我們想要改變所有iPhone的豎屏界面內的字體大小,在size classes
的表格中我們可以看出,它對應的size class
應為compact width-regular height
。
想要為不同尺寸設置不同的字體大小,首先在storyboard中選中我們的標題標簽,在屬性選項卡中Font選項左邊,你會發現有一個加號按鈕。
點擊加號按鈕然后依次選擇compact width-regular height
,
然后你會發現新添加了一條Font選項專門用來設置你所指定的size class
,保持原始的Font選項不變,我們只需將新的Font選項內的字體大小改為20。
這樣做的意思是通知系統,在所有iPhone設備的豎屏顯示時,使用更小一點的字體大小,而在iPad上顯示時,仍然使用我們早期設置的大小。接下來選擇產品描述標簽,同樣更改它在compact width-regular height
下的字體大小為13。查看預覽視圖,我們的文字已經能夠完美地展示在不同設備上了。
使用Size Classes自定義界面的設計
現在我們的界面已經適配所有的豎屏尺寸了,那么在橫屏顯示時,我們的界面又是如何展示的呢?
在預覽窗口內,點擊設備下部的旋轉按鈕(比如點擊4英寸的iPhone)。很明顯,我們的界面在iPhone的橫屏上并不能很好的展示。
你可能會想到我們可以為橫屏尺寸的屏幕設置更小的字體,就跟我們之前做的一樣,我并不打算這么去做。
我將重新設計橫屏界面的布局來代替之前的設計,目的是為了更好的利用橫屏模式下更寬的尺寸,這才是Size Classes
技術能力的體現。
對于橫屏界面來說,將產品圖片視圖和產品簡介視圖并排顯示效果可能更好,并且每一個視圖占據主視圖的50%,我們的界面應該看上去像下圖這樣。
到目前為止,我們并沒有介紹Size Classes
在視圖布局上如何應用,我們之前設計的界面一直是在wAny hAny
的size class
下進行的。
現在,點擊storyboard下方的wAny hAny
按鈕,在這里我們能夠通過選擇表格大小來指定不同的size class
。我們想要更改iPhone設備橫屏下的界面布局,所以我們選擇Any Width-Compact Height
,它代表了所有iPhone設備的橫屏顯示(包括iPhone 6 Plus)。
在表格中移動鼠標,選擇Any Width-Compact Height
的size class
。
當你選擇特定的size class
之后,控制器視圖的尺寸也會發生相應的改變,在storyboard底部會標示出你當前所指定的size class
為wAny hCompact,接下來你做的所有布局,都只會針對當前的size class
,而不會影響到其他尺寸,這就是在Xcode中如何使用一個storyboard文件來展示多種界面布局的原理。
好,接下來我們開始設計我們的界面。
當前的界面會將我們之前添加的布局約束保留下來(size class
為wAny hAny的時候),因為我們接下來想要重新去布局我們的界面,所以我們應該先把之前的約束全部清空。
在大綱視圖中選擇主視圖,然后點擊自動布局菜單中的Issues按鈕,然后選擇All Views下面的Clear Constraints選項。
這樣會清空我們當前size class
下的所有布局約束,注意那些約束在wAny hAny下仍然有效,我們只是將wAny hCompact下的所有約束給清空了。
你可以打開大綱視圖展開約束列表,你會發現所有約束都還在只是變成了灰色,意味著當前size class
下它們不可用。
如果你選中任意一個約束,然后打開尺寸選項卡,你會發現有兩個Installed復選框,而且wAny hCompact下的Installed沒有被選中,而通用布局下的Installed被選中了,這驗證了我們只是將當前size class
下的約束給清除了,并沒有影響其他尺寸下的布局約束。
接下來,我們開始布局我們的新界面,讓產品圖片視圖和產品簡介視圖并排顯示在一行。
首先,選擇產品圖片視圖,在尺寸選項卡下,設置X為0、Y為0、寬度為300、高度為400,然后選擇產品簡介視圖,設置它的尺寸X為300、Y為0、寬度為300、高度為400,界面的效果應該像下圖一樣。
接下來,選擇標題標簽,設置它的尺寸X為22、Y為15、寬度為256、高度為70,標題文字的字體太大了,所以我們要為當前size class
下的字體設置特定的大小,在屬性選項卡中,點擊Font旁邊的加號按鈕,選擇Any Width | Compact Height (current)選項。
這樣就新建了一個對當前size class
下的字體設置,把它的大小改為25。現在我們的標題標簽只能顯示在一行里,將Lines屬性從1改為0,標題標簽會自動判斷自己應該顯示幾行。
選擇產品描述標簽,設置它的尺寸X為22、Y為85、寬度為256、高度為250,現在我們的界面應該如下圖所示。
因為我們之前已經把所有約束清空了,所以接下來我們必須為當前size class
下的界面添加自動布局約束。
選擇產品圖片視圖,點擊Pin按鈕,添加上邊、左邊、下邊間距約束為0,記住不要選中Constrain to margins
,然后點擊添加三個約束。
接下來選擇產品簡介視圖,同樣設置它的上邊、右邊、下邊間距約束為0。
在大綱視圖內,按住Control鍵從主視圖拖到產品圖片視圖,選擇等寬約束。
這條約束是要用來表示產品圖片視圖占據主視圖的一半,就像之前我們將等高約束相乘系數改為0.65一樣,這里我們把等寬約束的相乘系數改為0.5,這樣產品圖片視圖就只占據主視圖的50%了。
接下來我們為產品圖片視圖和產品簡介視圖添加間距約束。在大綱視圖中,按住Control鍵從產品圖片視圖拖到產品簡介視圖,在彈出的菜單中選擇豎向間距約束。
好了,現在我們搞定產品圖片視圖和產品簡介視圖的所有約束了,剩下的就是為兩個標簽添加約束了。
首先選擇標題標簽,設置它的約束為上邊15、左邊22、右邊22,記住不要選中Constrain to margins
選項。
按住Control鍵從標題標簽拖到產品描述標簽,在彈出的菜單中選擇豎向間距約束。
選擇產品描述標簽,設置它的約束為左邊22、右邊22,記住不要選中Constrain to margins
選項。
storyboard會提示我們現在的約束有些問題。在大綱視圖中點擊右上角的黃色箭頭,然后點擊黃色指示器按鈕,然后選擇Update frame選項來解決我們所有的布局問題。
非常棒!現在再去預覽視圖中查看我們應用的界面在橫屏下的顯示,如下圖所示。
我們的界面看上去非常精美,對吧?然而在3.5和4英寸的iPhone上,產品描述標簽的顯示還有一些小問題。要解決這個問題,你需要新建一個針對這種情況下的size class
的字體設置選項,我將這一步留給你們自行練習解決。
接下來,點擊運行按鈕使用模擬器運行我們的程序,當你將設備從豎屏旋轉到橫屏時,你會發現我們的界面在發生變化的過程中還帶有非常平滑的轉場動畫。
使用Size Classes自定義約束
希望你現在已經理解如何使用Size Classes
技術來自定義字體和自定義界面設計,除此之外,使用Size Classes
技術還可以自定義約束。
如果我想讓我們的界面在iPhone 6 Plus下展示成下圖的樣子,卻不改變它在其他iPhone設備上的展示,那么我該如何去做呢?
從圖中我們可以看到,標題標簽和產品描述標簽被移到了右下方,很明顯,我們應該自定義標題標簽同它的父視圖(產品簡介視圖)之間的上邊間距,讓我們來看看應該如何去做。
iPhone 6 Plus橫屏的size class
應該為Regular width-Compact Height
,在storyboard下方的Size Classes表格選項中選擇Regular width-Compact Height
并確認。
在大綱視圖中,找到標題標簽同它父控件之間的豎向間距約束。
在尺寸選項卡中,你可以在Constant選項旁邊看到一個加號按鈕,Constant的值現在被設置為15,我們希望在當前size class
下增大這個間距,所以點擊加號按鈕,選擇Regular Width | Compact Height (current)
選項。
在新建的wR hC選項中,將間距值設為230。
如果你的大綱視圖中顯示存在布局約束問題,你可以點擊箭頭,然后根據Xcode的建議來解決約束問題。
再次在預覽視圖中查看我們的界面,你會發現5.5英寸的iPhone界面改變了,而其他iPhone設備上的界面并沒有變化。
總結
在iOS 8和Xcode 6中,蘋果為開發者提供了非常強大的工具來搭建自適應界面。在本文中,我們介紹了Size Classes
技術和相關概念,并展示了如何搭建一個自適應的界面。
自適應布局是iOS 8中一個非常重要的新特性,開發者只針對一種設備、一種屏幕尺寸去設計界面的日子不可能再有了,如果你正打算開發一款新的應用,確保你已經熟悉自動布局和Size Classes
技術,從而讓你的應用能夠適配多種屏幕尺寸和方向,未來的應用界面肯定更需要自適應布局技術。
你可以在這里下載本教程的完整示例代碼,供你學習和參考。(譯注:原文資源文件地址需要FQ訪問,本人已轉存到GitHub上,詳見這里)