iOS多設備適配簡史以及相應的API支撐實現

遠古的iPhone3和iPhone4時代,設備尺寸都是固定3.5inch,沒有所謂的適配的問題,只須要用視圖的frame屬性進行硬編碼便可。隨着時間的推移,蘋果的設備種類愈來愈多,尺寸也愈來愈大,單純的frame已經不能簡單解決問題了,因而推出了AutoLayout技術和SizeClasses技術來解決多種設備的適配問題。一直在作iOS開發的程序員相信在下面的兩個版本交界處須要處理適配的坎必定讓你焦頭爛額過:ios

  1. iOS7出來後視圖控制器的根視圖默認的尺寸是佔據整個屏幕的,若是有半透明導航條的話也默認是延伸到導航欄和狀態欄的下面。這段時間相信你對要同時知足iOS7和如下的版本進行大面積的改版和特殊適配處理,尤爲是狀態欄的高度問題尤其棘手。git

  2. iOS11出來後尤爲是iPhoneX設備推出,iPhoneX設備的特殊性表現爲頂部的狀態欄高度由20變爲了44,底部還出現了一個34的安全區,當橫屏時還須要考慮左右兩邊的44的縮進處理。你須要對全部的佈局代碼進行從新適配和梳理以便兼容iPhoneX和其餘設備,這裏面仍是狀態欄的高度以及底部安全區的的高度尤其棘手。程序員

我的認爲這兩個版本的發佈是iOS開發人員遇到的須要大量佈局改版的版本。爲了達到完美適配咱們可能須要寫大量的if,else以及寫不少宏以及版本兼容來進行特殊處理。固然蘋果也爲上面兩次大改版提供了諸多的解決方案:github

  1. iOS7中對視圖控制器提供了以下屬性來解決版本兼容性的問題:
@property(nonatomic,assign) UIRectEdge edgesForExtendedLayout NS_AVAILABLE_IOS(7_0); // Defaults to UIRectEdgeAll
@property(nonatomic,assign) BOOL extendedLayoutIncludesOpaqueBars NS_AVAILABLE_IOS(7_0); // Defaults to NO, but bars are translucent by default on 7_0.  
@property(nonatomic,assign) BOOL automaticallyAdjustsScrollViewInsets API_DEPRECATED_WITH_REPLACEMENT("Use UIScrollView's contentInsetAdjustmentBehavior instead", ios(7.0,11.0),tvos(7.0,11.0)); // Defaults to YES

@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));

複製代碼
  1. iOS11中提出了一個安全區的概念,要求咱們的可操做視圖都放置在安全區內,並對視圖和滾動視圖提供了以下擴展屬性:
@property (nonatomic,readonly) UIEdgeInsets safeAreaInsets API_AVAILABLE(ios(11.0),tvos(11.0));
- (void)safeAreaInsetsDidChange API_AVAILABLE(ios(11.0),tvos(11.0));

/* The top of the safeAreaLayoutGuide indicates the unobscured top edge of the view (e.g, not behind
 the status bar or navigation bar, if present). Similarly for the other edges.
 */
@property(nonatomic,readonly,strong) UILayoutGuide *safeAreaLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0));
複製代碼
/* When contentInsetAdjustmentBehavior allows, UIScrollView may incorporate
 its safeAreaInsets into the adjustedContentInset.
 */
@property(nonatomic, readonly) UIEdgeInsets adjustedContentInset API_AVAILABLE(ios(11.0),tvos(11.0));

/* Also see -scrollViewDidChangeAdjustedContentInset: in the UIScrollViewDelegate protocol.
 */
- (void)adjustedContentInsetDidChange API_AVAILABLE(ios(11.0),tvos(11.0)) NS_REQUIRES_SUPER;

/* Configure the behavior of adjustedContentInset.
 Default is UIScrollViewContentInsetAdjustmentAutomatic.
 */
@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior API_AVAILABLE(ios(11.0),tvos(11.0));

/* contentLayoutGuide anchors (e.g., contentLayoutGuide.centerXAnchor, etc.) refer to
 the untranslated content area of the scroll view.
 */
@property(nonatomic,readonly,strong) UILayoutGuide *contentLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0));

/* frameLayoutGuide anchors (e.g., frameLayoutGuide.centerXAnchor) refer to
 the untransformed frame of the scroll view.
 */
@property(nonatomic,readonly,strong) UILayoutGuide *frameLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0));
複製代碼

這些屬性的具體意義這裏就很少說了,網絡上以及蘋果的官方都有不少資料在介紹這些屬性的意思。從上面的這些屬性中能夠看出蘋果提出的這些解決方案其主要是圍繞解決視圖和導航條、滾動視圖、狀態欄、屏幕邊緣之間的關係而進行的。由於iOS7和iOS11兩個版本中控制器中的視圖和上面所列出的一些內容之間的關係變化最大。安全

NSLayoutConstraint約束以及iOS9上的封裝改進

在iOS6時代蘋果推出了AutoLayout的技術解決方案,這是一套採用以相對約束來替代硬編碼的解決方法,然而糟糕的方法名和使用方式致使使用成本和代碼量的急劇增長。好比下面的一段代碼:bash

