iOS11适配-Safe Area

在iOS 7中,苹果介绍了UIViewController中的topLayoutGuide和bottomLayoutGuide这两个属性,用来描述一个不被任何内容遮挡的屏幕区域,比如说顶部状态栏status bar、导航栏navigation bar、工具栏toolbar、菜单栏tab bar等。在IOS 11,苹果弃用了这些属性而启用safe area。苹果建议我们不要把任何交互放在safe area之外,从iOS 11开始,我们在开发iOS应用的时候,需要进行视图布局时,必须使用新的safe area API。 
当我们做iPhone X适配或者去试着支持safe area时,会发现UIKit中的很多类里都有新的safe area(安全区)特性。本文的目的就是对他们进行概括和介绍。

目录

本文分为以下几个部分。

  • UIView
  • UIViewController
  • UIScrollView
  • UITableView
  • UICollectionView

这些都是具有安全区属性和方法的类。

示例

可以在Github中找到示例代码,阅读本文时可以同步运行。

UIView

在iOS 11,UIViewController中的UIView的topLayoutGuide和bottomLayoutGuide被替换成了新的安全区属性。

@available(iOS 11.0, *)
open var safeAreaInsets: UIEdgeInsets { get }

@available(iOS 11.0, *)
open var safeAreaLayoutGuide: UILayoutGuide { get }
  • 1
  • 2
  • 3
  • 4
  • 5

safeAreaInsets属性意味着屏幕可以被任何方向遮挡,并不只是上下,当iPhone X出现时,我们就明白了为什么我们需要对左右两边也进行缩进。 
iPhone 8 vs iPhone X safe area (portrait orientation)
iPhone 8 vs iPhone X safe area (landscape orientation)
可以看到,iPhone X在纵向时有上、下的safe area缩进,而在横向时有左、右、下的缩进。


看一下示例中的情况,我们在一个控制器的view中添加了两个子视图,它们分别包含一个label和一段特定的高度,然后,让他们抵住了view的顶部和底部,也都贴到了view的边缘。 
Subviews are attached to the view’s edges
我们看到子视图的内容和顶部的刘海、底部的home指示器重叠了。要正确布局子视图的位置,我们可以使用手动布局将它们附加到safe area。

topSubview.frame.origin.x = view.safeAreaInsets.left
topSubview.frame.origin.y = view.safeAreaInsets.top
topSubview.frame.size.width = view.bounds.width - view.safeAreaInsets.left - view.safeAreaInsets.right
topSubview.frame.size.height = 300
  • 1
  • 2
  • 3
  • 4

或者使用自动布局

bottomSubview.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
bottomSubview.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
bottomSubview.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
bottomSubview.heightAnchor.constraint(equalToConstant: 300).isActive = true
  • 1
  • 2
  • 3
  • 4

Subviews are attached to the superview safe area
看起来好点了。此外还可以在子视图的类里直接设置子视图上的孙视图的位置,将孙视图放在子视图的safe area里。

label.frame = safeAreaLayoutGuide.layoutFrame
  • 1

或者使用自动布局

label.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor).isActive = true
label.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor).isActive = true
label.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor).isActive = true
  • 1
  • 2
  • 3
  • 4

子视图填满了view,而label放在了它的父视图的安全区
这下就厉害了,我们不仅在控制器里可以将view放在安全区,还可以在任意一层子视图结构中,将view放在其父控件的安全区。这样一来,我们可以在保证文本不被刘海和home指示器遮挡的情况下,还能自定义安全区外部分的颜色或者图片背景。

UIViewController

在iOS 11,UIViewController有了一个新属性(额外的安全区缩进):

@available(iOS 11.0, *)
open var additionalSafeAreaInsets: UIEdgeInsets
  • 1
  • 2

当ViewController包含其他嵌入的子ViewController时,将使用这个属性。比如说苹果会在UINavigationController和UITabBarController中当它们各自的bar为半透明时使用这个属性。 
导航栏和菜单栏为半透明时的安全区。view的子视图贴在view的边缘,label在子视图的安全区
效果还不错,但是当状态栏隐藏时,奇怪的事情发生了。 
导航栏和菜单栏为半透明而且状态栏为隐藏时的安全区。view的子视图贴在view的边缘,label在子视图的安全区
其他安全区的缩进尺寸都在预料中,但是导航栏向上移动到了刘海下面。这是个棘手的bug,而且除了在Stack Overflow上找到的一个还好的方法之外,目前没找到更好的方法去解决。 
当我们修改additional safe area insets属性或者safe area insets被系统修改时,我们可以通过UIViewController或者UIView中特定的方法来监听。

