當前位置:
首頁 > 科技 > 58 同城 iOS 客戶端 iOS11及iPhone X 適配實踐

58 同城 iOS 客戶端 iOS11及iPhone X 適配實踐

關鍵時刻,第一時間送達!

一、前言

前段時間 WWDC 大會上蘋果推出了 iOS11 系統 和 iPhone X 新機型,相信各個 iOS 團隊的開發者都已經在計劃新系統和新機型的適配工作了。不得不說,新系統和新機型的發布確實是給 iOS 開發者帶來了不小的工作量,因此有必要將 58 同城 iOS 客戶端適配過程中遇到的問題跟大家分享一下。

二、iOS 11 UIKit

在適配 iOS 11 之前,我們首先要弄清楚 iOS 11 做了哪些改動,哪些改動會對我們現有的應用產生影響,這樣有助於我們分析現象。建議大家看下 WWDC 的官方視頻,視頻中提到了如何適配 iOS 11 及如何設計和適配 iPhoneX 新機型:

Updating Your App for iOS 11:https://developer.apple.com/videos/play/wwdc2017/204/

Building Apps for iPhone X:https://developer.apple.com/videos/play/fall2017/201/

Designing for iPhone X:https://developer.apple.com/videos/play/fall2017/801/

1. UIKit』sBars

圖 1 UIKit』sBar 提供 size 的方法截圖

iOS11 UIKit』sBar 改動很大,對現有的應用來說 UINavigationBar 的 titleView 適配應該是最大的問題,LargeTitle 雖然看起來似乎是很大的改動,但是實際上對現有的應用沒有太多的影響。iOS11 之後 UINavigationBar 和 UIToolbar 支持 Auto Layout ,開發者必須要提供自定義視圖的 size。如果自定義 titleView 中使用了 Auto Layout,那麼通過設置自定義 titleView 的 frame 的方式來設定 size 在某些場景下可能就不再合適了,開發者應該著重注意下自定義 view 內部的約束(具體案例將在下文中闡述)。如圖 1 所示,我們可以通過以下三種方式來提供 size:

約束自身寬高;

實現 intrinsicContentSize 方法;

利用子視圖寬高及間距來約束 titleView;

2. UIScrollView

圖 2 安全區域示意圖

iOS11 引入了安全區域的概念,默認情況下安全區域是指 NavigationBar 以下 TabBar 以上的區域(iPhoneX 則不包括底部的虛擬 Home 區域),也就是說 StatusBar、NavigationBar、TabBar、虛擬 Home 都不是安全區域(可參見圖 2)。為了幫助開發者判斷各個 view 與安全區域的距離關係,iOS11 在 UIView 加入了一個屬性:

需要注意的是這個屬性是描述 view 與頁面安全區域的距離關係,如果 view 的某個方向超過了安全區域則這個方向的數值為正數,如果 view 完全在安全區域內則 safeAreaInsets 的值全為 0。如在圖 3 所示一個空白頁面中。

圖3 安全區域測試圖

紅、藍 view 是父子視圖的關係,且藍色子視圖完全在紅色視圖內,由於紅、藍兩個 view 頂部超過安全區域 44 點(狀態欄 44 高度),所以它們的 safeAreaInsets 都為 ,也就是說在同一個控制器中,他們的安全區域是一致的,與視圖層級無關。在一個控制器中,安全區域並不是固定不變的,可以通過 ViewController 的 additionalSafeAreaInsets 方法來修改頁面的安全區域,如果此時將安全區域上延 11 個點。

那麼它們的 safeAreaInsets 都會變為 。

我們再來看 UIScrollView 新增加的兩個很重要的屬性:

iOS 11 中 adjustedContentInset 是用來調整 scrollView 內容邊距的屬性,這個屬性實際上是 contentInset 和 safeAreaInsets 在各個方向上的加和,即 contentInset+safeAreaInsets。contentInsetAdjustmentBehavior 是控制採取何種策略來控制調整邊距的屬性,默認為 UIScrollViewContentInsetAdjustmentAutomatic。在 iOS 11 之前,控制是否自動調整內邊距的屬性是 UIViewController 的 automaticallyAdjustsScrollViewInsets,但是這個屬性現在已經廢棄,取而代之的是 UIScrollView 的 contentInsetAdjustmentBehavior 。contentInsetAdjustmentBehavior 共有四種設置,如圖 4 所示:

