http://www.xs360.cn 《IOS应用开发教程》 QQ学习群:262779381
第七章 IOS视图控制器的使用 QQ学习群:262779381
1.认识了解MVC设计模式中视图控制器的概念和作用。 2.掌握创建视图控制器的方法。 3.掌握视图控制器的生命周期和内存警告。 教学目标: 1.认识了解MVC设计模式中视图控制器的概念和作用。 2.掌握创建视图控制器的方法。 3.掌握视图控制器的生命周期和内存警告。 4.掌握视图控制器的出现和消失。 QQ学习群:262779381
7.1UIViewController视图控制器 在前面章节中介绍的内容都是基于单个视图的应用,并不能实现多屏幕之间的 切换功能。随着我们所学知识的深入,我们要处理一些多视图切换的相关知识 ,比如要实现类似于iPhone中设置功能的多视图切换效果,如图所示,那我们 该如何实现呢? QQ学习群:262779381
这就要通过视图控制器UIViewControl来实现了,在图中是通过一个视图控制器对象UITableView来实现的,它是UIViewControl的子类,而UIViewControl这个类是所有视图控制器的父类,所有子类都继承于它。说了这么多,那到底视图控制器是什么呢?我们该如何使用它完成视图切换的功能呢?我们会在本章中一一为大家进行讲解。 QQ学习群:262779381
7.1.1视图控制器的基本概念 在前面的学习中,我们采用的设计模式大多为协议代理设计模式,通过采用一个协议并实现协议中的代理方法来达到我们需要的效果。而从本节开始,我们开始在协议代理模式上加入MVC设计模式,那么视图控制器毫无疑问担当了MVC中控制层的角色。 视图控制器为iPhone应用程序提供了基础的视图控制模型,用户可以通过视图控制器来管理视图的继承关系。所以使用视图控制器可以方便的管理视图中的子视图,如果你不使用视图控制器要操作视图的话,那么所有的视图都必须要有继承关系,可想而知这是非常繁琐的。 在以前我们写的例子中,我们都是直接将创建的UIView实例添加到Window的视图上,其实这都是不规范的,所以在编译过程中,控制台中会显示一条警告语句:Application windows are expected to have a root view controller at the end of application launch。这条语句的意思是应用程序在载入之后需要有一个根视图控制器,所以我们都是先创建一个视图控制器实例,并将实例设置为根视图控制器。当视图控制器实例创建好之后,它自己也有一个view,接下来我们就可将在view上创建自己想创建的各种视图,并通过视图控制器来管理这些新创建的视图。下图显示了视图控制器在应用程序中担当视图和重要数据之间的重要桥梁。 QQ学习群:262779381
那有的读者可能会有以下的疑问,UIVewController 和UIView是什么关系呢 ?UIView不是也能添加子视图完成一些操作吗?我们举一个简单地例子来说明 二者之间的关系。将UIViewController比作是一个电视屏幕,UIView比作各 种频道,我们可以通过电视观看想要看的频道。UIView的作用是向用户展现要 表现的内容,并接收用户交互,而UIViewController负责安排底下UIView的 表现形式,比如视图翻转效果,淡入淡出效果等。 通过这一小节的学习,想必读者对视图控制器有了一个大致的了解,在下一 小节中,我们会介绍如何创建一个视图控制器,并对它进行相应的操作。 QQ学习群:262779381
7.1.2视图控制器的创建 视图控制器的创建有两种方法,一种是代码创建,另一种则是用XIB来创建。 首先我们来学习用代码来创建一个视图控制器。 新建一个Single View Application项目模板。然后在AppDelegate.m文件 中导入ViewController类。然后在以前创建UIView实例的application didFinishLaunchingWithOptions方法中创建我们的视图控制器,并设置它 的实例为根控制器。 QQ学习群:262779381
1. #import "ViewController. h" 2. self 1.#import "ViewController.h" 2.self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; 3.// Override point for customization after application launch. 4.self.window.backgroundColor = [UIColor whiteColor]; 5.ViewController *vc= [[ViewController alloc]init]; 6.self.window.rootViewController = vc; 7.[self.window makeKeyAndVisible]; 8.return YES; QQ学习群:262779381
我们可以看到window中有一个rootViewController(根视图控制器)属性, 它的默认值是nil,所以以前我们没有设置根视图控制器时编译器会报一个警告。 这样我们就将实例设置为了根视图控制器,因为实例将它的使用交给了 window来管理,所以在设置之后要将实例释放。 在前面的内容中我们也提到过创建视图控制器时,它会自带一个view,现 在我们就可以通过视图控制器的实例对view进行操作,比如改变它的背景颜色。 9.ViewController *vc = [[ViewController alloc]init]; 10.self.window.rootViewController = vc; 11.vc.view.backgroundColor = [UIColor purpleColor]; 看上去虽然和前面所讲的创建视图没什么区别,但是它作为控制视图的一个工 具,为我们下面创建更多地视图搭建了一个强大的框架,使接下来的的工作变 得轻松。 QQ学习群:262779381
7.1.3视图控制器的生命周期 在前面的章节中我们了解了视图控制器的概念,并学习了如何创建视 图控制器,这里我们再总结一下视图控制器的作用。 我们知道视图控制器是来管理与之关联的多个视图,它自身也会 协调与其他视图控制器的通信。在管理与之关联的视图时,系统规定 只有当应用需要视图时才会加载视图,而在不需要时卸载视图,以节 省系统的资源。 QQ学习群:262779381
·视图控制器视图的加载 我们前面也介绍过,视图控制器自身有一个View属性,是在控 制器的生命周期里,所以对它进行内存管理时,它必须在视图控制 器释放前release。如果用户没有定义view视图,系统会首先调用 [self loadView]方法,返回系统的view(也就是视图控制器的 view)。用户可以自己重写视图中loadView方法。我们通过一个 view加载过程图来了解系统是如何加载一个视图的。如图所示。 QQ学习群:262779381
QQ学习群:262779381
如果用户在子类中重写了loadView方法,系统就会使用用户自定义方法创建视 图,我们在重写loadView方法中,要创建一个view视图赋值给视图控制器的 view属性;如果用户在子类中调用父类的loadView方法[super loadView] ,可能就会三种创建视图的情况,第一种是通过StoryBoard,第二种是通过 XIB文件,第三种则是直接创建一个空的视图。 从图中我们可以看到,在调用viewDidLoad方法前,视图控制器的view是空 的,这是因为视图控制器view使用时,会调用view的getter方法,它会判断 view是否创建,如果没有创建,则会调用loadView方法来创建view。当 loadView方法执行完后,会执行viewDidLoad方法,此时,view已经创建好 了。 loadView方法中是用来自定义创建视图,我们不应该通过视图控制器的实 例来调用loadView方法,比如创建一个视图控制器实例rootViewController, 我们不应该在.m文件中调用loadView方法,[rootViewController loadView]。在viewDidLoad方法中,可以定义一些其他的操作,比如访问网 络资源等。 QQ学习群:262779381
·视图控制器视图的显示与消失 UIViewController会在特定的时间调用以下4个方法。 ·viewWillAppear:当视图控制器对象的视图即将加入窗口。 ·viewDidAppear:当视图控制器对象的视图已经加入窗口。 ·viewWillDisappear:当视图控制器对象的视图即将消失、被覆盖或隐藏。 ·viewDidDisappear:当视图控制器对象的视图已经消失、被覆盖或隐藏。 我们在编写程序中可以发现,UIViewController中的这些方法都是空的,不做任何事情, 它只是提供子类覆盖。 - (void)viewWillAppear:(BOOL)animated; - (void)viewDidAppear:(BOOL)animated; - (void)viewWillDisappear:(BOOL)animated; - (void)viewDidDisappear:(BOOL)animated; 虽然UIViewController只创建一次,但是与之关联的视图会显示与消失多次, 所以覆盖这4个方法在视图每次显示或消失时产生特定的效果是比较重要的, 比如每次显示视图时都刷新当前的系统时间,或者在应用中每次刷新当前在前 的人数等。我们会在模态视图中详细介绍这4个方法的使用。 QQ学习群:262779381
·视图控制器视图的卸载 如果不需要用到视图是,需要将视图进行卸载操作,图展现了视图的卸 载过程。 QQ学习群:262779381
从上图我们可以看到,当系统发出警告或者调用 didReveiveMemoryWarning时,系统会判断当前是否有视图,视图 是否能被卸载,如果能得话,就会卸载当前的视图,调用 viewWillUnload方法后释放掉当前view。然后再调用 viewDidUnload方法,在图中右侧可以看到与视图控制器关联的视图 已经为nil,表明视图卸载成功。 我们只需了解view卸载的过程,这对编写代码的逻辑性有很好地帮助 。 QQ学习群:262779381
7.1.4模态视图 模态视图是iPhone开发中一个比较重要的概念,它的功能是弹出一个优先级很 高的视图,当弹出模态视图时,系统会中断程序正常的执行流程,我们前面介 绍的UIAlertView和UIActionSheet都是模态视图。 视图控制器通过presentModalViewController方法弹出模态视图,而模态 视图常常用在一下几种情况: 1.需要收集用户信息。 2. 临时呈现内容或改变工作状态。 3. 改变设备的方向。 4. 显示一个新的view层级。 QQ学习群:262779381
模态视图的常用属性设置和方法主要有4种: 1. presentViewController:(UIViewController 模态视图的常用属性设置和方法主要有4种: 1. presentViewController:(UIViewController *) animated:(BOOL) completion:(void)completion 这个方法的作用就是弹出一个模态视图,新版本中摒弃了原有的旧方法,我们不能使用原 有的方法。 2. modalPresentationStyle 这个属性是用来设置弹出的模态视图的风格,它是一个枚举类型,有4中风格, UIModalPresentationFullScreen UIModalPresentationPageSheet UIModalPresentationFormSheet UIModalPresentationCurrentContext 其中第四中风格是继承父类的风格。我们要注意的是,弹出风格在iPhone和iPad上是不 同的,在iPad中,这4种方法都是有效的;而在iPhone中,不管怎样设置,都只能是 UIModalPresentationFullScreen风格,所以在编写代码无法在iPhone上实现其他风格时 也不用感到奇怪。 QQ学习群:262779381
3. modalTransitionStyle modalTransitionStyle是用来设置弹出和消失模态视图时视图之间切换的动画 效果。它也是一个枚举类型,有4中效果。 UIModalTransitionStyleCoverVertical UIModalTransitionStyleFlipHorizontal UIModalTransitionStyleCrossDissolve UIModalTransitionStylePartialCurl 也可将模态视图看做一种实现视图切换动画效果的方式。 4. dismissViewControllerAnimated:(BOOL) completion:(void)completion 该方法和第一种方法相反,是用来使模态视图消失的方法。 下面我们通过一个例子来学些这些方法是如何实现的,并且将上小节中视图控 制器出现消失的4个方法引入。 QQ学习群:262779381
新建一个Single View Application项目模板,然后在项目里创建一个模态视图 文件,父类是UIViewController。 然后在AppDelegate.m文件中创建一个视图控制器类的实例,并将它设置为 根视图控制器。记得将视图控制器类头文件导入到进来。然后在application didFinishLaunchingWithOptions方法中定义根视图控制器。 1.#import "RootViewController.h" 2.ViewController *vc = [[ViewController alloc]init]; 3.self.window.rootViewController = vc; 4.[vc release]; 接下来我们在ViewController.m文件中创建一个弹出模态视图的按钮,并实 现相应的方法。创建视图是在loadView方法中实现,而在自定义视图之前还要 调用父类的loadView方法。别忘了将创建好的ModalViewController导入到 项目中。 QQ学习群:262779381
5. - (void)loadView 6. { 7. [super loadView]; //调用父类的方法 8. self. view 5.- (void)loadView 6.{ 7. [super loadView]; //调用父类的方法 8.self.view.backgroundColor = [UIColor orangeColor]; 9.//创建弹出模态视图按钮 10.UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 11. [button1 setTitle:@"PrensentModal" forState:UIControlStateNormal]; 12. button1.frame = CGRectMake(100, 100, 120, 40); 13.[button1 addTarget:self action:@selector(presentModal) 14.forControlEvents:UIControlEventTouchUpInside]; 15. [self.view addSubview:button1]; 16.} QQ学习群:262779381
17. - (void)presentModal 18. { 19. ModalViewController 17.- (void)presentModal 18.{ 19.ModalViewController *modalViewController = [[ModalViewController alloc]init]; 20.//iPhone模拟器中无法显示该风格 21.modalViewController.modalPresentationStyle = UIModalPresentationFormSheet; 22.//设置模态视图翻转动画效果 23.modalViewController.modalTransitionStyle = UIModalTransitionStylePartialCurl; 24.[self presentViewController:modalViewController animated:YES 25.completion:^{ 26. NSLog(@"显示"); 27. }]; 28.} QQ学习群:262779381
在presentModal方法后,我们在视图控制器显示消失的4个方法中添加了简单的 打印语句,来看一下它的执行过程。 在实现弹出模态视图前,要先创建一个模态视图的实例,并设置它的风格和切换的 动画效果。最后调用presentViewController方法弹出模态视图,在这个方法中, 使用到了block语法,读者如果感兴趣的话可以查阅有关block的相关知识。 在presentModal方法后,我们在视图控制器显示消失的4个方法中添加了简单的 打印语句,来看一下它的执行过程。 29.- (void)viewWillAppear:(BOOL)animated 30.{ 31. [super viewWillAppear:YES]; 32. NSLog(@"视图将要显示"); 33.} 34.- (void)viewDidAppear:(BOOL)animated 35.{ 36. [super viewDidAppear:YES]; 37. NSLog(@"视图已经显示"); 38.} 39.- (void)viewWillDisappear:(BOOL)animated 40.{ 41. [super viewWillDisappear:YES]; 42. NSLog(@"视图将要消失"); 43.} 44.- (void)viewDidDisappear:(BOOL)animated 45.{ 46. [super viewDidDisappear:YES]; 47. NSLog(@"视图已经消失"); 48.} QQ学习群:262779381
在重写这几个方法时,要先调用父类的方法,做到代码的完整性的统一。我们等 下可以在程序运行时观察控制台上的输出情况。 接着在模态视图ModalViewController中创建一个让模态视图消失的按钮, 并实现该方法。 49.- (void)loadView 50.{ 51. [super loadView]; 52. self.view.backgroundColor = [UIColor purpleColor]; 53. UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 54. [button2 setTitle:@"Modaldismiss" forState:UIControlStateNormal]; 55. button2.frame = CGRectMake(100, 100, 120, 40); 56. [button2 addTarget:self action:@selector(Modaldismiss) 57. forControlEvents:UIControlEventTouchUpInside]; 58. [self.view addSubview:button2]; 59.} 60.- (void)Modaldismiss 61.{ 62. [self dismissViewControllerAnimated:YES completion:^{ 63. NSLog(@"消失"); 64. }]; 65.} QQ学习群:262779381
构建并运行,可以看到模态视图的效果如图7-12所示,并且在模态视图弹出和 消失时会调用相应的视图方法,会在控制台上打印相应的语句。 ModelViewController[495:c07] 视图将要显示 ModelViewController[495:c07] 视图已经显示 ModelViewController[495:c07] 显示 ModelViewController[495:c07] 消失 QQ学习群:262779381
7.1.4模态视图设计方法 假设我们的项目中正在实现用户注册登录信息的功能,需要用模态视图 来实现,那么我们如何将弹出模态视图中输入的值传入到父视图中呢? 在iPhone的是设计模式中有很多中方法可以实现这一功能,比如通知 、协议代理、KVO等等。本小节中我们着重讲解如何用协议代理方法 将模态视图中的值传给父视图中,以实现相应的功能。 前面的章节中也介绍过协议代理设计模式的概念,在模态视图中, 我们需要将模态视图设置为代理,委托ViewController(视图控制器 类)去实现相应协议中的方法。 首先我们要在模态视图的.h文件(ModalViewController.h)中定 义一个协议,用于值的传递。并用property属性创建协议的存取方法 。 QQ学习群:262779381
@protocol ModalViewTextChangeDelegate <NSObject> @optional - (void)ChangeView:(NSString *)text; @end @property(nonatomic,assign)id<ModalViewTextChangeDelegate> delegate; 需要注意的是,我们定义的是一个带参数的方法,所以需要将对应的参数传给 需要改变值的视图,比如我们要将TextField视图中的值传给父视图中的label ,这里就要将TextField中的值传给相应的代理方法。 [self.delegate ChangeView:textField.text]; 接下来就要在视图控制器类中实现我们的代理方法。首先要将声明好的协议 引入到视图控制器类中。(记得引入模态视图的头文件) QQ学习群:262779381
1.#import <UIKit/UIKit.h> 2.#import "ModalViewController.h" 3.@interface ViewController : UIViewController<ModalViewTextChangeDelegate> 我们可以通过定义全局变量或者使用viewWithTag类方法来获得需要改变内容 的label视图。并将代理方法中的参数传给label视图中的值。 4.- (void)ChangeView:(NSString *)text 5.{ 6. UILabel *label = (UILabel *)[self.view viewWithTag:100]; 7. label.text = text; 8.} 最后一步也是最容易遗忘的一步,就是要设置模态控制器的实例为代理,不然 它的默认值为nil,就无法实现相应协议中的方法。 modalViewController.delegate = self; 好了,我们大功告成了,读者可以浏览最后的效果。这里我们主要是教大家如何 在模态视图中使用协议代理模式,读者还需要自行添加相应的标签、文本框和按 钮视图来实现最后的效果。 QQ学习群:262779381
7.2UINavigationController导航控制器 在介绍了视图控制器之后,在本章中我们要介绍一个在App中运用广泛的视图控制器子 类,导航视图控制器(UINavigationController)。它的功能时用于构建多层次的应 用程序,并且管理多个视图切换,图展示了iPhone设置功能中的导航视图控制器。 QQ学习群:262779381
有了这个视图控制器的存在,就像一个导航器一样指引我们跳转到想要的视图 上,右图中导航控制器里地小按钮也可以自定义,可以自己添加相应的功能来 达到需要的效果。是不是很炫?接下来我们就一起来学习如何创建这样的导航 视图控制器。 QQ学习群:262779381
7.2.1导航控制器介绍 导航视图控制器是视图控制器(UIViewController)的一个子类,而在导航视 图控制下面还有两个子类控制器,UIImagePickerController和 UIVideoEditorController。我们可以通过图来了解UIViewController的类图。 QQ学习群:262779381
在导航视图控制器中也有很多的view视图,它们之间的层次关系也是本章内容 中比较重要的部分,掌握了各个视图和控制器之间的层次关系,对今后的学习或 者项目会有很大帮助。我们通过一张苹果官方的图示来了解一下在导航视图控制 器中各个视图的层级关系,如图所示。 QQ学习群:262779381
一个导航视图控制器由3个部分组成,NavigationBar、Custom content和 Navigation toolbar。 NavigationBar主要是用来负责视图之间的切换,比如弹到下一级的视图;还可 以用来控制和管理主视图。它位于整个导航视图控制器的最上方。 Custom content是用来显示内容的视图,自定义视图的内容将会在这里显示。 Navigation toolbar是导航控制器的辅助工具栏视图,它默认是隐藏的,需要 时可以将它显示。它的作用就是添加了一些特定的功能供用户使用,比如说 iPhone中常见的微博分享功能等等。 另个一需要了解的重点是,我们的导航视图控制是以栈的形式来实现的。有 些读者可能没有接触过数据结构,那这里我们就简单地介绍一下栈这个在编程中 常用到的数据结构。 QQ学习群:262779381
栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它 按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶, 需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)[ 摘自 百度百科中对栈的解释与说明 ]。我们可以将导航视图控制器当做一个栈,将一个视图添加到导航视图控制器中 的操作叫做入栈(push),从栈中删除一个视图的操作叫做出栈(pop)。但我 们要记住栈这个数据结构的特点是“先进后出”,或者说是“后进先出”,这是什么 意思呢?就是说最先添加进来的视图,如果要删除的话,必须等后面的视图全部 删除完毕了,它才能得到删除;同样地道理,最后添加进来的视图必须是第一个 删除的对象。 介绍完相关导航视图控制器的知识后,我们将在下节中介绍如何创建一个导航 视图控制器,并且使用相关方法来实现视图之间切换的功能。 QQ学习群:262779381
7.2.1导航视图控制器的创建及方法属性的使用 ·导航视图控制器的创建 接下来我们就通过实例来了解如何在视图中添加一个导航视图控制器。 在XCode中新建一个Single View Application项目模板,在 AppDelete.m中设置根视图控制器了,但是在导航视图控制器中设置和以前的 方法会有点不一样。比如以前我们设置根视图控制器时,在新建文件 AppDelegate.m文件中的 application didFinishLaunchingWithOptions方法中,添加下列代码。 1.ViewController *vc = [[ViewController alloc] init]; 2.self.window.rootViewController = vc; 3.[vc release]; QQ学习群:262779381
当然要将ViewController.h文件添加进来。而在添加导航视图控制器时,就要 将根视图控制器设置为导航视图控制器的实例而不是视图控制器的实例。 我们就要创建一个UINavigationController的实例,将它作为根视图控制器。 4.ViewController *vc = [[ViewController alloc] init]; 5.UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:vc]; 6.[vc release]; 7.self.window.rootViewController = navigationController; 8.[navigationController release]; 在初始化导航视图控制器时,使用initWithRootViewController方法将视图控 制器实例加到导航控制器上,做为栈的栈底,然后释放掉视图控制器的实例,最 后将导航视图控制器的实例设置为根视图控制器。为了更好显示效果,我们将导 航栏中的标题设置为“首页”。 QQ学习群:262779381
9. ViewController. vc = [[ViewController alloc] init]; 10 9.ViewController *vc = [[ViewController alloc] init]; 10.UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:vc]; 11.[vc setTitle:@"首页"]; 12.[vc release]; 13.self.window.rootViewController = navigationController; 14.[navigationController release]; QQ学习群:262779381
在创建了导航视图控制器后,我们可以进一步完善导航栏功能,比如在前面我们 提到了构成导航视图控制器的3个元素中的navigation toolbar,它默认是隐藏 的,我们可以通过设置它的相关属性让它显示。因为这个视图用的比较少,如果 想在工具栏上添加一些按钮,需要自定义toolbar。 ·自定义toolbar 首先我们看一下navigationController自带的toolbar视图的显示方式,但通 常在项目中,都是自定义和应用元素相匹配的toolbar。 在ViewController类中的viewDidAppear方法里设置toolbar的隐藏值为 NO就可以显示。 15.- (void)viewDidAppear:(BOOL)animated 16.{ 17. [super viewDidAppear:animated]; 18. [self.navigationController setToolbarHidden:NO animated:YES]; 19.} QQ学习群:262779381
然后我们在工具栏上加上系统按钮,按钮是属于UIBarButtonItem类,在初始 化时,提供了一个设置按钮类型的方法,initWithBarButtonSystemItem, 然后还可以设置它的动作。 20.- (void)viewDidAppear:(BOOL)animated 21.{ 22. [super viewDidAppear:animated]; 23. [self.navigationController setToolbarHidden:NO animated:YES]; 24. UIBarButtonItem *toolButton1 = [[UIBarButtonItem 25. alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:nil]; 26. UIBarButtonItem *toolButton2 = [[UIBarButtonItem 27. alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemReply target:self action:nil]; 28.} QQ学习群:262779381
然后定义一个数组,用这两个按钮实例初始化数组,最后将数组添加到工具栏上 。这样我们就完成了一个简单的工具栏的定义。 29 然后定义一个数组,用这两个按钮实例初始化数组,最后将数组添加到工具栏上 。这样我们就完成了一个简单的工具栏的定义。 29.UIBarButtonItem *toolButton2 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemReply target:self action:nil]; 30.NSArray *items = @[toolButton1,toolButton2]; 31.[self.navigationController setToolbarItems:items animated:YES]; 32.[toolButton1 release]; 33.[toolButton2 release]; 这里就出现了问题,我们不是设置了工具栏上的按钮吗?怎么没有显示在工具栏 上呢?回到程序中,创建按钮实例的代码是没有问题的,定义一个数组去存储这 几个按钮实例也是没有问题的,那么问题就出现在将设置工具栏按钮上了。 [self.navigationController setToolbarItems:items animated:YES]; QQ学习群:262779381
我们是在当前视图的导航控制器上设置工具栏的,这样是错误的,即使你将按钮 加了上去,如果你的应用有切换视图的功能,那么下一个视图的工具栏上也会有 同样的按钮,这肯定是你不想看到的。而工具栏是由当前视图控制,而不是导航 控制器,所以在设置时代码要改为: 34.NSArray *items = @[toolButton1,toolButton2]; 35.[self setToolbarItems:items animated:YES]; 36.[toolButton1 release]; 37.[toolButton2 release]; QQ学习群:262779381
我们还可以为这些按钮添加动作,在初始化的时候在action中设置,比如添加一个视图,弹出一个UIActionSheet等等。读者可以自行实验一下。 除了添加系统自带的按钮之外,还可以添加自己制作的图片作为按钮,系统也提供了两种方法,一种是UIBarButtonItem中自带的initWithImage方法;另一种是创建一个UIImageView实例,然后通过UIBarButtonItem中的 initWithCustomView方法将创建好的图片视图实例添加进来即可。下面我们依次来讲解这两种方法的具体使用方法。 QQ学习群:262779381
第一种方法用initWithImage方法: UIBarButtonItem 第一种方法用initWithImage方法: UIBarButtonItem *ImageButton = [[UIBarButtonItem alloc]initWithImage: [UIImage imageNamed:@"分享.png"] style:UIBarButtonItemStylePlain target:self action:nil]; 然后将ImageButton实例添加到数组中,并且最后释放掉ImageButton实例。 38.NSArray *items = @[toolButton1,toolButton2,imageButton]; 39.[toolButton1 release]; 40.[toolButton2 release]; 41.[imageButton release]; QQ学习群:262779381
第二种方法用先创建一个UIImageView的实例,然后通过UIBarButtonItem 的initWithCustomView方法将创建好的视图添加到BarButtonItem实例上, 最后释放掉这两个创建的实例。 42.UIImageView *image = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"分享.png"]]; 43.UIBarButtonItem *imageButton = [UIBarButtonItem alloc]initWithCustomView:image]; 44.NSArray *items = @[toolButton1,toolButton2,imageButton]; 45.[toolButton1 release]; 46.[toolButton2 release]; 47.[imageButton release]; 48.[image release]; QQ学习群:262779381
第二种方法和第一种方法的最后效果是一样的。用户可以在这两种方法中随便选 一种进行创建图片按钮。 大家可能会觉得这些按钮都靠在了左侧,怎么让它们按照工具栏的长度平均 分割它们之间的距离呢?UIBarButtonItem中提供了两种分割按钮间隔的按钮 类型, UIBarButtonSystemItemFlexibleSpace和 UIBarButtonSystemItemFixedSpace。我们还是要创建一个 UIBarButtonItem实例,然后将按钮类型设置为这两种,第一中类型是自动分 割间距,第二种是自定义分割距离,通过实例的width属性可以设置距离。 49.UIBarButtonItem *flexibleButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleS pace target:self action:nil]; QQ学习群:262779381
然后在数组中需要自动分割间隔的按钮之间添加上这个实例即可。 NSArray 然后在数组中需要自动分割间隔的按钮之间添加上这个实例即可。 NSArray *items = @[toolButton1,flexibleButton,toolButton2,flexibleButton,imageButton ]; 构建并运行,现在可以看到按钮按照平均的距离分隔开了。这样使得我们的 应用显得更美观。 使用第二种自定义间距的方法时,只需要手动设置一下间隔值,其他的操作 和第一种自动分隔方法一样,读者可以自己完成。 系统还提供了自定义工具栏的方法,通过创建UIToolBar的实例,可以设置 它的尺寸,样式风格,还可以根据应用的需要添加相应的背景图片。 QQ学习群:262779381
视图名称 尺寸大小(竖屏/横屏)/横屏) 状态栏 20px 导航栏 44px/32px 工具栏 图标 在讲自定义工具栏之前,我们务必要了解一下iPhone中各个视图的大小尺寸, 已在下表中列出。 视图名称 尺寸大小(竖屏/横屏)/横屏) 状态栏 20px 导航栏 44px/32px 工具栏 图标 QQ学习群:262779381
了解了各个视图尺寸之后,我们就可以开始自定义我们的工具栏了。首先,系统 为我们提供了3种风格的ToolBar(除默认的风格之外)。 UIBarStyleDefault UIBarStyleBlack UIBarStyleBlackOpaque UIBarStyleBlackTranslucent 其中第四种风格其实是在第三种风格的基础上,将实例的Translucent属性设 置为YES,效果是一样的。读者可以自己设置下这些风格,了解下各风格样式的 效果。 在loadView或viewDidAppear方法中添加自定义导航栏。 QQ学习群:262779381
50.UIToolbar *toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 524, 320, 44)]; 51.toolBar.barStyle = UIBarStyleBlackOpaque; 52.toolBar.translucent = YES; 53.//上两行代码相当于toolBar.barStyle = UIBarStyleBlackTranslucent; 54.[self.view addSubview:toolBar]; 55.[toolBar release]; 然后我们将创建好的按钮视图添加到toolbar上。 56.NSArray *items = @[toolButton1,flexibleButton,toolButton2]; 57.[toolBar setItems:items animated:YES]; QQ学习群:262779381
·自定义导航栏 导航栏的定制和工具栏很相似,都是先创建一个UIBarButton的实例,然后 将实例添加到UINavigationBar上。在了解如何自定义导航栏之前,我们先来 了解一下导航栏的组成,图展示了iPhone设置功能中一个导航栏的事例。 QQ学习群:262779381
从结构图中我们可以了解到,一个NavigationBar中主要由5种元素构成, LeftBarButtonItem,RightBarButtonItem,backBarButtonItem,Title和 TitleView。TitleVIew可以是用户自己定义的视图,这也是自定义导航栏一种 常用的方法。 在了解了ToolBar和NavigationBar相关知识后,我们务必要介绍一下整个 NavigationController中的结构,以便读者在以后的定制导航控制器时不会出 现低级的错误,图表示了整个NavigationController的结构。 QQ学习群:262779381
可以看到,在一个导航控制器中,只有一个ToolBar和一个NavigationBar,然 后NavigationController管理了多个视图控制器ViewController,在 NavigationBar上的Item和ToolBar上的Item都是由当前的视图所管理,而不 是由NavigationBar和NavigationController来管理。这一点也是大多数人容 易翻的错误,他们通常都是用navigationController来添加按钮: self.navigationController.navigationItem.rightBarButtonItem,但你会 发现这样无法将相应的按钮设置在NavigationBar上。这点和前面所讲的 ToolBar是一样的。大家可以结合NavigationController的机构图来思考一下原 因。 下面我们来讲解如何自定义导航栏。首先我们向导航栏中分别添加上左右两个系 统图标按钮。在loadView方法中添加如下代码(将前面创建的ToolBar和相关 按钮删除)。 QQ学习群:262779381
58. - (void)loadView 59. { 60. //创建左边按钮 61. UIBarButtonItem 58.- (void)loadView 59.{ 60. //创建左边按钮 61. UIBarButtonItem *barButton1 = [[UIBarButtonItem 62. alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:nil]; 63. [self.navigationItem setLeftBarButtonItem:barButton1 animated:YES]; 64. [barButton1 release]; 65. //创建右边按钮 66. UIBarButtonItem *barButton2 = [[UIBarButtonItem 67. alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:nil]; 68. [self.navigationItem setRightBarButtonItem:barButton2 animated:YES]; 69. [barButton2 release]; 70.} QQ学习群:262779381
构建并运行,可以看到在导航栏上在左右两侧各出现了个一个按钮,如图7-23 所示。 除了系统提供的按钮之外,和ToolBar中的元素一样,我们还可以通过图片来设 置我们的NavigationBar中的按钮,使用initWithImage初始化方法,这里读者 们可以自己搜寻一个图片素材添加到NavigationBar上。 我们还要介绍一下titleView方法,还可以在NavigationBar上添加一个子视图 ,用户显示导航控制器的标题。比如在一个音乐播放器中,导航栏中的标题一般 都会是歌曲的名称,那如果歌曲的名称过长,那么可能就显示不完整。这时,如 果我们定义一个子视图用于显示歌曲的名称,定制一个定时器,设置视图移动动 画的时间间隔,也无不是一种实现的方法。在后面的音视频播放的章节中我们可 以试着实现这一效果。 使用titleView方法之前,我们需要先定义一个视图,相信方法大家都已经掌握 的很牢固了。 QQ学习群:262779381
QQ学习群:262779381
71.[self.navigationItem setRightBarButtonItem:barButton2 animated:YES]; 72.[barButton2 release]; 73.//创建Title视图 74.UIView *titleView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 30)]; 75.[self.navigationItem setTitleView:titleView]; 76.//创建Title视图的子视图,用于显示标题 77.UILabel *titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 30)]; 78.[titleLabel setText:@"Home"]; 79.titleLabel.textAlignment = NSTextAlignmentCenter; 80.[titleView addSubview:titleLabel]; 81.[titleLabel release]; 82.[titleView release]; QQ学习群:262779381
构建并运行,可以看到我们自定义的视图加在了NavigationBar上,如图7-24所示。 ①LeftBarButtonItem: a)如果当前的viewController设置了leftBarButtonItem,那么就显示用户所设置的leftBarButtonItem。 b)如果当前的viewController没有设置leftBarButtonItem,而且当前的viewController不是根视图控制器时,就显示前一层的视图控制器的backBarButtonItem。比如前一层的title属性是SubView,则在backBarButtonItem上显示的就是SubView。如果前一层的视图控制器没有显示的指定backBarButtonItem的话,系统将会根据前一视图控制器的title属性自动生成一个back按钮,并显示出来。 c)如果当前视图是根视图,且没有设置相应的LeftBarButtonItem时,那么就不显示任何内容。 QQ学习群:262779381
②title属性 a)如果用户自定义了一个视图,并将当前视图的titleView设置成了自定义视图,那么title上就会显示用户自定义的视图。 b)如果没有设置titleView,系统就会根据当前视图的navigationController.title的值创建一个UILabel并显示其内容。 ③RightBarButtonItem a)如果当前的视图控制器设置了rightBarButtonItem,就显示设置的内容。 b)如果当前的视图控制器没有设置相应的rightBarButtonItem属性,就不显示任何内容。 QQ学习群:262779381
7.2.3导航控制器实现视图之间的切换 在本节内容中,我们将来学习如何通过导航控制器来实现各个视图之间的切换效果。在UINavigationController类中提供了4中视图切换的方法,分别为: //弹出到下一个视图中(进栈) - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; //弹回到上一个视图(出栈) - (UIViewController *)popViewControllerAnimated:(BOOL)animated; //弹到指定的视图 - (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated; //弹到栈底,此时栈中只有一个元素 - (NSArray *)popToRootViewControllerAnimated:(BOOL)animated; 注意在第三种方式中,可以通过设置navigationController中数组viewControllers的元素来选择需要弹到的视图。接下来我们就来了解这几个方法的使用。 QQ学习群:262779381
我们在上小节代码的基础上进行操作,打开UINavigationController项目,我们需要完成3个视图之间的切换,当前我们只有一个ViewController文件,所以接下来我们新建两个子视图控制器。我们在ViewController.m文件中添加一个按钮,实现弹到下一个视图的功能。 在添加之前,我们需要将弹出的视图的头文件添加进来。 1.#import "ViewViewController.h" 2.#import "SubViewController.h" 然后创建按钮,并实现跳转视图的功能。在loadView方法中继续输入以下代码。 3.UIButton *PushButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 4.PushButton.frame = CGRectMake(100, 150, 100, 40); 5.[PushButton setTitle:@"下一视图" forState:UIControlStateNormal]; 6.[PushButton addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside]; 7.[BaseView addSubview:PushButton]; 8.- (void)push 9.{ 10. SubViewController *subViewController = [[SubViewController alloc]init]; 11.[self.navigationController pushViewController:subViewController animated:YES]; 12.} QQ学习群:262779381
在push方法中,我们先创建了一个SubViewController子视图的实例,然后我们就让导航控制器push到子视图控制器中。为了让效果更明显,我们可以在子视图控制器中初始化一个视图。 在SubViewController.m文件中初始化视图。 首先系统提供了一个 -(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil方法。我们可以在这里面自定义视图,比如设置导航栏的标题。 13.if (self) { 14. // Custom initialization 15. [self setTitle:@"SubView"]; 16.} 在loadView方法中创建一个视图。 17.- (void)loadView 18.{ 19. UIView *view = [[UIView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame]; 20. view.backgroundColor = [UIColor orangeColor]; 21. self.view = view; 22.} QQ学习群:262779381
QQ学习群:262779381
因为我们没有设置栈底视图的title属性,所以遵循上面总结的NavigationBar显示规则,backBarButtonItem会自动生成一个back按钮用于返回上一个视图。此时在栈中就有两个元素,当我们点击SubView中的back按钮时,子视图就会出栈。而在数组viewControllerS中,两个视图的index值分别为0和1。那么有的读者可能会问,这个数组有什么用呢?它是用来提供返回特定视图的功能,比如我们要返回第二个视图,我们就可在pop视图的方法中设置index值为1(数组从0开始)即可。 接下来我们就在SubView视图中创建一个按钮,弹到下一个视图,在第三个视图中实现返回主页和特定视图的功能。新建SubViewController2类。 在SubViewController.m文件中添加一个按钮,并实现弹到下一个视图的功能。 QQ学习群:262779381
QQ学习群:262779381 23.#import "SubViewController.h" 25.- (void)loadView 26.{ 27. UIView *view = [[UIView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame]; 28. view.backgroundColor = [UIColor orangeColor]; 29. self.view = view; 30. UIButton *pushButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 31. [pushButton setTitle:@"下一视图" forState:UIControlStateNormal]; 32. pushButton.frame = CGRectMake(100, 100, 100, 40); 33. [pushButton addTarget:self action:@selector(push) 34. forControlEvents:UIControlEventTouchUpInside]; 35. [view addSubview:pushButton]; 36.} 37.- (void)push 38.{ 39. SubViewController2 *subView2 = [[SubViewController2 alloc]init]; 40. [self.navigationController pushViewController:subView2 animated:YES]; 41.} QQ学习群:262779381
构建并运行,看到效果如图7-26所示。因为系统为我们提供了一个返回的按钮,所以就没有必要再添加一个按钮去实现返回上一个视图。那么为了学习这个方法,我们将在第三个视图中实现方法。 同样的,我们在SubViewController2.m中的 initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil方法中设置当前导航栏中的标题。 42.if (self) { 43. // Custom initialization 44. self.title = @"SubView2"; 45.} 然后在loadView方法中创建三个按钮,分别用于返回首页,返回指定视图和返回上一页。 QQ学习群:262779381
QQ学习群:262779381 46.- (void)loadView 47.{ 48. UIView *view = [[UIView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame]; 49. view.backgroundColor = [UIColor redColor]; 50. self.view = view; 51. UIButton *topButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 52. [topButton setTitle:@"返回主页" forState:UIControlStateNormal]; 53. topButton.frame = CGRectMake(100, 100, 100, 40); 54. [topButton addTarget:self action:@selector(backroot) 55. forControlEvents:UIControlEventTouchUpInside]; 56. [view addSubview:topButton]; 57. UIButton *indexButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 58. [indexButton setTitle:@"返回指定页面" forState:UIControlStateNormal]; 59. indexButton.frame = CGRectMake(100, 200, 100, 40); 60. [indexButton addTarget:self action:@selector(indexVC) 61. forControlEvents:UIControlEventTouchUpInside]; 62. [view addSubview:indexButton]; 63. UIButton *backButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 64. [backButton setTitle:@"返回上一页" forState:UIControlStateNormal]; 65. backButton.frame = CGRectMake(100, 300, 100, 40); 66. [backButton addTarget:self action:@selector(back) 67. forControlEvents:UIControlEventTouchUpInside]; 68. [view addSubview:backButton]; 69.} QQ学习群:262779381
QQ学习群:262779381
构建并运行,可以看到第三个视图的界面如图7-27所示。但我们发现如果没有定义leftBarButtonItem,还是会自动设置一个返回按钮,那么返回上一页按钮的按钮就没有什么作用了,所以这里我们定义一个leftBarButtonItem去覆盖掉系统自带的backBarButtonItem。 70.UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(save)]; 71.self.navigationItem.leftBarButtonItem = item; 72.[item release]; 创建leftBarButtonItem之后,我们在这里可以添加一个AlertView,当点击leftButton之后会弹出一个警告框,相信创建的方法大家也很熟悉了。 73.- (void)save 74.{ 75. UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"是否要保存?" delegate:self cancelButtonTitle:@"否" otherButtonTitles:@"是",nil]; 76. [alert show]; 77. [alert release]; 78.} QQ学习群:262779381
QQ学习群:262779381 接下来我们就来实现三个按钮,分别实现返回主页,返回指定页面和返回上一页的功能。 79.//返回主页面 80.- (void)backroot 81.{ 82. [self.navigationController popToRootViewControllerAnimated:YES]; 83.} 84.//返回上一页 85.- (void)back 86.{ 87. [self.navigationController popViewControllerAnimated:YES]; 88.} 89.//返回指定页面 90.- (void)indexVC 91.{ 92. UIViewController *subView = [[self.navigationController viewControllers]objectAtIndex:1]; 93. [self.navigationController popToViewController:subView animated:YES]; 94.} QQ学习群:262779381
构建并运行,可以看到我们分别实现了4中页面切换的方法。需要注意返回特定页面的实现方法。最终效果如图所示。从这个例子中我们还可以看到,添加BarButtonItem的方式还是我们在前面提到的,并不是由NavigationBar控制,也不是由NavigationController来控制,而是由当前的视图来控制。这一点也是大多数读者容易犯的错误,所以笔者在这里还要提一下。 QQ学习群:262779381
7.2.4UIImagePickerController的使用 在前面的小节中,我们提到了UINavigationController类还有两个子类,分别是UIImagePickerController和UIVideoEditorController。后者并不是很常见,而UIImagePickerController是一个很常用的类,比如在选择头像时,可以从系统中选择一个图片作为头像,这时就要用到UIImagePickerController这个类。 UIImagePickerController类是一个模态视图,记得我们在前面的内容中提到过模态视图的用法,现在我们就可以直接运用。 首先我们在需要显示图片的视图中,假设在ViewController.m中创建一个imageView,用于显示我们选择的图片。首先在AppDelegate.m中设置导航栏控制器为根视图控制器。 QQ学习群:262779381
1. - (BOOL)application:(UIApplication 1.- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 2. // Override point for customization after application launch. 3. ViewController *vc = [[ViewController alloc]init]; 4. vc.view.backgroundColor = [UIColor whiteColor]; 5. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:vc]; 6. self.window.rootViewController = navi; 7. [vc release]; 8. [navi release]; 9. return YES; 10.} 因为便于我们在后面调用imageView的实例,我们在相对应的.h文件中定义一个全局变量,而且在使用UIImagePickerController模态视图时,需要使用到相应的代理方法,所以我们还要将UINavigationControllerDelegate和UIImagePickerControllerDelegate两个协议添加到头文件中。 QQ学习群:262779381
QQ学习群:262779381 11.@interface ViewController :UIViewController 12.<UINavigationControllerDelegate,UIImagePickerControllerDelegate> 13.{ 14. UIImageView *imageView ; 15.} 16.- (void)loadView 17.{ 18. ………… 19. imageView = [[UIImageView alloc]initWithFrame:CGRectMake(10, 80, 300, 300)]; 20. imageView.backgroundColor = [UIColor purpleColor]; 21. [self.view addSubview:imageView]; 22. [imageView release]; 23.} 然后我们在ViewController.m文件中实现点击按钮实现选择图片的效果。 在前一小节的内容中,我们在一个视图中定义了一个rightBarButtonItem,并将它的样式设置为了照相机样式,这正好和我们需要实现的效果一致。所以我们在这个按钮的基础上添加一个单击事件。 QQ学习群:262779381
24.UIBarButtonItem *barButton2 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action: @selector(pictureEvent)]; 25.[self.navigationItem setRightBarButtonItem:barButton2 animated:YES]; 26.[barButton2 release]; 27.- (void) pictureEvent 28.{ 29. //先设定sourceType为相机,然后判断相机是否可用(ipod)没相机,不可用将sourceType设定为相片库 30. UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera; 31. if (![UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) { 32. sourceType = UIImagePickerControllerSourceTypePhotoLibrary; 33. } 34. UIImagePickerController *picker = [[UIImagePickerController alloc] init]; 35. picker.delegate = self; 36. picker.allowsEditing = YES; 37. picker.sourceType = sourceType; 38. picker.modalPresentationStyle = UIModalPresentationPageSheet; 39. picker.modalTransitionStyle = UIModalTransitionStylePartialCurl; 40. [self presentViewController:picker animated:YES completion:^{ 41. }]; 42. [picker release]; 43.} QQ学习群:262779381
我们解释一下这段代码,在使用UIimagePickerController类时需要定义一个照片源,系统提供了3中照片源: a.UIImagePickerControllerSourceTypePhotoLibrary:照片库 b.UIImagePickerControllerSourceTypeCamera:使用相机拍摄的新图片 c.UIImagePickerControllerSourceTypeSavedPhotosAlbum:相机胶卷 然后我们通过使用isSourceTypeAvailable方法来检测图像源是否可用,如果不可用,就选择照片库图像源。接下来创建一个UIIMagePickerController的实例,并设置代理,以及模态视图的呈现和动画方式,这些读者可以根据自己的喜好来设置,最后通过新IOS版本中的显示模态视图的方式显示选择照片视图。 最后实现相关的代理方法,将选择的图片显示到定义好的imageView中。 44.- (void)editImage:(UIImage *)image 45.{ 46. [imageView setImage:image]; 47.} 48.-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo 49.{ 50. [picker dismissModalViewControllerAnimated:YES]; 51. [self performSelector:@selector(editImage:) withObject:image afterDelay:1.0]; 52.} QQ学习群:262779381
有些读者可能看到这个代理方法这么长,怎么记得住呢?这个不必担心,我们在使用相应的类,比如UIIMagePickerController类时,查看它相应的API可以看到所有的代理方法,所以我们并不需要记住这些方法的名称,只须根据文档了解这些方法的作用。 [self performSelector:@selector(editImage:) withObject:image afterDelay:1.0];这行代码的含义就是当前视图选择一个显示方法,可以选择自己定义的方法,比如我们在前面定义的显示图片的方法editImage。然后设置响应时间,比如设置了1秒,那么选择照片1秒钟之后才会在imageView中显示图片。 构建并运行,可以看到选择图片的效果如图所示。 QQ学习群:262779381
通过学习UIImagePickerController类又给我们的学习提供了一个和好的例子。我们在学习iPhone编程时,并不是需要掌握所有的类,也是不现实的,因为框架中提供的类实在太多了。所以这就要求我们掌握一些重要的父类,从属它的子类只须做一定的了解即可,当项目中需要用的时候,花上一点时间看一下官方文档,就能够很好的使用。比如这小节所讲的UIImagePickerController类,有很多应用中并不会用到,所以大家只须了解什么类实现了什么效果,在项目中再确定使用哪些类,具体类的实现方法不需要都掌握。 QQ学习群:262779381
7.3分栏控制器 和导航视图控制器一样,UITabBarController也是用来控制和管理视图控制器的,它们都继承与UIViewController类。在iPhone和iPad应用中,UITabBarController运用的也是比较多的,比如我们来看一下IOS应用中自带的游戏中心应用,如图所示。 从上图中可以看到,UINavigationController和UITabBarController都是通过自定义的形式来创建的,在一般的项目中,开发者也大都是运用自定义的方式来制定与自身应用主题相符合的控制器,这样使得应用更加美观。 在本章的内容中,我们会为大家介绍如何创建系统自带的UITabBarController,并且介绍如何添加图片,添加动画效果等;还会介绍如何自定义UITabBarController以应对在实际项目中的开发。最后将上一节内容中的UINavigationController融入到本节内容中,将二者结合起来运用。 QQ学习群:262779381
7.3.1UITabbarController的创建 和UINavigationController类似,UITabBarController也是用来控制和管理UIviewController的类,但是二者的一个主要的区别是,UINavigationController的管理是通过栈的形式,会有一层层的层级关系,在出栈之后,当前的视图就会卸载;而UITabBarController是以数组的形式将分栏信息添加到手机屏幕上,而且视图之间的关系是平级的,并没有上下层的关系,而且在切换视图时,其他的视图并不会移除,这是它们二者最主要的区别。 在学习如何创建UITabBarController之前,我们先通过IOS中的时钟应用来了解一下UITabBarController的结构。图来自于苹果官方文档,它详细的剖析了UITabBarController的结构。 QQ学习群:262779381
QQ学习群:262779381
和UINavigationController相对应,通常情况下二者分别位于屏幕的上下方。我们在UINavigationController中提到过,UIKit框架中有一个UIBarItem类,可以将该类的实例添加到导航栏控制器和分栏控制器中,其实添加方法和导航栏控制器的方法类似,在后面的内容中我们会进行详细讲解。 下面我们就来学习如何在应用中添加一个UITabBarController分栏控制器。 打开Xcode,新建一个single View Application项目模板,命名完成后在AppDelegate.m文件中的application didFinishLaunchingWithOptions:方法中创建实例,因为UITabBarController通常是作为整个应用的根视图控制器,所以可以在屏幕载入时就显示分栏控制器。 因为UITabBarController是通过数组来管理,我们首先创建4个数组中的元素,也就是视图控制器的实例。 QQ学习群:262779381
1. - (BOOL)application:(UIApplication 1.- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 2.{ 3.self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; 4. // Override point for customization after application launch. 5. self.window.backgroundColor = [UIColor whiteColor]; 6. [self.window makeKeyAndVisible]; 7. UIViewController *view1 = [[UIViewController alloc]init]; 8. view1.title = @"home"; 9. UILabel *label1 = [[UILabel alloc]initWithFrame:CGRectMake(60, 100,200,30)]; 10. [label1 setText:@"主页面"]; 11. label1.textAlignment = NSTextAlignmentCenter; 12. [view1.view addSubview:label1]; 13. [label1 release]; 14. UIViewController *view2 = [[UIViewController alloc]init]; 15. view2.title = @"music"; 16. UILabel *label2 = [[UILabel alloc]initWithFrame:CGRectMake(60, 100,200,30)]; 17. [label2 setText:@"音乐页面"]; 18. label2.textAlignment = NSTextAlignmentCenter; QQ学习群:262779381
QQ学习群:262779381 19. [view2.view addSubview:label2]; 20. [label2 release]; 21. UIViewController *view3 = [[UIViewController alloc]init]; 22. view3.title = @"news"; 23. UILabel *label3 = [[UILabel alloc]initWithFrame:CGRectMake(60, 100,200,30)]; 24. [label3 setText:@"新闻页面"]; 25. label3.textAlignment = NSTextAlignmentCenter; 26. [view3.view addSubview:label3]; 27. [label3 release]; 28. UIViewController *view4 = [[UIViewController alloc]init]; 29. view4.title = @"setting"; 30. UILabel *label4 = [[UILabel alloc]initWithFrame:CGRectMake(60, 100,200,30)]; 31. [label4 setText:@"设置页面"]; 32. label4.textAlignment = NSTextAlignmentCenter; 33. [view4.view addSubview:label4]; 34. [label4 release]; 35. return YES; 36.} QQ学习群:262779381
QQ学习群:262779381 为了便于读者识别各个视图的内容细节,所以我们在每个视图上都添加了一个标签用于显示当前视图的信息。 创建了4个视图控制器的实例之后,我们就可以创建一个数组,将创建好的4各实例添加到数组中,将自己的所有权交给数组来管理。注意,在将实例添加到数组之前,不要对其进行内存释放,不然在编译过程中会导致内存的泄露,从而出现相应的错误。 37.label4.textAlignment = NSTextAlignmentCenter; 38.[view4.view addSubview:label4]; 39.NSArray *viewController = @[view1,view2,view3,view4]; 40.[view1 release]; 41.[view2 release]; 42.[view3 release]; 43.[view4 release]; QQ学习群:262779381
QQ学习群:262779381 我们通过新语法知识在新创建的数组实例中添加了4个成员,现在可以将他们各自的内存进行释放了。 这里我们又要回顾一下内存管理的知识,如何选择实例的释放时机,有两种方法,一种是autorelease方法,当程序结束时会自动释放实例的内存。但是如果当前的实例在以后的代码中没有运用时,这种自动释放的方法会显得效率比较低;另一种方法则是手动释放,在选择释放时机时要注意的原则是,谁创建,谁释放,后面用到,用过释放。 回到分栏控制器的学习上来,在创建一个用于管理视图控制器的数组实例后,接下来就要创建一个UITabBarController的实例,而且我们在前面提到过,有UITabBarController或UINavigationController时,要将二者设置为根视图控制器。那有的读者就会问,如果在一个应用中既有UITabBarController也有UINavigationController时,将谁设置为根视图控制器呢?一般来说,是将UINavigationController作为根视图控制器,我们会在下面的内容中讲解这二者结合的例子。 44.[view3 release]; 45.[view4 release]; 46.UITabBarController *TabBarController = [[UITabBarController alloc]init]; 47.[TabBarController setViewControllers:viewController animated:YES]; 48.[self.window setRootViewController:TabBarController]; 49.[TabBarController release]; QQ学习群:262779381
读者可以试一下将setRootViewController设置根视图控制器这句代码屏蔽,再运行一下程序,会发现什么也显示不出来。这是因为虽然我们定义了一个TabBarController的实例,但是我们并没有将它加到window窗口上,就好比我们创建一个UILabel标签实例,但并没有调用addsubview这条命令,那么标签就不会再屏幕上显示。在本例子中,我们用setRootViewController代替了原先的addSubview,其实其含义是类似的。 在TabBarController中有一个ViewControllers属性,记得在前面的内容中提到UINavigationController中也有一个相同的属性,通过这个属性我们可以设置在屏幕下方的分栏中显示的内容,然后可以设置它的动画效果。接下来将UITabBarController的实例设置为窗口的根视图控制器,最后释放掉对应分栏导航栏实例的内存。 这样,我们就在应用中创建一个简单地UITabBarController,我们可以来看一下应用最后的效果,如图所示。 QQ学习群:262779381
QQ学习群:262779381
7.3.2UITabbarController的的常用属性 我们知道,在TabBarController里面,放置的是多个TabBarItem组成的数组,而每一个TabBarItem则又对应了一个ViewController。 在本小节中,我们会介绍TabBarItem的使用方法和一些重要的属性,它主要包括3个属性: ·title:设置每一个分栏的名称; ·image:设置每一个分栏上的图片; ·badgeValue:设置tab右上角的小标。 我们在这里提一下自定义TabBarController时,各个视图的尺寸,在iPhone中,TabBarController的高度是49,宽度是320,而item的尺寸则是30*30。在学习自定义TabBarController时我们还会提到。title属性在上小节的内容中已经学习过了,下面我们首先来学习如何在TabBarController上添加系统自带的图片。 我们在7.3.1小节创建的例子的基础上,添加图片。 QQ学习群:262779381
QQ学习群:262779381 我们将图片添加在view3实例上,并通过默认选择按钮属性,将view3设置为默认的视图。 50.UIViewController *view3 = [[UIViewController alloc]init]; 51.view3.title = @"news"; 52.UITabBarItem *newItem = [[UITabBarItem alloc]initWithTabBarSystemItem:UITabBarSystemItemFeatured tag:3]; 53.view3.tabBarItem = newItem; 54.[newItem release]; 55.UITabBarController *TabBarController = [[UITabBarController alloc]init]; 56.[TabBarController setViewControllers:viewController animated:YES]; 57.TabBarController.selectedViewController = view3; 58.[self.window setRootViewController:TabBarController]; 59.[TabBarController release]; QQ学习群:262779381
从上面的代码可以看到,添加图片的方法和UINavigationController类似,通过一个图片初始化TabBarItem实例,然后将该视图的TabBarItem属性设置为该实例。 而设置默认按钮的方法也比较简单,通过selectedViewController属性设置相应的视图即可。 我们在iPhone应用中可以看到很多右上角有一个红色的数字小标,用于提示用户。特别是在App Store应用里运用的最为广泛,它提示用户当前有多少应用有最新的版本,可以用于更新。那我们该如何在上面添加这个数字小标呢?其实添加的方法很简单,只需要设置tabBarItem中的badgeValue属性即可。 60.UILabel *label3 = [[UILabel alloc]initWithFrame:CGRectMake(60, 100, 200, 30)]; 61.[label3 setText:@"新闻页面"]; 62.label3.textAlignment = NSTextAlignmentCenter; 63.[view3.view addSubview:label3]; 64.[label3 release]; 65.view3.tabBarItem.badgeValue = [NSString stringWithFormat:@"%d",10]; QQ学习群:262779381
QQ学习群:262779381 如果要点击一个按钮让上标值进行改变,则需要写相关的方法,主要还是通过设置badgeValue来改变值的大小。 在前面的内容中,我们介绍了如何在TabBar分栏控制器上添加系统自带的图片,其实在实际的项目中,这些TabBar中的图片都会由公司的美工人员去制作和当前项目主题相适应的,尺寸合适的图片。 那么在这一小节中,我们将一起来学习如何在TabBar上添加用户自定义的图片。 添加用户自定义图片的方法比较简单,只需要在创建TabBarItem时,使用另一种初始化的方法[initWithTitle: image: tag:]即可,但在使用这个初始化方法之前,我们需要将用户自定义的图片添加到项目中,添加的方法相信读者已经很熟悉了。 我们在上小节代码例子中的view1上添加一个用户自定义的图片。 66.UIViewController *view1 = [[UIViewController alloc]init]; 67.view1.title = @"home"; 68.UITabBarItem *imageItem = [[UITabBarItem alloc]initWithTitle:@"home" image:[UIImage imageNamed:@"CloseSelected.png"] tag:101]; 69.view1.tabBarItem = imageItem; 70.[imageItem release]; 71.UILabel *label1 = [[UILabel alloc]initWithFrame:CGRectMake(60, 100, 200, 30)]; 72.[label1 setText:@"主页面"]; 73.label1.textAlignment = NSTextAlignmentCenter; 74.[view1.view addSubview:label1]; 75.[label1 release]; QQ学习群:262779381
在有些应用中,TabBar中的Item可能不止4个,比如在iPod应用中,TabBar中的Item有数十个之多。而在iPhone中,TabBar中最多能容纳的Item为5个,如果超过了5个,它会自己集成一个更多的选项供用户选择和编辑。下面我们就一起来学习一下这种效果是如何实现的。 我们在上面例子的基础上,再添加3个视图控制器,并将它们都添加到TabBar数组中进行管理。 76.UIViewController *view5 = [[UIViewController alloc]init]; 77.view5.title = @"movie"; 78.UIViewController *view6 = [[UIViewController alloc]init]; 79.view6.title = @"search"; 80.UIViewController *view7 = [[UIViewController alloc]init]; 81.view7.title = @"style"; 82.NSArray *viewController = @[view1,view2,view3,view4,view5,view6,view7]; 83.[view1 release]; 84.[view2 release]; 85.[view3 release]; 86.[view4 release]; 87.[view5 release]; 88.[view6 release]; 89.[view7 release]; QQ学习群:262779381
添加到数组中之后,别忘了释放掉相应实例的内存。构建并运行,可以看到在TabBar中多了一个“more”选项,如图所示。 这种多个Item集成的功能,苹果公司已经帮我们写好了,我们需要做的只是编辑自己想要的Item到TabBar上即可。我们点击“more”按钮,会出现其余视图控制器的选项界面,这个界面是一个UITableView,右上角还集成一个编辑的功能,可以让用户将自己喜欢的或需要的Item放到主界面上,编辑的结果如图所示。 QQ学习群:262779381
7.3.3UITabBarController和UINavigationController的集成 我们知道,通常在IOS应用中,UITabBarController和UInavigationController这2个视图控制器会同时配合着使用,单独使用的情况很少。那么现在就出现了一个新的问题,我们是在UINavigationController的基础上添加TabBar分栏控制器?还是在UITabBarController的基础上添加navigationController导航控制器?这就要考虑到这2者之间的结构问题了。 我们假设在UITabBarController的基础上添加navigationController,我们通过代码来实现一下。同样地,我们在上小节例子的基础上为各个视图控制器添加导航栏。 QQ学习群:262779381
1.UIViewController *view1 = [[UIViewController alloc]init]; 2.view1.title = @"home"; 3.UITabBarItem *imageItem = [[UITabBarItem alloc]initWithTitle:@"home" image:[UIImage imageNamed:@"CloseSelected.png"] tag:101]; 4.view1.tabBarItem = imageItem; 5.[imageItem release]; 6.UILabel *label1 = [[UILabel alloc]initWithFrame:CGRectMake(60, 100, 200, 30)]; 7.[label1 setText:@"主页面"]; 8.label1.textAlignment = NSTextAlignmentCenter; 9.[view1.view addSubview:label1]; 10.[label1 release]; 11.UINavigationController *navigationController1 = [[UINavigationController alloc]initWithRootViewController:view1]; 12.[view1 release]; 13.UIBarButtonItem *addButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:nil]; 14.[view1.navigationItem setLeftBarButtonItem:addButton animated:YES]; 15.[addButton release]; QQ学习群:262779381
在创建一个UINavigationController的实例之后,我们将它添加到TabBar上,然后通过7 在创建一个UINavigationController的实例之后,我们将它添加到TabBar上,然后通过7.2小节内容讲述的navigationItem的添加方法添加一个按钮到导航栏上,最后,还需要在分栏控制器管理的数组中,用UINavigationController的实例替换当前试图控制器的实例,并释放掉相应实例的内存。 16.NSArray *viewController = @[navigationController1,view2,view3,view4,view5,view6,view7]; 17.[navigationController1 release]; 18.[view2 release]; 19.[view3 release]; 20.[view4 release]; 21.[view5 release]; 22.[view6 release]; 23.[view7 release]; 构建并运行,可以看到添加导航栏最后的效果如图所示。 QQ学习群:262779381
看来我们在UITabBarController的基础上添加导航栏控制器是可行的,那么如果我们先创建UINavigationController然后在它的基础上添加TabBar会成功吗?我们可以想象一下,如果我们是将UINavigationController作为根视图控制器,再添加TabBar,那当我们单击TabBar时,上面的导航栏是不会改变的,这样做就会出现相应的问题。 我们可以来试着用这种方式来实现一下,将UINavigationController的实例作为根视图控制器。 24.UITabBarController *TabBarController = [[UITabBarController alloc]init]; 25.[TabBarController setViewControllers:viewController animated:YES]; 26.UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:TabBarController]; 27.[TabBarController release]; 28.[self.window setRootViewController:navigationController]; 29.[navigationController release]; 我们可以看到最后的效果并不是我们所想要的,点击不同的TabBar按钮,会发现导航栏上并没有显示任何的信息,哪怕是前面设置的每个视图控制器的title属性都没有显示。所以说这种方法会出现很大的问题。 我们在第一种方法的基础上,就可以利用我们在7.2小节中所学与UINavigationController相关的知识来添加各种按钮。我们在这里就不做详细的演示了,相信读者有能力自己完成。 QQ学习群:262779381
7.3.4自定义TabBar 我们现在介绍完了如何自定义TabBarItem,那么有的读者就会有疑问,那TabBar就不能自定义吗?答案是可以的,我们也可以自己制作一个TabBar栏,并将它添加到window上。比如我们在本章开始时介绍的Game Center应用中的TabBar就是添加了一个用户自定义的TabBar,它的背景颜色和整体应用的元素是相适应的,这就使得整个应用看起来更加的美观,整体性也会更强。 下面我们就一起来学习如何自定义我们的TabBar。首先我们要了解自定义TabBar的流程。顾名思义,要自定义TabBar,就要把系统自带的TabBar给隐藏,所以我们在开始定义TabBar之前,要将TabBar.Hidden属性设置为YES。接下来我们就可以添加一个UIImageView,并设置自己喜欢的背景图片,最后添加上相应的按钮,并添加背景图片,就完成了一个自定义TabBar的创建。 我们在Xcode中新建一个Single View Application项目模板。然后我们新建4个UIViewController的子类,在前面的例子中,我们是将所有的视图控制器都放在AppDelegate文件中,这样对于小的例子是没有问题的,但是对于大型的项目工程肯定是行不通的。所以这里我们就将每一个视图控制器分开创建,这样使得项目条理更加的清晰,创建好4个文件如图所示。 QQ学习群:262779381
然后我们创建一个UITabBarController的子类文件,并创建实例,让它作为根视图控制器。在创建之前,我们要在AppDelegate 然后我们创建一个UITabBarController的子类文件,并创建实例,让它作为根视图控制器。在创建之前,我们要在AppDelegate.m文件中将UITabBarController子类的文件导入到文件中。 30.#import "AppDelegate.h" 31.#import "TabBarViewController.h" 32.@implementation AppDelegate 在application didFinishLaunchingWithOptions:方法中创建TabBarViewController的实例,并将它设置为窗口的根视图控制器。 33.self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; 34.// Override point for customization after application launch. 35.self.window.backgroundColor = [UIColor whiteColor]; 36.[self.window makeKeyAndVisible]; 37.TabBarViewController *TabBarController = [[TabBarViewController alloc]init]; 38.self.window.rootViewController = TabBarController; 39.[TabBarController release]; 40.return YES; QQ学习群:262779381
然后在UITabBarController子类的.m文件中将所有UIViewController子类文件都添加进来。 41.#import "TabBarViewController.h" 42.#import "HomeViewController.h" 43.#import "NewsViewController.h" 44.#import "MusicViewController.h" 45.#import "SearchViewController.h" 46.@interface TabBarViewController () 下一步在viewDidLoad方法中创建相应ViewController的实例,并集成相对应的导航栏控制器。在这之前,我们要注意前面所提到的,要将tabBar.hidden属性设置为YES,这样才能隐藏系统自带的TabBarController,在init初始化方法中设置属性。 47.- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 48.{ 49. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 50. if (self) { 51. self.tabBar.hidden = YES; 52. } 53. return self; 54.} 55.- (void)viewDidLoad 56.{ 57.[super viewDidLoad]; 58.HomeViewController *homeView = [[HomeViewController alloc]init]; 59.homeView.title = @"首页"; 60.UINavigationController *homeNavigation = [[UINavigationController alloc]initWithRootViewController:homeView]; 61.[homeView release]; 62.NewsViewController *newsView = [[NewsViewController alloc]init]; 63.newsView.title = @"新闻"; 64.UINavigationController *newsNavigation = [[UINavigationController alloc]initWithRootViewController:newsView]; 65.[newsView release]; QQ学习群:262779381
66.MusicViewController *musicView = [[MusicViewController alloc]init]; 67.musicView.title = @"音乐"; 68.UINavigationController *musicNavigation = [[UINavigationController alloc]initWithRootViewController:musicView]; 69.[musicView release]; 70.SearchViewController *searchView = [[SearchViewController alloc]init]; 71.searchView.title = @"搜索"; 72.UINavigationController *searchNavigation = [[UINavigationController alloc]initWithRootViewController:searchView]; 73.[searchView release]; 74.} 记得创建完所需要的ViewController之后下一步是做什么吗?对了,就是创建一个数组,将这些实例都放在数组中管理。但是在集成导航栏控制器之后,各个视图控制器都交给导航栏控制其管理了,所以在将实例添加到数组中时,就是添加相应的导航栏控制器的实例了。 75.NSArray *viewControllers = @[homeNavigation,newsNavigation,musicNavigation,searchNavigation]; 76.[homeNavigation release]; 77.[newsNavigation release]; 78.[musicNavigation release]; 79.[searchNavigation release]; 80.[self setViewControllers:viewControllers]; QQ学习群:262779381
在设置ViewController属性时,我们可以直接用self来进行设置。这样就将各个视图控制器的导航栏设置好了,如果需要在导航栏上添加相应的按钮,可以在对应的视图控制器文件中添加。比如我们在主页中也就是HomeViewController.m文件中创建一个导航控制器的左按钮。可以直接在viewDidLoad方法中添加按钮。 81.- (void)viewDidLoad 82.{ 83. [super viewDidLoad]; 84. UIBarButtonItem *addButton = [[UIBarButtonItem 85. alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:nil]; 86. self.navigationItem.leftBarButtonItem = addButton; 87. [addButton release]; 88.} 因为在前面我们隐藏了系统自带的TabBar,现在如果我们运行程序的话,下面的TabBar会是一个空白的区域,因为并没有新的视图添加进来。正因为这样,我们就要自己添加一个imageView。将自己所需要的图片作为TabBar的背景图,以达到自定义的目的。 我们回到TabBarViewController.m文件中,继续在viewDidLoad方法中添加UIimageView的实例,我们还需要将素材文件添加到项目中,想必添加的方法读者已经很熟悉了。 89.UIImageView *TabBarImage = [[UIImageView alloc]initWithFrame:CGRectMake(0, 431, 320, 49)]; 90.TabBarImage.image = [UIImage imageNamed:@"NavigationBar"]; 91.[self.view addSubview:TabBarImage]; 92.TabBarImage.userInteractionEnabled = YES; 93.[TabBarImage release]; QQ学习群:262779381
在前面的内容中,我们提到过UINavigationBar和UITabBar的长度是320,而高度则是49,所以在设置它的frame属性时要注意计算一下它在屏幕中的位置,因为iPhone4的屏幕高度是480,减去tabBar的高度49,它的Y轴坐标就为431。如果是在其他尺寸的iPhone上运行,可以去参考该iPhone的尺寸。 还需要注意的是,如果不将userInteractionEnabled属性设置为YES,那么我们定义的tabBar是不能交互的,即使有按钮也不能点击,所以这里就要将ImageView实例的userInteractionEnabled属性设置为YES。 构建并运行,可以看到我们自己的图片已经添加到了屏幕上,和tabBar很相似了。效果如图所示。 QQ学习群:262779381
虽然自定义好了tabBar,但是我们无法实现页面的切换,而在系统自带的tabBar中,是利用TabBarButton来实现切换的。在这里,类似的,我们定义4个按钮来完成4个视图控制器的切换。我们利用一个for循环来完成对4个按钮的创建工作。 94.int X = 0; 95.for(int index = 0;index < 4;index++){ 96.UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 97.button.frame = CGRectMake(34+X, 4, 42, 42); 98.button.tag = index; 99.button.backgroundColor = [UIColor blueColor]; 100.[button addTarget:self action:@selector(changeView:) forControlEvents:UIControlEventTouchUpInside]; 101.[TabBarImage addSubview:button]; 102.X += 70; 103.} 我们定义了一整型变量X,每完成一个按钮的创建只有,会让X坐标向右移动34,使得4个按钮平局分布在自定义的tabBar上,设置button的tag值是用来控制各个视图控制器之间的切换。这个实现的过程也比较简单,只需要将selectedIndex值设置为当前index值的按钮即可。下面是切换方法的实现。 104.- (void)changeView:(UIButton *)button 105.{ 106. self.selectedIndex = button.tag; 107.} QQ学习群:262779381
7.4视图间数据传递方式 在IOS编程中,有时候我们会碰到需要用户输入,但是输入框是在另一个视图中,那么我们怎么将值传递给第一个视图中呢?其实IOS实现视图之间传值的方式有很多,如导航控制器的push方法,协议方法,通知,NSUserDefaults等等。在本小节中,我们分别通过这4个方法实现两个视图之间的传值。 QQ学习群:262779381
7.4.1导航控制器属性传值方法 使用导航控制器传值,我们讲解最简单的单项传值方法,即第一个视图控制器的值传递给给第二个视图控制器,通过导航控制器的push方法。下面我们来实现导航控制器方法。 首先我们新建Xcode项目,使用Single View Application模板,在项目中,再创建一个视图控制器viewController2,继承于UIViewController。然后在AppDelete中设置导航控制器作为UIWindow的根视图。 1.- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 2. // Override point for customization after application launch. 3. ViewController *view1 = [[ViewController alloc]init]; 4. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:view1]; 5. self.window.rootViewController = navi; 6. return YES; 7.} 接下来在viewController.h文件中创建UILabel、UItextField和UIButton的实例。 -------------viewController.h------------ 8.#import <UIKit/UIKit.h> 9.@interface ViewController : UIViewController 10.@property (nonatomic, retain) UITextField *textField; 11.@property (nonatomic, retain) UILabel *label; 12.@property (nonatomic, retain) UIButton *button; 13.@end QQ学习群:262779381
QQ学习群:262779381 然后在.m文件中对实例进行初始化并设置相关属性。 -------------viewController.m------------ 14.- (void)viewDidLoad { 15. [super viewDidLoad]; 16. self.title = @"view1"; 17. self.view.backgroundColor = [UIColor whiteColor]; 18. _label = [[UILabel alloc]init]; 19. _label.text = @"值"; 20. _label.frame = CGRectMake(40, 110, 80, 20); 21. [self.view addSubview:_label]; 22. _textField = [[UITextField alloc]initWithFrame:CGRectMake(100, 100, 120, 40)]; 23. _textField.borderStyle = UITextBorderStyleRoundedRect; 24. [self.view addSubview:_textField]; 25. _button = [UIButton buttonWithType:UIButtonTypeSystem]; 26. [_button setTitle:@"传值" forState:UIControlStateNormal]; 27. [_button setTintColor:[UIColor whiteColor]]; 28. _button.backgroundColor = [UIColor grayColor]; 29. _button.frame = CGRectMake(100, 240, 120, 40); 30. [_button addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside]; 31. [self.view addSubview:_button]; 32.} 33.- (void)push 34.{ 35.} QQ学习群:262779381
接下来我们对viewController2进行布局,同样的在viewController2. h文件中定义相关实例,在 接下来我们对viewController2进行布局,同样的在viewController2.h文件中定义相关实例,在.m文件中对实例进行初始化与属性设置。 -------------viewController2.h------------ 36.#import <UIKit/UIKit.h> 37.@interface ViewController2 : UIViewController 38.@property (nonatomic ,retain) UITextField *textField; 39.@property (nonatomic, retain) UIButton *button; 40.@property (nonatomic, copy) NSString *value; 41.@end 这里我们定义了一个NSString字符串实例,用于存储视图控制器1传过来的值。 -------------viewController2.m------------ 42.- (void)viewDidLoad { 43. [super viewDidLoad]; 44. self.title = @"view2"; 45. self.view.backgroundColor = [UIColor whiteColor]; 46. _textField = [[UITextField alloc]initWithFrame:CGRectMake(100, 100, 120, 40)]; 47. _textField.borderStyle = UITextBorderStyleRoundedRect; 48. [self.view addSubview:_textField]; 49. _button = [UIButton buttonWithType:UIButtonTypeSystem]; 50. [_button setTitle:@"确定" forState:UIControlStateNormal]; 51. [_button setTintColor:[UIColor whiteColor]]; 52. _button.backgroundColor = [UIColor grayColor]; 53. _button.frame = CGRectMake(100, 240, 120, 40); 54. [_button addTarget:self action:@selector(ok) forControlEvents:UIControlEventTouchUpInside]; 55. [self.view addSubview:_button]; 56.} 57.- (void)ok 58.{ 59.} QQ学习群:262779381
现在我们只是对两个视图控制器进行了布局,并通过UINavigationController导航控制器的实例控制这两个视图控制器。接下来我们来实现视图间值的传递。 在viewController.m的push方法中实现跳转,并将textField的值传给viewController2的value字符串。 60.- (void)push 61.{ 62. ViewController2 *view2 = [[ViewController2 alloc]init]; 63. view2.value = _textField.text; 64. [self.navigationController pushViewController:view2 animated:YES]; 65.} 然后在viewController2.m文件中的viewDidLoad方法中,将textField的值等于value字符串。 66._textField = [[UITextField alloc]initWithFrame:CGRectMake(100, 100, 120, 40)]; 67._textField.borderStyle = UITextBorderStyleRoundedRect; 68._textField.text = _value; 69.[self.view addSubview:_textField]; 最后实现返回按钮的方法。 70.- (void)ok 71.{ 72. [self.navigationController popViewControllerAnimated:YES]; 73.} QQ学习群:262779381
QQ学习群:262779381 运行模拟器,我们可以看到最后的运行效果如图所示。 从图中我们可以看到,当我们在view1的textField中输入需要传给view2的值后,单击传值按钮,导航控制器控制视图控制器跳转到view2,并将值传给view2,最后显示,这就是一个简单的导航控制器传值方法。 QQ学习群:262779381
7.4.2协议传值方法 在第四章的内容中,我们介绍了IOS中有一种协议代理设计模式,也就是自己不去完成,让他人带完成的一种模式。而在视图见传值时,我们也可以通过协议方法来实现。 我们在7.4.1小节的代码基础上,实现协议传值方法。 实现协议传值方法时,我们需要确定谁是委托人,谁是代理人,我们要在代理人种声明协议,拿上小节的例子来说,我们将view2设置为代理,也就是在view2中进行传值,将值传给view1。那么我们在view2中声明一个协议,用于传值。 1.#import <UIKit/UIKit.h> 2.//值改变协议 3.@protocol changeValueDelegate <NSObject> 4.@optional 5.- (void)changeValue:(NSString *)value; 6.@end 7.@interface ViewController2 : UIViewController 8.@property (nonatomic ,retain) UITextField *textField; 9.@property (nonatomic, retain) UIButton *button; 10.@property (nonatomic, copy) NSString *value; 11.@property (nonatomic, assign)id<changeValueDelegate>valueDelegate; 12.@end QQ学习群:262779381
QQ学习群:262779381 我们定义了一个用于值改变的协议changeValueDelegate,并声明了一个id类型该协议的实例。 接下来在viewController2.m文件中实现ok返回方法 13.- (void)ok 14.{ 15. if ([self.valueDelegate respondsToSelector:@selector(changeValue:)]) { 16. [self.valueDelegate changeValue:_textField.text]; 17. } 18. [self.navigationController popViewControllerAnimated:YES]; 19.} 这里使用valueDelegate协议调用changeValue方法,并将textField的值作为协议方法的参数。 在完成了代理类的设置后,我们需要在委托类中设置代理,也就是在viewController中设置view2为代理。在实现前,要在.h文件导入协议方法。 20.#import <UIKit/UIKit.h> 21.#import "ViewController2.h" 22.@interface ViewController : UIViewController<changeValueDelegate> 23.@property (nonatomic, retain) UITextField *textField; 24.@property (nonatomic, retain) UILabel *label; 25.@property (nonatomic, retain) UIButton *button; 26.@end 27.- (void)changeValue:(NSString *)value 28.{ 29. _textField.text = value; 30.} QQ学习群:262779381
到这里协议传值基本完成,但是最后一步也是最关键的一步一定不能忘记,就是设置代理,如果不设置,那么传值无法进行,在push方法中设置view2为代理,去完成传值任务。 31.- (void)push 32.{ 33. ViewController2 *view2 = [[ViewController2 alloc]init]; 34. view2.valueDelegate = self; 35. [self.navigationController pushViewController:view2 animated:YES]; 36.} view2.valueDelegate = self这句话是整个协议传值方法的精髓,千万不能忘记设置。 QQ学习群:262779381
7.4.3通知传值方法 在第三章中我们也向读者介绍了通知设计模式,NSNotificationCenter的存在就像一个中心发射站一样,可以接收与它相关的信息。两个view之间的传值也可以使用通知来实现。 同样的,我们在代码清单上进行修改,修改viewController.m中viewDidLoad方法,注册一个通知,名为changeValueNotification。 1.- (void)viewDidLoad { 2. [super viewDidLoad]; 3. …… 4. _button.frame = CGRectMake(100, 240, 120, 40); 5. [_button addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside]; 6. [self.view addSubview:_button]; 7. //注册一个改变值的通知 8. [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(changeValue:) 9. name:@"changeValueNotification" object:nil]; 10.} 我们可以看到,现在在通知中心添加了一个名为“changeValueNotification”的通知,它实现的方法是“changeValue”。这里我们还要对该方法进行修改。 11.- (void)changeValue:(NSNotification *)notification 12.{ 13. id value = notification.object; 14. _textField.text = value; 15.} QQ学习群:262779381
现在的方法和以前不同,它的参数是一个NSNotification对象,在方法里,我们通过定义一个id类型的变量,去获取通过通知传递过来的object参数,最后将它赋值给textField。 最后,在进行传值的页面,也就是viewController2中post一条改变值的通知。 16.- (void)ok 17.{ 18.[[NSNotificationCenter defaultCenter]postNotificationName:@"changeValueNotification" 19.object:_textField.text]; 20. [self.navigationController popViewControllerAnimated:YES]; 21.} 运行模拟器,可以看到最后的效果如图和图所示。 通知在代码中不能出现的太多,它解耦合的程度太深,让两个完全没有关系的类都可以进行消息的传递,这样不利于代码的维护,也不利于其他程序员阅读你的代码。但是相比其他传值方法,通知应该是最简单的一种传值方法,读者一定要掌握。 QQ学习群:262779381
7.4.4NSUserDefaults传值方法 QQ学习群:262779381 NSUserDefaults是NSObject类提供的一个偏好存储自定义类,它是一个用于轻量级数据存储的类。在界面传值时,我们可以将view2中的值保存在NSUserDefaults中,然后在view1中读取完成传值。修改ok方法,在viewController2中创建NSUserDefaults。 1.- (void)ok 2.{ 3. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 4. [defaults setValue:_textField.text forKey:@"value"]; 5. [defaults synchronize]; 6. [self.navigationController popViewControllerAnimated:YES]; 7.} NSUserDefaults的使用方法也很简单,首先创建一个NSUserDefaults的实例,然后通过setValue方法设置键值对,这里可以对属性设置,也可以使用setObject对对象设置,最后使用synchronize方法进行同步。这样我们就将textField的值保存在NSUserDefaults中。 接下来,我们可以在viewController中读取名为“value”的值,并显示在textField中,完成传值。我们在viewWillAppear方法中读取数据。 QQ学习群:262779381
QQ学习群:262779381 8.- (void)viewWillAppear:(BOOL)animated 9.{ 10. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 11. NSString *value = [defaults valueForKey:@"value"]; 12. _textField.text = value; 13.} QQ学习群:262779381
本章小结 本章讲解了IOS开发中UIViewController视图控制器的基本知识,并讲解了UIViewController的几个子类,UINavigationController,UITabbarController,UIImagePickerController的基本使用方法,UIViewController是IOS开中的核心知识,也是支撑MVC框架的核心,希望读者在课余时间多加练习,掌握它们的用法。 QQ学习群:262779381
课后习题 1.在Singe View Application模板中搭建UINavigation和UITabbarController框架,拥有3个子视图控制器,分别是新闻,音乐和照片。 2.在新闻页面中通过UITableView表视图显示新闻,新闻的内容自己选择,当移动到最下时,有一个“单击显示更多”按钮,单击后可以显示后20条新闻。 3.单击新闻cell,可以跳转到照片视图,并将新闻的内容传到照片视图中。 4.通过NSUserDefaults方法,将新闻对应的关键词显示出来。 QQ学习群:262779381