Auto Layout

Swift 新手系列之三:Auto Layout 介紹

Swift 新手系列之三:Auto Layout 介紹
Swift 新手系列之三:Auto Layout 介紹
In: Auto Layout, Swift 程式語言
本文摘自《iOS 9 App程式設計實力超進化實戰攻略》一書,博碩授權轉載。這是Swift 新手系列的第三篇文章,如果你還沒閱讀之前所發表的文章,可從這裡開始。

Auto Layout是一個以約束條件為基礎的佈局系統(constraint-based layout system),它讓開發者能夠開發一個能自我調整型的UI,可以依照螢幕的尺寸以及裝置的方向來調整。有些初學者會覺得這個部分很難,而儘量避免去使用它,但請相信我,當你習慣之後,Auto Layout會成為你之後無比仰賴、非常重要的App開發工具。

自從iPhone 6與iPhone 6 Plus釋出之後,Apple的iPhone有了不同的螢幕尺寸,包含3.5英吋、4英吋、4.7英吋與5.5英吋顯示器。如果沒有使用Auto Layout,想建立一個支援各種螢幕解析度的App將會非常困難。然而,從Xcode 6開始,使用Auto Layout來設計使用者介面,已經是不可避免了。這也是我想要在本書一開始的地方,先教你學會Auto Layout,而不是直接寫App程式碼的原因。本章以及接下來的章節,我會協助你建立「設計自適應使用者介面(adaptive user interface)」的紮實基礎。

提示: Auto Layout不如一些開發者所想像的困難。如果你能了解基礎部分,你便能使用Auto Layout來對不同型態的iOS裝置建立複雜的使用者介面。

為什麼需要Auto Layout?

這裡舉個例子,或許你就會對我們為何要使用Auto Layout更有概念了。打開之前開發的Hello World專案,以iPhone 6模擬器(或者是iPhone 4s)取代在iPhone 5s模擬器中執行程式。結果會如下圖所示,在iPhone 6中的按鈕沒有置中。

App的UI在iPhone 4s、iPhone 6(4.7英吋)與iPhone 6 Plus(5.5英吋)看起來不太一樣

我們來試試其他狀況。

點選「Stop」按鈕,並選擇iPhone 5s模擬器來執行App。在模擬器推出後,在選單上點選「Hardware→Rotate Left(或Rotate Right)」,此時裝置便會轉為橫向(landscape)模式。另外,你可以按下command鍵+左右鍵,將裝置往側邊轉向。而Hello World按鈕位置還是沒有置中。

為什麼會這樣呢?出了什麼問題嗎?

首先,你應該知道iPhone 5s、iPhone 6以及iPhone 6 Plus螢幕尺寸不同。以iPhone 5/5s來說,直立(portrait)模式的情況下,水平方向是320點(640像素),垂直方向為568點(1136像素)。而iPhone 4s,螢幕的大小則分別為320點(640像素)以及480點(960像素)。至於iPhone 6,螢幕大小在水平方向是375點(或750像素),垂直方向則是667點(或1334像素)。

為什麼是點(Points)而不是像素(Pixels)呢?

回到2007年,Apple推出的最原始iPhone,3.5英吋、320×480像素的畫面解析度的顯示器,即水平方向是320像素,垂直方向是480像素。Apple把這螢幕解析度繼續沿用到iPhone 3G與iPhone 3GS。很明顯的,如果我們在那時候建造一個App,一個點相對應一個像素。之後Apple推出了iPhone 4搭配視網膜(retina)顯示器。畫面的解析度變成兩倍到640×960像素。也就是一個點相對應兩個像素。

這個點系統(point system),讓開發者輕鬆不少,不論螢幕解析度如何變化(例如解析度再變兩倍至1280×1920),我們要面對的依然是點與最基本的解析度(也就是iPhone 4/4s是320×480,iPhone 5/5s 是320×568),而這些點與像素間的轉換工作,則由iOS處理。

如果沒有使用Auto Layout,我們在Storyboard所佈局的按鈕位置是固定的,換句話說,我們將按鈕框架的原點以程式定在(120,269),因此不論你是使用3.5英吋或4英吋模擬器,iOS會將按鈕畫在特定位置。下圖顯示了這個按鈕在不同裝置下的原始位置。因此這說明了Hello World按鈕只能在iPhone 5/5s置中,在其他裝置以及橫向模式中都會偏移的原因。

很清楚地,我們要求App在所有iPhone機種,以及不管是在橫向或直向模式都要看起來一致才是。此即我們需要學習Auto Layout的原因,也是針對我們剛討論到畫面佈局問題的答案。

iPhone 6、iPhone 5/5s與4s的螢幕尺寸

Auto Layout與約束條件有關

