iOS開發-LayoutGuide(從top/bottom LayoutGuide到Safe Area)

來源:互聯網
上載者:User

標籤:nts   sub   keyword   creation   hidden   png   eve   nsstring   橫屏   

iOS7 topLayoutGuide/bottomLayoutGuide

建立一個叫做LayoutGuideStudy的工程,我們開啟看一下Main.storyboard:



storyboard-top_bottom_layoutGuide.png

可以看到View Controller下面出現topLayoutGuide/bottomLayoutGuide這兩個東西,並且和Controller的View處於同一層級。
並且在UIViewController標頭檔裡面,這兩個屬性是id類型遵守一個UILayoutSupport協議並且是唯讀屬性:

// These objects may be used as layout items in the NSLayoutConstraint API@property(nonatomic,readonly,strong) id<UILayoutSupport> topLayoutGuide NS_AVAILABLE_IOS(7_0);@property(nonatomic,readonly,strong) id<UILayoutSupport> bottomLayoutGuide NS_AVAILABLE_IOS(7_0);

這就說明了這兩個LayoutGuide是系統自動建立並管理的,這也解釋了剛剛我們建立的工程裡面Main.storyboard為什麼會自動出現topLayoutGuide/bottomLayoutGuide。

看看有什麼用

我們拖拽一個紅色的view到Controller的view裡,添加約束的時候,注意到是右下角的約束設定框,關於頂部約束的基準view下拉選擇,xcode預設勾選了Top Layout Guide:



autoLayout-useLayoutGuide.png

,再添加完寬高約束,最後約束結果:



constraint_result.png
直接build看結果:

run-result.png

可以看出top約束基於系統提供的topLayoutGuide,系統會自動為這個view避開頂部狀態列。
我們在ViewController裡面列印紅色view:

<UIView: 0x7ff10860fa90; frame = (0 20; 240 128); autoresize = RM+BM; layer = <CALayer: 0x60000003b5c0>>

看到紅色view的y值就是20.剛好是狀態列的高度。由此看出Top Layout Guide的作用就是在進行自動布局的時候,協助開發人員隔離出狀態列的空間。那麼我們再看看導航控制器(頂部出現導覽列)的情況:



navagation-result.png

build看結果:



run_nav_result.png
Top Layout Guide同樣自動協助隔離出狀態列+導覽列。
在ViewController裡面列印黃色view:
<UIView: 0x7fb04fe08040; frame = (0 64; 240 128); autoresize = RM+BM; layer = <CALayer: 0x61800003ef60>>

看到黃色view的y值就是64.剛好是狀態列+導覽列的高度。

同理,bottomLayoutGuide就是用於在TabbarController裡面隔離底部的tabbar:



tabbar-bottomGuide.png扒一扒topLayoutGuide/bottomLayoutGuide對象:

在UIViewController的viewDidLayoutSubviews方法列印

- (void)viewDidLayoutSubviews {    [super viewDidLayoutSubviews];    NSLog(@"topLayoutGuide-%@",self.topLayoutGuide);    NSLog(@"bottomLayoutGuide-%@",self.bottomLayoutGuide);}列印結果:topLayoutGuide-<_UILayoutGuide: 0x7fd7cce0c350; frame = (0 0; 0 64); hidden = YES; layer = <CALayer: 0x61000003f2c0>>bottomLayoutGuide-<_UILayoutGuide: 0x7fd7cce0d6b0; frame = (0 667; 0 0); hidden = YES; layer = <CALayer: 0x610000221620>>

這個是_UILayoutGuide類型的私人對象,看起來裡面有frame,hidden,layer屬性,感覺十分像UIView啊,那我們就驗證一下:

if ([self.topLayoutGuide isKindOfClass:[UIView class]]) {    NSLog(@"topLayoutGuide is an UIView");} if ([self.bottomLayoutGuide isKindOfClass:[UIView class]]) {    NSLog(@"bottomLayoutGuide is an UIView");}列印結果:topLayoutGuide is an UIViewbottomLayoutGuide is an UIView

得到結論就是topLayoutGuide/bottomLayoutGuide其實是一個UIView類型的對象。
我們再列印一下UIViewController的view的subviews:

- (void)viewDidLayoutSubviews {    [super viewDidLayoutSubviews];     NSLog(@"viewController view subViews %@",self.view.subviews);}列印結果:viewController view subViews (    "<UIView: 0x7ffc774035b0; frame = (0 64; 240 128); autoresize = RM+BM; layer = <CALayer: 0x60800002c720>>",    "<_UILayoutGuide: 0x7ffc7740ae10; frame = (0 0; 0 64); hidden = YES; layer = <CALayer: 0x60800002c480>>",    "<_UILayoutGuide: 0x7ffc7740b1e0; frame = (0 667; 0 0); hidden = YES; layer = <CALayer: 0x60800002b820>>")

這樣就明了了!
總結一下:
topLayoutGuide/bottomLayoutGuide其實是作為虛擬占坑view,用於在自動布局的時候協助開發人員避開頂部的狀態列,導覽列以及底部的tabbar等

iOS9 UILayoutGuide

iOS9開始,蘋果新增加了一個UILayoutGuide的類,看看蘋果官方對它的解釋:

The UILayoutGuide class defines a rectangular area that can interact with Auto Layout. Use layout guides to replace the dummy views you may have created to representinter-view spaces or encapsulation in your user interface

大概意思是UILayoutGuide用於提供一個矩形地區可以用Auto Layout來定製一些約束特性,作為一個虛擬view使用。
我想大概是蘋果的工程師覺得以前的topLayoutGuide/bottomLayoutGuide提供虛擬占坑view,隔離導覽列/tabber的思想不錯,進而有了啟發,能不能讓整個LayoutGuide變得更靈活,讓開發人員能夠自由定製,於是這個UILayoutGuide類就設計出來了。。

那麼如何自由定製一個UILayoutGuide,我們看看這個類的幾個屬性:

@property(readonly, strong) NSLayoutXAxisAnchor *leadingAnchor;@property(readonly, strong) NSLayoutXAxisAnchor *trailingAnchor;@property(readonly, strong) NSLayoutXAxisAnchor *leftAnchor;@property(readonly, strong) NSLayoutXAxisAnchor *rightAnchor;@property(readonly, strong) NSLayoutYAxisAnchor *topAnchor;@property(readonly, strong) NSLayoutYAxisAnchor *bottomAnchor;@property(readonly, strong) NSLayoutDimension *widthAnchor;@property(readonly, strong) NSLayoutDimension *heightAnchor;@property(readonly, strong) NSLayoutXAxisAnchor *centerXAnchor;@property(readonly, strong) NSLayoutYAxisAnchor *centerYAnchor;

NSLayoutXAxisAnchor,NSLayoutYAxisAnchor,NSLayoutDimension這幾個類也是跟隨UILayoutGuide在
iOS9以後新增的,即便很陌生,但我們看上面UILayoutGuide的幾個屬性裡面leading,trailing,top,bottom,center等熟悉的字眼,就能明白這些屬性就是用於給UILayoutGuide對象增加布局約束的。

我們在看UIView裡面新增的一個分類:

@interface UIView (UIViewLayoutConstraintCreation)@property(readonly, strong) NSLayoutXAxisAnchor *leadingAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutXAxisAnchor *trailingAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutXAxisAnchor *leftAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutXAxisAnchor *rightAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutYAxisAnchor *topAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutYAxisAnchor *bottomAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutDimension *widthAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutDimension *heightAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutXAxisAnchor *centerXAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutYAxisAnchor *centerYAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutYAxisAnchor *firstBaselineAnchor NS_AVAILABLE_IOS(9_0);@property(readonly, strong) NSLayoutYAxisAnchor *lastBaselineAnchor NS_AVAILABLE_IOS(9_0);@end

也是跟UILayoutGuide一樣的提供了一致的屬性。這就說明了UILayoutGuide是可以跟UIView進行Auto Layout的約束互動的。

我們用一個例子說明:

建立一個UILayoutGuide,約束它距離控制器view的頂部64,左邊0,寬250,高200,於是在viewDidLoad方法裡面的代碼:

