推薦閱讀:iOS開發(fā)——BAT面試題合集(持續(xù)更新中)
iOS7 topLayoutGuide/bottomLayoutGuide
創(chuàng)建一個(gè)叫做LayoutGuideStudy的工程套啤,我們打開看一下Main.storyboard:
可以看到View Controller下面出現(xiàn)topLayoutGuide/bottomLayoutGuide這兩個(gè)東西懒浮,并且和Controller的View處于同一層級(jí)。
并且在UIViewController頭文件里面,這兩個(gè)屬性是id類型遵守一個(gè)UILayoutSupport協(xié)議并且是只讀的屬性:
// 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);
這就說明了這兩個(gè)LayoutGuide是系統(tǒng)自動(dòng)創(chuàng)建并管理的发乔,這也解釋了剛剛我們創(chuàng)建的工程里面Main.storyboard為什么會(huì)自動(dòng)出現(xiàn)topLayoutGuide/bottomLayoutGuide已脓。
看看有什么用
我們拖拽一個(gè)紅色的view到Controller的view里,添加約束的時(shí)候,注意到是右下角的約束設(shè)定框异袄,關(guān)于頂部約束的基準(zhǔn)view下拉選擇,xcode默認(rèn)勾選了Top Layout Guide:
玛臂,再添加完寬高約束隙轻,最后約束結(jié)果:
直接build看結(jié)果:
可以看出top約束基于系統(tǒng)提供的topLayoutGuide,系統(tǒng)會(huì)自動(dòng)為這個(gè)view避開頂部狀態(tài)欄垢揩。
我們?cè)赩iewController里面打印紅色view:
<UIView: 0x7ff10860fa90; frame = (0 20; 240 128); autoresize = RM+BM; layer = <CALayer: 0x60000003b5c0>>
看到紅色view的y值就是20.剛好是狀態(tài)欄的高度玖绿。由此看出Top Layout Guide的作用就是在進(jìn)行自動(dòng)布局的時(shí)候,幫助開發(fā)者隔離出狀態(tài)欄的空間叁巨。那么我們?cè)倏纯磳?dǎo)航控制器(頂部出現(xiàn)導(dǎo)航欄)的情況:
build看結(jié)果:
Top Layout Guide同樣自動(dòng)幫助隔離出狀態(tài)欄+導(dǎo)航欄斑匪。
在ViewController里面打印黃色view:
<UIView: 0x7fb04fe08040; frame = (0 64; 240 128); autoresize = RM+BM; layer = <CALayer: 0x61800003ef60>>
看到黃色view的y值就是64.剛好是狀態(tài)欄+導(dǎo)航欄的高度。
同理,bottomLayoutGuide就是用于在TabbarController里面隔離底部的tabbar:
扒一扒topLayoutGuide/bottomLayoutGuide對(duì)象:
在UIViewController的viewDidLayoutSubviews方法打印
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
NSLog(@"topLayoutGuide-%@",self.topLayoutGuide);
NSLog(@"bottomLayoutGuide-%@",self.bottomLayoutGuide);
}
打印結(jié)果:
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>>
這個(gè)是_UILayoutGuide類型的私有對(duì)象蚀瘸,看起來里面有frame狡蝶,hidden,layer屬性贮勃,感覺十分像UIView啊贪惹,那我們就驗(yàn)證一下:
if ([self.topLayoutGuide isKindOfClass:[UIView class]]) {
NSLog(@"topLayoutGuide is an UIView");
}
if ([self.bottomLayoutGuide isKindOfClass:[UIView class]]) {
NSLog(@"bottomLayoutGuide is an UIView");
}
打印結(jié)果:
topLayoutGuide is an UIView
bottomLayoutGuide is an UIView
得到結(jié)論就是topLayoutGuide/bottomLayoutGuide其實(shí)是一個(gè)UIView類型的對(duì)象。
我們?cè)俅蛴∫幌耈IViewController的view的subviews:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
NSLog(@"viewController view subViews %@",self.view.subviews);
}
打印結(jié)果:
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>>"
)
這樣就明了了寂嘉!
總結(jié)一下:
topLayoutGuide/bottomLayoutGuide其實(shí)是作為虛擬的占坑view奏瞬,用于在自動(dòng)布局的時(shí)候幫助開發(fā)者避開頂部的狀態(tài)欄,導(dǎo)航欄以及底部的tabbar等泉孩。
iOS9 UILayoutGuide
iOS9開始硼端,蘋果新增加了一個(gè)UILayoutGuide的類,看看蘋果官方對(duì)它的解釋:
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 represent
inter-view spaces or encapsulation in your user interface
大概意思是UILayoutGuide用于提供一個(gè)矩形區(qū)域可以用Auto Layout來定制一些約束特性寓搬,作為一個(gè)虛擬的view使用珍昨。
我想大概是蘋果的工程師覺得以前的topLayoutGuide/bottomLayoutGuide提供虛擬占坑view,隔離導(dǎo)航欄/tabber的思想不錯(cuò)句喷,進(jìn)而有了啟發(fā)镣典,能不能讓整個(gè)LayoutGuide變得更靈活,讓開發(fā)者能夠自由定制唾琼,于是這個(gè)UILayoutGuide類就設(shè)計(jì)出來了骆撇。。
那么如何自由定制一個(gè)UILayoutGuide父叙,我們看看這個(gè)類的幾個(gè)屬性:
@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這幾個(gè)類也是跟隨UILayoutGuide在
iOS9以后新增的神郊,即便很陌生,但我們看上面UILayoutGuide的幾個(gè)屬性里面leading趾唱,trailing涌乳,top,bottom甜癞,center等熟悉的字眼夕晓,就能明白這些屬性就是用于給UILayoutGuide對(duì)象增加布局約束的。
我們?cè)诳碪IView里面新增的一個(gè)分類:
@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進(jìn)行Auto Layout的約束交互的蒸辆。
我們用一個(gè)例子說明:
創(chuàng)建一個(gè)UILayoutGuide,約束它距離控制器view的頂部64析既,左邊0躬贡,寬250,高200眼坏,于是在viewDidLoad方法里面的代碼:
// 創(chuàng)建
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簡(jiǎn)潔多了拂玻。
接著,我們?cè)賱?chuàng)建一個(gè)紫色view,基于這個(gè)創(chuàng)建的layoutGuide進(jìn)行約束檐蚜,紫色view頂部距離上述layoutGuide底部20魄懂,和layoutGuide左對(duì)齊,寬和高和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;
運(yùn)行程序的結(jié)果:
iOS11 Safe Area / safeAreaLayoutGuide
iOS11又引入了一個(gè)Safe Area(安全區(qū)域)的概念闯第,蘋果建議在這個(gè)安全區(qū)域內(nèi)放置UI控件市栗。這個(gè)安全區(qū)域的范圍其實(shí)就是整個(gè)屏幕隔離出狀態(tài)欄,導(dǎo)航欄咳短,tabar填帽,以及iPhone X頂部劉海,底部虛擬home手勢(shì)區(qū)域的范圍诲泌。
從這個(gè)介紹可以看得出盲赊,所謂的Safe Area其實(shí)也就是升級(jí)版本的topLayoutGuide/bottomLayoutGuide铣鹏,以前只能限制top/bottom的Layout敷扫,現(xiàn)在更加強(qiáng)大了。
再看一下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這兩個(gè)屬性在iOS11已經(jīng)過期诚卸,推薦使用UIView 的safeAreaLayoutGuide屬性(safeAreaLayoutGuide稍后會(huì)介紹)葵第。
另外用xcode9以上版本創(chuàng)建工程的時(shí)候,Main.storyboard會(huì)默認(rèn)選擇Use Safe Area Layout Guides合溺,控制器view下面會(huì)出現(xiàn)safe area:
驗(yàn)證使用safeArea的效果:
如上圖所示卒密,我們基于storyboard提供的控制器view的safeArea區(qū)域?qū)t色的view進(jìn)行約束:頂部距離
安全區(qū)域0,左邊距離安全區(qū)域0棠赛,寬240哮奇,高180:
在iPhone 8上運(yùn)行結(jié)果:
為了驗(yàn)證Safe Area在豎屏iPhone X底部起到的隔離作用,又增加了一個(gè)棕色的view:左邊距離安全區(qū)域0睛约,底部距離安全區(qū)域0鼎俘,寬240,高180:
在iPhone X上運(yùn)行結(jié)果:
利用安全區(qū)域進(jìn)行Auto Layout布局辩涝,分別在iPhone 8贸伐,iPhone X上以及避開了狀態(tài)欄/劉海/底部的home虛擬手勢(shì)區(qū)域,使得開發(fā)者不用關(guān)心狀態(tài)欄以及適配iPhone X避開劉海的高度怔揩,只需要安安心心的蘋果指定的這個(gè)安全區(qū)域放置子控件捉邢,布局就可以了。
UIView 的safeAreaLayoutGuide屬性
查看UIView在iOS11上關(guān)于Safe Area新增的兩個(gè)屬性:
@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));
很明顯這個(gè)只讀的safeAreaLayoutGuide屬性是系統(tǒng)自動(dòng)創(chuàng)建的商膊,可以讓開發(fā)者用代碼進(jìn)行基于安全區(qū)域進(jìn)行自動(dòng)布局伏伐。
點(diǎn)擊控制器的view觸發(fā)touchesBegan進(jìn)行打印驗(yàn)證:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(@"safeAreaInsets %@",NSStringFromUIEdgeInsets(self.view.safeAreaInsets));
NSLog(@"safeAreaGuide %@",self.view.safeAreaLayoutGuide);
}
打印結(jié)果:
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>>>
根據(jù)打印結(jié)果safeAreaInsets.top=44,剛好是蘋果規(guī)定的適配iPhone X要避開的劉海的距離晕拆,
safeAreaInsets.bottom=34秘案,剛好是底部的home虛擬手勢(shì)區(qū)域的高度。
橫屏旋轉(zhuǎn)測(cè)試:
進(jìn)行橫屏切換后:
再次點(diǎn)擊控制器的view觸發(fā)touchesBegan進(jìn)行打印驗(yàn)證,打印結(jié)果:
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>>>
旋轉(zhuǎn)之后阱高,safeAreaInsets.left距離劉海隔離區(qū)域依然是44赚导,底部的home虛擬手勢(shì)區(qū)域變成了21。
由此證明赤惊,系統(tǒng)也把屏幕旋轉(zhuǎn)的情況也自動(dòng)計(jì)算好了吼旧。
- iOS 11.0之后系統(tǒng)新增安全區(qū)域變化方法
UIViewController中新增:
- (``void``)viewSafeAreaInsetsDidChange;
UIView中新增:
- (``void``)viewSafeAreaInsetsDidChange;
- 通過安全區(qū)域變化來改變視圖的位置
如果屏幕旋轉(zhuǎn)未舟,相應(yīng)的安全區(qū)域也會(huì)變化圈暗,所以不比擔(dān)心。![safearea.gif](http://upload-
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];
}
|
/**
更新屏幕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
}
}
總結(jié)
這次為了適配iPhone X裕膀,個(gè)人從一開始看到iOS11的Safe Area這個(gè)概念员串,追溯到iOS7 topLayoutGuide/bottomLayoutGuide,從頭開始學(xué)習(xí)昼扛,受益匪淺寸齐。也體會(huì)到了蘋果工程師針對(duì)UI適配,面向開發(fā)者進(jìn)行的一系列探索抄谐,以及優(yōu)化的心路歷程渺鹦。也看到了他們?nèi)绾螌⒁粋€(gè)好的思路,面對(duì)當(dāng)前的需求變化蛹含,進(jìn)行合理的擴(kuò)展毅厚,設(shè)計(jì)出的靈活可擴(kuò)展的API:
1.iOS7: topLayoutGuide/bottomLayoutGuide,利用一個(gè)虛擬的view初步解決導(dǎo)航欄浦箱,tabbar的隔離問題吸耿。
2.iOS9:有了虛擬view的思路,又考慮能不能去除top/bottom概念的局限性,讓開發(fā)者都可以靈活自定義這個(gè)隔離區(qū)域酷窥,又提供一些更方便簡(jiǎn)潔易懂的API方便進(jìn)行代碼自動(dòng)布局咽安,于是有了UILayoutGuide這個(gè)類。竖幔。
3.兩年后的iOS11,有了iPhone X板乙,蘋果工程師順理成章的將他們?cè)趇OS9的探索成果利用起來,他們自定義了一個(gè)UILayoutGuide拳氢,給開發(fā)者提供了一個(gè)只讀屬性的safeAreaLayoutGuide募逞,并且提出安全區(qū)域的概念。
作為一個(gè)開發(fā)者馋评,有一個(gè)學(xué)習(xí)的氛圍跟一個(gè)交流圈子特別重要放接,這是一個(gè)我的iOS交流群:638302184,不管你是小白還是大牛歡迎入駐 留特,分享BAT,阿里面試題纠脾、面試經(jīng)驗(yàn)玛瘸,討論技術(shù), 大家一起交流學(xué)習(xí)成長(zhǎng)苟蹈!
群內(nèi)提供數(shù)據(jù)結(jié)構(gòu)與算法糊渊、底層進(jìn)階、swift慧脱、逆向渺绒、整合面試題等免費(fèi)資料
附上一份收集的各大廠面試題(附答案) ! 群文件直接獲取
各大廠面試題