如前所述,Auto Layout是以約束條件(constraints)為基礎的佈局系統。它讓開發者建立自我調整型UI,可以因應螢幕尺寸與方向的不同。嗯,聽起來好像不錯,但是什麼是「以約束條件為基礎的佈局」?讓我以白話來表達好了,再次以Hello World按鈕為例,如果你要把它擺在視圖中間,你會如何描述它的位置?也許你會這樣描述:「不管螢幕解析度與方向為何,按鈕應該是在水平以及垂直方向的中間位置。」

這邊就會得到兩個約束條件:

  • 水平置中。
  • 垂直置中。

這些約束條件顯示了按鈕在介面中的佈局。

這些約束條件顯示了按鈕在介面中的佈局,Auto Layout是和約束條件有關。如果把約束條件以文字表達,這些約束條件是以數學形式來呈現。舉例來說,當你定義一個按鈕的位置,你可能會這樣說「左側邊緣距離內容視圖的邊緣是30點」,這會被轉換為button.left=(container.left+30)。

你知道這樣就夠了,很幸運地,我們不需要去跟公式打交道。

好了,Auto Layout的理論就談到這裡。現在我們來了解如何在介面建構器中定義佈局約束條件,以讓Hello World 按鈕置中。

重新啟動尺寸類別

首先,打開Hello World專案的Main.storyboard。在加入佈局約束條件之前,我們重新啟動尺寸類別(Size Classes)。我們在建立第一個App 時關掉這個功能。在檔案檢閱器(File Inspector)中,勾選「Use Size Classes」功能來啟動它。提示出現後,點選「Enable Size Classes」來確認這個變更。

自由形態的畫面

這個視圖控制器恢復至自由形態(freeform)的設計畫面(canvas)。按鈕現在偏移至左方,如圖所示,你可以將它拖曳至視圖的中心。

為什麼需要做這些變更呢?雖然你仍然可不使用尺寸類別來定義約束條件,但我想要你開始使用標準畫面來設計UI。要熟悉自由形態的設計畫面需要點時間,但是你需要使用它來設計自適應UI,以配合所有螢幕的大小。那麼,開始來使用它吧。

提示: 你可能想知道「尺寸類別」的意義為何。先不管它,並將重點放在Auto Layout 的學習。我在稍後的章節會討論到它。

利用Auto Layout將按鈕置中

Xcode提供了兩種定義Auto Layout的方式:

  1. Auto Layout選單。
  2. Control鍵加上拖曳。

我們將繼續示範這兩種方法,先從Auto Layout選單開始。在介面建構器的編輯器右下角處,你會找到幾個按鈕。這些按鈕就是佈局選單,你可以使用它們來定義各種型態的佈局約束條件。

Auto Layout選單

每一個按鈕有它自己的功能:

  • Align – 建立對齊的約束條件,例如:對齊兩個視圖的左側。
  • Pin – 建立間距的約束條件,例如:訂出UI控制的寬度。
  • Issues – 解決佈局問題。
  • Stack – 視圖嵌入至堆疊視圖(stack view)。堆疊視圖是Xcode 7的新功能。我們在下一章會進一步討論它。

如同之前所說明的,要將Hello world按鈕置中,你必須要定義兩個約束條件:center horizontally與center vertically,這兩個約束條件都相對於該視圖。

要建立約束條件,我們將會使用Align選項。首先,選取在介面建構器的按鈕,然後在佈局選單中點選「Align」按鈕。在彈出式選單中,勾選「Vertical center in container」與「Horizontal center in container」,接著點選「Add 2 Constraints」按鈕。

使用Align按鈕來加入約束條件

此時你應該會見到一組約束線。如果你在文件大綱(Document Outline)視圖展開Constraints選項,你會見到兩個按鈕的新約束條件。這些約束條件可以確保按鈕總是保持在視圖中心。另外,你也可以在尺寸檢閱器(Size Inspector)中檢視這些約束條件。

在文件大綱與尺寸檢閱器檢視約束條件

提示: 當你的視圖的佈局設置正確且沒有模糊的話,這些約束線是以藍色來呈現。

好的,準備再一次測試App,你可以點選「Run」按鈕,分別以iPhone 6/6 Plus來啟動App,現在按鈕應該能完美置中對齊了。

解決佈局約束條件問題

剛剛我們所設定的佈局約束條件很完美,不過有時候並非都能如此順利,Xcode可以聰明的偵測出任何約束條件的問題。

試著拖曳Hello World按鈕到畫面的右下方,Xcode可以立刻偵測出一些佈局問題,同時相對應的約束線會變成橘色,指出錯位的項目。

介面建構器使用橘/紅線來指出Auto Layout問題

當你建立一個含糊不清或有衝突的約束條件時,就會產生自動佈局的問題(Auto Layout Issue),這裡我們設定按鈕在水平和垂直的位置都是置中,不過,按鈕現在置於視圖的左下角,Xcode發現了這個問題,因而產生困惑,因此就使用橘色線來指出佈局問題。