// 建立UILayoutGuide *layoutGuide = [[UILayoutGuide alloc] init];// 需要使用UIView的addLayoutGuide方法添加建立的layoutGuide[self.view addLayoutGuide:layoutGuide];// 正式的約束代碼[layoutGuide.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:64].active = YES;[layoutGuide.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;[layoutGuide.widthAnchor constraintEqualToConstant:250].active = YES;[layoutGuide.heightAnchor constraintEqualToConstant:200].active = YES;

這樣約束代碼明顯比使用NSLayoutConstraint簡潔多了。

接著,我們再建立一個紫色view,基於這個建立的layoutGuide進行約束,紫色view頂部距離上述layoutGuide底部20,和layoutGuide靠左對齊,寬和高和layoutGuide保持一致

UIView *viewBaseLayoutGuide = [[UIView alloc] init];viewBaseLayoutGuide.translatesAutoresizingMaskIntoConstraints = NO;viewBaseLayoutGuide.backgroundColor = [UIColor purpleColor];[self.view addSubview:viewBaseLayoutGuide];[viewBaseLayoutGuide.topAnchor constraintEqualToAnchor:layoutGuide.bottomAnchor constant:20].active = YES;[viewBaseLayoutGuide.leadingAnchor constraintEqualToAnchor:layoutGuide.leadingAnchor].active = YES;[viewBaseLayoutGuide.widthAnchor constraintEqualToAnchor:layoutGuide.widthAnchor].active = YES;[viewBaseLayoutGuide.heightAnchor constraintEqualToAnchor:layoutGuide.heightAnchor].active = YES;

運行程式的結果:



layoutGuide-test.pngiOS11 Safe Area / safeAreaLayoutGuide

iOS11又引入了一個Safe Area(安全區域)的概念,蘋果建議在這個安全區域內放置UI控制項。這個安全區域的範圍其實就是整個螢幕隔離出狀態列,導覽列,tabar,以及iPhone X頂部劉海,底部虛擬home手勢地區的範圍。
從這個介紹可以看得出,所謂的Safe Area其實也就是升級版本的topLayoutGuide/bottomLayoutGuide,以前只能限制top/bottom的Layout,現在更加強大了。
再看一下UIViewController標頭檔:(用xcode9以上版本開啟):

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

蘋果提示topLayoutGuide/bottomLayoutGuide這兩個屬性在iOS11已經到期,推薦使用UIView 的safeAreaLayoutGuide屬性(safeAreaLayoutGuide稍後會介紹)。

另外用xcode9以上版本建立工程的時候,Main.storyboard會預設選擇Use Safe Area Layout Guides,控制器view下面會出現safe area:


xcode9_safeArea.png驗證使用safeArea的效果:

如所示,我們基於storyboard提供的控制器view的safeArea地區對紅色的view進行約束:頂部距離
安全區域0,左邊距離安全區域0,寬240,高180:

 


constraint-safeArea.png
在iPhone 8上運行結果:

run-safeArea.png

為了驗證Safe Area在豎屏iPhone X底部起到的隔離作用,又增加了一個棕色的view:左邊距離安全區域0,底部距離安全區域0,寬240,高180:



iPhone X-bottom.png

在iPhone X上運行結果:


iPhone X-SafeArea.png

利用安全區域進行Auto Layout布局,分別在iPhone 8,iPhone X上以及避開了狀態列/劉海/底部的home虛擬手勢地區,使得開發人員不用關心狀態列以及適配iPhone X避開劉海的高度,只需要安安心心的蘋果指定的這個安全區域放置子控制項,布局就可以了。

UIView 的safeAreaLayoutGuide屬性

查看UIView在iOS11上關於Safe Area新增的兩個屬性:

@property (nonatomic,readonly) UIEdgeInsets safeAreaInsets API_AVAILABLE(ios(11.0),tvos(11.0));@property(nonatomic,readonly,strong) UILayoutGuide *safeAreaLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0));