// UIView
@available(iOS 11.0, *)
open func safeAreaInsetsDidChange()

//UIViewController
@available(iOS 11.0, *)
open func viewSafeAreaInsetsDidChange()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

模拟iPhone X的安全区

额外的安全区缩进也可以用于,当你没有iPhone X真机或者不方便用iPhone X的模拟器时,测试你的app是否支持iPhone X。比如像示例代码里一样,通过实时修改缩进值,观察安全区的变化。

//portrait orientation, status bar is shown
additionalSafeAreaInsets.top = 24.0
additionalSafeAreaInsets.bottom = 34.0

//portrait orientation, status bar is hidden
additionalSafeAreaInsets.top = 44.0
additionalSafeAreaInsets.bottom = 34.0

//landscape orientation
additionalSafeAreaInsets.left = 44.0
additionalSafeAreaInsets.bottom = 21.0
additionalSafeAreaInsets.right = 44.0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

模拟iPhone X上的安全区缩进

UIScrollView

在控制器上添加一个带label的scroll view,并且将其设置为贴着view边缘。 
scrollView贴着view边缘 
我们可以看到scroll view的缩进在顶部和底部自动适配了。在iOS 7及更高版本的系统,scroll view的内容缩进可以用UIViewController’s中的automaticallyAdjustsScrollViewInsets属性进行适配;但是在iOS 11中,这个属性被弃用了,取而代之的是UIScrollView中的一个新属性contentInsetAdjustmentBehavior。

@available(iOS 11.0, *)
public enum UIScrollViewContentInsetAdjustmentBehavior : Int {    
    case automatic          //default value
    case scrollableAxes
    case never
    case always
}

@available(iOS 11.0, *)
open var contentInsetAdjustmentBehavior: UIScrollViewContentInsetAdjustmentBehavior
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Content Insets Adjustment Behavior

never —— scroll view内容永远不适配,很好理解。 
scroll view贴在view的边缘(contentInsetAdjustmentBehavior == .never) 
scrollableAxes —— 只对可滚动的方向上适配。比如说当scroll view的content size高度大于frame.size的高度或者启用了alwaysBounceVertical属性时,纵向可滑动;类似的当content size宽度大于frame.size的宽度或者启用了alwaysBounceHorizontal属性时,横向可滑动。 
scroll view贴在view的边缘(contentInsetAdjustmentBehavior == .scrollableAxes)。纵向可滚动 
水平模式下只有底部content inset会适配,左侧和右侧content inset不会适配,因为此时横向不能滑动。 
always —— 不管是否可滑动,scroll view的content insets永远适配。 
scroll view贴在view的边缘(contentInsetAdjustmentBehavior == .always)。纵向可滚动 
automatic —— 是默认项,也是最有意思的。当它满足下列情况时,它和always 一样:

  • scroll view横向可滑动,纵向不可滑动
  • scroll view是控制器view的最底层子视图
  • 控制器是navigation控制器或tabBar控制器的子控制器
  • automaticallyAdjustsScrollViewInsets为真

在其他情况下automatic相当于scrollableAxes 
关于automatic选项的更多描述,可以查看UIScrollView类:

类似于.scrollableAxes,但是考虑到向下兼容,当scroll view位于navigation controller中且设定了automaticallyAdjustsScrollViewInsets为真时,也会自动调整顶部和底部的contentInset,不管scroll view是否可滑动。

然而xcode苹果文档中的描述略有不同

当一个控制器展示在一个navigation控制器或者tabBar控制器时,其中的scroll view总是会被自动调整。如果scroll view处于横向可滑动,那么当未设置安全区indest的时候,横向的content offset也会自动调整。 
因为考虑到向下兼容,automatic项被设置为默认。也就是说,在 iOS 10和 iOS 11中,横向可滑动的scroll view将会拥有相同的顶部和底部缩进。

Adjusted Content Insets

在iOS 11中,UIScrollView拥有了一个新的属性——adjustedContentInset:

@available(iOS 11.0, *)
open var adjustedContentInset: UIEdgeInsets { get }
  • 1
  • 2

那么contentInset和adjustedContentInset属性有什么不同?我们打印一下当scroll view被导航栏和菜单栏遮挡时两者的值:

//iOS 10
//contentInset = UIEdgeInsets(top: 64.0, left: 0.0, bottom: 49.0, right: 0.0)
//iOS 11
//contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
//adjustedContentInset = UIEdgeInsets(top: 64.0, left: 0.0, bottom: 49.0, right: 0.0)
  • 1
  • 2
  • 3
  • 4
  • 5