當有任何的佈局問題,如下圖所示,文件大綱視圖會顯示出一個紅/橘色的揭露箭頭(disclosure arrow),按下這個揭露箭頭,你會看見問題清單。介面建構器可以聰明地幫助我們解決佈局問題,點選問題旁邊的指示圖標,此時會出現幾個解決方案,以這個例子來說,選取「Update Frame」選項,然後點選「Fix Misplacement」按鈕。按鈕便會移到視圖中間。

解決錯位問題

這個問題是我自己動手來移動所產生的。我只是想要示範如何發現佈局問題並修復它。當你跟著本書的範例來練習時,很有可能會面臨到類似的問題。你應該要知道如何輕易且快速的解決佈局問題。

Storyboard預覽

至目前為止,我們只有使用模擬器來測試UI的變更,雖然Xcode內建的模擬器很好用,然而它並不是試驗App UI的最好方法。在Xcode 7中,它提供開發者預覽(Preview)功能,讓開發者可以直接在Xcode中可以取得UI的預覽圖。

訣竅: 如果你在筆電上使用Xcode,請至Xcode選單,並點選「View→Navigators→Hide Navigator」(或只要按下command+0鍵),就可以隱藏專案導覽器,讓Storyboard的編輯空間增大。

在介面建構器中,打開Assistant彈出式選單->Preview(1),按住option鍵,然後點選「Main.storyboard(Preview)」。你可以參考下圖所示的操作步驟。

Assistant彈出式選單

Xcode會在助理編輯器(Assistant Editor)中顯示你的App UI的預覽。預設它是顯示iPhone 4英吋裝置的預覽圖。你可以點擊助理編輯器左下角的「+」按鈕來加入iOS裝置(例如:iPhone 4.7英吋)來閱覽。如果你想觀察在橫向方向的顯示狀態,只要點選旋轉按鈕即可。這個預覽功能對於設計使用者介面而言是非常實用的,你可以將Storyboard做些改變(例如:加入另一個按鈕至視圖中),同時在預覽圖中觀察有何相對的變化。這可以讓你節省大量將App 載入模擬器的時間,尤其是你只是想要做些簡單的UI變化而已。

助理編輯器中的Storyboard預覽圖

訣竅: 當你在預覽助理中加入更多的裝置,Xcode可能無法同時容納各種大小的裝置在同一畫面中。如果你使用觸控板(trackpad),你可以使用兩根手指在預覽畫面上左右滑動來導覽。如果你使用的是滑鼠加上滾輪,則只要按住Shift鍵來水平滾動即可。

加上一個標籤

現在你對Auto Layout應該有了些概念,我們試著加上一個標籤到視圖的右下方,並了解該如何定義這個標籤的約束條件。在設計你的UI時,你需要時時記得必須讓它能夠相容各種尺寸。

在介面建構器中,從元件庫(Object Library)拖曳一個標籤,並將之置放到視圖的右下角。在標籤上點擊兩下,將標題改為「Welcome to Auto Layout」,或者任何你想放的文字皆可。

加入一個標籤至視圖中

如果助理編輯器中的預覽圖已經打開,你應該能夠即時見到UI的更新(如下圖所示),如你所見,沒有使用Auto Layout的情況下,此App無法正確地在所有的裝置上顯示標籤。

標籤無法正確顯示

那麼要怎麼解決這個問題呢?很明顯的,我們必須要設定一些約束條件來讓它能夠正確運作。問題是:我們要加入什麼約束條件呢?

我們用文字來描述這個標籤的需求。你可以像這樣來描述:「這個標籤應該要至於視圖的右下角」。

這樣敘述沒有問題,不過還不夠精確。更精確的描述這個標籤位置的敘述如下:「這個標籤位於距離視圖右側邊距0點的位置,且距離視圖底部20點」。

這樣好多了。當你精確地描述一個項目的位置,你可以很容易想到佈局約束條件。這個標籤的約束條件是:

  1. 這個標籤距離視圖右側邊距為0點。
  2. 標籤距離視圖底部是20點。

在Auto Layout中,我們以這樣的約束條件作為間距約束條件。想要建立這些間距約束條件,你可以使用佈局按鈕中的「Pin」按鈕。不過這次,我們使用Control鍵加上拖曳的方法來應用Auto Layout。在介面建構器,你可以按住Control鍵,從一個項目拖曳至自己,或者拖曳至另一個你想要加入約束條件的項目。要加入第一個間距約束條件,按住Control鍵並從標籤拖曳至右側,直到視圖變成藍色為止。現在放開按鈕,你會見到一個彈出式選單,顯示一串約束條件選項。選取「Trailing space to container margin」來加入從標籤至視圖右側邊距的間距約束條件。

