iOS11適配詳解

題外話

這是一次公司內部技術分享會的內容,內容共分爲三個部分:html

  • Xcode9新特性
  • iOS 11 適配
  • iPhone X適配
    這是第二部分,若有須要請持續關注。
    第一部分Xcode9新特性
    言歸正傳

掀起江湖恩怨

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的同行案列,結合觸手TVAPP實際問題,通過彙總,如下多是須要適配的點。找到問題的根源,才能幫助咱們解決問題。
爲何會出現上述問題呢?咱們看看iOS 11有些什麼新增改動。
這裏只列出部分以及跟今天主題相關的部分。詳情能夠看看官網what's new in iOS 11安全

UIViewController 與 UIView
主要變化部分:
  • UIViewController廢棄LayoutGuide。iOS7以後,爲了輔助Autolayout佈局系統,Apple新增UILayoutSupport協議。就是被不少人忽略的topLayoutGuidebottomLayoutGuide
@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

LayoutGuide
LayoutGuide

  • UIView增長safeAreaInsets:UIEdgeInsets安全區概念。用於替代輔助自動佈局的LayoutGuide。安全區域定義了佈局View除各類Bar後剩下的可見區域。此屬性是readonly的,想要改動,須要操做UIViewControlleradditionalSafeAreaInsets屬性。app

    簡單理解就是去除系統的各類Bar以及左右margin(若是有設置過) 後的可用佈局區域。

    圖中淡藍色區域即爲安全區域:
    ide

    safeAreaInsets
    safeAreaInsets

    若是在View上的控件沒有被遮擋,safeAreaInsets = {(0,0),(0,0)}分別對應於(top,left,bottom,right)
    假設子ViewA頂部被navigationBar遮擋20pt,相應safeAreaInsets = {(20,0),(0,0)}佈局

    注意:若是採用SB、XIB佈局UI的,safeAreaInsets最低支持版本爲iOS 9.0。考慮到兼容性,觸手TV是不能使用安全區域的。
  • UIViewController廢棄automaticallyAdjustsScrollViewInsetsAPI。這是個bool值屬性,描述了是否自動適配scrollView的contentInsets。值爲true時,scrollView的內容不會被系統可見的各類bar所遮擋(statusbarnavigationBartabBartoolBar),通常會引發tableViewcollectionViewscrollView的content下移bar的高度值個像素。若是不須要自動調整,將值設爲falsepost

  • UIViewController新增修改關聯View安全區域的APIadditionalSafeAreaInsets。可對安全區域的值進行增減,知足自定義UI的需求。並提供方法viewSafeAreaInsetsDidChange,用於在安全區域改變後,進行佈局的調整。ui

  • 相應的,UIScrollView增長枚舉屬性contentInsetAdjustmentBehavior,描述scrollView如何調整contentInset(實際是調整adjustedContentInset屬性),配合安全區域使用。最後UIscrollView的contentInset值爲adjustedContentInsetsafeAreaInsets之和。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改變後通知使用者進行佈局調整。

總結:

  • 若是使用SB、XIB佈局時啓用了安全區域,IB會參考安全區域進行佈局。
  • 佈局時沒有以安全區域做爲參照時,直接設置安全區域,系統並不會對佈局作出自適應動做,能夠經過安全區域改變回調方法進行調整。
  • 若是是UIscrollView或子類,若是設置了UIScrollViewContentInsetAdjustmentBehavior不等於UIScrollViewContentInsetAdjustmentNever,系統會自動適配UIScrollView的內容都在安全區域內,保證內容不被各類Bar遮住。
NavigationBar的改變

NavigationBar 多了個contentView,這個View在開啓了大標題時,上面會有個titleLable。
NavigationBartitleView支持自動佈局。須要使用者自動撐開添加到上面的View。

定位權限

在iOS11,原有的NSLocationAlwaysUsageDeion被降級爲NSLocationWhenInUseUsageDeion
.plist沒文件中配置NSLocationAlwaysAndWhenInUseUsageDeion,系統框纔會彈出,使用requestAlwaysAuthorization獲取權限。

