《iOS创意程序设计家》——第6.2节导航栏控制器UINavigationController

6.2 导航栏控制器UINavigationController
iOS创意程序设计家
导航栏控制器(UINavigationController)位于界面的最上方,主要用于将具有因果关系的界面连接起来,它由几个元素组成:左边按钮、右边按钮以及标题。我们可以通过导航栏的navigationItem来访问这3个元素。其中,左右两边的按钮都是UIBarButtonItem类,我们可以通过navigationItem.rightBarButtonItem以及navigationItem.leftBarButtonItem来设置这两个按钮。如果希望界面上不出现任何一边的按钮,那么只要将其设置为nil就可以了。至于中间标题的部分,我们也可以通过titleView来访问。例如:

self.navigationController.navigationItem.titleView = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:@"title"]];

由于titleView本身就是一个UIView,因此,也可以通过addSubview:的方式将其他界面控件加入到标题栏中。

从故事板中,我们可以很清楚地看出导航栏应用程序中界面的组成方式。这些界面形成了界面堆栈,由导航栏负责维护这些界面间的前后关系,如图6.5所示。

6.2.1 界面堆栈的概念
界面堆栈主要负责维护界面间的前后关系,不过,这里所谓的界面是以UIViewController为单位的,而不是UIView。它有点像是界面的历史记录,可以让您回到上一个界面。在UINavigation-Controller这个类中,有一个viewControllers属性,您可以很轻易地通过这个属性来访问界面堆栈,或是通过topViewController来取得位于该堆栈内最上层的界面。

那么,我们怎么将界面推入这个堆栈里面呢?除了通过viewControllers这个属性外,也可以通过pushViewController:animated:来将另一个UIViewController推入到堆栈中。被推入堆栈后的UIViewController所控制的界面就会位于该堆栈内最上层的界面,从而形成了界面切换的效果。pushViewController:在使用的时候有一个限制,其后面的参数只能是UIViewController,而不能是UINavigationController。如果您执意要将UINavigationController推入堆栈,那么应该使用presentViewController: animated: completion:来取代上面的方法。

好了,现在来看看在这个堆栈的过程中发生了哪些事情。当一个UIViewController被置入堆栈里面的时候,它会变为该堆栈里面最上层的界面控制器,当然,导航栏的界面也会随之更新,界面也会自动调整为适当的大小。这个时候,您可以发现导航栏的左边出现了一个按钮,而这个按钮上的文字正是上一个界面的标题。

这个标题的文字其实是在每一个UIViewController设置好的,您可以通过title属性来设置每一个界面的标题。如果忘了设置这些界面的标题,您将看不到返回键,但是,在相对应的位置按下时,这些返回键仍会有作用。除了使用上一页的标题作为返回键的文字外,也可以通过navigationItem.back
BarButtonItem来指定一个自定义的按钮或标题。不论是通过title还是navigationItem.backBar
ButtonItem来设置返回键,您都应该在上一页的UIViewController中设置,而不是在当前的页面内设置。

现在试着查询UIViewController的API,您会发现一件很有趣的事情,原来的UIViewController中就有一个navigationController的属性(而UINavigationContoller中有viewControllers属性)。如果在新建项目的时候不是使用Master-Detail应用程序样板的话,那么这个属性将会是nil,这是因为Master-Detail的应用程序已经帮您做好了关联。关于这一点,大家可以通过编辑器的版本模式来观察。

现在我们来看看相反的操作,也就是让界面回到上一页。可以通过UINavigationController的popViewControllerAnimated:方法来回到上一个界面,或是通过popToRootViewController- Animated:方法来将堆栈中除了最开始的界面外的界面全部退出堆栈。

好了,了解界面堆栈的使用后,我们来看一个实际的例子。您可以先把其他的界面准备好,然后通过UIViewController类的initWithNibName加载进来。例如,可以在XCode产生的ViewController类里面用下面这样的方式来切换界面。

AnotherViewController *anotherViewController= [
  [AnotherViewController alloc] initWithNibName:@"AnotherView" bundle:nil];
[self.navigationController
  pushViewController:anotherViewController animated:YES];
