iOS导航栏之UINavigationController,UINavigationbar和UINavigationItem的关系

前言

本文主要从UINavigationController,UINavigationBar和UINavigationItem的概念入手,通过介绍这三者之间的关系,来学会导航控制器的使用。

学习新知识分为两个状态:学会如何运用和理解知识的原理,自如运用。本篇主要侧重于后者。

self.navigationController.navigationBar.backgroundColor = [UIColor greenColor];
self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
self.navigationItem.title = @"我是标题";

通常,我们都是这样设置导航栏的样式。但是为什么标题需要使用视图控制器的navigationItem对象中设置,而导航栏的背景色却是在导航控制器navigationBar对象中设置?

接下来我们看一下苹果文档中对三者的解释:

一、UINavigationController

UINavigationController是一种可以管理层级内容的导航控制器,也就是我们常说的容器控制器。它通过一个栈对象实现了不同ViewController之间的跳转和返回操作。栈底的viewcontroller称为rootViewController, 栈顶的对象就是当前显示在屏幕上的ViewController.

官方文档

A navigation controller object manages the currently displayed screens using the navigation stack, which is represented by an array of view controllers. The first view controller in the array is the root view controller. The last view controller in the array is the view controller currently being displayed. You add and remove view controllers from the stack using segues or using the methods of this class. The user can also remove the topmost view controller using the back button in the navigation bar or using a left-edge swipe gesture.

二、UINavigationBar

UINavigationBar是显示在屏幕最上方的一条bar,包含了当前view上的一些导航按钮,主要有左侧的返回按钮,居中的标题栏和可选的右侧按钮。可以单独使用也可以和UINavigationController结合在一起使用。

UINavigationController负责控制显示在界面顶部的UINavigationBar和一个可选的显示在界面底部的toolbar。UINavigationBar默认显示,UINavigationController负责在页面跳转时使用当前栈顶viewController的内容更新UINavigationBar.

官方文档

A UINavigationBar object is a bar, typically displayed at the top of the window, containing buttons for navigating within a hierarchy of screens. The primary components are a left (back) button, a center title, and an optional right button. You can use a navigation bar as a standalone object or in conjunction with a navigation controller object.

The navigation controller manages the navigation bar at the top of the interface and an optional toolbar at the bottom of the interface. The navigation bar is always present and is managed by the navigation controller itself, which updates the navigation bar using the content provided by the view controllers on the navigation stack.

三、UINavigationItem

UINavigationItem对象负责管理显示在UINavigationBar中的按钮和视图。当创建一个UINavigationController界面时,每一个进栈的viewController都必须有一个UINavigationItem对象;UINavigationItem对象中包含了要显示在导航栏上的按钮或者视图。导航控制器使用最上层的两个viewcontroller中的navigationItem中的内容来展示当前的导航栏。

官方文档

A UINavigationItem object manages the buttons and views to be displayed in a UINavigationBar object. When building a navigation interface, each view controller pushed onto the navigation stack must have a UINavigationItem object that contains the buttons and views it wants displayed in the navigation bar. The managing UINavigationController object uses the navigation items of the topmost two view controllers to populate the navigation bar with content.

苹果的UINavigationBar官方文档中有一段对三者的关系的描述非常关键:

当视图控制器在导航过程中,导航控制利用视图控制器提供的navigationItem的属性作为导航控制器的导航栏的model对象。navigationItem默认使用视图控制器的title属性,但是我们也可以通过自定义navigationItem的属性达到完全控制导航栏内容的效果。

A navigation controller uses the navigationItem property on UIViewController to provide the model objects to its navigation bar when navigating a stack of view controllers. The default navigation item uses the view controller’s title, but you can override the navigationItem on a UIViewController subclass to gain complete control of the navigation bar’s content.

四、三者的关系:

  1. 每一个导航控制器都会自动生成一个与之对应的导航栏(UINavigationBar)对象;
  2. 当一个新的ViewController加入到导航控制器时,导航栏使用ViewController提供的navigationItem对象作为导航栏的model对象来渲染导航栏;
  3. 导航栏会保存导航控制器栈内最上层的两个ViewController的navigationItem对象;
  4. ViewController从导航控制器栈移除时,navigationItem会同时从navigationBar的视图栈内移除。

五、实践

理清楚三者之间的关系后, 实现一个指定效果的导航栏就变得非常简单:

  1. 生成导航控制器,并指定rootViewController;

