Safe Area解析(一) —— Safe Area由来及简单使用(一)

版本记录

版本号 时间
V1.0 2018.05.26

前言

Safe Area是iOS 9新出的,它的出现一定程度上解决了很多适配的问题,可以说解决了很多UI方面的问题,你再也不用担心状态栏是否被覆盖,特别是iphone X出现以后,状态栏和底部都留有和其他机型不同的高度,这给适配带来更多的麻烦,但是Safe Area预留出来安全区域,可以让你对UI的适配无后顾之忧。感兴趣的可以看我上面写的几篇文章。

Safe Area是什么?

在说Safe Area之前,我们先一起看两个东西:

@interface UIViewController (UILayoutSupport)
// These objects may be used as layout items in the NSLayoutConstraint API
@property(nonatomic,readonly,strong) id<UILayoutSupport> topLayoutGuide API_DEPRECATED("Use view.safeAreaLayoutGuide.topAnchor instead of topLayoutGuide.bottomAnchor", ios(7.0,11.0), tvos(7.0,11.0));
@property(nonatomic,readonly,strong) id<UILayoutSupport> bottomLayoutGuide API_DEPRECATED("Use view.safeAreaLayoutGuide.bottomAnchor instead of bottomLayoutGuide.topAnchor", ios(7.0,11.0), tvos(7.0,11.0));

/* Custom container UIViewController subclasses can use this property to add to the overlay
 that UIViewController calculates for the safeAreaInsets for contained view controllers.
 */
@property(nonatomic) UIEdgeInsets additionalSafeAreaInsets API_AVAILABLE(ios(11.0), tvos(11.0));

/* Minimum layoutMargins for the view determined by the view controller from context and hardware information.
 The view controller's view will respect these minimums unless viewRespectsSystemMinimumLayoutMargins
 (which defaults to YES) is set to NO.
 */
@property(nonatomic,readonly) NSDirectionalEdgeInsets systemMinimumLayoutMargins API_AVAILABLE(ios(11.0), tvos(11.0));

/* Default YES. The return value of the view's layoutMargins and directionalLayoutMargins properties will have
 values no smaller than the systemMinimumLayoutMargins. Set to NO for full customizability of the view's
 layoutMargins.
 */
@property(nonatomic) BOOL viewRespectsSystemMinimumLayoutMargins API_AVAILABLE(ios(11.0), tvos(11.0));

- (void)viewLayoutMarginsDidChange NS_REQUIRES_SUPER API_AVAILABLE(ios(11.0), tvos(11.0));
- (void)viewSafeAreaInsetsDidChange NS_REQUIRES_SUPER API_AVAILABLE(ios(11.0), tvos(11.0));

@end

上面就是UIViewController里面关于布局的一个分类,大家也看到了

// These objects may be used as layout items in the NSLayoutConstraint API
@property(nonatomic,readonly,strong) id<UILayoutSupport> topLayoutGuide API_DEPRECATED("Use view.safeAreaLayoutGuide.topAnchor instead of topLayoutGuide.bottomAnchor", ios(7.0,11.0), tvos(7.0,11.0));
@property(nonatomic,readonly,strong) id<UILayoutSupport> bottomLayoutGuide API_DEPRECATED("Use view.safeAreaLayoutGuide.bottomAnchor instead of bottomLayoutGuide.topAnchor", ios(7.0,11.0), tvos(7.0,11.0));

上面这两个是iOS 7出来的,iOS11已经被废弃了,替换他们的就是Safe Area,上面说的就是被view.safeAreaLayoutGuide.bottomAnchor替代,那个是在UIView的API中。

/* -layoutMargins returns a set of insets from the edge of the view's bounds that denote a default spacing for laying out content.
 If preservesSuperviewLayoutMargins is YES, margins cascade down the view tree, adjusting for geometry offsets, so that setting
 the left value of layoutMargins on a superview will affect the left value of layoutMargins for subviews positioned close to the
 left edge of their superview's bounds
   If your view subclass uses layoutMargins in its layout or drawing, override -layoutMarginsDidChange in order to refresh your 
 view if the margins change.
   On iOS 11.0 and later, please support both user interface layout directions by setting the directionalLayoutMargins property
 instead of the layoutMargins property. After setting the directionalLayoutMargins property, the values in the left and right
 fields of the layoutMargins property will depend on the user interface layout direction.
 */
@property (nonatomic) UIEdgeInsets layoutMargins NS_AVAILABLE_IOS(8_0);

/* directionalLayoutMargins.leading is used on the left when the user interface direction is LTR and on the right for RTL.
 Vice versa for directionalLayoutMargins.trailing.
 */
@property (nonatomic) NSDirectionalEdgeInsets directionalLayoutMargins API_AVAILABLE(ios(11.0),tvos(11.0));

@property (nonatomic) BOOL preservesSuperviewLayoutMargins NS_AVAILABLE_IOS(8_0); // default is NO - set to enable pass-through or cascading behavior of margins from this view’s parent to its children
@property (nonatomic) BOOL insetsLayoutMarginsFromSafeArea API_AVAILABLE(ios(11.0),tvos(11.0));  // Default: YES