我们刚刚提到pushViewController:后面的参数不可以是一个UINavigationController的对象,如果真碰上这种情况,就应该使用以下代码:

[self.navigationController
  presentViewController:anotherViewController animated:YES completion:nil];
最后,别忘了设置目前界面的标题,否则界面上就不会出现返回键。

self.title = @"First page";

6.2.2 使用故事板来处理界面堆栈
如果完全采用故事板的方式来设计界面,那么不需要使用诸如pushViewController:animated:的方式来处理界面堆栈。可以直接在两个界面间设置好它们的连接(Segue),然后一切的工作就交由系统自己去处理了。不过有些时候,两个界面间的连接也有可能会有多种情况发生,这通常是因为用户在第1个界面做了某些操作而导致第2个界面出现的结果不同。这时候,我们可以设置多个连接(Segue),然后再由程序去判断要走哪一条路径。

比如,在下面的界面中,我们可以将输入密码的界面与登录成功和登录失败的界面分别连接起来。选中连接后,在属性观测窗口中为每个连接设置一个标识符,在程序里面就可以通过这个标识符来判断要走的路径,如图6.6所示。例如,可以在第一个界面的View Controller中写入如下程序代码。

if( 登录成功 )
  [self performSegueWithIdentifier:@"LoginSuccessSegue" sender:nil];
else
  [self performSegueWithIdentifier:@"LogiFailSegue" sender:nil];

这样一来,界面就会跟着程序内的逻辑来跑了。

注意:
① 如果尝试将按钮链接到下一个界面,会发现路径只能有一个。如果希望做到上述的效果,那么应该将两个View Controller连接起来才对。

② 除了上述的方式外,也可以在AppDelegate.m中去编写这样的程序代码,以决定第一个界面是什么。其中,“LoginViewController”与“ViewController”是我们为不同的View Controller在属性观测窗口(attribute inspector)所设置的标识符。

UIStoryboard *storyboard = self.window.rootViewController.storyboard;
if( 未记忆密码 )
  self.window.rootViewController = [storyboard
    instantiateViewControllerWithIdentifier:@"LoginViewController"];
else
  self.window.rootViewController = [storyboard
    instantiateViewControllerWithIdentifier:@"ViewController"];

6.2.3 单选按钮
iPhone内建的通讯录其实就是一个很典型的导航栏模式,读者们应该也注意到了吧。我们可以看到在导航栏的右边有一个加号的按钮,这是怎么加上去的呢?其实导航栏控制器已经预留了左右两边的按钮。您可以在导航栏的两边各放入一个UIBarButtonItem类的按钮,使用的代码如下:

UIBarButtonItem * item=[[UIBarButtonItem alloc]
     initWithTitle:@"Done"
     style: UIBarButtonItemStyleDone
     target:self action:@selector(clickButtonItem)];
self.navigationController.navigationItem.rightBarButtonItem = item;
self.navigationController.navigationItem.leftBarButtonItem = nil;

把rightBarButtonItem 或leftBarButtonItem设置为nil时,导航栏原有的按钮就不见了,这个技巧可以应用到诸如文本编辑器这样的应用程序上。我们在第5章的UITextViewDemo的例子里面就已经运用了这个技巧。

可以使用的按钮的类型包括以下几种:

typedef enum {
  UIBarButtonItemStylePlain,
  UIBarButtonItemStyleBordered,
  UIBarButtonItemStyleDone,
} UIBarButtonItemStyle;

如果不想使用自定义的按钮样式,也可以直接使用系统内定的按钮类型,不过在初始化的时候得改用initWithBarButtonSystemItem:的构造函数,使用代码如下:

UIBarButtonItem * item=[[UIBarButtonItem alloc]
        initWithBarButtonSystemItem: UIBarButtonSystemItemDone
        target:self action:@selector(clickButtonItem)];
系统默认的按钮则有以下几种:

