譯文: Adaptive Layout Tutorial in iOS 11: Getting Started

翻譯自raywenderlich上的一篇教程:Adaptive Layout Tutorial in iOS 11: Getting Started。水平有限,如有謬誤,還請斧正。以下為譯文:

Update 6/20/17: 本教程由József Vesza更新至iOS 11,Xcode 9,Swift 4。原始教程由Sam Davies撰寫。

對iOS App設計師來說,Adaptive Layout的引入,導致「套路」變化甚多。現在,你設計App時,可以只創建一個layout,就能在所有iOS設備上工作——無須多餘的特定平台代碼。

本教程介紹Adaptive Layout。你會學到通用storyboards、size classes、layout、自定義字體,還有經過改良的Interface Builder。

本教程你會創建一個簡單天氣類App的用戶介面——從頭開始創建。如果對Auto Layout不感冒,不要緊;我們第一步會以一種很簡單的方式用Auto Layout構建介面。一行代買都沒寫就搞定了,相信會給你帶來足夠的驚喜。

Universal Storyboards

Universal Storyboards是走向Adaptive Layout的第一步。現在storyboard可以用於iPads和iPhones。不再需要保持每個設備storyboard彼此同步——一個無聊的、充滿坑的過程。

打開Xcode,選擇File\New\Project….

選擇 iOS\Application\Single View App,然後點Next:

Product Name設置為AdaptiveWeatherLanguageSwift。所有復選框都無需勾選,然後點擊Next

創建完項目後,會在Project Navigator看到如下檔案:

無論設備是什麼尺寸,Main.storyboard是唯一的一個storyboard。打開storyboard,可以看到其中包含了一個視圖控制器,目前的尺寸顯示的是iPhone 7屏幕的大小。

選擇storyboard,打開File Inspector,可以找到Use Trait Variations,勾選這個新格式,如下圖片所示(譯者:要選擇View Controller才能顯示如下菜單):

在用新版Xocde創建的新工程中,這個復選框默認是勾選的。如果你打開的是舊項目,可以手動勾選,以便使用新版的storyboard。

Setting Up Your Storyboard

打開Main.storyboard,從Object Library中拖拽一個Image View到視圖控制器畫布中。在Size Inspector,設置X37Y20Width300Height265

接著,拖拽一個View對象放到剛剛的image view下面,在Size Inspector,設置X37Y340Width300Height265

選中剛剛拖進來的view,打開Identity Inspector並在Document下的文本框輸入TextContainer。注意,Document面板有可能被折疊起來了,點擊Show按鈕即可展開。這樣給view起一個名字,可以方便在Document Inspector找到你想要的視圖。最後的city、temperature labels,都會放進剛剛拖進來的view。

通常拖進來的對象,背景顏色都和視圖控制器一樣都是白色,導致難以辨認。選擇視圖控制器的view,打開Attributes Inspector然後設置背景顏色為#4AABF7,可以解決這一問題。(譯者:不懂如何自定義顏色的朋友,參考:)

接著,選擇TextContainer view並設置背景顏色為#3780BA

視圖控制器看起來應該像以下截圖:

視圖控制器的view,目前只有這兩個views;現在應該為它們設置約束(constraints)了。

Adaptive Layout

選擇image view,點擊layout 工具欄的Align按鈕。勾選Horizontally in Container復選框,確認數值為0,然後點擊Add 1 Constraint

然後點擊Add New Constraints按鈕,添加頂部間距,約束為20,如下圖所示:

點擊Add 1 Constraint按鈕。

上面添加的兩個約束,確保了image view 距頂部有固定的間距,並且從左到右居中。現在需要設置的是image view和text container view的間距。從image view Ctrl-drag(譯者: 按著Ctrl並拖拽) 到text container view,如下:

接著會顯示約束菜單。選擇Vertical Spacing:

這個約束,限定了image view 底部和TextContainer view頂部的垂直間距。

選擇image view並打開Size Inspector可以看到現在的約束設置情況:

可以看到剛剛添加的三個約束;在Size Inspector,可以很方便地設置每條約束。點擊Bottom Space To: TextContainer約束的Edit按鈕;會彈出一個設置約束屬性的對話框。把Constant設為20

點擊其他地方,關閉對話框。

現在已經設置了TextContainer view底部離image view有20個點(point)的間隙,但是還需要給其他三個方向添加約束。

選擇TextContainer view,點擊底部的Add New Constraints按鈕彈出對話框。在Spacing to nearest neighbor部分,設置leftrightbottom離父視圖的間距為0。並確保Constrain to margins復選框沒有被勾選。