圖 4 contentInsetAdjustmentBehavior

如果 UIScrollView 需要調整內容邊距則加上安全區域的偏移,即 adjustedContentInset = contentInset+safeAreaInsets,如果不調整則 safeAreaInsets 不參與到計算中,即 adjustedContentInset = contentInset。簡而言之,contentInsetAdjustmentBehavior 就是告訴 UIScrollView 在計算 adjustedContentInset 時要不要加上 safeAreaInsets。

3. UITableView

UITableView 除了繼承自 UIScrollView 的特性外,還有自身 API 的變動。新系統中,UITableView 開啟了估算行高,estimatedRowHeight、 estimatedSectionHeaderHeight、estimatedSectionFooterHeight 不再默認是 0,而是 UITableViewAutomaticDimension(這個值列印輸出是 -1),這一舉措旨在幫助開發者提高性能,減少 heightForRowAtIndexPath: 方法的的調用次數,但是這樣會導致 API 執行順序發生變化。在沒有開啟估算行高之前 tableView 總是先執行:

再執行

API 調用順序如圖 5 所示:

圖 5 關閉預估高度方法執行順序列印

而開啟估算行高之後 tableView 會先執行:

再執行:

方法調用次數減少且調用順序變為如圖 6 所示:

圖 6 開啟預估高度方法執行順序列印

由此可見:

的調用次數大大減少。但是如果開發者實現:

方法,那麼無論是否關閉 estimatedRowHeight,API 的調用順序和調用次數都是一樣的,如圖 7 所示。

圖 7 實現預估高度方法後方法執行順序列印

因此我們要注意新系統中 API 執行順序和執行次數的變化。

除此之外我們還應該注意在開啟預估高度tableView.estimatedSectionHeaderHeight = UITableViewAutomaticDimension; 的情況下,如果不實現

那麼

是不會被執行的。

三、58 同城的實戰經歷

適配 iPhoneX 首先要添加一個 1125x2436 的啟動圖,否則會在屏幕的上方和下方留下兩道黑框。在適配開始前先簡單介紹下 58 同城 iOS 客戶端的頁面結構,因為頁面結構不同可能遇到的問題會不盡相同。58 同城 iOS 客戶端的 key window 的 rootViewController 是一個導航控制器,而不是 UITabBarController。導航控制器的 rootViewController 是一個 UITabBarController 樣式(非 UITabBarController)的 ViewController。了解了應用的結構之後,下面介紹下 58 同城 iOS 客戶端適配過程中遇到的比較典型的問題:

1. 首頁

先看下適配之前的首頁,如圖 8 所示:

圖 8 適配前首頁圖

適配前,首頁主要存在的問題有 3 個:

頂部的天氣與狀態欄有重疊;

首頁的動畫默認已經進行了一部分;

底部的 tabBar 在非安全區域內。

先看問題 1,產生問題 1 的原因是由於 iPhoneX 的狀態欄高度產生了變化,由 20 變成了 44,天氣組件的位置沒有預留出新增的 24 點,導致與狀態欄重疊。解決的辦法是對頂部 view 進行重新布局約束,根據機型來判斷是夠需要增加 24 點。機型的判斷由於沒有真機驗證,我們無法準確地獲取到設備名稱,因此暫時採用設備高度來判斷機型。如果屏幕高度為 812,則暫時可以認為是 iPhoneX:

問題 2 的原因是 tableView 的安全區域的引起的。由於首頁 tableView 的 contentInsetAdjustmentBehavior 默認為 UIScrollViewContentInsetAdjustmentAutomatic, 在隱藏導航欄的情況下 tableView 的內容偏移為安全區域的 44 點,因此 tableView 自動偏移了 44 點,看起來像動畫進行了一半。解決方法是將 contentInsetAdjustmentBehavior 設置為 never 忽略安全區域偏移。

問題 3 的原因是 iPhoneX 新增了 34 點高度的虛擬 home 區域替代了 home 實體鍵。虛擬 home 區域並不是安全區域,因此需要在 iPhoneX 的機型上,屏幕底部預留出 34 點高度。