现在我们给contentInset从四个方向分别加10个点,然后再打印一下:

//iOS 10
//contentInset = UIEdgeInsets(top: 74.0, left: 10.0, bottom: 59.0, right: 10.0)
//iOS 11
//contentInset = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
//adjustedContentInset = UIEdgeInsets(top: 74.0, left: 10.0, bottom: 59.0, right: 10.0)
  • 1
  • 2
  • 3
  • 4
  • 5

我们可以看到在iOS 11中,scroll view实际的content insets将从adjustedContentInset属性中取得,而不再是contentInset属性。意思是当app需要同时支持iOS 10和 iOS 11时,我们需要为content insets的适配写两套不同的逻辑。 
想要监听contentInset的值被修改的情况,UIScrollView和UIScrollViewDelegate提供了响应的方法。

//UIScrollView
@available(iOS 11.0, *)
open func adjustedContentInsetDidChange()

//UIScrollViewDelegate
@available(iOS 11.0, *)
optional public func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

UITableView

在控制器上添加一个带有自定义header和自定义cell的table view。 

自定义的header上有一个label。自定义的cell也有一个label,带一个分割线。header和cell是透明的,cell的content view背景为白色,header的content view背景为红色。 
我们可以看到,当横屏时,header和cell的content view frame变了,但是同时cell以及分割线的frame没有变。这是个默认项,我们可以用UITableView的新属性insetsContentViewsToSafeArea来进行控制。

@available(iOS 11.0, *)
open var insetsContentViewsToSafeArea: Bool
  • 1
  • 2

如果将此属性设置为NO: 

我们可以看到现在header、footer、cell的content views frame就等于他们各自的frame。 
这就意味着在iOS 11,我们在为header、footer、cell添加子控件时,不需要改变子控件的位置,UITableView自动帮我们适配。

UICollectionView

我们再试着在UICollectionView做一个同样的列表: 

在collection view中使用了UICollectionViewFlowLayout,滑动方向设为纵向,cell是透明的,cell的content view背景设为白色,header也就是UICollectionReusableView没有contentView,我们把它的背景设为红色。 
从截图中可以看到,collection view在默认情况下没有缩进header、footer和cell的内容。想要正确布局它的内容,唯一的方法是把子视图放到安全区: 

好,现在改变cell的size,使我们的collection view显示网格: 

可以看到在横屏时cell被刘海遮挡了一部分。想要解决这个问题我们需要在section的content insets上添加安全区,但是其实在iOS 11中UICollectionViewFlowLayout有了一个新属性sectionInsetReference用来处理它。

@available(iOS 11.0, *)
public enum UICollectionViewFlowLayoutSectionInsetReference : Int {    
    case fromContentInset        //default value
    case fromSafeArea
    case fromLayoutMargins
}

@available(iOS 11.0, *)
open var sectionInsetReference: UICollectionViewFlowLayoutSectionInsetReference
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

想做出我们想要的效果只需要设置成fromSafeArea。这个时候section的实际 content insets就等于content insets加上安全区insets。 

类似地,当使用fromLayoutMargins时,collection view就在content insets基础上加了margins。 

总结

在iOS 11中,苹果为我们加了很多实用的工具来使用安全区。在这篇文章我试着把他们全部介绍了一遍。希望本文可以帮助大家更好地在app中应用安全区。另外,我建议大家看一下相关的WWDC视频或者阅读相关文章:

如果使用storyboards的话,还有专门的讲解(我不用):

也可以看一下我为这篇文章专门写的示例代码: 
示例代码

如果有什么问题欢迎留言,谢谢观看! 
 
原文地址

  • 点赞
  • 评论
  • 分享
  • 收藏2
  • 举报
  • 关注
  • 一键三连

    点赞Mark关注该博主, 随时了解TA的最新博文

HarmonyOS开发者创新大赛,邀您一起打造智慧生活!

02-01

立即报名参加HarmonyOS开发者创新大赛,赢取150W奖金! 通过学习HarmonyOS,创造性开发出具有全新体验、全新交互的跨终端应用,共同推进HarmonyOS生态建设,打造全场景智慧生活!

iOS 11 safeArea详解及iphoneX 适配

01-05

最近看了许多iPhone X适配的文章,发现很少有介绍safeArea的,就来随便写写 现在对于iPhone X的适配,有一种常见的做法是给导航栏或tabbar增加一个固定的距离,比如顶部增加44pt

表情包