如下圖所示:

點擊Add 3 Constraints為視圖添加約束。這樣就會把text container view固定在視圖控制器左側、右側、底部邊緣。

你的storyboard現在應該像如下截圖:

你會看到視圖中有一些橙色和紅色的約束;這表示約束有一些錯誤是需要你注意的。當然,storyboard可以自動更新contained view的frames來滿足這些約束,但是現在自動更新的話,image view會被收縮到看不見了(zero size)。

這是因為你的image view還沒有任何內容——也就是說這個視圖的固有(intrinsic)寬高為0。如果沒有給視圖的約束指定物理上(physical)的寬高,Auto Layout就會依賴固有(intrinsic)的寬高。

Project Navigator,打開Assets.xcassets。下載cloud image.zip並解壓。裡面有三個文件。在Finder(譯者:最新的macOS,把Finder翻譯成「訪達」)中選擇這三個文件,並拖到asset目錄的右邊空白處。

這樣就創建了一個新的image set,並分配好3種尺寸:

現在就可以用image set中的圖片來填充之前的image view了。回到Main.storyboard並選擇image view。切到Attributes Inspector,在Image框內輸入cloud_small,在Content Mode的下拉菜單,選擇Aspect Fit,如下:

現在的storyboard應該長這樣:

謝天謝地,目前好像一切正常;視圖控制器會自動重新組織視圖去匹配新的約束。

Previewing Layouts

如果是以前,現在你就要在不同的版本的模擬器build和run你的工程——還有不同方向(譯者:橫屏、竪屏)——以便預覽、測試。這個過程費時費力,不過現在Xcode 9提供了一個更好的方式進行預覽。

打開Main.storyboard,然後點擊幕布底部的View as按鈕。就會展開選擇菜單:

Devices,選擇iPhone 4sDevice區域最右邊的按鈕)。

幕布就會切換到另一個形狀:4寸的iPhone屏幕(譯者:iPhone 4s屏幕應為3.5寸,這裡應該是原作者筆誤),如下圖:

要預覽橫屏模式,在Orientation區域,選擇Landscape

與在多個模擬器中預覽相比,Xcode 9的這個改進簡直不要太方便:點擊一個按鈕,就可以檢查佈局在不同的設備是不是都能工作。

有沒有注意到上面的橫屏預覽有個怪怪的地方?沒錯,那朵「雲」太大了。我們需要往image view增加一個新的約束來修正這個問題。

回到storyboard。從image view上Ctrl-drag到視圖控制器的view,創建一個新的約束。在彈出來的菜單中選擇Equal Heights

這時候storyboard上的約束會變成紅色。這是因為剛剛加的約束,和已經存在的約束產生了衝突,具體的衝突是:image view要和視圖控制器的view有相同的高度,又要保持之前設定的垂直間距。這兩個約束不可能同時滿足。

Document Outline(譯者:就是storyboard左邊的導航欄)選擇剛剛添加的約束,打開Attribute Inspector。如果First Item不是cloud_small.Height,就在First Item下拉菜單選擇Reverse First and Second Item,如下:

接著,Relation選擇Less Than or EqualMultiplier設置為0.4,如下圖:

這就是說,cloud image要麼是本身的尺寸(intrinsic size),要麼就是屏幕高度的40%,以較小者為準。

可以看到,更新了約束之後,幕布的佈局就自動更新了,如下:

完美!

因為這是一個天氣app,現在需要添加一些labels來顯示城市的名字和氣溫。

Adding Content to the TextContainer

Main.storyboard,切換到Portrait iPhone 7,從Object Library拖拽兩個LabelsTextContainer view,位置大概如下:

選擇最上面的label,利用AlignAdd New Constraints菜單來將label水平居中,並將頂部間距設置為10(離最近的視圖間距是10),如下:

接著,在Attribute Inspector裡,文本框輸入CupertinoColor設置為White Color,字體設置為System ThinSize150

可能你會發現文字無法辨認,這是因為label的frame限制導致的——後面會解決。

選擇選擇另一個label,用相同的方法讓其水平居中,再設置label離底部間距為10。Size Inspector現在如下圖:

Attributes Inspector,輸入28C,顏色設置為Whilt Color,字體是SystemThin,size是250

storyboard中的兩個labels都超出了邊界,並且重疊,顯然不是我們要的那樣子。不過,在修正之前,在不同的設備上預覽一下,在iPad Pro 9.7"上看起來不錯:

可以想像到,對於iPhone來說,目前的字號太大了:

