隨手記 iOS 11 適配實踐總結

歡迎關注微信公衆號「隨手記技術團隊」,查看更多隨手記團隊的技術文章。
本文做者:丁同舟、王博文
原文連接:mp.weixin.qq.com/s/rP_ryWL2r…swift

iOS 11 已經發布了一段時間了,隨手記團隊也早早的完成了適配。在這裏,咱們作了點總結,與你們一塊兒分享一下,關於 iOS 11 一些新特性的適配。數組

UIView & UIViewController

Layout Margins

iOS 11 中,官方提供了一種新的佈局方法——經過 layout margins 進行佈局。官方文檔 Positioning Content Within Layout Margins 稱,使用這種佈局能夠保證各個 content 之間不會相互覆蓋。bash

總的來講,layout margins 能夠視做視圖的內容和內容之間的空隙。它由每一個邊的 insetValues 組成,分別是 top, bottom, leading and trailing. 對應的是上、下、左、右。微信

View's Margin
View's Margin

Auto Layout with Layout Margins

若是使用 Auto Layout 進行佈局,並但願約束遵循 layout margins,那麼必需要在 Xcode 中打開 Constrain to margins 選項。這樣,若是父視圖的 layout margins 改變,那麼全部綁定於父視圖 margins 的子視圖都會更新佈局。app

打開Constrain to margins選項
打開Constrain to margins選項

注意iview

若是沒有開啓這個選項,那麼全部創建的約束都會依賴於父視圖的 bounds.異步

Manually Layout with Layout Margins

若是沒有使用 Auto Layout, 而是經過設置 frame 佈局的話,要遵循 layout margins 也並不困難,只須要在佈局計算時使用 directionalLayoutMargins 這個屬性。ide

var directionalLayoutMargins: NSDirectionalEdgeInsets { get set }複製代碼

官方文檔中闡述道,對於 view controller 的根視圖,它的 directionalLayoutMargins 默認值是由 systemMinimumLayoutMarginsSafeAreaInsets 決定的。在 iPhone X 下打印根視圖的這三個屬性能夠看到它們的關係。佈局

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    print("SafeAreaInsets :" + "\(self.view.safeAreaInsets)")
    print("systemMinimumLayoutMargins :" + "\(self.systemMinimumLayoutMargins)")
    print("directionalLayoutMargins: " + "\(self.view.directionalLayoutMargins)")

    // SafeAreaInsets :UIEdgeInsets(top: 88.0, left: 0.0, bottom: 34.0, right: 0.0)
    // systemMinimumLayoutMargins :NSDirectionalEdgeInsets(top: 0.0, leading: 16.0, bottom: 0.0, trailing: 16.0)
    // directionalLayoutMargins: NSDirectionalEdgeInsets(top: 88.0, leading: 16.0, bottom: 34.0, trailing: 16.0)
}複製代碼

結果顯而易見,directionalLayoutMargins 的默認值由 systemMinimumLayoutMarginssafeAreaInsets 組成。ui

注意

systemMinimumLayoutMargins 屬性是否可用由 view controller 的布爾值屬性viewRespectsSystemMinimumLayoutMargins決定,默認爲true.

若是手動對 directionalLayoutMargins 賦值,那麼在 viewRespectsSystemMinimumLayoutMargins 開啓的狀況下,系統會比較賦值後的 directionalLayoutMarginssystemMinimumLayoutMargins ,並取其較大值做爲最終的 margins。

print("systemMinimumLayoutMargins :" + "\(self.systemMinimumLayoutMargins)")
print("origin directionalLayoutMargins: " + "\(self.view.directionalLayoutMargins)")

// 這裏把 leading 和 trailing 分別賦值爲相對於 systemMinimumLayoutMargins 的較大值20和較小值10
self.view.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 10)
print("assigned directionalLayoutMargins: " + "\(self.view.directionalLayoutMargins)")