IPad Pro 12.9英寸版本的界面适配

懵懵懂懂

 8759

IOS开发怎么样适配12.9英寸的Pad Pro12.9英寸的iPad pro,宽高是(1366*1024),Pad其他版本都是(1024*768),按普通尺寸开发的pad应用,最后需要兼容一下pro。为此我自己写了三个宏。应该写在公共文件中#define FULL_SCREEN_RECT CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.widt

iOS开发-iPhoneX的适配-iPhoneX屏幕适配分享

伪随机的张三

 1万+

iOS 11正式发布,原本bate版就很多坑,现在还没解决就正式发布了~内心也是酸爽的。除了代码上的坑,更恶劣的就是新的iPhone咯,看到iPhone8和8P的时候内心还算不错的,不过iPhone X就…

iOS11适配-Safe Area_WakeUpYQ

1-6

Safe area 是iOS11的新特性, 帮助你将视图布局在可访问区域内,不被一些特殊视图覆盖,如:状态栏,导航控制器的导航栏等,尤其是具有顶部头帘和底部横条的iPhone X...

iOS 11 安全区域适配总结_coding_girl的博客

1-25

导语:本文主要是对iOS 11下APP中tableView内容下移20pt或下移64pt的问题适配的一个总结。内容包括五个部分:问题的原因分析、adjustContentInset属性的计算方式、什么...

iOS 获取safeAreaInsets bottom 失败

LeeCSDN77的博客

 1620

主要是注意获取时机, 需要在viewWillAppear之后,viewWillLayoutSubviews 方法中获取 安全距离改变时机 这里有一个坑要踩踩了一开始笔者也是在这个坑了踩了很久 还以为苹果搞个安全距离的概念来忽悠我们的 如果我们在ViewController - (void)viewDidLoad来打印self.view.safeAreaInsets会发现始终 显示 self...

IOS11 适配遇到的坑

JefferDevs的专栏

 1万+

IOS11正式版马上就要发布了,是时候适配一波了。下面就来说说我适配过程中遇到的坑吧。UITableView:默认开启Self-Sizing 首先要知道Self-Sizing是个什么东东。看官方文档的解释: 大概就是说我们不再需要自己去计算cell的高度了,只要设置好这两个属性,约束好布局,系统会自动计算好cell的高度。IOS11以后,Self-Sizing默认开启,包括

iOS11safeArea详解及iphoneX适配-其它代码类资源

2-1

本篇文章主要介绍了iOS 11 safeArea详解及iphoneX 适配,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 iOS11 safeArea ...

iOS 11.0 iPhone X safeArea适配_iOS学习的博客

2-4

说明:使用xib创建视图,工程适配8.0以上系统,xib inspector中未勾选Safe Aear Layout Guide选项(iOS 9.0前无Safe Area Layout Guide)。其中粉色为当前viewcontroller...

iOS 11 适配集锦

hou_manager的博客

 5945

iOS 11适配 飞机票1.Demo源码地址 2.简书地址安全区域的适配用Xcode 9 创建storyboard或者xib时,最低版本支持iOS 8时会报: Safe Area Layout Guide before iOS 9.0 如图:原因:在iOS7中引入的Top Layout Guide和Bottom Layout Guide,这些布局指南在iOS 11中被弃用,取而代之的是Safe A

React Native 组件之SafeAreaView

xiangzhihong8的专栏

 1万+

SafeAreaView简介 ReactNative官方从0.50.1版本开始,加入了针对iPhone X设备齐刘海页面适配的组件SafeAreaView,为ReactNative开发APP时对iPhone X的页面适配提供了很大的方便。目前,SafeAreaView只适合iOS设备。 SafeAreaView的使用也非常简单,只需要将SafeAreaView嵌套在最根级别的视图中即可,并且在...

iOS开发:iOS11安全区域适配问题_软贱开发攻城狮-CSDN博...

1-24

造成下移原因是iOS 11之后,controller的automaticallyAdjustsScrollViewInsets属性被废弃,导致tableview如果超出安全区域时系统自动调整了SafeAreaInsets值,从而影响了adjusted...

适配iOS11,适配iPhoneX,适配安全区的几个文章和宏_码农...

12-28

#define LL_ViewSafeAreInsets(view) ({UIEdgeInsets insets; if(@available(iOS 11.0, *)) {insets = view.safeAreaInsets;} else {insets = UIEdge...

swift实现类似宏定义功能,举例实现safeAreaInsets

weixin_30632883的博客

 113

