UINavigationViewController
使用苹果官方网站的架构图:
UINavigationViewController(下边简称UINaviVC)整体功能由3部分组成:UINaviVC本身, Bars(navigationBar,toolBar)和ViewControllers。UINaviVC作为管理者负责ViewController和Bar的交互。
具体说来就是UINaviVC从它的viewControllers中取得navigationItem对象,使用这个对象来更新Bar。如果Bar上发生了事件,比如Back按钮点击,UINaviVC作为Bar的Delegate来处理事件,比如弹出当前View Controller。
UINavigationBar
UINavigationBar继承自UIView,是UINaviVC顶部的Bar。苹果给出的UINavigationBar的组成部分是固定的,一般来讲我们不是通过addSubView的方式来构建UINavigationBar,而是使用UINavigationItem来构建UINavigationBar。
当然,既然UINavigationBar继承自UIView,我们也可以使用addSubView的方式给UINavigationBar添加子视图,但是需要自己管理布局。
UINavigationBar的组成部分:
UINavigationItem本身不是一个View,是一个继承自NSObject的对象,用来配置UINavigationBar上的界面。
UINavigationBar管理了一系列的UINavigationItem,以栈的形式,所以UINavigationBar上也有pushItem,popItem等API,如果我们使用UINaviVC,UINaviVC会替我们做这些管理。如果要自定义NavigationBar可能需要关注这些API。
所以,重复一遍:UINavigationBar的整体配置是通过UINaviVC的navigationBar属性来配置的,然后针对不同的UINaviVC的ViewController,进行更新。更新的依据就是ViewController的navigationItem属性。
几个例子:
设置风格:
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
设置背景图片:
UIImage *backgroundImg = [[UIImage imageNamed:@"barBackgroundImg"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[self.navigationController.navigationBar setBackgroundImage:backgroundImg forBarMetrics:UIBarMetricsDefault];
调整title的上下位置:
[self.navigationController.navigationBar setTitleVerticalPositionAdjustment:8 forBarMetrics:UIBarMetricsDefault];
设置title的文字样式:
NSDictionary * attrs = @{NSForegroundColorAttributeName:[UIColor yellowColor]};
self.navigationController.navigationBar.titleTextAttributes = attrs;
上述这些例子设置都是全局的,在任何一处设置了,以后的Bar就是一直这样。所以如果仅仅是一个ViewController界面需要改变这些属性,应该在viewWillAppear设置,然后viewDidDisappear中恢复原来的设置。
设置title:
title的内容由Top Navigation Item的title属性来决定。Top ViewController的navigationItem的title属性默认值是ViewController的title,可以重新赋值覆盖。
self.navigationItem.title = @"我的Title";
上边的代码要在当前展示的ViewController中设置,之所以强调这个,是因为马上要介绍的back button是在上一个ViewController中做设置。
设置Back按钮:
Back按钮的配置是由TopViewController的上一个ViewController的navigationItem决定的,具体的来说是navigationItem的back
UIBarButtonItem *backBtnItem = [[UIBarButtonItem alloc] init];
backBtnItem.title = @"返回Main";
self.navigationItem.backBarButtonItem = backBtnItem;
上述代码要在前一个ViewController中。
这里有几点说明:
1. 默认backBarButtonItem是nil,所以需要创建一个UIBarButtonItem,如果直接设置,由于OC语言的机制,不会报错,也不会生效。
2. 如果没有backBarButtonItem是nil,则NavigationBar默认的返回按钮显示返回的那个ViewController的title,但是,如果title太长放不下,就什么都不显示。但是如果创建了backBarButtonItem,但是没有给title赋值,那么NavigationBar什么都不显示,但是一旦赋值了backBarButtonItem的title,则不管多长都会显示。基本思想就是隐式设置的东西给一个合理的配置,程序员显示设置的不管是否合理,都去执行。
3. backBarButtonItem还有一个属性是image,如果设置了image,则不再显示title。
4. 创建backBarButtonItem的时候,不能使用initWithCustomView:初始化函数,因为NavigationBar不支持。
When configuring your bar button item, do not assign a custom view to it; the navigation item ignores custom views in the back bar button anyway.
修改返回指示图片(箭头):
修改返回箭头是一个整体的操作,需要在bar上设置
UIImage *backImg = [UIImage imageNamed:@"backBtn"];
self.navigationController.navigationBar.backIndicatorImage = backImg;
self.navigationController.navigationBar.backIndicatorTransitionMaskImage = backImg;
这里要注意的是一定同时设置backIndicatorTransitionMaskImage属性,否则不生效。
修改返回指示图片(箭头)的位置:
更改back按钮返回指示图片的位置在iOS11之前有两种方式:制作一个有insets的图片或者使用leftBarButtonItem,代码分别如下:
//这段代码只能调整高度
UIImage *backImg = [UIImage imageNamed:@"backBtn"];
backImg = [backImg imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
backImg = [backImg imageWithAlignmentRectInsets:UIEdgeInsetsMake(0, 0, -3, 0)];
self.navigationController.navigationBar.backIndicatorImage = backImg;
self.navigationController.navigationBar.backIndicatorTransitionMaskImage = backImg;
//注意:和backBarButtonItem不同,leftBarButtonItem是当前页面的属性,所以要在当前显示的ViewController中设置。
//并且leftBarButtonItem没有默认的响应函数,需要自己写pop的事件。
UIImage *backImg = [UIImage imageNamed:@"backBtn"];
UIBarButtonItem *leftItem = [[UIBarButtonItem alloc] initWithImage:backImg style:UIBarButtonItemStylePlain target:self action:@selector(clickLeftItem)];
self.navigationItem.leftBarButtonItem = leftItem;
iOS11失效的原因:https://forums.developer.apple.com/thread/80075
简单的说来就是iOS11在NavigationBar中增加了图层,写死了约束(至少没放开修改)。导致我们自己对位置的调整不能随心所欲。
对于iOS11,有两种办法解决:一是使用addSubview的方式添加按钮,自己管理布局和事件响应;二是彻底重写NavigationBar。但是对于已有的功能,上述两种方法代价都不小。
针对已有的代码,这篇文章给出了解决方案。思路就是研究Apple的图层然后修改布局。但是如果系统的升级对图层又有修改,会有问题。对于这一点应该密切注意系统的升级。
为了方式上述链接失效,再贴一篇。
设置prompt:
这是一个不常见的功能
self.navigationItem.prompt = @"hello, 大家好";
效果如下:
除非每一个界面都有prompt,否则导航栏高度会变化,效果不太好。
设置toolBar
toolBar默认是隐藏的,需要主动的显示出来。
UIView *toolBarCustomView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 400,30)];
toolBarCustomView.backgroundColor = [UIColor blueColor];
UIBarButtonItem *barBtnItem = [[UIBarButtonItem alloc] initWithCustomView:toolBarCustomView];
[self.navigationController setToolbarHidden:NO];
self.toolbarItems = @[barBtnItem];
常见的评论栏,如果效果是点击评论栏进入新页面评论,那就可以考虑用toolbar来做。