原文:http://www.raywenderlich.com/113768/adaptive-layout-tutorial-in-ios-9-getting-started譯者:@featherJ
【轉(zhuǎn)載請寫明出處,多謝!】
更新日期:2015/9/22:教程為iOS 9, Xcode 7 以及 Swift 2由Sam Davies更新。
自適應(yīng)布局的介紹引發(fā)了ios開發(fā)者們的一個巨大的轉(zhuǎn)變。現(xiàn)在,當你設(shè)計你的app的時候,你已經(jīng)可以使用一個單獨布局并且不添加任何設(shè)備適配的代碼,使你的應(yīng)用工作到所有的iOS設(shè)備上了!
這一片教程將為你介紹自適應(yīng)布局。你將會學(xué)習(xí)到關(guān)于通用的storyboards,size classes,布局(layout)以及字體(font)自定義以及超有用的預(yù)覽助手編輯器(Preview Assistant Editor)。
在本教程中,你將從頭開始創(chuàng)建一個簡單的天氣應(yīng)用的用戶界面。如果你并不是特別熱衷于自動布局也別擔心,教程的第一部分將提供一步一步的講解讓你通過自動布局來創(chuàng)建一個用戶界面。你將會驚奇的發(fā)現(xiàn)你可以不用寫一行代碼就完成這個界面。
通用的Storyboard
通用的Storyboard是你開始Adaptive Layout (自適應(yīng)布局)?之旅的第一步。一個相同的Storyboard可以同時被用于iPad和iPhone設(shè)備。你并不需要為每一個設(shè)備存留一份Storyboard。
打開Xcode并且選擇Create a new Xcode project,來創(chuàng)建一個新的Xcode項目:
選擇 iOS -> Application -> Single View Application,然后點擊下一步:
設(shè)置?Product Name(項目名)?為 AdaptiveWeather,Language(語言)?設(shè)置為Swift,同時設(shè)置Devices(設(shè)備) 為Universal(通用):
當項目開啟之后,查看?Project Navigator(項目導(dǎo)航)?你講會看到這個單獨的storyboard文件:
Main.storyboard是一個為所有設(shè)備準備的單獨的文件。打開這個storyboard,你將會看到他包含一個單獨的?view controller(視圖控制器),但是他的尺寸可能有些古怪:
好吧,他現(xiàn)在是個正方形!如果 Storyboard 在 Xcode 的編輯過程中匹配到某個目標設(shè)備的屏幕尺寸那就顯然不太符合“一個Storyboard作用所有設(shè)備”的原則了,所以現(xiàn)在Storyboard是用一個抽象的尺寸替代的。
選擇storyboard文件,打開File Inspector(文件檢查器) ,你將可以看到use Size Classes的選項,在你的項目中,勾上它:
這個選項默認為所有新的iOS工程勾選。你可以在將舊項目升級到新項目的過程中手動激活這個勾選。
構(gòu)建你的Storyboard
下面開始這個小節(jié),打開Main.storyboard文件,從Object Library中拖出一個Image
View到你的view controller(視圖控制器)畫布中。在 Size Inspector
(尺寸檢查器)中設(shè)置X的值為150,Y的值為20。同時設(shè)置Width(寬度)為300,Height(高度)為265。
下一步,從Object Library中拖出一個View放到Image View的下面。在 Size Inspector (尺寸檢查器)中設(shè)置X為150,Y為315,同時設(shè)置Width(寬度)為300,Height(高度)為265。
選擇你剛剛添加的View,打開Identity Inspector(身份檢查器),編輯Document下的Label的內(nèi)容為TextContainer:
因為剛剛拖出來的View和View
Controller(視圖控制器)的背景色默認都為白色,所以可能不太容易找到這個View。下面我們修改一下他們的背景色。選擇View
Controller控件,打開Attributes Inspector(屬性查看器)設(shè)置Background(背景色)為#4AABF7。
(其實隨便一個顏色就好,因為Xcode的顏色選擇器沒沒法設(shè)置16進制顏色值)。
下一步選擇選擇TextContainer,設(shè)置它的Background(背景色)為#3780BA。
接下來你的視圖控制器看上去將是下面截圖的樣紙:
Image View和TextContainer這兩個控件是view controller的子項,下面我們將為他們設(shè)置布局約束。
添加布局約束
選擇Image View并且點擊在下方自動布局欄中的Align按鈕,勾選中Horizontal Center in Container(容器中的水平中心),設(shè)置值為0,然后點擊Add 1 Constraint(添加一個約束)。
接下來,點擊Pin按鈕,添加一個頂部距離最近控件的距離為0的約束,如下圖:
你添加的這些約束將使得Image View有一個距離頂部和距離水平中心的固定距離。現(xiàn)在我們將設(shè)置 Image
View 和 TextContainer 之間的間距。在 Image
View上按住Control鍵并拖拽至TextContainer然后釋放鼠標,如下圖:
接下來將顯示一個約束內(nèi)容的菜單,選擇Vertical Spacing(垂直間距):
這個約束用于決定從 Image View 控件的底部到TextContainer控件的頂部的距離。
選擇你的 Image View 然后打開 Size Inspector(尺寸檢查器) 你會看到如下:
你會看到你已經(jīng)添加了三個約束到布局上了,你可以在Size
Inspector(尺寸檢查器)內(nèi)簡單的配置內(nèi)一個約束的參數(shù)。在Bottom Space To:
TextContainer約束上,點擊Edit(編輯)按鈕,將會彈出一個配置約束屬性的對話框,設(shè)置Constant的值為20:
然后點擊對話框外部,關(guān)閉這個對話框。
現(xiàn)在已經(jīng)將Image View和TextContainer之間的間隔設(shè)置為了20點。現(xiàn)在你還需要設(shè)置TextContainer另外三個邊的約束。
選擇TextContainer然后點擊底部欄的Pin圖標,在Spacing to nearest
neighbor(約束到最近部件)部分,設(shè)置left,right和bottom的間隔為0。保持Constrain to
margins(約束到邊緣)復(fù)選框為未選中狀態(tài),如果勾選這個復(fù)選框,將會移除掉視圖的外邊距。
作為參考,這個對話框應(yīng)該是如下的樣紙:
點擊Add 3 constraints(添加3個約束)按鈕將這些新的約束添加到TextContainer上。這樣便設(shè)置好了TextContainer距離容器的左右下邊距的約束。
接下來你的storyboard看上去應(yīng)該是如同下面截圖的樣紙:
你應(yīng)該已經(jīng)注意到了在你的視圖上有一些橘黃色的和一些紅色的約束。這表示你的約束存在一些問題需要你注意一下。你可以通過storyboard的自動更新到frames的功能來調(diào)整這些約束,但是如果你現(xiàn)在這么做,Image View的尺寸將收縮為0。
這是因為現(xiàn)在這個Image View內(nèi)部還沒有任何內(nèi)容,這意味著他的內(nèi)部高度和寬度都為0。如果沒有物理寬高的約束,自動布局是會依賴于部件的內(nèi)部尺寸來決定他的寬度和高度的。
接下來,在Project Navigator(項目導(dǎo)航)中打開Images.xcassets文件。下載cloud_images.zip文件并解壓,你將會發(fā)現(xiàn)里面有三個圖片。 在Finder中選擇這個三個圖片,并且拖拽他們到空的資源目錄中:
他們將創(chuàng)建一個新的圖像集,并且已經(jīng)分配好了這三個圖像:
現(xiàn)在你可以應(yīng)用你的圖像集到Image View了,返回Main.storyboard 文件并選擇Image
View。切換到Attributes Inspector(屬性查看器),在Image輸入框中輸入cloud然后選擇View -> Mode
為Aspect Fit (這幾個選項試一下就知道大概是做什么的) 如下圖:
接下來你的storyboard將看上去如下圖這樣:
但是目前仍有一些橘紅色的約束,我們還有很多的事情要去做。 首先,在Document Outline(文檔大綱中)選擇View Controller的View:
然后點擊在底欄中選擇 Resolve Auto Layout Issues 圖標按鈕。然后在彈出的對話框中選擇All Views -> Update Frames:
然后 View Controller 內(nèi)的空間將會自動重新排列,以解決之前不太符合的約束。接下來你看到的storyboard將會是如下的樣紙:
預(yù)覽助手編輯器
通常你需要構(gòu)建并在 iPad, iPhone 4s, 5s, 6 和 6
Plus設(shè)備上運行你的項目,并在每一個旋轉(zhuǎn)方向上檢查,以保證這個新的通用storyboard。這個過程是很麻煩的,但是Xcode6提供了一個更好
的選擇即新的Preview Assistant Editor(預(yù)覽助手編輯器)。
打開Main.storyboard文件,然后點擊View -> Assistant Editor
-> Show Assistant Editor。這將編輯區(qū)分成兩部分。在Jump
Bar中點擊Automatic,然后在彈出的下拉菜單中選擇Preview,然后選擇Main.storyboard。
然后新的預(yù)覽編輯器將呈現(xiàn)出一個storyboard在4英寸iPhone的屏幕的效果。如下:
通過點擊預(yù)覽框底部的 rotation icon(旋轉(zhuǎn)圖標)可以旋轉(zhuǎn)預(yù)覽效果。現(xiàn)在預(yù)覽效果將旋轉(zhuǎn)為橫向:
這是一個模擬多設(shè)備的巨大的改進,但是,還有更多!點擊預(yù)覽編輯器左下角的+按鈕將可以得到更多的可用預(yù)覽:
在列表中選擇5.5英寸的iPhone和iPad,以將他們添加到預(yù)覽區(qū)中一起顯示:
注意到在橫向的iPhone中有什么奇怪的效果了么?是的,云圖片實在太大了!為了修復(fù)這個問題,我們將為Image View加入一個新的約束。
返回storyboard,在Image View上按下Control并拖拽到View Controller的View中釋放鼠標來創(chuàng)建一個新的約束,在彈出的菜單中選擇Equal Heights(等高):
現(xiàn)在在storyboard中又出現(xiàn)了紅色的約束。這是因為你剛剛添加的約束與現(xiàn)有的約束發(fā)生了沖突,當View Controller的View保持垂直方向的時候,不可能有相同的高度。
在 Document Outline (文檔大綱) 中選擇剛剛添加的約束,然后打開Attributes
Inspector (屬性檢查器)。如果第一項并沒有被設(shè)置為cloud_small.Height,那么選擇第一項的下拉菜單中的Reverse
First and Second Item選項。
然后,設(shè)置Relation為 Less Than or Equal,設(shè)置 Multiplier (乘數(shù)) 為0.40。如下圖:
這意味著,云的圖片將在是原始尺寸,或者當高度低于屏幕的40%的時候變小。
你會發(fā)現(xiàn)預(yù)覽面板的效果會在你更新約束的時候自動刷新。效果如下:
那么此時是否可以讓多個設(shè)備的預(yù)覽效果同時自動更新呢?答案是肯定的,新的Preview Assistant Editor (預(yù)覽助手編輯器)真的很有用!
要把現(xiàn)在的效果做成一個天氣的app,你還需要添加一些標簽,用于顯示城市名和當前的溫度。
為TextContainer添加內(nèi)容
打開Main.storyboard文件,從Object Library中拖兩個Label(文本標簽)到TextContainer視圖中,然后把他們排列成如下的樣紙:
選擇上面的一個標簽然后通過Align和Pin菜單設(shè)置它的水平位置到中心,然后再添加一個 top spacing to nearest neighbor為10的約束,如下圖所示:
下一步,選擇 Attributes Inspector(屬性檢查器),設(shè)置Text (文本)為Cupertino,Color(顏色)為White(白色),然后設(shè)置font(字體)為System, Thin同時Size為150。
現(xiàn)在你可能會發(fā)現(xiàn)文字變得無法辨認,這是因為Label的框架的緣故,我們很快就可以解決這個問題了。
現(xiàn)在選擇另一個Label,同時依舊通過Align和Pin菜單設(shè)置它的水平位置到中心,然后設(shè)置bottom space to nearest neighbor為10,檢查Size Inspector(尺寸檢查器),是否和下圖一致:
通過Attributes Inspector(屬性檢查器),設(shè)置Text為28C,然后設(shè)置Color為White,字體為System, Thin并且字號為250。
現(xiàn)在你可以解決剛才我們提到的由于標簽的框架導(dǎo)致的問題了。選擇View Controller 的
view,點擊storyboard底欄的Resolve Auto Layout Issues按鈕,然后選擇All Views ->
Update Frames。然后你將會看到storyboard更新為如下圖:
現(xiàn)在你發(fā)現(xiàn)兩個Label發(fā)生了重疊,看上去并不是我們所期待的樣子。但是,在我們修復(fù)任何問題之前,可以發(fā)現(xiàn)在預(yù)覽區(qū)中iPad版本看上去是好的:
可以預(yù)見的,iPhone的字號太大了:
我們將在下一個章節(jié)中修復(fù)這個尺寸的問題。
Size Classes
通用的storyboard是很給力的,但是已經(jīng)發(fā)現(xiàn)為所有的設(shè)備創(chuàng)建一個通用的布局已經(jīng)有些挑戰(zhàn)性了。然而,Adaptive Layout (自適應(yīng)布局)有更多的工具可以用來解決這些問題。
自適應(yīng)布局的核心概念之一便是size classes。Size Class是一個可以應(yīng)用于任何能夠在橫向或縱向進行顯示的視圖或者視圖控制器的屬性。
Xcode提供了兩種Size Classe:Regular(正常的)和Compact(緊湊)。盡管他們與視圖的物理尺寸是有關(guān)聯(lián)的,但是他們?nèi)匀豢梢杂靡环N語義上的尺寸呈現(xiàn)出來。
下面這個表格列出了size classe可以應(yīng)用的不同的設(shè)備和方向:
這些都是設(shè)備可以應(yīng)用的size classe。當然,你也可以在視圖結(jié)構(gòu)中的任意一部分中重寫size classe。這在比屏幕要小的容器中是相當有用的。
Size Classes 和你
這對你和你所設(shè)計的app到底意味著什么呢?盡管你的app是有size class的,但是你構(gòu)建的布局和size class是無關(guān)的,也就是說,你的布局需要為所有的size class進行調(diào)整。
在涉及到自適應(yīng)布局的設(shè)計的時候,這是很重要的一點。首先你需要創(chuàng)建一個基本的布局,然后在為每一個特殊的size
class去調(diào)整你需要的size class。這里,不是要把每一個size
class視為一個全新的東西去從頭設(shè)計。你可以把一個自適應(yīng)布局,想象成是一個樹狀的層次結(jié)構(gòu),在這個層次中,你可以把所有通用的設(shè)計放在父級節(jié)點中,
然后把需要單獨處理的部分放到子級的size class中。
目前為止,我們幾乎還沒有提到具體的設(shè)備布局適配問題。這是因為在自適應(yīng)布局的核心概念中,size class是抽象于具體設(shè)備的。意思是說,一個支持自適應(yīng)布局的視圖,可以在一個完整的屏幕中顯示,也可以被包含與另一個視圖控制器中顯示。
這樣做也是有益于蘋果的,這樣即使設(shè)備的屏幕范圍擴大了,也不需要迫使程序的開發(fā)者或者設(shè)計人員從新設(shè)計他們的app。
現(xiàn)在我們將使用size class來iPhone平放時候的布局,已解決目前的用戶界面看起來不好看的問題。
使用Class Sizes
點擊底欄的w Any h Any按鈕,你將看見size class的選擇器彈出:
在這里你可以通過選擇表格單元的形式來選擇用哪種size class來呈現(xiàn)。一共有九個可能的選項:你有三個縱向的選擇和三中水平選擇(any(任意),regular(正常),compact(緊湊)),那么總共有九種size class可供選擇。
注意:這里在命名上有些輕微的差異。Size class
通常是和horizontal(水平)與vertical(垂直)聯(lián)系起來的。但是,在這里卻用了width和height,在這里width 等同于
horizontal,而height 等同于 vertical。
我們現(xiàn)在的布局還不能很好的工作于compact(緊湊)的高上。為了解決這個問題,我們將選擇 Any(任何)Width | Compact(緊湊) Height的size class:
于是你將立刻發(fā)現(xiàn)在編輯器中的兩個變化:
畫布的形狀變成了一個新的size class。
底欄變成了藍色。這表示,你現(xiàn)在正在工作于一個特定的size class布局上。
為了改變布局,我們需要暫時的改變一些約束。在自適應(yīng)布局中有這樣兩個術(shù)語installing(安裝)和uninstalling(卸載)。一個約束被安裝表示它處于激活狀態(tài)了,反之,如果約束被卸載則表示它在當前的size class中不在起作用。
然后我們通過點擊的方式選擇Image View,打開Size Inspector(尺寸檢查器)。你可以看到指定視圖的所有約束:
通過單擊的方式選擇Align Center X to: Superview約束,然后按Delete鍵從當前的size class中卸載這個約束。這個約束邊立即從當前的欄中消失了,同時在Document Outline(文檔大綱)中變?yōu)榱嘶疑?/p>
注意:你可以在Size Inspector(尺寸檢查器)中This Size Class選項切換為All來查看已經(jīng)被卸載的約束。
在Size Inspector(尺寸檢查器)中雙擊被卸載的約束,你可以看到在末尾顯示的這部分:
這表示這個約束在基本布局中是被安裝的,但是在Any Width | Compact Height布局中是沒有被安裝的。
接下來我們用同樣的方式卸載掉image view上的其他三個約束。然后你的document outline(文檔大綱) 和image view的Size Inspector(尺寸檢查器)將會是如下的樣紙:
現(xiàn)在我們將為當前的size class添加需要的約束。通過Align和Pin菜單來添加Vertically in the Container約束以及設(shè)置 left spacing to nearest neighbor為10:
通過 Control-拖拽 的方式從 image view拖到view controller的view中然后在彈出的菜單中選擇Equal Widths。
打開image view的Size Inspector(尺寸檢查器)然后雙擊 Equal Width to:
Superview約束。如果First Item 不是cloud_small.Width,你需要點擊下拉菜單中的Reverse First
and Second Item。然后設(shè)置Multiplier為0.45。
現(xiàn)在,在image view 中已經(jīng)為當前的size class設(shè)置好了約束,但是text container還是有問題的。現(xiàn)在我們需要通過約束的方式在使得在當前size class中文字部分變成居右。
現(xiàn)在TextContainer內(nèi)部針對標簽的約束看起來還是可以接受的。為了使得TextContainer居右,我們先要卸載掉居左的約束。
在Document outline(文檔大綱)中選擇居左的約束,標簽為TextContainer.leader = leading:
然后通過Cmd-Delete的組合鍵來卸載掉這個約束。
現(xiàn)在我們需要添加兩個新的約束到TextContainer,來使得他的位置保持正確。
下面我們可以直接在Document outline(文檔大綱)中通過 Control-拖拽 的方式從TextContainer拖到view controller的View中:
然后通過 Shift-點擊 的方式加選 Top Space to Top Layout Guide和 Equal Widths。然后點擊Add Constraints(添加約束)來創(chuàng)建新的約束:
打開 TextContainer 的Size Inspector(尺寸檢查器) 然后更新我們剛剛創(chuàng)建的兩個約束為如下:
Top Space to: Top Layout Guide 的 Constant值設(shè)置為0。
Equal Width to: Superview 的 Multiplier 值設(shè)置為 0.5。 注意這里你可能需要翻轉(zhuǎn)第一項和第二項,如果已經(jīng)是正確的則不需要再次翻轉(zhuǎn)。 通過雙擊的方式來進入這個約束進行設(shè)置即可。
點擊底部的 Resolve Auto Layout Issues? 圖標,然后選擇 All Views -> Update frames。接下來storyboard將更新成新布局的效果如下:
于是乎,我們的布局就修改完成啦,現(xiàn)在距離成品已經(jīng)近在咫尺了。但是仍有一些字號問題需要解決,我們將在下一個章節(jié)中繼續(xù)講解。
適配字體
現(xiàn)在TextContainer中的字號在iPad設(shè)備上看上去還好,但是在compact(緊湊)的size class中就顯得太大了。現(xiàn)在我們需要在size class中來重寫字號。
注意:不想布局的重寫,對于字體的配置將會影響到基本布局。對于字體的配置并不遵從當前的size class重寫原則。我們需要用下面的方法來實現(xiàn)。
點擊底部的size class按鈕,然后通過表格選擇當前的size class為基本的Any Width | Any Height。然后你的size class 變成為了之前我們創(chuàng)建的基本布局。
現(xiàn)在選擇Cupertino字樣的標簽然后打開Attributes Inspector(屬性檢查器)。點擊Font左側(cè)的+號按鈕:
在彈出的菜單中選擇對應(yīng)的size class 來重寫字號。 選擇Compact Width -> Any Height:
這個操作會創(chuàng)建第二個字體選擇器,用于應(yīng)用特殊的size class。現(xiàn)在更新新的選擇器中的字號為90:
現(xiàn)在我們用同樣的方式來更新溫度的Label,這次設(shè)置Compact Width > Any Height的字號為150。
接下來在預(yù)覽區(qū)中自動更新的效果如下:
現(xiàn)在,看上去似乎好了一些,但是Cupertino標簽還是被裁減了。反復(fù)調(diào)整字號到適合的大小明顯不是一個明智之
舉。Cupertino稍微有點長了,但是Washington, D.C.會更長,Kleinfeltersville,
PA會更加的長!那么我們應(yīng)該如何做呢?
我們又一次需要用自動布局來解決這個問題了。我們只需要簡單的設(shè)置這兩個文本標簽的寬度與TextContainer相匹配就可以了。用 Control-過拽 的方式從Cupertino標簽拖拽到TextContainer,然后選擇Equal Widths。
同樣對溫度標簽也這樣做。于是顯示的效果如下:
嗯,文本的截斷并不是我們期望的。這是當文本比容器長時候的默認填充方式。現(xiàn)在我們需要一個方式來動態(tài)的調(diào)整字號到容器的尺寸。
選擇Cupertino標簽,然后打開Attributes Inspector(屬性檢查器)。改變
AutoShrink 為 Minimum font scale 然后設(shè)置值為0.5。與此同時,我們設(shè)置Text
Alignment為Centered。就像下面的樣子:
我們對溫度標簽也做同樣的處理。
現(xiàn)在再來看預(yù)覽區(qū),iPhone的布局看上去就非常好了:
在預(yù)覽區(qū)中看上去還不錯,但是我們可能還是可以花一些時間去看看是否每一樣?xùn)|西都是正常工作的。iPhone的屏幕看上去像是正確的樣子:
恭喜,你已經(jīng)學(xué)到了一些基本的自適應(yīng)布局的知識了。