当我们使用UINavigationController时,插入一个控制器,然后往这个控制器的view上加subview(比如一个tableView)时,经常会碰到tableView的实际展示跟自己设置的frame不一致的情况。这里就总结记录一下平时自己遇到过的相关问题。
iOS7之前控制器有一个属性wantsFullScreenLayout
。当把它设置为YES时,你添加的subview的y就是从屏幕的最顶部开始算,包含navigationbar,设置为NO时,则从navigationbar的bottom开始算。因为iOS7之前,navigationbar默认一般是不透明的,所以wantsFullScreenLayout
一般默认为NO。从iOS7开始,这个属性被废弃了,代替它的是三个新的属性:edgesForExtendedLayout
、extendedLayoutIncludesOpaqueBars
、automaticallyAdjustsScrollViewInsets
,今天重点说的就是这三个经常用的属性。
edgesForExtendedLayout
The extended edges to use for the layout.
This property is applied only to view controllers that are embedded in a container such as UINavigationController. The window’s root view controller does not react to this property. The default value of this property is UIRectEdgeAll
这个属性是通过扩展子视图的边缘来适配屏幕,因为iOS7之后鼓励全屏,navigationbar变得半透明,所以这个属性默认为UIRectEdgeAll
,及x和y是从屏幕的左上角开始算的。比如设置tableView的frame如下:
self.tableView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 350);
这里为了防止automaticallyAdjustsScrollViewInsets的干扰,我们在viewDidAppear:
方法里加上一句代码,下文会细说。
// 相当于把automaticallyAdjustsScrollViewInsets设置为NO了
self.tableView.contentInset = UIEdgeInsetsZero;
效果如下图:
当如下更改edgesForExtendedLayout
的设置时
self.edgesForExtendedLayout = UIRectEdgeNone;
效果如下:
显然这是我们要的tableView效果,但是我们发现navigationbar的颜色变灰了,这是因为navigationbar是半透明的,但是navigationbar下面没有视图。
这时我们可以通过改变navigationbar的透明度来实现。
self.navigationController.navigationBar.translucent = NO;
// self.edgesForExtendedLayout = UIRectEdgeNone;
设置不透明之后,subview的边缘就扩展不到顶部上了,只能到navigationbar的底部。效果
extendedLayoutIncludesOpaqueBars
A Boolean value indicating whether or not the extended layout includes opaque bars.
The default value of this property is NO
这个属性是指当navigationbar不透明时,layout是否包含navigationbar。当navigationbar透明时,此属性不起作用。比如在上面设置不透明的基础上,把这个属性设置为YES(该属性默认NO),
self.navigationController.navigationBar.translucent = NO;
// self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = YES;
发现tableView的y从屏幕最顶部开始算了。
automaticallyAdjustsScrollViewInsets
A Boolean value that indicates whether the view controller should automatically adjust its scroll view insets.
The default value of this property is YES, which lets container view controllers know that they should adjust the scroll view insets of this view controller’s view to account for screen areas consumed by a status bar, search bar, navigation bar, toolbar, or tab bar. Set this property to NO if your view controller implementation manages its own scroll view inset adjustments.
这个属性太强大了,它会根据navigationbar和tabbar的存在,自动调整scrollView的contentInset。如下图
透过半透明的navigationbar可以看到tableView的y其实还是在屏幕的最顶部,但是第一个cell已经变成从navigationbar的底部开始了,这是因为automaticallyAdjustsScrollViewInsets
属性默认为YES,tableView的contentInset已经由UIEdgeInsetsZero
自动调整为UIEdgeInsetsMake(64, 0, 0, 0)
了。当我们设置self.automaticallyAdjustsScrollViewInsets = NO;
时,就变成上文第一张效果图的效果了。但是它也有不足,就是scrollview必须是根视图添加的第一个子视图,否则此属性无效。
最后
这都是作者在实际工作中的一些总结,如有不对之处,欢迎指正。