//注意:要获取safeAreaInsets最好的办法是重写viewSafeAreaInsetsDidChange函数,在里面获取。因为viewDidLoad之前,safeAreaInsets是0值。并且屏幕也会转动。 override func viewSafeAreaInsetsDidChange () { print(view.safeAreaInsets.b...

iPhone X 适配

LiqunZhang的博客

 1万+

iPhone X(10)屏幕分辨率与适配iOS开发适配与UI设计问题。iPhone人机交互指南。 北京时间的9月13日凌晨,美国当地时间的9月12日上午,苹果在发布会上发布了四款产品,本包括全新的Apple Watch Series 3,Apple TV 4K,iPhone 8/8 Plus,和全新iPhone X四款全新产品。其中X是数字10的意思,因此苹果将其读音读作“iPhone Ten

iPhone X 和 iOS 11 适配(Safe Area)_weixin_30500473...

1-18

iPhone X 和 iOS 11 适配(Safe Area) 参考链接: http://www.cocoachina.com/ios/20170921/20623.html https://www.lee1994.com/guan-yu-iphone/...

iOS 11 安全区域适配总结_码农天后的博客_ios 安全区

2-6

导语:本文主要是对iOS 11下APP中tableView内容下移20pt或下移64pt的问题适配的一个总结。内容包括五个部分:问题的原因分析、adjustContentInset属性的计算方式、什么...

iOS 11 安全区域适配总结》

腾讯Bugly的专栏

 3029

本文主要是对iOS 11下企鹅 FM APP中tableView内容下移20pt或下移64pt的问题适配的一个总结。内容包括五个部分:问题的原因分析、adjustContentInset属性的计算方式、什么情况下的tableView会发生内容下移、有哪些解决方法、解决这个问题时遇到的另外一个小问题。

关于iOS中的布局向导(Layout Guide)和安全区域(Safe Area

Puzhi的专栏

 5353

iOS在默认情况下,竖屏会显示状态栏,横屏自动隐藏状态栏。而视图控制器的主视图默认位于屏幕顶端,在竖屏时会被顶部状态栏遮挡。如果我们希望不被状态栏遮挡,则需要先判断横竖屏,然后动态显示隐藏状态栏。如果界面中还存在导航栏、分页栏,情况就更复杂一些。总之,自己处理是一件很麻烦的事情。 布局向导 在iOS 7中,苹果引入了Top Layout Guide(顶部布局向导)和Bottom Layout Gu...

iOS 11 安全区域适配总结_mgr406176009的专栏

1-21

导语:本文主要是对iOS 11下APP中tableView内容下移20pt或下移64pt的问题适配的一个总结。内容包括五个部分:问题的原因分析、adjustContentInset属性的计算方式、什么...

【转】最近很火的 Safe Area 到底是什么

weixin_34082695的博客

 136

iOS 7 之后苹果给 UIViewController 引入了 topLayoutGuide 和 bottomLayoutGuide 两个属性来描述不希望被透明的状态栏或者导航栏遮挡的最高位置(status bar, navigation bar, toolbar, tab bar 等)。这个属性的值是一个 length 属性( topLayoutGuide.length)。 这个值可能由当...

iOS开发 关于iPhone X 的适配

wuWu的博客

 1万+

1.屏幕尺寸相关变化 高度增加了145pt,变成812pt. 屏幕圆角显示,注意至少留10pt边距。 状态栏高度由20pt变成44pt,留意这个距离就能避开“刘海”的尴尬,相应的导航栏以上变化64->88。 底部工具栏需要为home indicator留出34pt边距。 物理分辨率为1125px * 2436px. 2.横竖屏安全区对比

近很火的 Safe Area 到底是什么

Gabriel的专栏

 1286

转自:https://blog.csdn.net/yst19910702/article/details/79260937 iOS 7 之后苹果给 UIViewController 引入了 topLayoutGuide 和 bottomLayoutGuide 两个属性来描述不希望被透明的状态栏或者导航栏遮挡的最高位置(status bar, navigation bar, toolbar, ta...

Xcode9 Safe Area Layout Guide Before iOS9.0报错解决

JackerooChu的博客

 2935

9月20日苹果爸爸推送了iOS11.0以及Xcode9,作为开发小白一枚,第一时间更新了Xcode9,去体验一下所谓的跨时代的iPhone X,像往常新建个新项目,结果一运行直接报错(弱弱的说一句,这个xcode9好像并没有那么友好~)然后一Google,苹果爸爸在苹果在iOS7中引入的Top Layout Guide和Bottom Layout Guide,这些布局指南在iOS 11中被弃用,取而

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值