適配後首頁在 iPhoneX 上的效果如圖 9:

圖 9 適配後首頁圖

2. 列表

在適配過程中,我們發現在二手物品列表,上拉載入數據時底部自定義的 tabBar 會來回反覆地進行顯示隱藏,tabBar 為下圖藍框內區域,如圖 10 所示:

圖 10 帶有 tab 的列表圖

經過排查我們發現 tableView 的 contentOffset 在有新的內容被添加進來的時候會產生跳動,如:不斷上拉的一個列表,列表的 contentOffset 的 Y 值會不斷增大,如果上拉過程中觸發網路請求添加了新的內容到列表中,那麼列表的 contentOffset 會產生一個跳動。如圖 11 所示,在 contentOffset 的 Y 值為 9000 左右的時候列表新增了一頁數據,導致 contentOffset 的 Y 值變為 3000 左右。

圖 11 上拉載入更多時 scrollView 的偏移量 Y 值列印圖

當下拉返回頂部時,contentOffset 的 Y 值會產生一系列不連續的跳動,如當前 contentOffset 的 Y 值為 3600,下拉後變為 3500 左右然後又變為 3600 左右,如圖 12 列印所示:

圖 12 下拉返回時 scrollView 的偏移量 Y 值列印圖

如果利用 contentOffset 的 Y 值變化判斷滑動方向的話,那麼現在會判斷為方向時而向上時而向下,導致底部的 tabBar 反覆隱藏顯示。

問題根本原因是由於

方法中獲取 aScrollView 的偏移量會存在跳動的現象。具體的解決辦法與各自的實現邏輯有較強的關聯,在此不進行詳細闡述。

3. IM

在使用 iOS11 beta 版時我們發現 IM 會話頁的很多消息都變成了未識別消息類型,圖 13 中左側為適配前效果,右側為適配後效果:

圖 13 IM 會話頁適配前後對比圖

當消息滑出屏幕再滑回來時消息才被正確解析出來。最開始以為是 Xcode9 beta 版的 bug,因為只有 Xcode 9 打包安裝的應用才會出現這種問題,而通過 Xcode8 正式版打包後安裝到 iOS11 的手機上並不會出現這種問題。不過隨著 beta 版的不斷穩定,這種現象並沒有隨之消失,因此我們覺得有必要查看下消息不識別的具體原因。經過排查後我們發現,異常情況出現的原 l 因是由於 tableView 沒有取到可重用的 cell,代碼如圖 14:

圖 14 tableView 獲取 Cell 的方法圖

會話頁將未取到 cell 作為一種異常情況進行了處理,處理方式是將這種情況當做未識別消息展示,處理邏輯如圖 15 所示:

圖 15 IM 會話列表異常處理代碼

經過分析,取不到 cell 很有可能是因為 cell 沒有被註冊,而之前代碼中註冊 cell 的邏輯在

方法中。由於 iOS11 默認開啟了 estimatedRowHeight,導致

方法先執行,

後執行,因此 cell 沒有被註冊成功,代碼走進了異常處理邏輯。為了盡量不修改之前的業務邏輯,我們採用的適配方式的將估算高度關閉。

除此之外,IM 還存在適配安全區域的問題。未適配之前,IM 會話頁面的輸入框在 iPhoneX 的非安全區域內,見圖 16 標註區域。

圖 16 IM 會話頁底部輸入未適配前圖

因此頁面的底部需要設置 34 點的邊距。iPhoneX 與其他手機很明顯的不同就是鍵盤的高度,iPhoneX 的鍵盤高度為 333,這個鍵盤是從屏幕底部抬起並跨過了底部的虛擬 home 區域,鍵盤在安全區域內的高度為(333-34)。因此在監聽鍵盤的事件中要注意對抬起高度的處理,以 IM 會話頁為例,在非 iPhoneX 手機上輸入框會上升 keyboardHeight 的高度,但是在 iPhoneX 上則只需要上升 keyboardHeight-34 點的高度。在 iPhoneX 手機上我們要格外注意鍵盤高度和安全區域結合帶來的潛在問題。

4. 發布

在適配發布頁面的時候,我們發現小區搜索頁面的 titleView 在 iOS11 手機上變得很小,沒有完全展開,效果如圖 17 所示:

