Session 235: UIKit: Apps for Every Size and Shapegit
做者簡介:@Hale 丁香園 iOS 工程師github
現現在蘋果的移動設備已經不像初代的時候只有一種分辨率尺寸。iOS12 支持包括 iPhone5s、iPhone八、iPhone8 Plus、iPhone X、iPad 等各類尺寸的設備。相信必定有許多開發者對多設備的適配開發有過困擾,本 Session 對快速適配全部型號 iOS 移動設備的開發方法進行了介紹。下面介紹的屬性和方法可讓開發者用最短的時間讓開發項目適配蘋果全系列移動設備,同時還保證了用戶體驗不會受到影響。swift
Safe Area
在 iOS11 中被提出,它是一個很是重要的屬性。相信大多數開發者對這個屬性已經並不陌生,Safe Area
的提出主要是爲了適配像 iPhone X 同樣的全面屏。咱們能夠經過 UIView
的 safeAreaInsets
和 safeAreaLayoutGuide
屬性來肯定安全區域,同時安全區域限制了視圖的可見部分,如 圖1 所示。安全
經過下面兩張圖咱們能夠知道,最底部視圖A的安全區域如 圖2 所示,隨後在上添加一個不受限於視圖A安全區域的淺灰色視圖B,而後在視圖B上添加子視圖C,並設置視圖C的約束依賴於視圖B的 safeAreaLayoutGuide
,視圖C的可視範圍會被限制在 圖3 黃色區域,咱們能夠得出父視圖的安全區域會向上傳遞,如 圖二、3 所示。bash
UIViewController
支持使用 additionalSafeAreaInsets
屬性,自定義擴展安全區域大小,以知足一些應用場景。此外 UIView
提供了 safeAreaInsetsDidChange()
方法,UIViewController
提供了 viewSafeAreaInsetsDidChange()
方法,用於檢測安全區域的改變。當視圖的安全區域發生改變,對應的方法就會被調用,如 圖4 所示。app
iOS8 中提出了 layoutMargins
的概念,其主要用於設置子視圖與父視圖之間的邊距,在 iOS11 中新增了 directionalLayoutMargins
,主要爲了 Right To Left(RTL)語言下能夠進行自動適配。默認狀況下,layoutMargin
到各邊的距離是8個點。經過在 Interface Builder
裏面勾選 Constrain to margins
它會根據版本在 iOS11 及以上的系統中自動使用 directionalLayoutMargins
,如 圖五、6 所示。iphone
正常狀況下子視圖的佈局邊距會依賴父視圖的安全區域,可是當設置了insetsLayoutMarginsFromSafeArea = false
以後,子視圖能夠達到突破父視圖安全區域的佈局效果,如 圖七、8 所示。ide
當一個視圖的preservesSuperviewLayoutMargins
屬性爲 true 時,在對它的子視圖進行佈局時,父視圖的 margin 也會被考慮在內。若是存在一個子視圖的 frame 恰好和父視圖的 margin 表示區域有重合,此時設置 preservesSuperviewLayoutMargins
爲 true ,則子視圖會被恰好限制在父視圖的 margin 內,如 圖九、10 所示。佈局
UIViewController
存在屬性 systemMinimumLayoutMargins
,能夠對其進行重寫,默認狀況下 view 的佈局邊距會受這個屬性的返回值制約。以下重寫了該屬性,則 view 的邊距最小會爲 [20,20,20,20]。性能
override var systemMinimumLayoutMargins: NSDirectionalEdgeInsets {
return NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20)
}
複製代碼
若設置 viewRespectsSystemMinimumLayoutMargins
爲 false,則 view 佈局邊距不受 systemMinimumLayoutMargins
屬性的影響,默認爲 [8,8,8,8]。
iOS11 中提出了 UIScrollView
的新屬性 adjustedContentInset
,它的值等於 UIScrollView
原有的 contentInset
加上 安全區域等 system inset
,如 圖11 所示。
adjustedContentInset = contentInset + system inset
複製代碼
本 Session 再次提到 iOS11 以後廢除了原有的 UIViewController
屬性 automaticallyAdjustsScrollViewInsets
取而代之的是 UIScrollView
的新增枚舉 ContentInsetAdjustmentBehavior
,該枚舉結構以下:
public enum ContentInsetAdjustmentBehavior : Int {
case automatic
case scrollableAxes
case never
case always
}
複製代碼
以下圖若是設置枚舉值爲 .always
,則默認狀況下 scrollView
的 adjustedContentInset
就等於 safeAreaInsets
,便可視區域不會被 navigationBar
和 tabBar
遮擋,如 圖12 所示。
若是將枚舉值設置爲 .scrollableAxes
,則在能夠滾動的方向上,或者設置了 alwaysBounceHorizontal/Vertical
爲 true 的時候,Inset 纔會生效。如 圖14,頁面內容比較少的時候,垂直方向上 scrollView
不可滾動,致使文本標題部分被 navigationBar
遮擋,如 圖1三、14 所示。
系統默認設置的枚舉值是 .automatic
, 這個枚舉值基本和 .scrollableAxes
表現一致,但惟一不一樣的是它還秉承了原來 automaticallyAdjustsScrollViewInsets = true
的特性,在有 navigationBar
且 isTranslucent
爲 true 時,即便垂直方向上不可以滾動,依然可以調整 Inset 使內容可見,如 圖15 所示。
若是將枚舉值設置爲 .nerver
,則 scrollView
的 Inset 不會受 safeAreaInserts
的影響而改變、如 圖16 所示。
若果在一些場景下須要隱藏 status bar
,咱們通常會這麼作:
class ArticleViewController: UIViewController {
override var prefersStatusBarHidden: Bool {
return true
}
}
複製代碼
不幸的是在 iPhone X 上面這麼寫是無效的,在 iPhone X 上面只有在隱藏了 navigationBar
的前提下,上面這段代碼纔會生效,因此蘋果官方給出的建議是同時隱藏 navigationBar
和 status bar
,如 圖17 所示。
iOS9 就提出了 readableContentGuide
這一律念,主要是用於一些閱讀類應用,在可視寬度較大的時候,但願可以經過佈局將閱讀區域限定在必定範圍,已緩解用戶閱讀的過程當中追蹤內容移動頭部所形成的疲勞。readableContentGuide
的間距大小會隨着字體大小、設備不一樣等因素而發生改變。現這一屬性一樣兼容 safeAreaInsets
。一樣的 UITableView
的 cellLayoutMarginsFollowreadableWidth
屬性也一樣兼容 safeAreaInsets
,如 圖1八、1九、20 所示。
UITableView
在iOS11開始添加了一個新的屬性insetsContentViewsToSafeArea
,該屬性可以控制 TableViewCell
的 ContentView
是否被 safeAreaInsets
所影響,如 圖20、21 所示。
iPhone X 以後,咱們在開發過程當中常常會遇到如何佈局底部按鈕的問題。在本 Session 中官方給出了一種方案,例如設置按鈕距底部相對於 superView 的約束爲16,約束的 Priority
爲 999,同時設置按鈕底部相對於 safeAreaLayoutGuide
的約束值爲大於等於 0。便可實現按鈕在 iphone X 和 其餘設備上的不一樣佈局,如 圖23 所示。
其實本 Session 並無提出任何新的屬性和方法,最新的屬性在 iOS11 SDK 中就已經提出來了。可能不少開發者,在適配iPhone X 的時候遇到的問題也都解決的差很少了。但我的認爲這個 Session 仍是頗有必要的,它將現有的用於適配開發的 UIKit SDK 進行了概括總結,這將有助於開發者進一步瞭解這些屬性之間的關聯關係對快速適配多種尺寸設備的項目開發會有很大幫助。
查看更多 WWDC 18 相關文章請前往 老司機x知識小集xSwiftGG WWDC 18 專題目錄