// 打印日誌可見只有 leading 的值改變爲手動賦的值,trailing 依然遵循於 systemMinimumLayoutMargins
systemMinimumLayoutMargins :NSDirectionalEdgeInsets(top: 0.0, leading: 16.0, bottom: 0.0, trailing: 16.0)
origin directionalLayoutMargins: NSDirectionalEdgeInsets(top: 88.0, leading: 16.0, bottom: 34.0, trailing: 16.0)
assigned directionalLayoutMargins: NSDirectionalEdgeInsets(top: 88.0, leading: 20.0, bottom: 34.0, trailing: 16.0)複製代碼

注意

若是不但願受到systemMinimumLayoutMargins的影響,那麼把 view controller 的viewRespectsSystemMinimumLayoutMargins設爲false便可.

Navigation bar

進入了 iOS 11,蘋果爲提供了更爲漂亮和醒目的大標題的樣式,若是想開啓這樣的功能,其實很簡單。
只須要將 navigation bar 中的 prefersLargeTitles 置爲 true 便可,這樣便自動有了來自 iOS 11 中的大標題的樣式。

self.navigationController.navigationBar.prefersLargeTitles = true複製代碼

這裏能夠注意到,prefersLargeTitles 是配置在的 navigation controller 中的 navigation bar 中的。也就是說該 navigation controller 容器中的全部的 view controller 在此配置以後,都會受到影響。因此若是你要在當前的 navigation controller 中推入一個新的 view controller 的話,那麼該 view controller 也會是大標題。所以爲了不這個問題,UIKit 爲 UINavigationItem 提供了 largeTitleDisplayMode 屬性。

該屬性默認爲 UINavigationItem.LargeTitleDisplayMode.automatic, 即保持與前面已經顯示過的 navigation item 一致的樣式。 若是想在後來的一個 view controller 避免大標題樣式那麼能夠這麼配置:

self.navigationItem.largeTitleDisplayMode = .never複製代碼

除了大標題之外,在 iOS 11 中,UIKit 還爲 navigation item 提供了便於管理搜索的接口。
具體參考以下:

self.navigationItem.searchController = self.searchController
self.navigationItem.hidesSearchBarWhenScrolling = true複製代碼

在配置好你的 search controller 以後即可以直接提供給 navigation item 的 searchController 屬性上,這樣的便可以在導航欄看到一個漂亮的搜索框了。
此外還能夠給 navigation item 中的屬性 hidesSearchBarWhenScrolling 設置爲 true, 他可使你 view controller 中管理的 scroll view 在滑動的時候自動隱藏 search bar.

Scroll view

若是使用過 view controller 管理過 scroll view 的話,想必對 automaticallyAdjustsScrollViewInsets 這個屬性必定不陌生。在 iOS 11 以前,該屬性可讓 view controller 自動管理 scroll view 中的 content inset. 可是,在實際在開發的過程當中,這樣的自動管理的方式會帶來麻煩,尤爲是一些在 content inset 須要動態調整的狀況。

爲此,在 iOS 11, UIKit 廢棄了 automaticallyAdjustsScrollViewInsets 屬性,並將該的職責轉移到 scroll view 自己。所以,在 iOS 11 中,爲了解決這個問題,有兩個 scroll view 的新屬性。一個是用於管理調整 content inset 行爲的屬性 contentInsetAdjustmentBehavior, 另外一個是獲取調整後的填充的屬性 adjustedContentInset. 同時,UIScrollViewDelegate 也提供了新的代理方法,以方便開發者獲取 inset 變化的時機:

optional func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView)複製代碼

至此,對於這個「自動爲開發者設置 inset」 的特性,蘋果算是提供了至關完備的接口了。

不過做爲開發者的咱們要注意的是,若是對本來自動設置 contentInset 屬性的行爲有依賴,在新的 iOS 11 的適配中,可能得作出調整。

