天天看點

你可能需要為你的APP适配iOS11

作者:sonia,騰訊移動用戶端開發 工程師

商業轉載請聯系騰訊WeTest獲得授權,非商業轉載請注明出處。

原文連結:http://wetest.qq.com/lab/view/326.html

WeTest 導讀

iOS 11 為整個生态系統的 UI 元素帶來了一種更加大膽、動态的新風格。 本文介紹了iOS11在UI方面做了哪些更新,有些更新可以為使用者提供更加完美的體驗,但也有的可能會給目前的APP帶來異常bug。

前言

前幾天發現在做的APP在iOS11系統上動畫有異常,在其他系統的裝置上都是正常的,動畫的操作是觀察tableView的contentOffset變化後執行的,異常動畫發生在tableView reloadData之後,也就是說tableView reloadData之後,tableView的contentOffset發生了幾次變化。查了下資料發現原因是iOS11中預設開啟了Self-Sizing,在WWDC 2017 session204 Updating Your App for iOS 11 中有介紹,是以研究了下這個session,本文作為一個總結,下文的第三部分會有對上述的動畫異常的原因分析及解決方式。

本文内容包括:內建了搜尋的大标題欄、橫向頁籤欄、Margins 和 Insets以及 UIScrollView和UITableView 的更新和功能更強大的滑動操作。

一、在UIKit’s Bars中加入的新功能

WWDC通過iOS新增的檔案管理App:Files開始介紹,在Files這個APP中能夠看到iOS11中UIKit’s Bars的一些新特性:在浏覽功能上的大标題視圖(向上滑動後标題會回到原來的UI效果)、橫屏狀态下tab上的文字和icon會變為左右排列。我用iOS11的模拟器體驗了一下Files這個APP的豎屏和橫屏,如下圖所示:

你可能需要為你的APP适配iOS11
你可能需要為你的APP适配iOS11

(command+向左的箭頭讓模拟器橫屏)

橫屏時,在iPhone上,tab上的圖示較小,tab bar較小,這樣垂直空間可多放置内容。如果有人看不清楚tab bar上的圖示或文字,可以通過長按tab bar上的任意item,會将該item顯示在HUD上,這樣可以清楚的看清icon和text。對tool bar 和 navigation bar同理,長按item也會放大顯示。如下圖顯示:

你可能需要為你的APP适配iOS11

1、UIBarItem

UIBarItem是UI tab bar item和UI bar button item的父類,要想實作上面介紹的效果,隻需要為UIBarItem 設定landscapeImagePhone屬性,在storyboard中也支援這個設定,對于HUD的image需要設定另一個iOS11新增的屬性:largeContentSizeImage,關于這部分更詳細的讨論,可以參考 WWDC2017 Session 215:What’s New in Accessibility

2、控制大标題的顯示

在UI navigation bar中新增了一個BOOL屬性prefersLargeTitles,将該屬性設定為ture,navigation bar就會在整個APP中顯示大标題,如果想要在控制不同頁面大标題的顯示,可以通過設定目前頁面的navigationItem的largeTitleDisplayMode屬性;

你可能需要為你的APP适配iOS11

3、Navigation 內建 UISearchController

把你的UISearchController指派給navigationItem,就可以實作将UISearchController內建到Navigation。

你可能需要為你的APP适配iOS11

4、UINavigationController和滾動互動

滾動的時候,以下互動操作都是由UINavigationController負責調動的:

你可能需要為你的APP适配iOS11

是以,如果你使用navigation bar,組裝push和pop體驗,你不會得到searchController的內建、大标題的控制更新和Rubber banding效果,因為這些都是由UINavigationController控制的。

5、UIToolbar and UINavigationBar— Layout

在 iOS 11 中,當蘋果進行所有這些新特性時,也進行了其他的優化,針對 UIToolbar 和 UINavigaBar 做了新的自動布局擴充支援,自定義的bar button items、自定義的title都可以通過layout來表示尺寸。 需要注意的是,你的constraints需要在view内部設定,是以如果你有一個自定義的标題視圖,你需要確定任何限制隻依賴于标題視圖及其任何子視圖。當你使用自動布局,系統假設你知道你在做什麼。

6、Avoiding Zero-Sized Custom Views

自定義視圖的size為0是因為你有一些模糊的限制布局。要避免視圖尺寸為0,可以從以下方面做:

● UINavigationBar 和 UIToolbar 提供位置

● 開發者則必須提供視圖的size,有三種方式:

① 對寬度和高度的限制;

② 實作 intrinsicContentSize;

③ 通過限制關聯你的子視圖;

二、管理margins 和 insets

1、layout margins

基于限制的Auto Layout,使我們搭建能夠動态響應内部和外部變化的使用者界面。Auto Layout為每一個view都定義了margin。margin指的是控件顯示内容部分的邊緣和控件邊緣的距離。 可以用layoutMargins或者layoutMarginsGuide屬性獲得view的margin,margin是視圖内部的一部分。layoutMargins允許擷取或者設定UIEdgeInsets結構的margin。layoutMarginsGuide則擷取到隻讀的UILayoutGuide對象。

