iOS頁面適配的正確姿勢
我這裡講解使用的是Masonry,我假設你對約束有一定的瞭解。
隨著iPhone X的出現,iOS頁面的適配似乎也麻煩了起來,我見得最多的就是通過某種手段判斷機型或者獲取導航欄的高度,然後計算寬高。我不說這種方法好不好,因為它也能解決你目前的問題,但不是我喜歡的方式。
在正式開始之前,我先介紹幾個重要的知識:
1.topLayoutGuide和bottomLayoutGuide
這兩個屬性屬於UIViewController,topLayoutGuide主要就是指導航欄,狀態列;bottomLayoutGuide主要指TabBar(劉海手機上也可指代底部黑條的部分),主要就是為了讓你使用約束的時候對頂部和底部有個參考,避免檢視上的內容被遮擋。
2.safeAreaLayoutGuide
這個屬性是iOS11才有的,也就是蘋果對於劉海屏給出的一種解決方案。它和1中的兩個屬性作用類似,由於屬於UIView類,不受限於UIViewController,所以在靈活性上更強。
從它的名字可以看出來,它是一種安全區域的參考,那麼安全區域指哪塊呢?如下圖:
從上面的圖可以看得出來,安全區域可以保證我們的內容不被遮擋。
3. Masonry中相應的屬性
既然我們用Masonry,我們就需要知道與系統中對應的屬性是哪些:
//安全區域對應的屬性 @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuide NS_AVAILABLE_IOS(11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideLeading NS_AVAILABLE_IOS(11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideTrailing NS_AVAILABLE_IOS(11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideLeft NS_AVAILABLE_IOS(11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideRight NS_AVAILABLE_IOS(11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideTop NS_AVAILABLE_IOS(11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideBottom NS_AVAILABLE_IOS(11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideWidth NS_AVAILABLE_IOS(11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideHeight NS_AVAILABLE_IOS(11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideCenterX NS_AVAILABLE_IOS(11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideCenterY NS_AVAILABLE_IOS(11.0);
// 一般參考對應的屬性 @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuide NS_DEPRECATED_IOS(8.0, 11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuide NS_DEPRECATED_IOS(8.0, 11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuideTop NS_DEPRECATED_IOS(8.0, 11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuideBottom NS_DEPRECATED_IOS(8.0, 11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuideTop NS_DEPRECATED_IOS(8.0, 11.0); @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuideBottom NS_DEPRECATED_IOS(8.0, 11.0);
從名字中我們就能很清晰的識別出來
4.實戰
我們建立一個簡單的工程,初始頁面是一個帶有導航欄的紅色檢視控制器,如下圖所示:
我們將在這上面建立一個綠色的檢視,來具體看一下上面1和2提到的屬性怎麼去用。
首先我們不使用上面的屬性來設定約束,程式碼如下:
UIView *view = [[UIView alloc] init]; view.backgroundColor = [UIColor greenColor]; [self.view addSubview:view]; [view mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view.mas_top); make.height.equalTo(@200); make.left.right.equalTo(self.view); }];
我們只是簡單的設定了子檢視和父檢視之間的約束,似乎看起來沒什麼問題,但是當我們執行一下就會發現,不好的事情發生了。
我們的綠色檢視竟然被導航欄遮住了一部分,這不是我們所希望了,因為將來有可能遮住我們的重要資訊,也許你想著我們可以修改約束中make.top.equalTo(self.view.mas_top)為make.top.equalTo(self.view.mas_top).offset(88),讓其偏移88,但是這樣的壞處是顯而易見的,現在偏移88是沒問題的,但執行在沒有劉海的手機上又要改為偏移64,假如蘋果將來又出個神奇的手機,你是不是又要去判斷手機型號,然後偏移某一個值呢?從現在起,放棄這種適配方法吧(除了個別目的)。
我們把程式碼修改為下面這個樣子
UIView *view = [[UIView alloc] init]; view.backgroundColor = [UIColor greenColor]; [self.view addSubview:view]; [view mas_makeConstraints:^(MASConstraintMaker *make) { if (@available(iOS 11, *)) { make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop); } else { make.top.equalTo(self.mas_topLayoutGuide); } make.height.equalTo(@200); make.left.right.equalTo(self.view); }];
這個程式碼執行出來的效果如下,這樣的效果就是我們想要的,內容不會被導航欄遮擋。注意上面的程式碼,有一個if語句,我們判斷了相應API能否在指定平臺獲取,而這也是我們適配的關鍵,因為iPhone X以及之後出來的手機,系統肯定是在iOS11之上的(除了個別越獄的),所以我們適配劉海屏的思路就是判斷系統版本就夠了。
我們再來看看底部的適配,先上程式碼
UIView *view = [[UIView alloc] init]; view.backgroundColor = [UIColor greenColor]; [self.view addSubview:view]; [view mas_makeConstraints:^(MASConstraintMaker *make) { make.height.equalTo(@200); make.left.right.equalTo(self.view); make.bottom.equalTo(self.view.mas_bottom); }];
這種執行出來的效果為:
也許你認為這樣沒什麼不好,你說得對,這樣是沒什麼不好,但是底部大約有32的距離是系統不希望我們使用的,因為怕和系統的手勢衝突。所以這裡我們也是要用到底部的參考和安全區域的參考
UIView *view = [[UIView alloc] init]; view.backgroundColor = [UIColor greenColor]; [self.view addSubview:view]; [view mas_makeConstraints:^(MASConstraintMaker *make) { make.height.equalTo(@200); make.left.right.equalTo(self.view); make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom); // 或者make.bottom.equalTo(self.mas_bottomLayoutGuide); }];
執行出來的效果如下,我們可以看到底部空出來了一段距離,這段距離就是系統不希望我們使用的。
4.總結
通過上面的例子,我們可以看到topLayoutGuide和bottomLayoutGuide與safeAreaLayoutGuide的作用區別不大,但是safeAreaLayoutGuide是在檢視類中就可以使用,更加方便了我們去做適配。通過參考安全區域或者之前的頂部佈局參考,我們不在需要去判斷機型,也能達到頁面完美適配的目的。