- (void)layoutMarginsDidChange NS_AVAILABLE_IOS(8_0);

@property (nonatomic,readonly) UIEdgeInsets safeAreaInsets API_AVAILABLE(ios(11.0),tvos(11.0));
- (void)safeAreaInsetsDidChange API_AVAILABLE(ios(11.0),tvos(11.0));

/* The edges of this guide are constrained to equal the edges of the view inset by the layoutMargins
 */
@property(readonly,strong) UILayoutGuide *layoutMarginsGuide NS_AVAILABLE_IOS(9_0);

/// This content guide provides a layout area that you can use to place text and related content whose width should generally be constrained to a size that is easy for the user to read. This guide provides a centered region that you can place content within to get this behavior for this view.
@property (nonatomic, readonly, strong) UILayoutGuide *readableContentGuide  NS_AVAILABLE_IOS(9_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));

我们先说一下以前的topLayoutGuidebottomLayoutGuide都是干什么的。

topLayoutGuidebottomLayoutGuide 两个属性来描述不希望被透明的状态栏或者导航栏遮挡的最高位置。这个属性的值是一个length属性( topLayoutGuide.length)。 这个值可能由当前的ViewController或者 NavigationController 或者TabbarController决定。

一个独立的ViewController,不包含于任何其他的ViewController。如果状态栏可见,topLayoutGuide表示状态栏的底部,否则表示这个ViewController的上边缘。包含于其他ViewController的ViewController不对这个属性起决定作用,而是由容器ViewController决定这个属性的含义。

  • 如果导航栏(Navigation Bar)可见,topLayoutGuide表示导航栏的底部

  • 如果状态栏可见,topLayoutGuide表示状态栏的底部。

  • 如果都不可见,表示ViewController的上边缘。

iOS 11以后为了获得更好的适配体验就弃用了这两个属性,更改为Safe Area相关的概念和属性。苹果建议:不要把VC放在Safe Area之外。


Safe Area简单应用

先看一个最简单的应用,在根view上脱上一个子视图,设置如下约束:

设置约束
视图结构

下面在6s模拟器上运行,如下所示:

NSLog(@"safeAreaInsets = %lf, %lf, %lf, %lf", self.testView.safeAreaInsets.left, self.testView.safeAreaInsets.right, self.testView.safeAreaInsets.top, self.testView.safeAreaInsets.bottom);
NSLog(@"self.testView = %@", self.testView);

看运行结果

2018-05-26 14:50:56.158103+0800 JJWebImage[64139:1049305] safeAreaInsets = 0.000000, 0.000000, 0.000000, 0.000000
2018-05-26 14:50:56.158496+0800 JJWebImage[64139:1049305] self.testView = <UIView: 0x7ff272f096f0; frame = (0 20; 375 647); autoresize = RM+BM; layer = <CALayer: 0x60c00003c3e0>>

可见safeAreaInsets上左下右的间距均为0,并且预留出来上边20状态栏的高度。

具体效果如下所示:

iphone 6s效果

下面换成iPhone X看一下输出和实际效果

先看输出

2018-05-26 15:07:33.072648+0800 JJWebImage[64417:1065730] safeAreaInsets = 0.000000, 0.000000, 0.000000, 0.000000
2018-05-26 15:07:33.072999+0800 JJWebImage[64417:1065730] self.testView = <UIView: 0x7feb48c1a8f0; frame = (0 44; 375 734); autoresize = RM+BM; layer = <CALayer: 0x600000037180>>

再看实际效果

iphone X

点击testView可见其frame及在父视图中的位置。

可见其顶部预留大小为44个点,底部预留大小为34个点。扩展下:iPhone X竖屏时占满整个屏幕的控制器的view的safeAreaInsets(44,0,34,0),横屏是(0,44,21,44),inset后的区域正好是safeAreaLayoutGuide区域。


总结

  • Xcode9 用 IB 可以看得出来, safe area 到处都是了。理解起来很简单。就是系统对每个 View 都添加了 safe area, 这个区域的大小,是否跟 view 的大小相同是系统来决定的。在这个 View 上的布局只需要相对于 safe area 就可以了。每个 View 的 safe area 都可以通过 iOS 11 新增的 API safeAreaInsets 或者safeAreaLayoutGuide获取。

  • 对与 UIViewController 来说新增了additionalSafeAreaInsets这个属性, 用来管理有 tabbar 或者 navigation bar 的情况下额外的情况。

  • scrollView 只需要设置contentInsetAdjustmentBehavior就可以很容易的适配iPhoneX。

  • tableView 只需要在cell header footer等设置约束的时候相对于 safe area 来做。

  • collection view来说修改sectionInsetReference.safeArea就可以做大多数的事情了。

参考文章

1. 最近很火的 Safe Area 到底是什么

后记

本篇主要介绍了Safe Area的由来以及其简单的使用,可以说是很基础的东西,感兴趣的给个赞或者关注,谢谢~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容