很明顯這個唯讀safeAreaLayoutGuide屬性是系統自動建立的,可以讓開發人員用代碼進行基於安全區域進行自動布局。
點擊控制器的view觸發touchesBegan進行列印驗證:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    NSLog(@"safeAreaInsets %@",NSStringFromUIEdgeInsets(self.view.safeAreaInsets));    NSLog(@"safeAreaGuide %@",self.view.safeAreaLayoutGuide);}列印結果:safeAreaInsets {44, 0, 34, 0}safeAreaGuide <UILayoutGuide: 0x6080009a3c60 - "UIViewSafeAreaLayoutGuide",layoutFrame = {{0, 44}, {375, 734}}, owningView = <UIView: 0x7f888240c3b0; frame = (0 0; 375 812); autoresize = W+H; layer = <CALayer: 0x608000431ec0>>>

根據列印結果safeAreaInsets.top=44,剛好是蘋果規定的適配iPhone X要避開的劉海的距離,
safeAreaInsets.bottom=34,剛好是底部的home虛擬手勢地區的高度。

橫屏旋轉測試:

進行橫屏切換後:



lascape-1.png

再次點擊控制器的view觸發touchesBegan進行列印驗證,列印結果:

safeAreaInsets {0, 44, 21, 44}safeAreaGuide <UILayoutGuide: 0x6080009a3c60 - "UIViewSafeAreaLayoutGuide", layoutFrame ={{44, 0}, {724, 354}}, owningView = <UIView: 0x7f888240c3b0; frame = (0 0; 812 375); autoresize =W+H; layer = <CALayer: 0x608000431ec0>>>

旋轉之後,safeAreaInsets.left距離劉海隔離地區依然是44,底部的home虛擬手勢地區變成了21。
由此證明,系統也把旋轉螢幕的情況也自動計算好了。

 

 

  • iOS 11.0之後系統新增安全區域變化方法

1234 UIViewController中新增:- (void)viewSafeAreaInsetsDidChange;UIView中新增:- (void)viewSafeAreaInsetsDidChange;
  • 通過安全區域變化來改變視圖的位置

如果旋轉螢幕,相應的安全區域也會變化,所以不比擔心。![safearea.gif](http://upload-

12345678 images.jianshu.io/upload_images/1186277-ab32b1be56378531.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)- (void)viewSafeAreaInsetsDidChange {    [super viewSafeAreaInsetsDidChange];         NSLog(@"viewSafeAreaInsetsDidChange-%@",NSStringFromUIEdgeInsets(self.view.safeAreaInsets));         [self updateOrientation];}
1234567891011121314 /** 更新螢幕safearea frame */- (void)updateOrientation {    if (@available(iOS 11.0, *)) {        CGRect frame = self.customerView.frame;        frame.origin.x = self.view.safeAreaInsets.left;        frame.size.width = self.view.frame.size.width - self.view.safeAreaInsets.left - self.view.safeAreaInsets.right;        frame.size.height = self.view.frame.size.height - self.view.safeAreaInsets.bottom;        self.customerView.frame = frame;    else {        // Fallback on earlier versions    }}

 

 

總結

這次為了適配iPhone X,個人從一開始看到iOS11的Safe Area這個概念,追溯到iOS7 topLayoutGuide/bottomLayoutGuide,從頭開始學習,受益匪淺。也體會到了蘋果工程師針對UI適配,面向開發人員進行的一系列探索,以及最佳化的心路曆程。也看到了他們如何將一個好的思路,面對當前的需求變化,進行合理的擴充,設計出的靈活可擴充的API:
1.iOS7: topLayoutGuide/bottomLayoutGuide,利用一個虛擬view初步解決導覽列,tabbar的隔離問題。

2.iOS9:有了虛擬view的思路,又考慮能不能去除top/bottom概念的局限性,讓開發人員都可以靈活自訂這個隔離地區,又提供一些更方便簡潔易懂的API方便進行代碼自動布局,於是有了UILayoutGuide這個類。。

3.兩年後的iOS11,有了iPhone X,蘋果工程師順理成章的將他們在iOS9的探索成果利用起來,他們自訂了一個UILayoutGuide,給開發人員提供了一個唯讀屬性的safeAreaLayoutGuide,並且提出安全區域的概念。

  參考連結:https://stackoverflow.com/questions/46184197/ios-11-safe-area-layout-guide-backwards-compatibility

iOS開發-LayoutGuide(從top/bottom LayoutGuide到Safe Area)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.