圖 17 小區選擇未適配前圖

圖 16 中的導航欄的 titleView 是 WBPUBSearchBar 類型的。由於自定義的 WBPUBSearchBar 是原來是通過 initWithFrame: 來創建並指明寬高的,WBPUBSearchBar 內部布局是依賴 Masonry 的,但是布局並沒有設置 textFiled 和 button 的寬度,僅僅指明了 textFiled 和 button 各自的左右間距。在此情況下,自定義的 titleView 無法依賴內部子視圖的約束得到正確的寬度,因此 titleView 的寬度並不正確。因此需要實現方法:

向外界提供自身的 size。當然除了實現 intrinsicContentSize 方法外也可以通過添加自身約束的方式來設置自身的正確合理的 size。

5. 視頻

58 同城的二手房詳情頁支持視頻展示房源,當全屏播放視頻的時候會導致虛擬 home 條遮擋住視頻內容,適配前視頻全屏播放效果如圖 18。

圖 18 房源視頻全屏播放圖

由於在播放視頻的時候有交互較少,蘋果允許開發者在當前頁面隱藏虛擬 home 條。因此可以在視頻播放控制器中實現下述方法來隱藏虛擬 home 條:

在適當時機刷新虛擬 home。

實現 prefersHomeIndicatorAutoHidden 方法,返回是否需要隱藏虛擬 home。

當用戶觸碰屏幕時虛擬 home 會再次出現。這樣就可以解決橫屏虛擬 home 遮擋屏幕的問題。

6. 寵物

在使用 iOS11 beta 版時我們發現導航欄的自定義返回按鈕距離屏幕左側過遠。這種情況只有在 Xcode9 編譯時才會出現,Xcode8 編譯打包在 iOS11 beta 的手機上運行並不會出現這種問題。對比導航欄的視圖層級結構我們發現導航欄的層級做了很大的變動。

圖 19 iOS 11 前導航欄層級結構和實際展示圖

圖 20 iOS11 導航欄層級結構和實際展示圖

在 iOS11 下設置 UIBarButtonItem 時,系統新增了 _UINavigationBarContentView 和 _UIButtonBarStackView 兩層 View,新增的兩層 frame 是有偏移的,而且布局採用的是約束方式。因此需要遍歷層級找到 _UIButtonBarStackView 這層視圖並修改它與 _UINavigationBarContentView 間的約束,修改約束的時機放自定義導航控制器的在 viewDidLayoutSubviews 方法中。修改約束關鍵代碼如圖 21 所示:

圖 21 修改約束關鍵代碼

四、總結

總結一下,58 同城 iOS 客戶端在適配過程中遇到了以下問題:

由於安全區域的引入,引起了 UIScrollView 的偏移。安全區域引起的 UIScrollView 偏移導致的問題非常多,文中僅僅以首頁為例向大家展示了 58 同城的解決方式。當然,解決方式有很多種,只要了解問題的原因,那麼解決方式可以根據自己的場景自由決定。文中沒有提及 RN 和 web 的適配方式,主要原因是 RN 和 web 的適配方式與 Native 的適配方式類似,最主要的工作仍然是適配安全區域。

tableView 的變化總體而言還是很大的,因此需要我們仔細觀察現象排查代碼。

對於導航欄的適配,我們應該注意 titleView 的內部約束,返回按鈕的適配需要注意的是修改約束的時機。

鍵盤抬起本身並不會引起適配問題,但是涉及到安全區域的適配可能就會導致 UI 處理不正確。

開發者應該根據場景來控制虛擬 home 的顯示和隱藏。

以上是 58 同城 iOS 客戶端在適配過程中遇到的問題。當然,實際適配過程中遇到的問題遠比文中描述的多,且情況要更為複雜。文中只是列出了一些比較典型的問題,希望對讀者有所幫助。

-------- 熱聞回顧 --------


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

從工程師轉變為工程經理過程中所學到的
今天去買化妝品,接待我的導購是位機器人
你的工資是高了還是低了,薪資計算器會告訴你
微軟新推三款機器學習工具 幫助開發者打造強AI應用
盤點各大互聯網公司2017中秋月餅設計,你最喜歡哪一個?

TAG:CSDN |