在下一章節,會修正這個字號的問題。

Size Classes

Universal(通用) storyboard很好用,不過就像你上面發現的問題一樣,只用一種佈局來適配所有屏幕,非常困難。還好Adaptive Layout有一些工具和技巧來解決這些問題。

Adaptive layout的一個核心概念,就是size class,size class可以應用在所有view或者view controller,表示的是:在給定的水平或垂直維度中,能顯示的內容量。

Xcode 提供了兩種size classes:RegularCompact。雖然這兩種size class和具體的物理尺寸相關,但同時也表示view「語義學」上的尺寸。(譯者:semantic size,我理解就是抽象意義上的尺寸,用Regular和Compact表示「常規」和「緊湊」兩種「尺寸」)。

下面的表格,顯示了size class 如何應用到不同的設備和方向。

Size Classes and You

Size class意味著什麼?雖然app知道size calss,但是你的layout對於size class不可知的——換言之,layout在所有size class中,都是一樣的。

在進行自適應佈局時,這是一個重要的觀念。應該先構建一個基本佈局,然後根據需要,自定義每個size class。不要把每個size calss都看作是完全獨立、分離的。把adaptive layout想像成一個層次結構,將大家可以共享的設計放在父級中,然後在子size classes中進行必要的修改。

到目前為止,我們都沒有提到為特定的設備進行佈局。這是因為adaptive layout的一個核心理念,就是將size class從特定的設備中抽象出來。這就意味著,支持adaptive layout的的view,可以用在全屏的視圖控制器、容器視圖控制器都可以正常工作,儘管外觀不一樣。(譯者:作者應該是說無論在什麼情況下,layout都是我們想要的)

這對Apple也大有益處,因為這樣可以擴展設備的屏幕,而開發人員和設計人員又無需重新構建他們的apps。

下面將使用size classes來自定義iPhone的橫屏佈局,以為當前的layout,並不滿足要求。

Working with Size Classes

接下來介紹trait variations,確保自己選擇了Compact Height這個設置(譯者:預覽)(比如橫屏的iPhone SE),然後點擊右邊的Vary for Traits按鈕。

這裏可以選擇一種size class來进行定製(基於寬度、高度)。