ViewController *viewController = [[ViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
self.window.rootViewController = navigationController;
  1. 通过navigationBar设置导航栏的外观,包括颜色和tintColor;
self.navigationController.navigationBar.backgroundColor = [UIColor orangeColor];
self.navigationController.navigationBar.tintColor = [UIColor grayColor];

  1. 新建一个ViewController,通过设置其navigationItem来实现指定的效果;
self.navigationItem.title = @"首页";

P.S. 总之,如果需要改变navigationBar的外观,就在navigationBar对象上做文章,如果需要在导航栏上显示按钮或者视图,就在navigationItem属性上做文章。

六、深入验证理论(不感兴趣的可以跳过不看)

这个部分主要通过调试手段验证第四部分的理论,有兴趣的可以动手验证一下,对您的学习大有裨益。
在你的项目中,准备三个ViewController, 分别设置其导航栏标题和背景色,跳转顺序为A -> B -> C,并分别在三个页面的viewDidAppear代理方法中打上断点,运行程序。

验证一:程序停留在A页面,使用lldb打印navigationBar对象

po self.navigationController.navigationBar //UINavigationBar: 0x7f8b1b508e50; 

继续执行,并点击按钮,从A页面跳转到B页面,程序会停留在B页面,同样打印navigationBar

po self.navigationController.navigationBar //UINavigationBar: 0x7f8b1b508e50; 

由此可见,无论在哪个页面,使用的都是同一个navigationBar对象;

验证二、有视图控制器进栈时,导航栏使用视图控制器的navigationItem对象作为model对象。

保留断点,重新运行项目,让断点停留在A页面,使用lldb打印navigationItem和navigationBar


po self.navigationItem //<<UINavigationItem: 0x6180001c8ac0>: title:'页面A'>
po self.navigationController.navigationBar.subviews 

//
//<__NSArrayM 0x600000055870>(
//<_UIBarBackground: 0x7f8b1b509310; frame = (0 -20; 375 64); userInteractionEnabled = NO; layer = <CALayer: 0x618000032a00>>,
//<<UINavigationItemView: 0x7f8b1b614930; frame = (144 8; 87 27); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600000234200>>: item=<<UINavigationItem: 0x6180001c8ac0>: title:'页面A'> title=页面A>,
//<_UINavigationBarBackIndicatorView: 0x7f8b1b6095d0; frame = (8 11.5; 13 21); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6080000338e0>>
//)
//

由此可知,当新的ViewController入栈时,navigationBar会使用ViewController提供的navigationItem对象。

验证三、navigationBar只保留最上层的2个viewController的navigationItem.

在页面B和C分别打印navigaitonBar, 得到的结果如下:


po self.navigationController.navigationBar.subviews 
//__NSArrayM 0x608000053da0>(
//<_UIBarBackground: 0x7f8b1b509310; frame = (0 -20; 375 64); userInteractionEnabled = NO; layer = <CALayer: 0x618000032a00>>,
//<<UINavigationItemView: 0x7f8b1d00f9d0; frame = (144 8; 87 27); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600000234200>>: item=<<UINavigationItem: 0x6000001c8e80>: title:'页面B'> title=页面B>,
//<<UINavigationItemButtonView: 0x7f8b1b614930; frame = (8 6; 54 30); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x608000036520>>: item=<<UINavigationItem: 0x6180001c8ac0>: title:'页面A'> title=页面A>,
//<_UINavigationBarBackIndicatorView: 0x7f8b1b6095d0; frame = (8 11.5; 13 21); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6080000338e0>>
)

 po self.navigationController.navigationBar.subviews
//<__NSArrayM 0x618000053fe0>(
//<_UIBarBackground: 0x7f8b1b509310; frame = (0 -20; 375 64); userInteractionEnabled = NO; layer = <CALayer: 0x618000032a00>>,
//<<UINavigationItemView: 0x7f8b1d00fdc0; frame = (144 8; 87 27); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600000236120>>: item=<<UINavigationItem: 0x6000001c9240>: title:'页面C'> title=页面C>,
//<<UINavigationItemButtonView: 0x7f8b1b617410; frame = (8 6; 106 30); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6080000373a0>>: item=<<UINavigationItem: 0x6000001c8e80>: title:'页面B'> title=页面B>,
//<_UINavigationBarBackIndicatorView: 0x7f8b1b6095d0; frame = (8 11.5; 13 21); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6080000338e0>>
)

由此可见,navigationBar确实只保存了最上层的2个viewController的navigationItem.

验证四、ViewController从导航控制器移除后,navigationItem也会从navigationBar中被移除。

这个大家自行验证一下。

能坚持读到这里的同学,都非常有耐心的👍

七、练习部分

既然已经花费了这么多精力,那就趁热打铁,练习一下。

  1. 实现微信我的-朋友圈页面的导航效果;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容