適配iOS11&iPhoneX的一些坑
下面從幾個方面講解iOS11的特性,和iPhone X的那些事
螢幕
我們在編碼中使用的是以point為單位的螢幕尺寸(在不同裝置上每個point對應的實際畫素點是不一樣的),iPhone 8的尺寸是375pt × 667pt @2x,iPhone X的尺寸是375pt × 812pt @3x,也就是螢幕寬度是一樣的,但是高出了145pt,大概能多顯示20%的內容。
螢幕
注意iPhone X的螢幕素質比較好,所以它需要載入較高畫素的圖片,我們要提供必要的@3x資源。
另外由於iPhone X極高的長寬比,我們用作背景的圖片都需要重新設計,以保證比例適合,內容被裁切後效果仍然ok。
螢幕
status bar
iPhone X的狀態列高度是高於之前所有的iPhone版本的,所以在程式碼裡寫死kStatusBarHeight == 20的地方都會出錯!
劉海變高
也就是說,之前使用固定狀態列高度來進行佈局的程式碼都需要調整為動態佈局方式,否則會出現內容被遮擋的問題。特別是圖中這個64,一直在iOS開發中常數般的存在,最後也被蘋果爸爸擺了一道。
此處不能寫死
關於狀態列另外兩個需要注意的地方:
-
不要在iPhone X下隱藏狀態列,一個原因是顯示內容足夠高了,另一個是這樣內容會被劉海切割。
-
現在通話或者其它狀態下,狀態列高度不會變化了,程式不需要去做相容。
高度不變
佈局
iPhone X的佈局有很多改變,這是因為:
-
劉海的存在使橫屏的情況變得複雜
-
四個角變圓,需要防止內容被切割
-
代替home鍵的長條(不知道官方名字叫什麼,homeBar?)使得螢幕底部的佈局需要調整
很多系統經典的控制元件,比如TableView,Collection,Navigation,TabBar等都會自動適應iPhone X(呵呵,一定會有很多坑等著打補丁或者讓開發者擦屁股)。
Safe Area
iOS11引入了safeArea的概念,用來替代之前的topLayoutGuide和bottomLayoutGuide,safeArea用來描述檢視不被任何內容遮擋的部分。它提供兩種方式:safeAreaInsets
safeArea
在iPhone X上,檢視的預設safeArea如下圖所示:
safeArea2
可以看到,底部的Bar還有四周圓角,都對這個safeArea進行了切割。蘋果官方的設計指導是使用以safeArea為框,以layoutMargin為間距來進行UI佈局。
橫屏
在橫屏狀態下,不能因為劉海的原因將內容向左或者向右便宜,要保證內容的中心對稱:
center
center2
另外,之前所有版本的iPhone tableView的cell和它的contentView的大小是相同的,開發者相對cell佈局和相對contentView佈局效果上不會有太大區別。
但是在iPhone X下,由於劉海和圓角的存在,tableView的contentView會被裁切,所以所有的佈局都應該被調整為相對contentView佈局,否則會越界:
tableView
一致性
蘋果對iOS中執行的app的一致性有很高的要求,在iPhone X下,開發者不能自己去遮蓋圓角、狀態列,特別是底部的homeBar,即使你認為劉海很醜,也許使用者看著看著就習慣了呢……
關於homeBar,它的顏色是會自動適應的,保持著『時刻能找到但是又不那麼顯眼』的狀態,所以開發者不用去為它費心新增背景啊,強調啊啥的,蘋果爸爸會十分感動然後拒絕掉你的……
互動
在互動方面,iPhone X最大的改變就是底部那個無時無刻不存在的homeBar了,代替了原來home按鍵的功能,系統級的任務切換和回到桌面 、、,都是上滑這個細細的長條。
homeBar
所以蘋果爸爸的意思是:
趕緊把你自己寫的上滑手勢乖乖刪掉~
當然如果app確實需要這個手勢,可以開啟程式開關覆蓋系統的手勢,但是這樣使用者就需要滑動兩次來回到桌面了,這會讓他們非常懷念home鍵。
相容
首先是iPhone X下的鍵盤和其他系統有區別,會多出來那個很有趣的animateEmoji工具欄,所以在做鍵盤相關處理的時候要關注相容性問題,至少:高度不要寫死了……
iPhone X的認證使用的是全新的faceId,所以如果app需要使用認證相關api,需要根據裝置區分touchId和faceId,可以看文件。
認證
導航欄
導航欄高度的變化
iOS11之前導航欄預設高度為64pt(這裡高度指statusBar + NavigationBar),iOS11之後如果設定了prefersLargeTitles = YES則為96pt,預設情況下還是64pt,但在iPhoneX上由於劉海的出現statusBar由以前的20pt變成了44pt,所以iPhoneX上高度變為88pt,如果專案裡隱藏了導航欄加了自定義按鈕之類的,這裡需要注意適配一下。
導航欄圖層及對titleView佈局的影響
iOS11之前導航欄的title是新增在UINavigationItemView上面,而navigationBarButton則直接新增在UINavigationBar上面,如果設定了titleView,則titleView也是直接新增在UINavigationBar上面。iOS11之後,大概因為largeTitle的原因,檢視層級發生了變化,如果沒有給titleView賦值,則titleView會直接新增在_UINavigationBarContentView上面,如果賦值了titleView,則會把titleView新增在_UITAMICAdaptorView上,而navigationBarButton被加在了_UIButtonBarStackView上,然後他們都被加在了_UINavigationBarContentView上,如圖:
所以如果你的專案是自定義的navigationBar,那麼在iOS11上執行就可能出現佈局錯亂的bug,解決辦法是重寫UINavigationBar的layoutSubviews方法,調整佈局,上程式碼:
- (void)layoutSubviews {
[super layoutSubviews];
//注意導航欄及狀態列高度適配
self.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), naviBarHeight);
for (UIView *view in self.subviews) {
if([NSStringFromClass([view class]) containsString:@"Background"]) {
view.frame = self.bounds;
}
else if ([NSStringFromClass([view class]) containsString:@"ContentView"]) {
CGRect frame = view.frame;
frame.origin.y = statusBarHeight;
frame.size.height = self.bounds.size.height - frame.origin.y;
view.frame = frame;
}
}
}
再補充一點,看了簡書App適配iOS11發現titleView支援autolayout,這要求titleView必須是能夠自撐開的或實現了- intrinsicContentSize方法
- (CGSize)intrinsicContentSize {
return UILayoutFittingExpandedSize;
}
UIScrollView、UITableView、UICollectionView
大家在iOS11裝置上執行出現最多問題應該就是tableview莫名奇妙的偏移20pt或者64pt了。。原因是iOS11棄用了automaticallyAdjustsScrollViewInsets屬性,取而代之的是UIScrollView新增了contentInsetAdjustmentBehavior屬性,這一切的罪魁禍首都是新引入的safeArea,關於safeArea適配這篇文章iOS 11 安全區域適配總結講的很詳細,感興趣的可以看下,我直接貼適配程式碼,因為低版本直接用contentInsetAdjustmentBehavior會報警告,所有定義瞭如下的巨集(感謝@炒雞範的指正,之前的巨集犯了個低階錯誤...現改為)
#define adjustsScrollViewInsets(scrollView)\
do {\
_Pragma("clang diagnostic push")\
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"")\
if ([scrollView respondsToSelector:NSSelectorFromString(@"setContentInsetAdjustmentBehavior:")]) {\
NSMethodSignature *signature = [UIScrollView instanceMethodSignatureForSelector:@selector(setContentInsetAdjustmentBehavior:)];\
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];\
NSInteger argument = 2;\
invocation.target = scrollView;\
invocation.selector = @selector(setContentInsetAdjustmentBehavior:);\
[invocation setArgument:&argument atIndex:2];\
[invocation retainArguments];\
[invocation invoke];\
}\
_Pragma("clang diagnostic pop")\
} while (0)
還有的發現某些介面tableView的sectionHeader、sectionFooter高度與設定不符的問題,在iOS11中如果不實現 -tableView: viewForHeaderInSection:和-tableView: viewForFooterInSection: ,則-tableView: heightForHeaderInSection:和- tableView: heightForFooterInSection:不會被呼叫,導致它們都變成了預設高度,這是因為tableView在iOS11預設使用Self-Sizing,tableView的estimatedRowHeight、estimatedSectionHeaderHeight、 estimatedSectionFooterHeight三個高度估算屬性由預設的0變成了UITableViewAutomaticDimension,解決辦法簡單粗暴,就是實現對應方法或把這三個屬性設為0。
如果你使用了Masonry,那麼你需要適配safeArea
if (@available(iOS 11.0, *)) {
make.edges.equalTo()(self.view.safeAreaInsets)
} else {
make.edges.equalTo()(self.view)
}
iPhoneX
LaunchImage
關於iPhoneX(我就不吐槽劉海了...),如果你的APP在iPhoneX上執行發現沒有充滿螢幕,上下有黑色區域,那麼你應該也像我一樣LaunchImage沒有用storyboard而是用的Assets,解決辦法如圖,啟動圖的尺寸為1125x2436,or you can iOS開發時如何使用 Launch Screen Storyboard。
TabBarController
因為我們的專案用了第三方的TabBarController,在iPhoneX執行,tabBar看起來怪怪的...估計作者要等到猴年馬月才適配iPhoneX,專案又著急上線,就自己修改了第三方,主要是tabBar高度及tabBarItem偏移適配,iPhoneX由於底部安全區的原因UITabBar高度由49pt變成了83pt,可以通過判斷機型來修改相關介面程式碼
#define kDevice_Is_iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO)
目前遇到的就這些坑,歡迎大家指正補充~