UIButton *button = [self createDemoButton:NSLocalizedString(@"Pop layoutview at center", "") action:@selector(handleDemo1:)];
    button.translatesAutoresizingMaskIntoConstraints = NO;  //button使用AutoLayout
    [scrollView addSubview:button];

    //下面的代碼是iOS6以來自帶的約束佈局寫法,能夠看出代碼量較大。
    [scrollView addConstraint:[NSLayoutConstraint  constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
    
    [scrollView addConstraint:[NSLayoutConstraint  constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeTop multiplier:1 constant:10]];
    
    [scrollView addConstraint:[NSLayoutConstraint  constraintWithItem:button attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:40]];
    
    [scrollView addConstraint:[NSLayoutConstraint  constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeWidth multiplier:1 constant:-20]];
複製代碼

一個簡單的將按鈕放到一個UIScrollView中去的代碼,當用AutoLayout來實現時出現了代碼量風暴問題。對於約束的設置到了iOS9之後有了很大的改進,蘋果對約束的設置進行了封裝,提供了三個類:NSLayoutXAxisAnchor, NSLayoutYAxisAnchor, NSLayoutDimension來簡化約束的設置,仍是一樣的功能用新的類來寫約束就簡潔清晰不少了:網絡

UIButton *button = [self createDemoButton:NSLocalizedString(@"Pop layoutview at center", "") action:@selector(handleDemo1:)];
    button.translatesAutoresizingMaskIntoConstraints = NO;  //button使用AutoLayout
    [scrollView addSubview:button];
    [button.centerXAnchor constraintEqualToAnchor:scrollView.centerXAnchor].active = YES;
    [button.topAnchor constraintEqualToAnchor:scrollView.topAnchor constant:10].active = YES;
    [button.heightAnchor constraintEqualToConstant:40].active = YES;
    [button.widthAnchor constraintEqualToAnchor:scrollView.widthAnchor multiplier:1 constant:-20].active = YES;
複製代碼
UIStackView

在iOS9中還提供了一個UIStackView的類來簡化那些視圖須要從上往下或者從左往右依次添加排列的場景,經過UIStackView容器視圖的使用就再也不須要爲每一個子視圖添加冗餘的依賴約束關係了。在大量的實踐中不少應用的各板塊其實都是按順序從上到下排列或者從左到右排列的。因此若是您的應用最低支持到iOS9的話就能夠大量的應用這個類來構建你的程序了。ide

佔位視圖類UILayoutGuide

在iOS9之前兩個視圖之間的間距和間隔是沒法支持浮動和可伸縮設置的,以及咱們能夠須要在兩個視圖之間保留一個浮動尺寸的空白區域,解決的方法是在它們中間加入一個透明顏色的UIView來進行處理,無論如何只要是View都須要進行渲染和繪製從而有可能必定程度上影響程序的性能,而在iOS9之後提供了一個佔位視圖類UILayoutGuide,這個類就像是一個普通的視圖同樣能夠爲它設置約束,也能夠將它添加進入視圖中去,也能夠將這個佔位視圖做爲其餘視圖的約束依賴項,惟一的不一樣就是佔位視圖不會進行任何的渲染和繪製,它只會參與佈局處理。所以這個類的引入能夠很大程度上解決那些浮動間距的問題。佈局

SizeClasses多屏幕適配

當咱們的程序可能須要同時在橫屏和豎屏下運行而且橫屏和豎屏下的佈局還不一致時,並且但願咱們的應用在小屏幕上和大屏幕上(好比iPhone8 Plus 以及iPhoneX S Max)的佈局有差別時,咱們可能須要用到蘋果的SizeClasses技術。這是蘋果在iOS8中推出來的一個概念。 可是在實際的實踐中咱們不多有看到使用SizeClasses的例子和場景以及在咱們開發中不多有使用到這方面的技術,因此我認爲這應該是蘋果的一個多屏幕適配的失敗解決的方案。從字面理解SizeClasses就是尺寸的種類,蘋果將設備的寬和高分爲了壓縮和常規兩種尺寸類型,所以咱們能夠獲得以下幾種類型的設備:性能

設備 方向 類型
iPhone4/5/6/7/X 豎屏 w:Compact h:Regular
iPhone4/5/6/7/X 橫屏 w:Compact h:Compact
iPhone6/7Plus, iPhoneXMax 豎屏 w:Compact h:Regular
iPhone6/7Plus, iPhoneXMax 橫屏 w:Regular h:Compact
全部iPad 豎屏 w:Regular h: Regular
全部iPad 橫屏 w:Regular h: Regular
全部iWatch 豎屏 w: Compact h: Compact
全部iWatch 橫屏 w: Compact h: Compact

很欣慰的是若是您的應用是一個帶有系統導航條的應用時不少適配的問題都可以獲得很好的解決,由於系統已經爲你作了不少事情,你不須要作任何特殊的處理。而若是你的應用的某個界面是present出來的,或者是你本身實現的自定義導航條的話,那麼你可能就須要本身來處理各類版本的適配問題了。而且若是你的應用可能還有橫豎屏的話那這個問題就更加複雜了。

最後除了能夠用系統提供的API來解決全部的適配問題外,還向你們推薦個人開源佈局庫:MyLayout。它同時支持Objective-C以及Swift版本。並且用這個庫後上面的全部適配問題都不是問題。


歡迎你們訪問歐陽大哥2013的github地址

相關文章
相關標籤/搜索