明确一下以前一知半解的属性吧,文中所有的代码都默认写在UIViewController中,所以self特指控制器。
阅读时注意“bar的低下”和“bar的bottom处”两种说法的区别(bar的低下是指层级关系,bar的bottom处是指frame中的位置关系)。
edgesForExtendedLayout
在IOS7以后 ViewController 开始使用全屏布局的,默认控制器内self.view是从屏幕最顶部左上角开始布局,并且不论是当前控制器是否嵌入在UINavigationController还是UITabBarController内,self.view默认占满整个屏幕,edgesForExtendedLayout的默认值为UIRectEdgeAll
如下图设置橙色视图为self.view,绿色视图加在self.view上,设置frame=self.view.bounds,所以两个视图都从屏幕最顶端开始布局,如果底部有tabbar(即UINavigationController还是嵌套在UITabBarController)self.view依然会延伸到屏幕的最底部
由于上图会导致绿色视图被导航栏遮挡,我们想从导航栏的bottom处开始布局而还坚持想设置绿色视图的frame=self.view.bounds(因为自己写frame=CGRectMake()着实不爽),就可以设置控制器的edgesForExtendedLayout属性为UIRectEdgeNone,这样self.view就会从导航栏的botttom处开始布局:
- 哦,还有没说到的点是此属性仅适用于查看嵌入在容器(如UINavigationController)中的控制器。 窗口的根视图控制器不对此属性做出反应。
edgesForExtendedLayout是UIRectEdge枚举类型:
typedef NS_OPTIONS(NSUInteger, UIRectEdge) {
UIRectEdgeNone = 0,
UIRectEdgeTop = 1 << 0,
UIRectEdgeLeft = 1 << 1,
UIRectEdgeBottom = 1 << 2,
UIRectEdgeRight = 1 << 3,
UIRectEdgeAll = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeBottom | UIRectEdgeRight
} NS_ENUM_AVAILABLE_IOS(7_0);
可以自己尝试在控制器中分别设置看一下效果。
automaticallyAdjustsScrollViewInsets
下图层次结构为self.view->greenView->redView,设置self.edgesForExtendedLayout = UIRectEdgeAll(占满整个屏幕)
第一张图的greenView为UIScrollView对象,第二张图的greenView为UIView,观察greenView的子视图redView的布局位置
当greenView为UIScrollView对象的时候redView从navigationBar的bottom处开始布局,这是因为控制器的automaticallyAdjustsScrollViewInsets属性默认为YES,
automaticallyAdjustsScrollViewInsets是一个布尔值,指示视图控制器是否应自动调整滚动视图的contentInset属性。以解决状态栏,搜索栏,导航栏,工具栏或选项卡栏所消耗的屏幕区域。 给contentInset属性中的上,左,下,右设置值,以避免滚动视图中的子视图被导航条,tabbar等遮挡住。
例如上图打印greenView的contentInset如下:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
NSLog(@"%f-%f-%f-%f",self.greenView.contentInset.top,self.greenView.contentInset.left,self.greenView.contentInset.bottom,self.greenView.contentInset.right);
}
// 打印结果
2017-07-08 01:38:49.163 ZQNavigationBar[12648:786288] 64.000000-0.000000-0.000000-0.000000
greenView.contentInset.top被设置为了64,正好从导航栏bottom处开始布局,避免遮盖。
当设置self.automaticallyAdjustsScrollViewInsets = NO;时,greenView.contentInset将不会被自动设值,redView将从屏幕顶部开始布局
translucent
文档翻译如下:
指示导航栏是否为半透明(YES)的布尔值(否)。
默认值为YES。 如果导航栏具有自定义背景图像,则如果图像的任何像素的alpha值小于1.0,则默认值为YES,否则为否。
如果您在带有不透明的自定义背景图片的导航栏上将此属性设置为YES,则导航栏会将不足1.0的系统定义的不透明度应用于图像。
如果您在带有半透明自定义背景图片的导航栏上将此属性设置为NO,则如果导航栏具有UIBarStyleBlack样式,则导航栏将使用黑色为图像提供不透明的背景,如果导航栏具有UIBarStyleDefault,则为白色,或者导航栏的 barTintColor如果定义了自定义值。
可见关于导航条半透明,设置背景图片这一块幺蛾子甚多,慎重起见将另起文章进行研究,下图侧面证明了设置到导航条视觉效果设置的复杂性
下图层次结构为self.view->greenView->redView,都为UIView(避免受automaticallyAdjustsScrollViewInsets的干扰)
由图可见,设置self.navigationController.navigationBar.translucent = NO;时,不但导航条设置为不透明的了而且影响了self.view的布局开始位置(变成从导航条的bottom出开始布局了),导航条不透明了,导航条低下有什么颜色用户都看不到不能再提升视觉效果,干脆改变一下self.view的布局开始位置,方便开发人员——我猜猜的苹果爸爸的意图。实际上这里也可以控制self.view的布局从屏幕最顶部开始布局,如下图:
导航条不透明,self.view依然从屏幕顶部开始布局。HOW???
刚刚学过了设置self.edgesForExtendedLayout = UIRectEdgeAll;可以让self.view占满整个屏幕,难道是这个吗?试一下,结果可见本文第二张图——并没有什么卵用,当设置self.navigationController.navigationBar.translucent = NO;时就要用到了extendedLayoutIncludesOpaqueBars属性
extendedLayoutIncludesOpaqueBars
文档:
一个布尔值,指示扩展布局是否包括不透明的条。
此属性的默认值为NO。
即:设置self.view的布局是否包含设置为不透明之后的导航条,tabbar之类的bar,如果设置了YES,依然会延伸到这些bar的下面,占满整个屏幕,如果设置NO就会避开这些bar,不会延伸到它们下面。上边的考虑是在self.edgesForExtendedLayout = UIRectEdgeAll;的基础之上,如果设置
// 扩展布局包括不透明的条
self.extendedLayoutIncludesOpaqueBars = YES;
// 导航条不透明
self.navigationController.navigationBar.translucent = NO;
然后设置
self.edgesForExtendedLayout = UIRectEdgeNone;
呢?结果:
布局从导航条的botttom处开始!
结论(关于以上四个属性的py交易):
当
self.navigationController.navigationBar.translucent = YES;(导航条半透明)时,布局的扩展边界,从哪里开始布局受self.edgesForExtendedLayout属性的影响,
- 当self.edgesForExtendedLayout = UIRectEdgeNone;时self.view会避开一切系统的bar,不会占满整个屏幕
- 当self.edgesForExtendedLayout = UIRectEdgeAll;时,self.view会延伸到系统bar下面,占满整个屏幕,由于bar半透明可以隐约看到self.view或者其子视图的颜色透上来
当
self.navigationController.navigationBar.translucent = NO;(导航条不透明)时,self.view是否占满屏幕受到两个属性的影响:self.edgesForExtendedLayout和self.extendedLayoutIncludesOpaqueBars
- 只有两个同时满足时才会占满整个屏幕(self.extendedLayoutIncludesOpaqueBars = YES && self.edgesForExtendedLayout = UIRectEdgeAll;)
至于
automaticallyAdjustsScrollViewInsets的目的是为了避免系统bar遮挡了要显示的view,所以设置为YES时也只在self.view延伸到bar下面的时候并且子视图里有滚动视图的时候才有用,系统可以把滚动视图contentInset某一个值设置为bar的高度一致(自行学习contentInset属性),相当于对滚动进行了填充避免被遮挡要显示的内容。
最后总结的不太好,请自行结合前文的说明理解。本文举例说的bar都是用的navigationBar,结合文档来看,这几个属性对于系统提供的tabbar等也有相同的效果。
老人新手,如有说的不对之处,万望指正!