Chapter 1 構建自適應用戶界面
@(Intermediate iOS 10 Programming with Swift)
起初,蘋果發布的初代iPhone手機是固定3.5英寸屏幕,我們很容易為它開發應用程序,只需要考慮兩個不同的方向(垂直或水平)就可以。后來蘋果公司發布了一款9.7英寸的iPad,如果你在當時就是一位應用程序開發者,就必須要在Xcode中創建兩個不同的UI設計——一個適用于iPhone,而另一個則適用于iPad。
時光荏苒,到了2015年,蘋果的iPhone和iPad產品已經發生了巨大的變化。隨著iPhone6和iPhone6 Plus(現在的iPhone 7和7 Plus)推出,我們的應程序需要支持各種屏幕尺寸和分辨率的iOS設備。它包括:
- iPhone 4/4s (3.5-inch)
- iPhone 5/5c/5s (4-inch)
- iPhone 6/6s/7 (4.7-inch)
- iPhone 6/6s/7 Plus (5.5-inch)
- iPad / iPad 2 / iPad Air / iPad Air 2 (9.7-inch)
- iPad Mini / iPad Mini 2 / iPad Mini 3 / iPad Mini 4 (7.9-inch)
- iPad Pro (9.7/12.9-inch)
對于iOS開發人員來說,創建一個通用的應用程序,是一個非常大的挑戰,我們需要適應所有上面列出來的屏幕尺寸和方向的喲,唉,那么你可以如何來設計這種像素級的完美的應用程序UI呢?
從iOS 8開始,移動操作系統提供了一個稱之為Adaptive User Interface(自適應用戶界面)的概念,這是蘋果支持iOS設備的任何尺寸和任何方向的解決方案。現在,應程序可以對用戶界面的調整細節到特定設備甚至是特定的方向上。
這導致了一種稱為自適應布局的新的UI設計概念,從Xcode 7開始,開發工具允許開發人員使用Interface Builder構建適應所有不同設備屏幕尺寸和方向的應用程序UI,在Xcode 8中Interface Builder進一步重新設計,以便于我們簡化自適應用戶界面的制作。我們甚至可以實時預覽任何的iOS設備上呈現的UI設計效果。
要實現自適應布局,我們需要使用一個名為Size Classes的概念。這在iOS 8或更高的版本中可用,它也可能是實現自適應布局最重要的一個方面。Size Classes是根據屏幕的尺寸和方向對設備進行分類的抽象。你可以同時使用Size Classes和自動布局(Auto Layout)來設計自適應用戶界面。在iOS8/9/10中創建自適應布局的過程如下:
- 你首先要設計一個通用的布局,這個通用布局足以支持大多數屏幕尺寸和方向。
- 你選擇一個特定的size class并提供你的布局特性。例如,當設備處于橫向的時候,你希望增加兩個Label之間的距離。
在本章中,我們將通過構建通用應用程序引導大家了解所有自適應的概念,比如Size Classes類。該應用程序支持所有目前可用的屏幕尺寸和方向。
Adaptive UI demo
這個項目我們不需要編寫代碼,你將主要使用故事板布局用戶界面組件,了解如何使用自動布局和size Classes讓UI自適應。經過本章之后,你將擁有一個適用于多個屏幕尺寸和方向的單個視圖控制器的應用程序。
Creating the Xcode Project
首先啟動Xcode,并使用Single View Application模板創建一個新的項目,在項目選項中,將項目名稱命名為AdaptiveUIDemo,并確保device設置為Universal。
項目創建好以后在Project Setting頁面中將Deployment Target從10.0調整為9.3(或者更低)。因為iOS 10不再支持3.5英寸的設備,所以將其設置為9.3可以允許我們測試iPhone4s的應用程序。你可能不想測試舊一代的設備,但是在本書的示例中,我們想要驗證的是如何通過自適應界面構建適應所有的屏幕大小的UI。
現在,打開Main.storyboard。在Interface Builder中,您會發現有一個大小為iPhone 6的視圖控制器。如果你以前使用Xcode 7,我相信你超喜歡這個改變。它不會再顯示一個具有方形畫布的通用視圖控制器。新的Interface Builder允許你在模擬設備中布置用戶界面,您可以使用底部欄中的設備配置控件輕松地在不同設備之間切換。例如,選擇View as打開設備配置面板,然后選擇iPhone 6s Plus。故事板中的視圖控制器的大小便相應發生變化。
現在我們就來開始設計app的用戶界面。首先,從http://www.appcoda.com/resources/swift3/adaptiveui-images.zip下載圖像包,并將圖像導入到Assets.xcassets。
接下來回到故事板。我通常從iPhone 6s開始布局用戶界面,然后再為其他屏幕尺寸添加特定的布局。因此,如果您選擇了其他設備(例如iPhone 6s Plus),建議您將設備設置更改為iPhone 6s。
現在,將image view從對象庫拖動到視圖控制器。將其寬度設置為375,高度為390。選中image view,然后在Attributes檢查器中,設置image為tshirt,將mode設置為Aspect Fill。
然后,拖動一個view到視圖控制器,并將其放在image view的正下方。此view用作容納其他UI組件(如Label)的容器。通過分組相關的UI組件到同一個view下,可以使你以后的工作變得更加輕松。在Size檢查器中,確保將width設置為375,Height為277。
在整個章節中,我會將這個視圖稱為Product Info View。 您的界面布局應類似于下圖。
接下來,將Label拖到Product Info View。將Label更改為PSD T-Shirt Mockup Template
。將字體設置為Avenir Next
,其大小為25
點。按command + =
調整label到合適的大小。在Size檢查器中,將X的值更改為15
,將Y更改為15
。
拖拽另一個label,將其放在上一個label的正下方。在Attributes檢查器中,將text更改為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
點,將行數更改為0
。
在Size檢查器中,將X的值更改為15,將Y更改為58。將寬度設置為352
,將高度設置為182
。
請注意,兩個label應放置在Product Info View中。您可以通過雙擊打開Document Outline來仔細檢查。兩個label放在view的下面。如果你按照正確的程序,你的屏幕應該類似于下面這樣。
即使你的設計完全不符合理想的參考設計,但也沒有問題。我們將使用auto layout constraints來進行之后的布局。
現在,應用程序界面是iPhone 6s或4.7英寸的屏幕。我們進行一個快速測試,以查看不同設備上的設計外觀和感覺。
在Xcode 8中,您有兩種實時預覽app UI的方法:
- 通過使用設備配置面板
- 使用預覽助手(Preview Assistant)
如您以前嘗試過的,您可以單擊View as按鈕切換到另一個設備。現在嘗試將設備更改為iPhone SE,在看看它的外觀。看起來不好看圖像和文本都被截斷了。您可以繼續切換到其他設備來預覽用戶界面。
另外,你也可以使用Preview assistant,它可讓你一次評估不同尺寸iOS設備上的設計效果。
要打開預覽助手,請打開Assistant pop-up menu > Preview (1)。 然后按住option鍵,單擊Main.storyboard(Preview)。
然后,Xcode將在助理編輯器中顯示app UI的預覽。默認情況下,它顯示在iPhone 4英寸設備上的預覽。您可以點擊助手編輯器左下角的+按鈕來預覽iPhone 3.5英寸和其他設備。如果您將所有設備(包括iPad)添加到助理編輯器中,則屏幕應如下圖所示。如您所見,目前的設計在除iPhone 6之外的所有設備上都看起來不盡如人意。到目前為止,我們還沒有定義任何自動布局約束。這就是為什么視圖不適合所有設備。
Adding Auto Layout Constraints
現在,我們要定義UI組件的布局約束。首先,我們從image view開始。一些開發人員害怕使用自動布局。我曾經以描述性的方式寫過布局約束。以image view為例,這里有一些我可以想到的限制:
- image view的頂部,左側和右側與main view之間不應有間距。
- image view占主視圖的55-60%。
- image view和Product Info View之間沒有間距,應為零。
如果將上述約束轉換為自動布局約束,則它們將按原樣轉換:
- 為image view的top、leading(即左)和trailing(即右)邊緣創建間距約束。將距離設置為0。
- 定義image view和main view之間的高度約束,并將約束的乘數設置為0.585。 (我預先計算了這個值,但0.55和0.6之間的任何值都可以正常工作。)
- 在image view和Product Info View之間創建間距約束,并將其值設置為0。
現在選擇image view,然后單擊自動布局菜單上的Pin按鈕以創建間距約束。對于左側、上側和右側,將值設置為0
。確保Constrain to margin
選項未選中,因為我們要設置相對于super view邊緣的約束。 然后點擊Add 3 constraints
按鈕。
接下來,打開Document Outline。從image view (tshirt)拖動鼠標到main view。出現提示時,從彈出菜單中選擇Equal Heights
。
一旦添加了等高度約束,它應該出現在Document Outline的Constraints部分。選擇約束并轉到Size檢查器。在這里您可以編輯約束的值來更改它的定義。
在下一步之前,確保第一個item設置為tshirt.Height
,第二個item設置為Superview.height
。 如果不是,您可以單擊第一個item的選擇框,然后選擇Reverse First and Second item
。
默認情況下,multiplier的值設置為1
,這意味著tshirt image view占用main view的100%(這里的main view是superview)。如前所述,image view應該只占main view的大約60%。 所以將乘數從1改為0.585。
接下來,選擇Product Info View,然后單擊Pin按鈕。選擇左側、右側和底部,并將該值設置為0
。確保未勾選Constrain to margin
選項。然后單擊Add 3 constraints
按鈕。這為Product Info View添加了三個間距約束。
此外,我們必須在image view和Product Info View之間定義間距約束。在Document Outline中,拖拽image view(tshirt)到Product Info View。出現提示時,從菜單中選擇Vertical Spacing
。這會創建一個垂直間距約束,使image view底部和Product Info View頂部之間沒有間隙。
如果您現在在其他設備上查看所呈現的視圖,則image view現在應適用于所有設備;然而,label還有很多工作要做。
現在,我們來定義兩個label所需要的約束。
選擇字體較大的標題label。點擊Pin按鈕。 將top的值設置為15,左側為15和右側為15。同樣,確保取消選擇邊界約束,然后單擊添加3約束。
接下來,選擇另外一個標簽。為頂部、左側、右側和底部添加四個間距約束。單擊Pin按鈕并相應地添加約束。
一旦添加了約束,您將看到一些紅色的約束線,表示出現了一些布局問題。當某些約束是不明確的時候,可能會發生自動布局問題。要解決這些問題,請打開Document Outline,然后單擊紅色箭頭以查看問題列表。
Xcode會智能的嘗試為我們解決這些問題。只需點擊指示圖標,彈出窗口就會顯示可能的解決方案。出現提示時,單擊更改優先級以解決這些問題。
有時,您可能會看到黃色指示器。這表明有一些毛病。同樣,您可以讓Interface Builder通過updating the frame來匹配約束以解決這個問題。
酷!您已經創建了所有的自動布局約束。可以查看預覽并查看UI在各種設備上的外觀了。
注意:Interface Builder和Preview assistant在Xcode 8中仍然很差。有時,在實時預覽中渲染的UI與真實設備或模擬器上顯示的UI不完全相同。因此,如果您發現任何問題,請運行該項目并在模擬器中執行該應用程序。
視圖看起來好多了,image view完美顯示和對齊了。但是,還有幾個問題:
- 描述label在大尺寸設備的屏幕上垂直居中。我們希望它在標題label的正下方顯示。
- 某些型號的iPhone上標題和說明label只顯示了一部分。
我們來看看第一個問題。你還記得我們為描述label定義了幾個垂直空間限制嗎?描述label應距標題label 8點,距super view的底部15點。 為了滿足約束,iOS必須在更大的屏幕尺寸上擴展描述label。因此,描述文字就垂直居中了。
Content Hugging Priority
問你一個問題。為什么iOS不擴展標題label而是描述label呢?實際上有兩個選項讓iOS用于渲染滿足布局約束的UI。iOS如何實現這一選擇?
如果選擇說明label并轉到Size檢查器,則應注意,content hugging priority(vertical)被設置為250
。現在選擇標題label并查看其content hugging priority。你應該注意到它設置為251
。換句話說,它具有比說明label更高的content hugging priority(vertical)。
content hugging priority的value會幫助iOS確定應該放大哪個view。具有較高hugging優先級的view可以抵抗來自其內在的尺寸的增長。這里,標題label具有較高的hugging priority。這就是為什么iOS選擇讓說明label更大,而描述標簽的大小不變。
Editing the Relation of the Constraints
現在你應該對content hugging priority有一個基本的了解,讓我們繼續解決我們發現的第一個問題。
您可以隨時查看Size檢查器下特定組件的約束。選擇描述label,然后轉到Size檢查器。你將在Constraint部分找到約束的列表。
我們已經為這個label定義了四個間距約束。如果你查看這些約束,他們每個都有一個Equal關系。這意味著在渲染描述label時,label的每一邊應該與指定的約束完全相同。間隔不能大也不能小。
那么,如何修改約束,使描述label放置在標題label下,而不管屏幕尺寸如何?
我想你可能會知道答案。不用嚴格地將約束關系設置為等于,底部間隔約束應該有一些更大的靈活性。空間不需要等于15點。這只是我們想要的最小空間。該空間實際上可以隨著屏幕大小而增長。
現在,雙擊Bottom Space約束來編輯它。將關系從Equal更改為Greater than or Equal,一旦作出改變,空間問題應該是固定的。
好的,現在是研究第二個布局問題的時候了。
- 某些iPhone型號的標題和說明label會顯示一部分。
這個問題很容易解決。 我們可以讓較小屏幕尺寸的iOS設備顯示較小的字體。選擇標題label,然后轉到Attributes檢查器。將Autoshrink
選項的值設置為Minimium Font Size
。對描述label重復相同的步驟。
僅此而已。如果您預覽iPhone 4s / 5上的用戶界面或在這些設備上執行項目,則兩個label都將正確顯示。
Size Classes
正如我在本章開頭提到的,設計自適應用戶界面要分為兩個部分。到目前為止,我們已經創建了通用布局。基本布局足以支持大多數屏幕尺寸。該過程的第二部分是使用Size Classes來微調設計。
首先,什么是Size Classes?
一個Size Classes標識顯示空間的垂直(高)和水平(寬度)尺寸的相對量。iOS中有兩種類型的大小類:regular(常規)和compact(緊湊)。常規尺寸類別表示較大的屏幕空間,而緊湊尺寸類別表示較小的屏幕空間量。
通過使用Size Classes描述每個顯示維度,這導致有四個抽象設備:常規寬度 - 常規高度,常規寬度 - 緊湊高度,緊湊寬度 - 常規高度和緊湊寬度 - 緊湊的高度。
下表顯示了iOS設備及其相應的Size Classes。
要表征顯示環境,您必須同時指定水平Size Classes和垂直Size Classes。例如,iPad具有Regular水平(寬度)Size Classes和Regular垂直(高度)Size Classes。
使用基本布局,您可以使用Size Classes來提供布局特殊化,以覆蓋基本布局中的某些設計。例如,您可以更改采用緊湊高度 - 常規寬度Size的設備的Label字體大小。或者您可以更改按鈕的位置,特別是對于Regular Size。
請注意,縱向的所有iPhone都具有緊湊的寬度和常規高度。換句話說,您的用戶界面將顯示在iPhone 4上幾乎相同,就像在iPhone 7上一樣。
iPhone 6/7 Plus在橫向上具有Regular寬度和Compact的高度Size。這允許您創建一個完全不同于iPhone 7(或更低版本)的UI設計。
Using Size Classes for Font Customization
通過對Size Classes的一些基本了解,我們來看看如何使用它進行app 的布局專業化。
不用說,我們想讓iPhone的標題和描述label完美。當前的字體大小是iPhone的理想選擇,但對于iPad來說太小了。我們要做的是使iPad設備上的字體更大一些。
使用Size Classes,您現在可以調整特定屏幕尺寸的字體樣式。在這種情況下,我們要更改所有iPad型號的字體大小。在Size Classes方面,iPad設備默認為水平(寬度)的Regular和垂直(高度)的Regular Size Classes。
要設置此特定Size Classes的字體大小,請先在Interface Builder中切換到iPad設備,然后選擇標題label。在Attributes檢查器下,您應該看到字體設置旁邊的加號(+)按鈕。點擊+按鈕。將width和height都設為Regular,然后單擊Add Variation。
然后,您將看到一個新的Font選項條目,它專用于該特定的Size Classes。保持原始字體選項的大小不變,但將wR hR字體字段的大小更改為35點。
這將指明iOS在iPad上使用較大的第二種字體。對于iPhone設備,原始字體仍將用于顯示文本。現在選擇描述Label。再次,在Attributes檢查器下,單擊+按鈕,然后單擊Add Variation。 將wR hR
font字段的大小更改為25點。 在模擬器中查看預覽或測試應用程序。所有屏幕尺寸的布局看起來都很完美。
Using Size Classes to Customize a View
現在,用戶界面適合所有設備的縱向方向,他們如何對待橫向呢? 在Preview Assistant中,單擊設備上的旋轉按鈕(例如iPhone 4 inch)。或者您可以使用模擬器在橫向模式下查看UI。該視圖看起來不錯,但我認為有更好的方式在橫向上布局UI。
我將向您展示如何為視圖創建另一種設計,以利用更寬的屏幕尺寸。這是Size Classes的真正魅力。
更寬但更短的屏幕尺寸,最好同時呈現image view和Product Info View。每個占主視圖的50%。此屏幕顯示了iPhone 橫向的最終設計效果。
那么我們如何使用Size Classes創建兩個不同的UI呢?目前,我們只有一套適用于所有Size Classes的自動布局約束。為了創建兩個不同的UI,我們必須為每個UI使用兩組不同的布局約束:
- 對于iPad和iPhone(縱向),我們利用現有的布局和布局約束。
- 對于iPhone(橫向),我們重新啟動UI并定義一組新的布局約束。
首先,我們必須將現有的布局約束移動到Size Classes,以便當設備是縱向方向的iPad或iPhone時激活約束。在設備配置面板中,您將找到Vary for Traits
按鈕,它是用來設計用戶界面變體的。當您點擊按鈕時,會顯示一個popover,其中包含兩個選項供您選擇。在這種情況下,選擇高度,然后單擊Interface Builder中的任意位置。設備配置面板變為藍色,并顯示我們剛剛選擇的Size Classes的受影響設備。如果您單擊Varying 18 Regular Height Devices
選項,它將顯示所有受影響的設備,包括iPad和iPhone(人像)。
在變化模式下,您對畫布所做的任何更改僅適用于當前變體(或Size Classes)。因為我們希望將所有現有的限制遷移到這個變體。 在Document Outline視圖中選擇所有約束(按住Command并選擇每個約束)。接下來,轉到Size Classes檢查器,然后單擊+按鈕(安裝的選項旁邊)創建一個變體。
Interface Builder 會在Regular-height復選框中顯示Installed
。因為所有現存的約束都被應用在此Size Classes。取消選中Installed
的復選框,并選中hR Installed
的復選框。這意味著為iPad和iPhone(橫向)設備被激活所有選定的限制。最后,單擊完成更改以完成更改。
您如何知道約束是否僅應用于Regular-height設備?在設備配置窗格中,您可以將iPhone的方向更改為橫向。您應該發現,橫向的UI不再正確地呈現。而且,所有的約束都是灰色的,這意味著它們不屬于這個Size Classes。
現在是時候以橫向重新設計應用程序布局了,可以定義一組單獨的布局約束。
確保您在設備配置欄中選擇iPhone 6s設備和橫向。再次點擊Vary for Traits
按鈕。在popover中,選擇Height可以在橫向模式下為所有iPhone設備創建變體。
從現在開始,您所做的所有更改將僅適用于所選Size Classes(即Compact寬度和Compact高度)。
首先,選擇T恤image view。在Size檢查器中,將x
設置為0
,將y
設置為0
,將width
設置為333
,將height
設置為375
。在Attributes檢查器中,確保Clip to Bounds
選項已啟用。
接下來,選擇標題和描述Label。轉到Size檢查器,將x
設置為333
,將y
設置為0
,將width
設置為334
,將height
設置為375
。
最后,調整標題和描述標簽的大小,使其適合。這里我將兩個標簽的寬度改為303
點。您的布局應與圖1.31類似。
到目前為止,我們還沒有為此Size Classes定義任何布局約束。現在選擇T恤Image view
,然后點擊Pin按鈕。添加頂部、底部、左邊和右邊共四個空間約束。
接下來,選擇view,并為頂部,左側和右側添加三個空間約束。
其余的是添加標題和描述Label的布局約束。選擇標題Label,然后單擊Pin按鈕。添加頂部、底部、左側和右側的空間限制(參見圖1.34)。然后,為描述標簽添加兩個空間約束(左側和右側)。
由于我們希望這兩個視圖各占據屏幕的50%,因此可以從T恤image view拖動到容器視圖。當彈出面板出現時,選擇Equal Width
以添加約束。
一旦定義了約束,Interface Builder會檢測到一些歧義。單擊黃色圓圈按鈕以顯示問題列表,然后單擊每個指示符圖標,選擇更新框架以解決問題。最后,確保您單擊完成來保存更改。您的最終布局看起來與圖1.36相似。
如果您看到Document Outline中的約束,您應該看到啟用了一些約束,而有些則會變為灰色。正常顏色中的這些約束應用于當前Size Classes。在這種情況下,它是Compact寬度和Compact高度。如果切換到縱向模式,您將看到啟用了一組不同的約束。
這是您如何使用Size Classes應用到指定設備的布局約束集,并在Interface Builder中布置兩個不同的UI。如果您使用任何iPhone模擬器運行應用程序,您將看到兩個不同的用戶界面,用于縱向和橫向。另一件好事是,當視圖從縱向更改為橫向時,iOS會平滑過渡。 它看起來很棒,對吧?
Using Size Classes to Customize Constraints
希望您現在可以了解如何使用Size Classes自定義字體并查看特定Size Classes組合的設計。在這些自定義之上,您可以使用Size Classes來自定義特定的約束。
如果要將iPhone 6/7 Plus(橫向)的視圖設計更改為此視圖,但保持其他iPhone的設計完好無損?
您可以看到,標題和描述已被移動到視圖的右下角部分。顯然,我們必須定制標題標簽和其Super view之間的頂層間距約束。
讓我們看看如何做到這一點。
首先,將設備更改為iPhone 6s Plus,并在設備配置窗格中將方向設置為橫向。因為我們要在這個特定的方向上為這個設備應用特定的布局,請點擊Vary for Traits
按鈕,并選擇height和width選項。 Interface Builder應該指出這種更改只適用于Regular寬度和compact高度設備。接下來,選擇標題Label頂端的垂直空間約束。在屬性檢查器中,您應該看到常量字段。該值以點為單位定義垂直空間。因為我們要為這個特定的Size Classes增加這個值,請點擊+按鈕并確認添加變體。
這將為wR hC大小類創建一個附加字段。將值設置為150點。僅此而已。記住單擊Done Varying
按鈕保存更改。
您可以在Interface Builder中或使用模擬器預覽新的UI設計。在iPhone 6 / 6s / 7 Plus上,當設備處于橫向時,會出現新的UI。
總結
隨著Xcode 8中的改進的Interface Builder,Apple為開發人員提供了一個更好的工具來在iOS應用程序中構建自適應UI。我希望你已經了解了Size Classes的概念,并且知道如何使用它們來創建自適應布局。
自適應布局是自iOS 8以來引入的最重要的概念之一。開發人員只有單個設備和屏幕大小才能設計出來。如果您要構建下一個應用程序,請確保掌握Size Classes和自動布局的概念,并使您的應用適應多種屏幕尺寸和方向。 應用程序設計的未來更有可能適應。所以準備好了!
作為參考,您可以從http://www.appcoda.com/resources/swift3/AdaptiveUIDemo.zip下載Xcode項目。