使用Ctrl鍵加上拖曳來加入第一個約束條件

在文件大綱視圖中,你會見到新的約束條件。介面建構器現在以紅色顯示約束線,並指出還有一些漏掉的約束條件。這很正常,因為我們還沒有定義第二個約束條件。

現在按住Control鍵,從標籤拖曳至視圖底部。釋放按鈕並在彈出式選單中選取「Vertical Spacing to Bottom Layout Guide」,如此一來,便建立了一個從標籤至視圖底部佈局導引的間距約束條件。

使用Ctrl鍵加上拖曳來加入第二個約束條件

在你加入了這兩個約束條件後,所有的線都變成藍實線。當你預覽UI或者在模擬器中執行App,這個標籤在所有螢幕尺寸中應該都能夠正確顯示,即使在橫向模式也沒問題了。

現在UI能夠支援所有不同尺寸的螢幕

頂部與底部佈局導引

你也許想了解什麼是底部佈局導引(Bottom Layout Guide)。一般來說,底部佈局導引位置是以視圖底部為準,如同範例中所示。不過底部佈局導引也會變動,有時候我們嵌入一個標籤欄(tab bar,之後的章節會談到)至視圖控制器中,底部佈局導引位置則會變成標籤欄頂部。

至於頂部佈局導引(Top Layout Guide),則從視圖最上方往下設定20點距離,也就是狀態欄(status bar)的高度,同樣的,它也是會變動,當視圖控制器中嵌入一個導覽控制器(Navigation Controller),導引位置則會改為導覽列(navigation bar)的下方。

頂部佈局導引

如果你仍然覺得困惑的話,最好的方式就是在介面建構器中檢查一下導引線。在文件大綱中,選取「Top Layout Guide」或者「Bottom Layout Guide」,介面建構器會以藍色線來顯示導引線。

編輯約束條件

這個標籤現在距離視圖右側邊距為0點。那麼若你想要在標籤和視圖的右側邊距間加入一些間距呢?介面建構器提供一個傳統的方式來編輯一個約束條件的常數。

你只須在文件大綱視圖挑選約束條件。在這裡,你應該選取「trailingMargin」約束條件。在屬性檢閱器中,你可以找到這個約束條件的屬性,包括了關聯性(relation)、常數(constant)以及優先權(priority)。常數現在是設定為「0」,你可以將它修改為「20」來加入一些間距。

編輯約束條件

你的作業

現在,我希望你對如何佈局你的App UI,並且讓它能夠相容各種尺寸有一些基本概念了。這是你的第一個作業,過程其實非常簡單,我只需要你加入另一個標籤,命名為「My First App」至視圖中,並加上以下的約束條件:

  • 這個新標籤應該距離頂部佈局導引40點。
  • 這個新標籤應該要能水平置中。

另外,你也可以加大標籤的字型大小為30點,如果要變更字型大小,你只要打開屬性檢閱器並編輯Font選項。結果如下圖所示。

所期望的App佈局

本章小結

本章我們學到了Auto Layout的基礎內容,是的,這只是基礎而已,因為我不想學習Auto Layout後就馬上把你嚇跑。當我們更深入建立真實的App時,我們將繼續探討Auto Layout的其他功能。

多數的初學者(即使是一些有經驗的iOS程式設計師)都會避免使用Auto Layout,因為它容易讓人搞不清楚。當你已完全了解我在本章中所介紹的內容時,你即將步入成為一個優秀iOS開發者的道路。

最早的iPhone是在2007年推出,經過這些年後,iOS的發展已經有了許多的變化與進步,不像以前你的App只能在3.5英吋無Retina螢幕的裝置上運作,現在你必須滿足不同螢幕解析度及畫面尺寸,這也是為何我以整個章節的篇幅介紹Auto Layout的緣故。

本文摘自《iOS 9 App程式設計實力超進化實戰攻略》一書,博碩授權轉載。如果你想更深入學習Swift程式設計,可到這裡瞭解此書內容。
譯者簡介:王豪勳 -渥合數位服務創辦人,畢業於台灣大學應用力學研究所,曾在半導體產業服務多年,近年來專注於協助客戶進行App軟體以及網站開發,平常致力於研究各式最軟硬體技術,擁有多本譯作。
作者
Simon Ng
軟體工程師,AppCoda 創辦人。著有《iOS 16 App 程式設計實戰心法》、《iOS 16 App程式設計進階攻略》以及《精通SwiftUI》。曾任職於HSBC, FedEx等跨國企業,專責軟體開發、系統設計。2012年創立AppCoda技術部落格,定期發表iOS程式教學文章。現時專注發展AppCoda業務,致力於iOS程式教學、產品設計及開發。你可以到推特與我聯絡。
評論
更多來自 AppCoda 中文版
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。