在iOS11新增了一個屬性:directional layout margins,該屬性是NSDirectionalEdgeInsets結構體類型的屬性:

你可能需要為你的APP适配iOS11

layoutMargins是UIEdgeInsets結構體類型的屬性:

你可能需要為你的APP适配iOS11

從上面兩種結構體的對比可以看出,NSDirectionalEdgeInsets 屬性用leading 和 trailing 取代了之前的 left 和 right。

directional layout margins屬性的說明如下:

你可能需要為你的APP适配iOS11

例子:當你設定了trailing = 30;當在一個right to left 語言下trailing的值會被設定在view的左邊,可以通過layout margins的left屬性讀出該值。如下圖所示:

你可能需要為你的APP适配iOS11

還有其他一些更新。自從引入layout margins,當将一個view添加到viewController時,viewController會修複view的的layoutMargins為UIKit定義的一個值,這些調整對外是封閉的。從iOS11開始,這些不再是一個固定的值,它們實際是最小值,你可以改變你的view的layoutMargins為任意一個更大的值。而且,viewController新增了一個屬性:viewRespectsSystemMinimumLayoutMargins,如果你設定該屬性為”false”,你就可以改變你的layout margins為任意你想設定的值,包括0,如下圖所示:

你可能需要為你的APP适配iOS11

2、安全區域(Safe Area)

如下圖:照片應用程式

你可能需要為你的APP适配iOS11

從iOS 7以來,我們在整個作業系統中都有這些半透明的bars,蘋果鼓勵我們通過這些bars繪制内容,我們是通過viewController 的edgesForExtendedLayout屬性來做這些的。 iOS 7 開始,在 UIViewController中引入的 topLayoutGuide和 bottomLayoutGuide 在 iOS 11 中被廢棄了,取而代之的就是safeArea的概念,safeArea是描述你的視圖部分不被任何内容遮擋的方法。 它提供兩種方式:safeAreaInsets或safeAreaLayoutGuide來提供給你safeArea的參照值,即 insets 或者 layout guide。 safeArea區域如下圖所示:

你可能需要為你的APP适配iOS11

如果有一個自定義的viewController,你可能要添加你自己的bars,增加safeAreaInsets的值,可以通過一個新的屬性:addtionalSafeAreaInsets來改變safeAreaInsets的值,當你的viewController改變了它的safeAreaInsets值時,有兩種方式擷取到回調:

你可能需要為你的APP适配iOS11

三、UIScrollView and UITableView 的新特性

1、 Scroll Views

如果有一些文本位于UI滾動視圖的内部,并包含在導航控制器中,現在一般navigationContollers會傳入一個contentInset給其最頂層的viewController的scrollView,在iOS11中進行了一個很大的改變,不再通過scrollView的contentInset屬性了,而是新增了一個屬性:adjustedContentInset,下面的兩張圖的對比能夠表示adjustContentInset表示的區域:

你可能需要為你的APP适配iOS11
你可能需要為你的APP适配iOS11

新增的contentInsetAdjustmentBehavior屬性用來配置adjustedContentInset的行為,該結構體有以下幾種類型:

你可能需要為你的APP适配iOS11

2、Table Views :在iOS 11中預設啟用Self-Sizing

這個應該是UITableView最大的改變。我們知道在iOS8引入Self-Sizing 之後,我們可以通過實作estimatedRowHeight相關的屬性來展示動态的内容,實作了estimatedRowHeight屬性後,得到的初始contenSize是個估算值,是通過estimatedRowHeight x cell的個數得到的,并不是最終的contenSize,tableView就不會一次性計算所有的cell的高度了,隻會計算目前螢幕能夠顯示的cell個數再加上幾個,滑動時,tableView不停地得到新的cell,更新自己的contenSize,在滑到最後的時候,會得到正确的contenSize。在測試Demo中,建立tableView到顯示出來的過程中,contentSize的計算過程如下圖:

你可能需要為你的APP适配iOS11

Self-Sizing在iOS11下是預設開啟的,Headers, footers, and cells都預設開啟Self-Sizing,所有estimated 高度預設值從iOS11之前的 0 改變為UITableViewAutomaticDimension:

你可能需要為你的APP适配iOS11

如果目前項目中沒有使用estimateRowHeight屬性,在iOS11的環境下就要注意了,因為開啟Self-Sizing之後,tableView是使用estimateRowHeight屬性的,這樣就會造成contentSize和contentOffset值的變化,如果是有動畫是觀察這兩個屬性的變化進行的,就會造成動畫的異常,因為在估算行高機制下,contentSize的值是一點點地變化更新的,所有cell顯示完後才是最終的contentSize值。因為不會緩存正确的行高,tableView reloadData的時候,會重新計算contentSize,就有可能會引起contentOffset的變化。iOS11下不想使用Self-Sizing的話,可以通過以下方式關閉:(前言中提到的問題也是通過這種方式解決的)

