每个导航控制器会有一个根控制器,通过初始化方法设置
initWithRootViewController,这个初始化方法会调用pushViewController:animated:方法push根控制器,因此自定义导航控制器重写pushViewController:animated:方法的时候,要考虑到初始化时,对根控制器也会调用。
一、导航栏遮盖视图
iOS7 之前:
- UINavigationBar 的 translucent 属性默认 NO。 (导航栏默认完全不透明)
iOS7 之后:(view controllers默认使用全屏布局(full-screen layout))
- UINavigationBar 的 translucent 属性默认 YES。(导航栏默认半透明)
- UIViewController 多了 extendLayoutIncludesOpaqueBars(default: NO)属性。
- UIViewController 多了 edgesForExtendedLayout(default: UIRectEdgeAll)属性。
说明:extendLayoutIncludesOpaqueBars 延伸布局是否包括不透明的 bar ; edgesForExtendedLayout 延伸布局的样式,默认上下左右全部延伸。
注意:当translucent=NO的时候,不管edgesForExtendedLayout设置成UIRectEdgeAll还是UIRectEdgeNone,view都是从导航栏底部开始
那么问题来了,怎么让translucent=NO的时候,view也能从(0,0)开始布局呢?
苹果也考虑到了这种需求,提供了 extendedLayoutIncludesOpaqueBars 这个属性。extendedLayoutIncludesOpaqueBars 默认值是NO,下面把它改为YES
问题:
- 导航栏默认会遮蔽视图,因为视图默认布局为全延伸。
解决方案:
- 设置(个人用的少)self.navigationController.navigationBar.translucent = NO;
- 设置self.edgesForExtendedLayout = UIRectEdgeBottom | UIRectEdgeLeft | UIRectEdgeRight;或者self.edgesForExtendedLayout = UIRectEdgeNone(视图布局时不向任何方向延伸)
二、iOS设置导航栏标题
方法一:在UIViewController中设置self.title。
方法二:设置self.navigationItem.titleView。
三、automaticallyAdjustsScrollViewInsets
还有一个属性:
- automaticallyAdjustsScrollViewInsets:
默认值YES,表示在全屏模式下会自动修改第一个添加到
rootView 的 scrollview 的 contentInset 为(64,0,0,0)
先设置automaticallyAdjustsScrollViewInsets为NO
automaticallyAdjustsScrollViewInsets在iOS11中已废弃,需要使用ScrollView子类的contentInsetAdjustmentBehavior属性来代替(比如写成self.tableView.contentInsetAdjustmentBehavior=UIScrollViewContentInsetAdjustmentNever;)
需要注意的是automaticallyAdjustsScrollViewInsets=YES只对VC**第一个添加到 rootView 的 scrollview **有效。
安全区(safeArea)
在iOS11 中引入了安全区的概念,说白了就是:你放在这个区域里面的视图是不会被NavigationBar和TabBar和StatusBar遮住的.(前提是你不去手动修改安全区范围的情况下),安全区本身不是一个View,不会显示在我们的视图层级上,只是给你参考用.
safeAreaInset属性
iOS11之前,如果有tableview向下偏移64的情况的话.64这个值是在contentInset里面获取的,iOS11之后改成了从safeAreaInset获取.但是contentInset这个属性 !!!!依旧是有用的,并没有废弃. !!!!
contentInsetAdjustmentBehavior属性
上面的说过的automaticallyAdjustsScrollViewInsets属性在iOS 11中已经被废弃了(设置这个没用了),改用UIScrollview子类的contentInsetAdjustmentBehavior属性来代替例如
self.tableview.contentInsetAdjustmentBehavior=UIScrollViewContentInsetAdjustmentNever;//使用这行代码来代替
contentInsetAdjustmentBehavior属性有4个枚举值:{
UIScrollViewContentInsetAdjustmentAutomatic:在有导航栏的VC中,这个属性会设置上部和底部的adjustedContentInset值,
方式为adjustedContentInset = safeAreaInset + contentInset。其他情况下与UIScrollViewContentInsetAdjustmentScrollableAxes相同
UIScrollViewContentInsetAdjustmentScrollableAxes: 在可滚动方向上adjustedContentInset = safeAreaInset + contentInset,在不可滚动方向上adjustedContentInset = contentInset
UIScrollViewContentInsetAdjustmentNever: adjustedContentInset = contentInset
UIScrollViewContentInsetAdjustmentAlways: adjustedContentInset = safeAreaInset + contentInset
}
四、UINavigationBar和UINavigationItem
- UINavigationBar和UINavigationItem的区别
1、UINavigationBar的功能类似于UINavigationController中的栈,UINavigationController管理的是控制器的栈,而UINavigationBar管理的是UIBarButtonItem。在UINavigationController中有一个UINavigationBar类的属性-navigationBar,查看这个属性(@property(nonatomic,readonly) UINavigationBar *navigationBar; // The navigation bar managed by the controller. Pushing, popping or setting navigation items on a managed navigation bar is not supported.),虽然在UINavigationBar中有push、pop NavigaitonItem的方法,但我们看到在属性后又这样注释:“这个navigationbar是通过控制器来管理的,它本身不支持pushing,poping或者setting 一个navigationbar”,说明在一个导航控制器UINavigationController里,UInavigationItem的pop、push方法是由系统自动管理的,我们仅需知道UINavigationController push或者pop一个控制器的同时,UInavigationBar也在对应的push和pop UINavigationItem,导航栏和控制器是一一对应的同步关系。(因此UInavigationBar里面有push和pop UINavigationItem的方法,但是这些方法是用在单独使用UInavigationBar的时候)
2、UINavgationItem管理着显示在UINavigationBar上的按钮和图片,每一个push进栈的控制器都会自带一个navigationbar,navigationbar要实现显示图片和button,就需要通过UINavtionItem完成。为了验证以上说法,我们也可以查看UINavigationItem的类属性,UINavigationItem包含了NavigaitonBar视图的全部元素(如title,tileview,backBarButtonItem等,同时又受当前栈顶控制器管理,即NavigaitonBar形成整个NavigationController的导航视图,然后每个NavigationController页面的导航栏元素由所在页面的UINavigationItem管理。简单的概括:控制器视图中的导航栏显示由UINavigationBar和UiNavigationItem共同完成,在控制器进行跳转时,UINavigationBar管理导航栏与栈顶控制器的对应关系,而UINavigationItem管理导航栏的显示内容。
1、UINavigationBar
tintColor barTintColor 分别在iOS7和它之前的区别
iOS7以前:
tintColor:设置navigationBar和navigationItem的颜色,navigationItem里面的字体默认为白色,如果想修改navigationItem字体颜色,需要自定义给navigationItem(Custom)。
iOS7之后(新增barTintColor属性):
tintColor:不再是以前的设置navigationBar和navigationItem的颜色,而是变成了只修改navigationItem里面的字体颜色。
barTintColor:设置navigationBar和navigationItem的颜色,由于iOS7的navigationItem以文字的方式体现,默认为蓝色,所以barTintColor看似乎对navigationItem无效。
2、UINavigationItem
- iOS11下自定义leftBarButtonItem大小改变的问题修复
一般leftBarButtonItem加载网络图片或因为其他问题,必须要在leftBarButtonItem使用自定义的UIButton时,就会因为图片太大,导致设置为UIButton的背景图片时,因为图片太大,拉宽leftBarButtonItem(为了解决这个问题,只需要在button的外层包装一层view即可)
UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 15, 15)];
UIView *leftCustomView = [[UIView alloc] initWithFrame: btn.frame];
[leftCustomView addSubview:btn];
[btn setImage:[UIImage imageNamed:@"123"] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside];
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:leftCustomView];
两种设置当前viewController中navigationItem的barButtonItem方法
第一种是在viewController控制器内部设置(最好是在baseViewController里面设置,这样继承自baseViewController的控制器,可以通过重写设置控制器样式的方法,修改自己的控制器样式。)
第二种是在自定义的UINavigationcontroller里面-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated方法中统一设置leftBarButtonItem与backBarButtonItem的区别
从显示的优先级来讲(假如现在即将从A视图跳到B视图,从B视图角度说):
1、如果B视图有一个自定义的左侧按钮(leftBarButtonItem),则会显示这个自定义按钮;
2、如果B没有自定义按钮,但是A视图的backBarButtonItem属性有自定义项,则显示这个自定义项(依然是一个后退按钮,自定义的部分只有背景或title);
3、如果前2条都没有,则默认显示一个后退按钮,后退按钮的标题是A视图的标题。