typedef enum {
  UIBarButtonSystemItemDone,
  UIBarButtonSystemItemCancel,
  UIBarButtonSystemItemEdit, 
  UIBarButtonSystemItemSave, 
  UIBarButtonSystemItemAdd,
  UIBarButtonSystemItemFlexibleSpace,
  UIBarButtonSystemItemFixedSpace,
  UIBarButtonSystemItemCompose,
  UIBarButtonSystemItemReply,
  UIBarButtonSystemItemAction,
  UIBarButtonSystemItemOrganize,
  UIBarButtonSystemItemBookmarks,
  UIBarButtonSystemItemSearch,
  UIBarButtonSystemItemRefresh,
  UIBarButtonSystemItemStop,
  UIBarButtonSystemItemCamera,
  UIBarButtonSystemItemTrash,
  UIBarButtonSystemItemPlay,
  UIBarButtonSystemItemPause,
  UIBarButtonSystemItemRewind,
  UIBarButtonSystemItemFastForward,
} UIBarButtonSystemItem;

值得一提的是,一旦决定使用系统按钮,您将无法改变上面的标题文字;反之,可以通过UIBarButtonItem的title属性来动态地改变上面的文字。除了文字的按钮外,也可以通过图片的方式来呈现,使用代码如下:

UIBarButtonItem * item = [[UIBarButtonItem alloc]
     initWithImage:[UIImage imageNamed:@"done"]
     style:UIBarButtonItemStylePlain
     target:self action:@selector(clickButtonItem)];

6.2.4 如何建立导航栏应用程序
在所有的项目样板中只有Master-Detail的应用程序才会默认产生导航栏,在其他的样板中,我们都得自己去建立一个导航栏控制器。下面的内容将介绍如何在设计界面中建立导航栏控制器。可以使用XIB或是故事板来建立这个应用程序,如果可能的话,尽量使用故事板的方式,在开发上会更为直观些。

请记得勾选“Use Storyboard”以及“Use Automatic Reference Counting”选项。

如同往常一样,您应该可以看到一个空白的手机界面。现在,选中这个View Controller,并按下删除键将这个界面删除。现在的界面上应该是空无一物了。

导航栏控制器默认会带有一个UIViewController,而这个UIViewController就是它的界面堆栈内的第一个界面。现在,可以试着在这个UIViewController的标题栏的地方双击后输入它的标题文字。现在的界面看起来应该如图6.7所示。

完成之后,可以试着执行应用程序,您会发现界面上竟然是一片黑色的界面,这是因为我们还没有指定故事从哪边开始的缘故。

每个故事板的一开始一定要有一个起始的界面控制器。所以请选中界面中的Navigation Controller控制器,切换到属性观测窗口,并勾选“Is initial View Controller”,如图6.8所示。现在再试着执行看看,您应该可以看到第一个界面了。

接下来,我们要产生第2个界面。还记得导航栏控制器是将整个UIViewController推入到界面的堆栈里面吧。所以,在这里,我们必须要由控件库中拉入一个非导航栏的控制器。例如一个UIViewController,如图6.10所示。

现在再执行一下您的应用程序,应该可以顺利地由第1个界面切换到第2个界面了。

时间: 2025-01-31 05:44:22

《iOS创意程序设计家》——第6.2节导航栏控制器UINavigationController的相关文章

《iOS创意程序设计家》——第6.4节事件检测

6.4 事件检测 iOS创意程序设计家 界面控制器除了负责界面的管理以及布局外,还负责事件的传递.这些事件包括我们在第5章已经介绍过的触控事件,还有接下来要介绍的晃动检测事件.这些事件都定义在UIResponder类里面,而无论是界面控制器UIViewController还是界面UIView,它们都继承自UIResponder类. 6.4.1 晃动检测 首先,我们来看看晃动事件的处理.与触控事件类似的是,晃动检测也是由一连串的事件所组成的,不过,要让您的应用程序支持晃动检测,必须让您的界面控制器

《iOS创意程序设计家》——第6.5节Undo与Redo机制

6.5 Undo与Redo机制iOS创意程序设计家还记得第5章在提到UITextView的时候是怎样实现Undo机制的吗?其实,iPhone OS3.0以后就内建了Undo-Redo的机制.在默认的情况下,每一个应用程序的window对象都提供一个NSUndoManager对象,用以管理Undo与Redo的操作,而窗口内的每一个控件也都有其各自的NSUndoManager对象. 这个Undo-Redo机制是怎么运作的呢?首先,它会有两组堆栈(stack),分别用来存放Undo与Redo的操作,而