你可能需要為你的APP适配iOS11

iOS11下,如果沒有設定estimateRowHeight的值,也沒有設定rowHeight的值,那contentSize計算初始值是 44 * cell的個數,如下圖:rowHeight和estimateRowHeight都是預設值UITableViewAutomaticDimension 而rowNum = 15;則初始contentSize = 44 * 15 = 660;

你可能需要為你的APP适配iOS11

3、Table Views:separatorInset 擴充

OS 7 引入separatorInset屬性,用以設定 cell 的分割線邊距,在 iOS 11 中對其進行了擴充。可以通過新增的UITableViewSeparatorInsetReference枚舉類型的separatorInsetReference屬性來設定separatorInset屬性的參照值。

你可能需要為你的APP适配iOS11

下圖清晰的展示了這兩種參照值的差別:

你可能需要為你的APP适配iOS11

4、Table Views 和 Safe Area

有以下幾點需要注意:

● separatorInset 被自動地關聯到 safe area insets,是以,預設情況下,表視圖的整個内容避免了其根視圖控制器的安全區域的插入。

● UITableviewCell 和 UITableViewHeaderFooterView的 content view 在安全區域内;是以你應該始終在 content view 中使用add-subviews操作。

● 所有的 headers 和 footers 都應該使用UITableViewHeaderFooterView,包括 table headers 和 footers、section headers 和 footers。

5、滑動操作(Swipe Actions)

在iOS8之後,蘋果官方增加了UITableVIew的右滑操作接口,即新增了一個代理方法(tableView: editActionsForRowAtIndexPath:)和一個類(UITableViewRowAction),代理方法傳回的是一個數組,我們可以在這個代理方法中定義所需要的操作按鈕(删除、置頂等),這些按鈕的類就是UITableViewRowAction。這個類隻能定義按鈕的顯示文字、背景色、和按鈕事件。并且傳回數組的第一個元素在UITableViewCell的最右側顯示,最後一個元素在最左側顯示。從iOS 11開始有了一些改變,首先是可以給這些按鈕添加圖檔了,然後是如果實作了以下兩個iOS 11新增的代理方法,将會取代(tableView: editActionsForRowAtIndexPath:)代理方法:

你可能需要為你的APP适配iOS11

這兩個代理方法傳回的是UISwipeActionsConfiguration類型的對象,建立該對象及指派可看下面的代碼片段:

你可能需要為你的APP适配iOS11

建立UIContextualAction對象時,UIContextualActionStyle有兩種類型,如果是置頂、已讀等按鈕就使用UIContextualActionStyleNormal類型,delete操作按鈕可使用UIContextualActionStyleDestructive類型,當使用該類型時,如果是右滑操作,一直向右滑動某個cell,會直接執行删除操作,不用再點選删除按鈕,這也是一個好玩的更新。

你可能需要為你的APP适配iOS11

滑動操作這裡還有一個需要注意的是,當cell高度較小時,會隻顯示image,不顯示title,當cell高度夠大時,會同時顯示image和title。我寫demo測試的時候,因為每個cell的高度都較小,是以隻顯示image,然後我增加cell的高度後,就可以同時顯示image和title了。見下圖對比:

你可能需要為你的APP适配iOS11

總結

大概介紹了iOS 11的UI方面的一些更新,大部分内容都用代碼測試過了,有些更新确實是很實用,可以适配下iOS 11,有的更新可能會給現有APP造成bug,是以學習下這些内容還是很有必要的。

參考:

、Updating Your App for iOS  - WWDC  - Session  - iOS

、iOS 自動調整UITableView和UICollectionView布局

、Mysteries of Auto Layout, Part  - WWDC2015

、Mysteries of Auto Layout, Part  - WWDC2015
           

【騰訊WeTest iOS預審工具】

為了提高IEG蘋果稽核通過率,騰訊專門成立了蘋果稽核測試團隊,打造出iOS預審工具這款産品。經過兩年的内部營運,騰訊内部應用的iOS稽核通過率從平均35%提升到90%+。

現将騰訊内部産品的過審經驗共享給各位。在WeTest騰訊品質開放平台上可使用iOS預審工具,點選連結:http://wetest.qq.com/product/ios 咨詢體驗!

如果使用當中有任何疑問,歡迎聯系騰訊WeTest 企業QQ:800024531

iOS預審服務

【掃描工具】上傳IPA包、圖檔、視訊、應用描述即可進行測試; 多元度自動掃描提審材料的被拒風險;1小時内回報全面的掃描報告。

【專家預審】騰訊專家為您周遊App所有功能子產品;全面暴露App内容被拒風險;跟進問題直至上線(需提供官方拒絕郵件)。

【專家咨詢】資深預審專家一對一服務; 咨詢時間靈活可選,按需購買;有的放矢解 決稽核問題。

【ASO優化】專業團隊多元度深度剖析App的ASO現狀;圍繞App目标使用者群篩選高 度關聯的關鍵詞;幫助提升App在蘋果應用商店中的曝光率。