iOS

iOS导航栏之UINavigationController,UI

2017-06-08  本文已影响584人  木小土

前言

本文主要从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. 实现微信我的-朋友圈页面的导航效果;
上一篇下一篇

猜你喜欢

热点阅读