《iOS创意程序设计家》——第6.1节 界面的管理

6.1 界面的管理iOS创意程序设计家UIViewController提供了对于iOS应用程序的基本界面管理模式,包括界面的呈现.Modal窗口界面的管理以及iOS设备旋转后界面旋转的支持等. 每个UIViewController都会对应到一个唯一的UIView,这表示这个界面由相对应的UIViewCon-troller所控制.如同我们在前一章所提到的,在UIViewController类中,有一个view的属性,而UIViewController正是通过这个属性才得以与其管理的界面关联起来.在

《iOS创意程序设计家》——第6.3节标签页控制器UITabBarController

6.3 标签页控制器UITabBarControlleriOS创意程序设计家相对于导航栏控制器,标签页控制器(UITabBarController)是将几个具有独立功能的界面使用Tab分隔开来,并定义在界面的下方.这些界面会存放在一个数组里面,而里面每个元素则是UIView-Controller的类,当然也可以包括导航栏UINavigationController在内.可以通过viewControllers属性或是setViewControllers: animated:方法来设置标签页控制器所

《iOS创意程序设计家》——导读

目 录第1章 从硬件的认识开始第2章 Objective-C语言的基础第3章 开发工具第4章 基本类与机制第5章 界面控件第6章 界面控制器 6.1 界面的管理 6.2 导航栏控制器UINavigationController 6.3 标签页控制器UITabBarController 6.4 事件检测 6.5 Undo与Redo机制 6.6 热身操第7章 操作信息的截取第8章 数据访问与打印第9章 表格控件与Core Data第10章 通讯录第11章 相机与相簿第12章 影音多媒体第13章 定位

Android程序开发之Fragment实现底部导航栏实例代码_Android

流行的应用的导航一般分为两种,一种是底部导航,一种是侧边栏. 说明 IDE:AS,Android studio; 模拟器:genymotion; 实现的效果,见下图. 具体实现 为了讲明白这个实现过程,我们贴出来的代码多一写,这样更方便理解 [最后还会放出完整的代码实现] .看上图的界面做的比较粗糙,但实现过程的骨架都具有了,想要更完美的设计,之后自行完善吧 ^0^. 布局 通过观察上述效果图,发现任意一个选项页面都有三部分组成: 顶部去除ActionBar后的标题栏: 中间一个Fragment

Android程序开发之Fragment实现底部导航栏实例代码

流行的应用的导航一般分为两种,一种是底部导航,一种是侧边栏. 说明 IDE:AS,Android studio; 模拟器:genymotion; 实现的效果,见下图. 具体实现 为了讲明白这个实现过程,我们贴出来的代码多一写,这样更方便理解 [最后还会放出完整的代码实现] .看上图的界面做的比较粗糙,但实现过程的骨架都具有了,想要更完美的设计,之后自行完善吧 ^0^. 布局 通过观察上述效果图,发现任意一个选项页面都有三部分组成: 顶部去除ActionBar后的标题栏: 中间一个Fragment

【IOS开发必收藏】详解IOS应用程序内使用IAP/STOREKIT付费、沙盒(SANDBOX)测试、创建测试账号流程!【2012-12-11日更新获取”产品付费数量等于0的问题”】

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/iphone-cocos2d/550.html //--2012-12-11日更新   获取"产品付费数量等于0这个问题"的原因 看到很多童鞋问到,为什么每次都返回数量等于0?? 其实有童鞋已经找到原因了,原因是你在 ItunesConnect 里的 "Contracts, Tax, and Banking "没

IDA反汇编/反编译静态分析iOS模拟器程序(八)IDA for Mac

iOS多用OC(Objective-C)编程,Mac也类似,所以IDA for Mac对OC的支持似乎强些.Windows的IDA在反汇编某些SDK库文件时会识别不出OC的函数名,而且对OC运行时的结构体也没识别出来.当然,因为我用的是6.1版的windows IDA, Mac上用的是6.4版,不知道是不是windows IDA 6.1的bug了. 总之在界面操作流程上,感觉Mac IDA是对OC有做优化的.当加载一个app时, 会询问是否解析和重命名OC的函数: 如果选择No,所有OC函数都会