這是一次公司內部技術分享會的內容,內容共分爲三個部分:html
iOS 11正式版已經來了,做爲一個iOS開發者,這意味着沒有適配iOS 11都晚了。好在還在Beta階段我司技術大牛達叔第一時間體驗了一把,並仔細的跑了一遍播放端APP觸手TV
和錄製端APP觸手錄
,除了有一個由第三方庫WebViewJavascriptBridgeBase
引發的嚴重crash,兩個APP在iOS 11下基本沒什麼問題,發現的問題已經被達叔提早fixed了。因此組裏一直沒有進行適配工做,而是把精力放在了最近的大版本開發上。觀察發現,直接從AppStore下載的應用,在iOS 11上跑起來是沒有什麼問題的,若是使用Xcode 9 Building後在運行,就或多或少的出現問題。由於Xcode 9 的Base SDKS是基於iOS 11的。因此仍是須要進行適配的。ios
經過閱覽網上適配iOS 11的同行案列,結合觸手TV
APP實際問題,通過彙總,如下多是須要適配的點。找到問題的根源,才能幫助咱們解決問題。
爲何會出現上述問題呢?咱們看看iOS 11有些什麼新增改動。
這裏只列出部分以及跟今天主題相關的部分。詳情能夠看看官網what's new in iOS 11。安全
UIViewController
廢棄LayoutGuide
。iOS7以後,爲了輔助Autolayout佈局系統,Apple新增UILayoutSupport
協議。就是被不少人忽略的topLayoutGuide
,bottomLayoutGuide
。@interface UIViewController (UILayoutSupport)
// These objects may be used as layout items in the NSLayoutConstraint API
@property(nonatomic,readonly,strong) id<UILayoutSupport> topLayoutGuide API_DEPRECATED_WITH_REPLACEMENT("-[UIView safeAreaLayoutGuide]", ios(7.0,11.0), tvos(7.0,11.0));
@property(nonatomic,readonly,strong) id<UILayoutSupport> bottomLayoutGuide API_DEPRECATED_WITH_REPLACEMENT("-[UIView safeAreaLayoutGuide]", ios(7.0,11.0), tvos(7.0,11.0));
@property(nonatomic) UIEdgeInsets additionalSafeAreaInsets API_AVAILABLE(ios(11.0), tvos(11.0));複製代碼
這個兩個屬性是readonly
的,通常咱們也用不到,主要做用是輔助Controller的View在佈局時知道從哪裏開始佈局,到什麼地方結束佈局。在使用SB或者XIB文件佈局的時候,能夠進行設置,肯定開始佈局和結束佈局的參考點。在iOS 11 已經標記API_DEPRECATED_WITH_REPLACEMENT
。取而代之的是UIView
新的APIsafeAreaInsets
。
bash
UIView
增長safeAreaInsets:UIEdgeInsets
安全區概念。用於替代輔助自動佈局的LayoutGuide
。安全區域定義了佈局View除各類Bar後剩下的可見區域。此屬性是readonly
的,想要改動,須要操做UIViewController
的additionalSafeAreaInsets
屬性。app
margin(若是有設置過)
後的可用佈局區域。 圖中淡藍色區域即爲安全區域:
ide
若是在View上的控件沒有被遮擋,safeAreaInsets = {(0,0),(0,0)}
分別對應於(top,left,bottom,right)
。
假設子ViewA頂部被navigationBar
遮擋20pt,相應safeAreaInsets = {(20,0),(0,0)}
。佈局
safeAreaInsets
最低支持版本爲iOS 9.0。考慮到兼容性,觸手TV是不能使用安全區域的。 UIViewController
廢棄automaticallyAdjustsScrollViewInsets
API。這是個bool值屬性,描述了是否自動適配scrollView的contentInsets。值爲true
時,scrollView的內容不會被系統可見的各類bar所遮擋(statusbar
、navigationBar
,tabBar
,toolBar
),通常會引發tableView
,collectionView
,scrollView
的content下移bar的高度值個像素。若是不須要自動調整,將值設爲false
。post
UIViewController
新增修改關聯View
安全區域的APIadditionalSafeAreaInsets
。可對安全區域的值進行增減,知足自定義UI的需求。並提供方法viewSafeAreaInsetsDidChange
,用於在安全區域改變後,進行佈局的調整。ui
相應的,UIScrollView
增長枚舉屬性contentInsetAdjustmentBehavior
,描述scrollView
如何調整contentInset(實際是調整adjustedContentInset
屬性),配合安全區域使用。最後UIscrollView
的contentInset值爲adjustedContentInset
與safeAreaInsets
之和。atom
有4個可選值:
typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {
UIScrollViewContentInsetAdjustmentAutomatic, //與 automaticallyAdjustsScrollViewInsets 相似
UIScrollViewContentInsetAdjustmentScrollableAxes, // 在滾動的當前軸向上進行自動調整
UIScrollViewContentInsetAdjustmentNever, //不進行任何的調整
UIScrollViewContentInsetAdjustmentAlways, // contentInset等於View的safeAreaInsets
} API_AVAILABLE(ios(11.0),tvos(11.0));複製代碼
UIScrollViewDelegate
新增scrollViewDidChangeAdjustedContentInset
方法,當adjustedContentInset
改變後通知使用者進行佈局調整。
總結:
UIscrollView
或子類,若是設置了UIScrollViewContentInsetAdjustmentBehavior
不等於UIScrollViewContentInsetAdjustmentNever
,系統會自動適配UIScrollView
的內容都在安全區域內,保證內容不被各類Bar遮住。NavigationBar
多了個contentView
,這個View在開啓了大標題時,上面會有個titleLable。NavigationBar
的titleView
支持自動佈局。須要使用者自動撐開添加到上面的View。
在iOS11,原有的NSLocationAlwaysUsageDeion
被降級爲NSLocationWhenInUseUsageDeion
。
在.plist
沒文件中配置NSLocationAlwaysAndWhenInUseUsageDeion
,系統框纔會彈出,使用requestAlwaysAuthorization獲取權限。
navigationBar.prefersLargeTitles = true 新增大標題屬性,大標題displaymode控制顯示枚舉:navigationItem.largeTitleDisplayMode
枚舉屬性:
- automatic:自動保存上一次設置的值
- always:老是顯示大標題
- never:不顯示複製代碼
Navigation集成searchBar。navigationItem.searchController 屬性賦值能夠集成searchBar在navigationbar下方,navigationItem.hidesSearchBarWhenScrolling屬性控制在滾動時是否自動隱藏。
確保避免 size 爲 0 的自定義view,實現intrinsicContentSize 方法提供默認尺寸。
tableview開啓開啓高度估算(Self-Sizing),設置下面三個屬性,使高度估算失效:
tableView.estimatedRowHeight = 0
tableView.estimatedSectionHeaderHeight = 0
tableView.estimatedSectionFooterHeight = 0複製代碼
tableview 新增左滑右滑交互。
廢棄iOS7 之後的layoutMargins,取而代之的是新增的安全區域的概念。
新增directionalLayoutMargins。對應layoutMargins。
新增systemMinimumLayoutMargins,當directionalLayout小於systemMinimumLayoutMargins,使用systemMinimumLayoutMargins。
新增 UIViewController=.viewRespectsSystemMinimumLayoutMargins,默認爲FALSE。設爲TRUE,可設置任意值。
明白了iOS 11大法,再來看看,這大法帶來的各類問題。
通常使用NavigationBar
基本有三種手法。
NavigationBar
上添加任何的控件。NavigationBar
上有定製的控件。navigationBar
,直接用了View替代。針對上面三種狀況:
使用第一種姿式的人,很幸運,你不須要進行適配。(估計不多有人不定製)
使用第二種姿式的人,可能會有返回按鈕、titleView、添加的控件position
不正確的問題。
使用第三種姿式的人,很好,在非iPhone X上,也沒什麼大問題。
升級iOS 11後,發現有些使用UItableView
佈局的頁面,頂部多出來了20pt或者44pt,也或者64pt。也就是頂部可見Bar的高度的總和。包括使用MJRefresh引發的問題。也屬於這類。
升級Xcode9 後,你會發現,基於iOS 11打出來的包,tableView滑動的時候,一卡一頓的。
在iOS 11有些應用在請求定位時,未能彈出系統請求權限的對話框。
iOS 11的的leftBarButtonItem 或者右邊都距離邊距20像素。
YYKit
的大圖預覽控件YYPhotoGroupView
,dismiss的時候,有些頁面會抖一下。既然已經知道了iOS 11初出江湖的各類套路和帶來的血雨腥風。那就春風化雨,見招拆招了。
NavigationBar
引入了AutoLayout
,以往的frame
方式可能位置有誤差。之前使用CGRectMakeZero
自動撐大已經行不通。那麼使用自動佈局。或者實現下面View
的方法,提供默認尺寸。- (CGSize)intrinsicContentSize {
return CGSizeMake(100,100);
}複製代碼
NavigationBar
View可能會出各類問題,嘗試添加到contentView
上。並設置約束。frame
在賦值給navigationItem.leftBarButtonItem
。由於controllerView
廢棄了automaticallyAdjustsScrollViewInsets
,請使用UIScrollViewContentInsetAdjustmentNever
來告訴系統,不要調整。或者設置additionalSafeAreaInsets
來增長safeAreaInsets
來抵消。
由於tableView
默認開啓Self-Sizing
。設置下面三個屬性,使高度估算失效:
tableView.estimatedRowHeight = 0
tableView.estimatedSectionHeaderHeight = 0
tableView.estimatedSectionFooterHeight = 0複製代碼
在iOSi11,原有的NSLocationAlwaysUsageDeion被降級爲NSLocationWhenInUseUsageDeion。所以,在原來項目中使用requestAlwaysAuthorization獲取定位權限,而未在plist文件中配置NSLocationAlwaysAndWhenInUseUsageDeion,系統框不會彈出。建議新舊key值都在plist裏配置。
參照內容下沉問題解決。
iOS 11後,navigationBar新增了contentView
來承載開發者添加的barButton
。左右兩邊新增了20像素。如今很幾種解決方案。若是隻是想要調節返回按鈕,能夠直接使用系統的API:
@property(nullable,nonatomic,strong) UIImage *backIndicatorImage;
@property(nullable,nonatomic,strong) UIImage *backIndicatorTransitionMaskImage;複製代碼
可是這樣的處理方式,在有多個按鈕的使用情景下就引發問題。20個像素仍是存在的。
還有調整UIButton
的imageEdgeInsets
的,其實也會在多按鈕的時候出現佈局問題。
好比這篇帖子。
也有的在push
和pop
的時候進行設置的。修改約束會引發有些約束丟失。也有重寫drawRect的。
其實不須要這麼麻煩。新建一個類,繼承自UINavigationBar
,而後重寫layoutSubviews
,若是是ios11,設置contentView
的layoutMargins
爲須要的值,以前的版本就執行super
。
核心代碼以下:
@interface CustomNavigationBar:UINavigationBar
@end
const CGFloat LeftFiexSpace = 0;
const CGFloat RightFiexSpace = 8.0;
@implementation CustomNavigationBar
- (void)layoutSubviews {
[super layoutSubviews];
// 修正 ios 11 左右兩邊的邊距
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"11.0")) {
self.layoutMargins = UIEdgeInsetsZero;
for (UIView *subview in self.subviews) {
if ([NSStringFromClass(subview.class) containsString:@"ContentView"]) {
subview.layoutMargins = UIEdgeInsetsMake(0, LeftFiexSpace, 0, RightFiexSpace);
[self layoutIfNeeded];
}
}
}
}
@end複製代碼
在生成NavigationController
的地方使用KVC
將原來的NavigationBar
替換成本身的.
UINavigationController *nvc = [super initWithRootViewController:rootViewController];
CSNavigationBar *naviBar = [[CustomNavigationBar alloc] init];
[nvc setValue:naviBar forKey:@"navigationBar"];複製代碼
若是有寫得不對的歡迎指正,有更高好的解決方法,也歡迎交流。
你可能須要爲你的APP適配iOS11此篇基本上是官方視頻的文字版