此外,爲了便於開發者在 scroll view 中使用 Auto Layout. UIKit 還提供了兩個新的屬性。一個是 contentLayoutGuide, 它用來獲取當前在 scroll view 內的內容的 layout guides. 而另外一個是 frameLayoutGuide, 他用來獲取實際內容的 layout guides. 這樣說有點繁瑣,仍是看 WWDC 的原圖吧:

scroll-view-example-1
scroll-view-example-1

Table view

實際上對於 table view 而言,其最大的更新就在於新的特性 Drag and Drop 了吧。可是這個特性在適配中暫時不須要考慮,本文就不介紹了,讓咱們一塊兒來看看其餘有意思的變化。

首先是在 iOS 11 中,table view 默認開啓了 self-sizing, 能夠注意到 estimatedRowHeight, estimatedSectionHeaderHeight 以及 estimatedSectionFooterHeight 都被默認設置爲 UITableViewAutomaticDimension. 可是咱們知道,若是本來已經實現 tableView:heightForRowAtIndexPath: 之類的方法並返回了高度,那麼在佈局方面是不會有影響的,這對 iOS 11 適配而言是個好消息。

在 iOS 11 中,有了新的 layout margins 的概念,所以 UIKit 也爲 separator inset 作了額外的補充。如今 separator inset 能夠有兩個來源,一個是從 cell 的邊緣開始 (UITableViewSeparatorInsetReference.fromCellEdges) ,另外一個是從 table view 默認的填充開始 (UITableViewSeparatorInsetReference.fromAutomaticInsets)。其中,默認的填充由 table view 的 layout margins 進行控制。

此外,iOS 11 還爲 table view 添加了更多的滑動操做的控制能力。分別能夠經過如下兩個 UITableViewDelegate 的方法實現:

func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?複製代碼

咱們能夠注意到兩個方法均要求返回一個 UISwipeActionsConfiguration 實例。爲構造這個實例,咱們還須要構造一個由 UIContextualAction 實例組成的數組。UIContextualAction 與本來的 UITableViewRowAction 大體相似,可是要注意在 contextual action 的參數 handler 中,咱們須要調用 handler 參數中的 completionHandler 才能完成操做。從這一點咱們能夠看到,彷佛在新的 action 中,咱們能夠作一些異步操做相關的事情。
下面是一個刪除操做的示例:

override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let contextualAction = UIContextualAction.init(style: .destructive, title: "Delete") { (style, title, completionHandler) in
        // 刪除指定的數據
        completionHandler(true)
    }

    let actionsConfiguration = UISwipeActionsConfiguration.init(actions: [contextualAction])
    return actionsConfiguration
}複製代碼

在 swipe actions configuration 中,咱們還須要注意一點,那就是新的 performsFirstActionWithFullSwipe 屬性。經過開啓這個屬性的配置(默認開啓),咱們能夠爲第一個動做提供 full swipe 操做 (一種經過過分滑動從而觸發動做的交互) 。

若是僅僅實現了以往的編輯的代理方法,在 iOS 11 中,對於第一個動做將會默認支持 full swipe, 且不能關閉。

Face ID

若是已經作過了 Touch ID 那麼實際上適配 Face ID 便並不難了。即使是不作任何的改動,估計 Face ID 也是能夠直接使用的(寫做時, iPhone X 還未上市),固然相關的體驗確定會打點折扣,畢竟文案以及相關的提示操做仍是在僅有 Touch ID 的前提下實現的。

與以往同樣,能夠經過 LAContext 類實現生物識別認證。不過須要注意的是,由於支持了新的 Face ID 認證,蘋果便爲 LAContext 類添加了新的接口 biometryType 用於區分 Touch ID 以及 Face ID。同時,以往僅涵蓋 Touch ID 的錯誤類型,也在 iOS 11 中廢棄了,相應的,蘋果提供了新的更通用的錯誤類型予以替代。

Reference

Positioning Content Within Layout Margins

directionalLayoutMargins

systemMinimumLayoutMargins

SafeAreaInsets

相關文章
相關標籤/搜索