Note:這裏術語上有一點差異。Size classes一直使用「水平/horizontal」、「垂直/vertical」。但是,IB使用的是「寬度/width」和「高度/height」。這裏是一致的((width = horizontal; height = vertical),只是對同一個概念的不同表述方法。

你現在的layout,在compact heights這種情況下不能正常工作。下面來進行修正,在Vary for Traits菜單(底部右邊)選擇Height復選框:

可以看到底部工具欄馬上變成深藍色。這就表示,你正在處理的,是特定size calss的layout。

為了能夠改變layout,現在需要臨時改變一些約束。在Auto Layout中,有專門的術語來表示:installinguninstalling約束。如果一個約束是有效的,就是installed(安裝)了,一個uninstalled(卸載了)的約束,就表示在當前size class下是無效的、未激活的。

選擇image view,打開Size Inspector。可以看到對這個view起作用的所有約束:

單擊選擇Align Center X to: Superview約束,然後按下鍵盤的Delete鍵,這樣就在當前size class下卸載了該約束。可以看到,在storyboard上,約束馬上消失了,在Document Outline和Size Inspector該約束變為灰色。(譯者:其實在Size Inspector也是消失了——除非選擇的是All)

Note:如果需要查卸載的約束,可以將Size Inspector中的This Size Class切換到All

雙擊Size Inspector中卸載的約束。可以看到底部還有一行,如下:

這表示這個約束是用於基礎layout的,但並不適用於Compact Height下的佈局。(譯者:以前一直沒搞懂)

用同樣的方法,把image view的其他三個約束也卸載掉。你的document outline 和 view的Size Inspector介面類似如下:

現在就可以為此size class下添加必要的約束了。使用AlignPin菜單的Vertically Center in the Container,再設置離左邊的間距為10:

從image view Ctrl-drag到視圖控制器的view,然後在彈出菜單選擇Equal Widths

打開image view的Size Inspector,然後雙擊Equal Width to: Superview這條約束。如果First Item不是cloud_small.Width,選擇下拉菜單的Reverse First and Second Item。並更新Multiplier0.45

現在image view的約束,在所有size class下都能正常工作了(譯者:就是將image view從豎屏的水平居中,在橫屏下改為了垂直居中;並且將寬度設為整個view寬度的45%),但是,txet container在現在的size class下,還需要修改約束,以便讓label能移動到合適的位置。

TextContainer view有內部(internal)約束來定位labels,labels可以按照原來的約束工作。但是,有三條約束——左、右、底部——並不能正常工作。為了將label固定在右下角,需要卸載掉左側的約束。

在document outline選擇TextContainer,在size inspector卸載掉Leading Space。可以在document outline選擇這條約束,size inspector上可以確認是否已經卸載掉:

現在需要為TextContainer添加兩條約束以便正確將它定位。TextContainer的寬度應該設置為視圖控制器的view寬度的一半,並固定在頂部。

理論上,你可以像以前一樣,從TextContainer拖拽到視圖控制器的view上進行設置。然而,在實踐中,經常會有其他元素在view裡面,導致很難選中要拖拽的view。而使用document outline,則簡單得多。

在docuemnt outline中從TextContainer Ctrl-drag到視圖控制器的view:

在彈出菜單中,Shift-click,並選擇Vertical Spacing to Top Layout Guide 和Equal Widths。再點擊Add Constraints創建新的約束:(譯者:就是按著Shift進行多選。如果沒有看到Vertical Spacing to Top Layout Guide字樣,選第三個即可)

打開TextContainerSize Inspector,更新剛剛添加的兩條約束的值:

  • Top Space to: Top Layout Guide約束的Constant設置為0
  • Equal With to Superview約束的Multiplier設置為0.5。注意,這裏可能需要切換fitst和second items,就像之前切換過的一樣。雙擊約束選擇Reverse First and Second Item即可。

storyboard現在應該長這樣:

Layout已經改完了,現在離成品不遠了。字體的字號還有一些問題需要修正——在下一章節會解決這些問題。

Adaptive Fonts

在regular size classes(iPad)中,TextContainer當前的字號看起來很好。但是對compact size classes來說,字號太大了。不過不要擔心,在size class中,也可以重新設置字體的大小!

Note:不像重設layout(譯者:約束),更改字體設置,也會影響base layout。所以,修改字體設置,不是在當前size class中重新設置,而應該使用下面的方法。

先點擊右下角的Done Varying按鈕,完成之前的工作。底部工具欄變回灰色,表示回到base layout狀態。

選擇Cupertino文本label並打開Attributes Inspector。點擊Font左邊的小加號:

接著會彈出一個菜單,用於選擇size class——就是你要重新設置字體屬性的size class。Width選擇Compact,Height選擇Any,如下:(譯者:再點擊Add Variation)

接著Xcode會創建另一個字體選擇框,這裏的設置將應用在指定的size class中。我們將這裡的字號設置為90:

現在選擇temperature這個label,重複上面的步驟,在Compact WidthAny Height這個size class下,字號設置為150

Interface Builder會自動更新,顯示修改後的效果:

這樣看起來好一點了,不過Cupertino這個label被裁切了一點。Cupertino這個城市名太長了,另外還有更長的,要怎麼辦呢?

Auto Layout再次「救場」!這裡只需要簡單地限制label的寬度要匹配TextContainer的寬度即可。從Cupertino label Ctrl-drag 到TextContainer,選擇Equal Widths

在temperature label重複上面的步驟。幕布更新效果如下:

嗯,有字符被省略了,顯然不是我們想要的效果。當字符多於顯示空間時,這是label的默認行為。不過,我們可以選擇自動調整字號,進行自適應。

選擇Cupertino label並打開Attribues Inspector。在AutoShrink下拉菜單,選擇Minimum Font Scale,並確保值是0.5。同時設置Text Alignment 為居中。如下:

對temperature label重複上面的步驟。

看看Interface Builder的幕布,現在感覺好多了:

在Xcode預覽非常方便,但是我們也要在iPhone的屏幕上看看效果是否OK:

恭喜!你已經學會Adaptive Layout的基本功了。

Where To Go From Here?

這裡有完成的項目

想想看,我們只用一個storyboard,就在所有設備、橫屏豎屏都工作得很好了!

如果沒有人相信Adaptive Layout是未來的發展方向,請考慮這樣一個現實:我們現在的layout,即使在還沒發佈的iOS設備上,也可以正常工作,無需額外適配。

從本教程可以看出,作為一名開發人員,你需要重新考慮app的設計方法。我們應該考慮屏幕上UI元素之間的關係,而不是基於像素的layout。

如果你想學習更多關於Adaptive Layout的知識,可以查看我們的Adaptive Layout video tutorial series,可以讓你從一個Adaptive Layout的初學者成長為大師。看看WWDC2016的Part 1Part 2也很有用。

畢!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券