其餘變動
  1. 設置UIBarItem.landscapeImagePhone 與UIBarItem.largeContentSizeImage 來適配在豎屏和橫屏下的BarItem圖標和雙擊放大後的圖標,若是使用 PDF 資源圖,系統會自動從PDF資源圖提取相應圖標,就不用設置上述屬性了。
  2. navigationBar.prefersLargeTitles = true 新增大標題屬性,大標題displaymode控制顯示枚舉:navigationItem.largeTitleDisplayMode
    枚舉屬性:

    - automatic:自動保存上一次設置的值
     - always:老是顯示大標題
     - never:不顯示複製代碼
  3. Navigation集成searchBar。navigationItem.searchController 屬性賦值能夠集成searchBar在navigationbar下方,navigationItem.hidesSearchBarWhenScrolling屬性控制在滾動時是否自動隱藏。

  4. 確保避免 size 爲 0 的自定義view,實現intrinsicContentSize 方法提供默認尺寸。

  5. tableview開啓開啓高度估算(Self-Sizing),設置下面三個屬性,使高度估算失效:

    tableView.estimatedRowHeight = 0 
    
         tableView.estimatedSectionHeaderHeight = 0 
    
         tableView.estimatedSectionFooterHeight = 0複製代碼
  6. tableview 新增左滑右滑交互。

  7. 廢棄iOS7 之後的layoutMargins,取而代之的是新增的安全區域的概念。
    新增directionalLayoutMargins。對應layoutMargins。
    新增systemMinimumLayoutMargins,當directionalLayout小於systemMinimumLayoutMargins,使用systemMinimumLayoutMargins。
    新增 UIViewController=.viewRespectsSystemMinimumLayoutMargins,默認爲FALSE。設爲TRUE,可設置任意值。

新仇舊恨

明白了iOS 11大法,再來看看,這大法帶來的各類問題。

NavigationBar問題。

通常使用NavigationBar基本有三種手法。

  1. 純正血統,使用標註控件,不在NavigationBar上添加任何的控件。
  2. 混血,在NavigationBar上有定製的控件。
  3. 毫無血統,徹底自定義。隱藏了系統的navigationBar,直接用了View替代。

針對上面三種狀況:

  • 使用第一種姿式的人,很幸運,你不須要進行適配。(估計不多有人不定製)

  • 使用第二種姿式的人,可能會有返回按鈕、titleView、添加的控件position不正確的問題。

  • 使用第三種姿式的人,很好,在非iPhone X上,也沒什麼大問題。

UIscrollView、UItableView、UICollectionView內容下沉問題。

升級iOS 11後,發現有些使用UItableView佈局的頁面,頂部多出來了20pt或者44pt,也或者64pt。也就是頂部可見Bar的高度的總和。
包括使用MJRefresh引發的問題。也屬於這類。

Xcode9 打出的包(iOS 11 SDK)頁面卡頓問題

升級Xcode9 後,你會發現,基於iOS 11打出來的包,tableView滑動的時候,一卡一頓的。

請求定位框不彈出

在iOS 11有些應用在請求定位時,未能彈出系統請求權限的對話框。

返回按鈕位置偏移問題

iOS 11的的leftBarButtonItem 或者右邊都距離邊距20像素。

其餘問題

  1. 使用YYKit的大圖預覽控件YYPhotoGroupView,dismiss的時候,有些頁面會抖一下。

一笑泯恩仇

既然已經知道了iOS 11初出江湖的各類套路和帶來的血雨腥風。那就春風化雨,見招拆招了。

navigationBar 問題。

  • 由於NavigationBar引入了AutoLayout,以往的frame方式可能位置有誤差。之前使用CGRectMakeZero自動撐大已經行不通。那麼使用自動佈局。或者實現下面View的方法,提供默認尺寸。
- (CGSize)intrinsicContentSize {
    return CGSizeMake(100,100);
}複製代碼
  • 添加到NavigationBarView可能會出各類問題,嘗試添加到contentView上。並設置約束。
  • 返回按鈕問題,請設置好frame在賦值給navigationItem.leftBarButtonItem

UIscrollView、UItableView、UICollectionView內容下沉問題

由於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個像素仍是存在的。
還有調整UIButtonimageEdgeInsets的,其實也會在多按鈕的時候出現佈局問題。
好比這篇帖子
也有的在pushpop的時候進行設置的。修改約束會引發有些約束丟失。也有重寫drawRect的

其實不須要這麼麻煩。新建一個類,繼承自UINavigationBar,而後重寫layoutSubviews,若是是ios11,設置contentViewlayoutMargins爲須要的值,以前的版本就執行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此篇基本上是官方視頻的文字版

相關文章
相關標籤/搜索