這兩個(gè) Session 主要介紹了在 Xcode 8 與 iOS 10 中测僵,進(jìn)行 App 適配更方便的地方樊破。
現(xiàn)在由于設(shè)備越來越多巡语,屏幕的尺寸也在不斷增加缅疟。從現(xiàn)在還支持的最古老的設(shè)備 4S 開始分别,到屏幕尺寸最大的 iPad Pro,再加上屏幕方向的支持和 iPad 中的 splitView存淫,各種組合大概有 300 多種耘斩,要手動(dòng)管理的話十分麻煩。
不過 Apple 幫你簡(jiǎn)化了大部分工作桅咆,現(xiàn)在需要關(guān)心的主要是 Traits括授。下面這張圖簡(jiǎn)要介紹了 Traits 的幾個(gè)例子:
下面首先介紹 Size Classes。Size Classes 在 iOS 8 中引入岩饼,它主要用于簡(jiǎn)化以下幾個(gè)方面的操作:屏幕尺寸荚虚、屏幕方向、適配性(iPad splitView)籍茧。有了 Size Classes曲管,你需要關(guān)心的只是可用空間的大小,你可以對(duì)屏幕內(nèi)容有更精確的控制硕糊。
Size Classes 主要分為兩類:horizontalSizeClass 與 verticalSizeClass。每種又可細(xì)分為兩類:Compact 與 Regular腊徙。
有了 Size Classes简十,只需要考慮上面圖片中四種組合。不過一般來說主要考慮對(duì)寬度改變做出相應(yīng)調(diào)整撬腾。Size Class 可以根據(jù)屏幕的情況動(dòng)態(tài)調(diào)整(旋轉(zhuǎn)等)螟蝙,并且還能方便的根據(jù)設(shè)備的情況對(duì) View Controller 進(jìn)行調(diào)整。
下面介紹了一個(gè)重要的方法民傻,針對(duì) Trait 改變時(shí)進(jìn)行調(diào)整胰默,主要應(yīng)用在 View Controller 與 View 中。
override func traitCollectionDidChange(_ previousTraits: UITraitCollection?) {
super.traitCollectionDidChange(previousTraits)
if previousTraits?.horizontalSizeClass != traitCollection.horizontalSizeClass {
switch traitCollection.horizontalSizeClass {
case .compact:
setupConstraintsForCompactEnvironment()
case .unspecified: fallthrough
case .regular:
setupConstraintsForRegularEnvironment()
}
} }
在每個(gè) UITraitEnvironmet
中都會(huì)調(diào)用 traitCollectionDidChange(:)
方法漓踢,你只重寫需要進(jìn)行更改的部分牵署。而在 Interface Builder、Asset catalog喧半、UIAppearance 中奴迅,系統(tǒng)會(huì)幫你自動(dòng)處理這個(gè)方法。
總結(jié)一下:
- Trait 用于描述整個(gè)環(huán)境的變化挺据,包括 Layout取具、外觀脖隶、兼容性等。
- 如果需要根據(jù) Trait 的變化進(jìn)行調(diào)整暇检,重寫
traitCollectionDidChange(:)
方法产阱。 - Size Classes 簡(jiǎn)化了你需要考慮的事。
- 系統(tǒng)會(huì)幫你處理大部分的工作块仆。
下面有個(gè) Demo 展示构蹬,主要是關(guān)于 Xcode 8 中如何方便的根據(jù)設(shè)備情況對(duì) UI 進(jìn)行定制。具體可以去官網(wǎng)看看 Session 222 的后半部分榨乎。
Session 233 主要做了幾件事:
- 介紹了 Size 和 Size class 的基礎(chǔ)
- 如何最有效地使用 UIKit
- 在前面介紹的基礎(chǔ)上提升體驗(yàn)
Size 和 Size class 的基礎(chǔ)
之后介紹了一些因?yàn)椴煌?Size 產(chǎn)生的差別怎燥,如 view 與 controller、presentation(在 modal 上的不同)蜜暑、split view铐姚,session 222 也已經(jīng)提過。
如何最有效地使用 UIKit
- Xcode 的工具:
- Interface Builder
- Asset Catalogs
- UIKit
- Auto Layout
- UITraitCollection
- Dynamic Type
- Layout Guides
- UIAppearance
這里著重介紹了幾個(gè)功能:
Asset Catalogs
Alignment Inset:可以定義一個(gè) Inset肛捍,將圖片最重要的部分展現(xiàn)出來隐绵。
Slicing:類似 Android 的點(diǎn)九,現(xiàn)在可以在 Asset Catalogs 里面配置需要切分的圖片拙毫,將可拉伸與不可拉伸部分分隔開
依许,保證邊緣的如圓角部分不會(huì)因圖片拉伸產(chǎn)生變形。
Dynamic Type
可以根據(jù) Trait Collection 的變化調(diào)整字體缀蹄。
Layout Guides
根據(jù) Margin 來進(jìn)行 Layout:
自動(dòng)根據(jù)合適的閱讀寬度調(diào)整內(nèi)容的范圍以及字體的大星吞:
UIAppearance
下面這段代碼介紹了一種方法,用于根據(jù) Size class 來調(diào)整 UINavigationBar 的 BackgroundImage:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[NSObject: AnyObject]?) -> Bool {
//獲取垂直方向?yàn)?compact 時(shí)的 trait
let verticalCompactTrait = UITraitCollection(verticalSizeClass: .compact)
//通過該 trait 獲取 UINavigationBar compact 時(shí)的外觀
let compactAppearance = UINavigationBar.forTraitCollection(verticalCompactTrait)
//設(shè)置背景圖片
compactAppearance.setBackgroundImage(nil, for: .default)
let verticalRegularTrait = UITraitCollection(verticalSizeClass: .regular)
let verticalAppearance = UINavigationBar.forTraitCollection(verticalRegularTrait)
verticalAppearance.setBackgroundImage(UIImage(), for: .default)
}
可以達(dá)到如下的效果:
雖然前面有提到 trait缺前,但是還有一些不太理解蛀醉,查了一下官方文檔:
A trait collection describes the iOS interface environment for your app, including traits such as horizontal and vertical size class, display scale, and user interface idiom. To create an adaptive interface, write code to adjust your app’s layout according to changes in these traits.
簡(jiǎn)單來說,trait collection 可以表示某種環(huán)境下的 UI衅码,比如 size class拯刁,屏幕的 scale 等等。使用 trait collection逝段,就可以針對(duì)特定環(huán)境下的 UI 進(jìn)行更改垛玻,而不影響其他環(huán)境的 UI。
于是你就可以根據(jù)這些 trait collection奶躯,根據(jù)不同的 trait 組合來調(diào)整 UI帚桩。這也是下面官方要講的:
- 為不同設(shè)備、屏幕方向巫糙、尺寸進(jìn)行設(shè)計(jì)
- 不要這么做:只判斷屏幕尺寸是否完全相等
- 最好這樣:設(shè)定一些條件來判斷:制定規(guī)則
- 使用 Size class
- 將值與閾值相比
- 將值與其它值進(jìn)行比較
- 實(shí)現(xiàn)這些設(shè)計(jì)
- 找出 app 的尺寸數(shù)據(jù)
- 使用規(guī)則來決定應(yīng)該如何設(shè)計(jì)
- 將設(shè)計(jì)應(yīng)用到 UI 中去
- 關(guān)于代碼適合放置的位置:
- 這部分代碼不適合放在
viewDidLoad()
中朗儒,因?yàn)檫@時(shí)候 superview 還沒有確定,layout 也還沒有起作用。只有在整個(gè)設(shè)計(jì)中不變的東西才放在init()
loadView()
viewDidLoad()
中醉锄。 - 這部分代碼適合放在
viewWillLayoutSubviews()
中乏悄,這時(shí)候 view 已經(jīng)在 superview 中,并且 layout 已經(jīng)開始了恳不。在這個(gè)方法里面適合調(diào)整一些 view controller 相關(guān)的東西檩小。 - 但要小心使用:
- 盡量少放代碼在這個(gè)方法中
- 找出那些上次改變了的東西
- 小心不要引發(fā) layout 循環(huán)
- 這部分代碼不適合放在
- 復(fù)用組件
- 設(shè)計(jì)一些在不同設(shè)計(jì)中通用的部件
- 每一個(gè)部件都是一個(gè) view controller
- view 的層級(jí)與約束
- 與其它 view controller 的聯(lián)系
- 與 app 中其它部分的聯(lián)系
關(guān)于 layout,官方有個(gè)小 demo烟勋,演示如何實(shí)現(xiàn)以下效果:
即在屏幕旋轉(zhuǎn)時(shí)规求,UIStackView
內(nèi)部元素的方向也跟著旋轉(zhuǎn)。
其實(shí)代碼沒幾行:
class SimpleExampleViewController: UIViewController {
@IBOutlet var stackView : UIStackView!
override func viewWillLayoutSubviews() {
let size = view.bounds.size
let useWideDesign = size.width >= size.height
//根據(jù)屏幕的寬高比較卵惦,決定是否要修改 stactView 內(nèi)容方向
if useWideDesign {
stackView.axis = .horizontal
} else {
stackView.axis = .vertical
}
}
}
關(guān)于部件的復(fù)用這一塊阻肿,這個(gè) session 中還有一個(gè)比較長(zhǎng)的 demo,地址在這里 沮尿,需要 Xcode 8 與 iOS 10丛塌。
總而言之,這部分提出了一種類似于 web 中響應(yīng)式設(shè)計(jì)的解決方案畜疾,在 web 中已經(jīng)應(yīng)用了挺久了赴邻,但是在原生的 app 中還比較新,并且也有一些拓展啡捶,根據(jù)屏幕的 trait 能進(jìn)行更多的定制姥敛,包括點(diǎn)擊操作的效果等等。雖然感覺在原生 app 中做這件事比較怪異瞎暑,不過也期待有更多有趣的設(shè)計(jì)~