《iPhone与iPad开发实战 - IOS 经典应用剖析》样章

jin@open

贡献于2012-09-03

字数:0 关键词: iOS 开发 移动开发

第3章 工具类应用开发实战--密码生成应 用 作为本书的第一个应用案例,在选择的时候考虑很多,不能太复杂,涵盖的知识点 主要是 UI 方面。密码生成(Amuck Password Generator)应用正好符合本书的选择要求。 虽然,这个应用密码生成的算法也有很大的学问,但不是我们的关注点。本章重点如下:  MVC 设计模式  实用型应用模板  UIView、UIButton 和 UILabel 等基本控件使用  UIView 动画知识  发送 Email 3.1 应用介绍 密码生成(Amuck Password Generator ) 应用 App Store 下 载 网 址 http://itunes.apple.com/app/amuck-password-generator/id289301501?mt=8#,源代码下载地址 http://www.appsamuck.com/day29.html。该应用是为用户提供一个密码生成工具,在我们 现代生活中密码设置随处可见,密码生成(Amuck Password Generator)能够帮助我们产 生密码,它包含:小写字母、大写字母、数字和标点符号组合随机产生密码,具有很高 的安全性和防破解性。 应用启动之后视图如图 3-1 所示,在主视图中可以设定密码长度(默认是 8),还可 以通过开关按钮设定密码中是否包含:小写字母、大写字母、数字和标点符号。也可以 通过设置 Show Phonetics 开关选择是否显示发音视图。 图 3-1 启动视图 点击 Generate Password 按钮会产生如图 3-2 产生密码视图,同时生成密码。在显示 密码、发音内容和 E-Mail Password 按钮时候是以一种渐变动画显示出来的,如果原来控 件没有显示,则慢慢显示;如果原来有显示这些控件,这先消失再慢慢显示出来。如果 设置 Show Phonetics 开关为否时候,则视图如图 3-3 所示。 图 3-2 有发音产生密码视图 图 3-3 无发音产生密码视图 可以在图 3-2 和图 3-3 所示的视图,点击 E-Mail Password 按钮会调用 iOS 的内置 E-Mail 视图,通过该视图可以将产生的密码通过 E-Mail 发送。 本应用是采用实用型应用程序模板,实用型应用程序一般在主视图的右下角有 按钮,通过点击 按钮,视图会翻转到如图 3-4 所示的背后视图,点击按钮 Done 可以 返回如图 3-1 的主视图。这个背后视图是在介绍作者的另外一款软件,iFlame 一个虚拟 的打火机 iPhone 点击 Download 按钮就会打开该应用在 App Store 的连接。 图 3-4 实用型应用背后视图  由于从网上(如:http://www.appsamuck.com/day29.html)下载下来的源程序,一般 iOS SDK 版本比较低,使用开发工具 Xcode2.0 或 3.0 开发。本书采用开发 Xcode3.2.5(iOS SDK4.2) 打开工程的时候会出现如图 3-5“Base SDK Missing”问题,这需要修改工程的 Base SDK, 选择菜单 Project->Edit Project Settings 弹出如图 3-6 所示对话框把 Base SDK 修改为 iOS 4.2。 完成关闭对话框再在图 3-7 所示现在模拟器(Simulator)。 图 3-5 错误的 SDK 图 3-6 选择 Base SDK 为 iOS4.2 图 3-7 选择模拟器运行 3.2 应用剖析 这一节我们从应用的类图如入手,了解应用中有哪些类,以及这个类中的方法、成 员变量和属性。nib 文件有哪些。还会介绍 MVC 设计模式等。 3.2.1 核心类图 在该应用中涉及到 9 个类,1 个协议如图 3-8 所示。 图 3-8 核心类图 在类图其中有 3 个是 Cocoa Touch 提供的基类,即:UIViewController、UIView 和 NSObject,其它的 6 个类由我们编写。还有 1 个协议 UIApplicationDelegate。 这些类和 协议的作用如表 3-1 所示: 表 3-1 类和协议表 类名或协议 作用 备注 UIViewController 视图控制器基类 基类 UIView 视图基类 基类 NSObject Objective-C 根类 基类 RootViewController 应用中的根控制器 应用程序类 MainViewController 主视图的视图控制器 应用程序类 FlipsideViewController 背后视图的视图控制器 应用程序类 MainView 主视图的视图 应用程序类 FlipsideView 背后视图的视图 应用程序类 UIApplicationDelegate 应用程序委托协议 协议 PasswordGenAppDelegate 应用程序委托 应用程序委托 3.2.2 RootViewController 类 RootViewController 是根控制器,它控制其它的视图控制器。 RootViewController 类 中的成员变量和方法如表 3-2 所示: 表 3-2 RootViewController 类表 类成员 变量或方法类型 类型或返回类型 说明 infoButton 成员变量 IBOutlet UIButton* 定义有输出口的 按钮 flipsideNavigationBar 成员变量 IBOutlet UINavigationBar* 背后视图的导航栏 mainViewController 成员变量 IBOutlet MainViewController* 主视图控制器 flipsideViewController 成员变量 IBOutlet FlipsideViewController* 背后视图控制器 viewDidLoad 方法 void 视图加载完成回调方法 dealloc 方法 void 对象释放回调方法 didReceiveMemoryWarning 方法 void 内存报警时候回调方法 shouldAutorotateToInterfaceOrientation 方法 BOOL 视图控制器支持某个方向旋转 时候返回 YES toggleView 方法 IBAction 点击 按钮或 Done 按钮时候 调用 loadFlipsideViewController 方法 void 初始化背后视图控制器 其中 按钮的 IBOutlet(输出口)变量是 infoButton,在 Cocoa 编程中一个控件是否 定义输出口是要看这个控件是否需要通过程序代码改变它的状态,例如:如果我们需要 在控制器中改变按钮的背景或文字,我们就需要为该按钮控件定义输出口。 3.2.3 MainViewController 类 MainViewController 是主视图控制器,MainViewController 类中的成员变量和方法如 表 3-3 所示: 表 3-3 MainViewController 类表 类成员 变量或方法类型 类型或返回类型 说明 createPassword 成员变量 IBOutlet UIButton* 定义有输出口的 Generate Password 按钮 emailPassword 成员变量 IBOutlet UIButton* 定义有输出口的 E-Mail Password 按钮 viewDidLoad 方法 void 视图加载完成回调方法 dealloc 方法 void 对象释放回调方法 didReceiveMemoryWarning 方法 void 内存报警时候回调方法 shouldAutorotateToInterfaceOri entation 方法 BOOL 视图控制器支持某个方向旋转 时候返回 YES 定义有输出口的 Generate Password 和 E-Mail Password 按钮定义输出口是因为需要在 控制器的 viewDidLoad 方法初始化这两个按钮状态。 3.2.4 FlipsideViewController 类 FlipsideViewController 是背后视图控制器,FlipsideViewController 类中的成员变量和 方法如表 3-4 所示: 表 3-4 FlipsideViewController 类表 类成员 变量或方法类型 类型或返回类型 说明 viewDidLoad 方法 void 视图加载完成回调方法 dealloc 方法 void 对象释放回调方法 didReceiveMemoryWarning 方法 void 内存报警时候回调方法 shouldAutorotateToInterfaceOri entation 方法 BOOL 视图控制器支持某个方向旋转 时候返回 YES 3.2.5 MainView 类 MainView 是主视图,MainView 类中的成员变量和方法如表 3-5 所示: 表 3-5 MainView 类表 类成员 变量或方法类型 类型或返回类型 说明 passwordLength 成员变量 IBOutlet UITextField* 定义有输出口的密码长度文本 框 includeLowerCase 成员变量 IBOutlet UISwitch* 定义有输出口的包含小写开关 控件 includeNumbers 成员变量 IBOutlet UISwitch* 定义有输出口的包含数字开关 控件 includePunctuation 成员变量 IBOutlet UISwitch* 定义有输出口的包含标点符号 开关控件 includeUpperCase 成员变量 IBOutlet UISwitch* 定义有输出口的包含大写开关 控件 showPhonetics 成员变量 IBOutlet UISwitch* 定义有输出口的是否显示发音 开关控件 password 成员变量 IBOutlet UITextField* 定义有输出口的密码文本框 phoneticPassword 成员变量 IBOutlet UITextView* 定义有输出口的显示发音文本 视图 emailPasswordButton 成员变量 IBOutlet UIButton* 定义有输出口的发送 email 按钮 setPasssword 方法 IBAction 点击 Generate Password 按钮时 候调用 resignResponder 方法 IBAction 放弃密码文本框和密码长度文 本框的第一响应者 emailPassword 方法 IBAction 点击 E-Mail Password 按钮时候 调用 emailAddress 成员变量,但是从来没有使用过!还有比较严重的问题是,在该类中没 有重写 dealloc 方法,该方法是对象释放回调方法,在该方法中用来释放类中的成员变量, 代码如下: - (void)dealloc { [password release]; … … [super dealloc]; } dealloc 非常重要,特别是有很多成员变量需要释放内存时候,而在该类中没有重写 这个方法,这也是我们应该摒弃和借鉴的。 3.2.6 FlipsideView 类 FlipsideView 是背后视图,FlipsideView 类中的成员变量和方法如表 3-6 所示: 表 3-6 FlipsideView 类表 类成员 变量或方法类型 类型或返回类型 说明 dealloc 方法 void 对象释放回调方法 initWithFrame: 构造方法 id 视图的构造方法 drawRect: 方法 void 绘制视图方法,本应用实际没有 使用 openLink 方法 IBAction 点击 Download 按钮时候调用 3.2.7 Nib 文件 本应用中的 nib 文件有 3 个:MainWindow.xib、MainView.xib 和 FlipsideView.xib,这 3 个文件解释如下:  MainWindow.xib,在主窗口 nib 文件,默认 Cocoa 应用都有这 个文件,主程序在启动的时候读取这个文件,根据这个文件配置信 息启动相应的根控制器。  MainView.xib,是主视图的 nib 文件,它是 MainViewController 和 MainView 的纽带。  FlipsideView.xib ,是主视图的 nib 文 件 , 它 是 FlipsideViewController 和 FlipsideView 的纽带。  文件的后缀名是 xib 为什么 nib 文件?以前该文件的后缀名是 nib,官方一致延续了 nib 的叫 法。因此,在本书中也采用 nib 的叫法。 3.2.8 Cocoa 和 Cocoa Touch 中 MVC 设计模式 从上面的介绍看到应用中有很多的 UIViewController 和 UIView,还有很多 nib 文件, 它们之间是什么关系呢?这与 MVC 设计模式有关。 苹果的 Cocoa 和 Cocoa Touch 采用了 MVC 设计模式进行设计的,所谓设计模式就是 解决某一特定问题的方案。MVC 是解决具有 UI 的应用系统的成熟解决方案,MVC 解释 如下:  M-Model(模型)是应用系统中与视图对于部分的数据;  V-View(视图)是应用系统中用户看到并与之交互的界面;  C-Controller(控制器)是应用系统中起到控制器作用,接受 用户事件,显示数据等等,与视图进行交互等。  Cocoa 是苹果开发 Mac OS X 应用系统的框架,它是基于 Objective-C,它包括:Foundation 和 AppKit,Cocoa 框架在 iOS 上叫做 Cocoa Touch,Cocoa Touch 框架包含:Foundation 和 UIKit。 在 Cocoa 和 Cocoa Touch 应用中严格按照该模式实现如图 3-9 所示。在 Cocoa 和 Cocoa Touch 中除了视图和视图控制器外,还有 nib 文件,nib 文件是通过 Interface Builder 工具 维护,在新版的 Xcode 4.x 后 Interface Builder 与 Xcode 集成在一起了,本书采用的还是 Xcode3.2 版本,因此 Interface Builder 与 Xcode 工具还是分开的。 图 3-9 Cocoa MVC nib 文件是视图和控制器的“纽带”,如图 3-10 所示,通过这个“纽带”控制器能够 控制在视图上显示和读取内容,能够接受和响应视图上的事件。我们也可以通过 nib 文件 可视化地设计视图。 图 3-10 nib 文件 无论从图 3-9 还是图 3-10 我们都没有发现 Model(模型)的“痕迹”,在一般的简单 控件中模型类不是很突出,它可以是简单数据类型,也可以是 NSString 等 Objective-C 中 的类,也可以是我们自定义一些类,它们可以以成员变量的方式出现在视图控制器中, 也可以以成员变量的方式出现在视图中。因此,模型可以很灵活的出现,而控制器和视 图是很明显的,一般而言一个控制器可以控制多个视图。 3.3 应用实战与讲解 这一节我们将实际编写这个应用,边做边讲解的方式。按照搭建应用骨架、视图翻 转动画、主视图 UI 和代码、主视图控制器代码、背后视图 UI 和代码、背后视图控制器, 这样的顺序编写和讲解。 3.3.1 构建应用骨架 在本书采用 Xcode3.2 工具中提供了一个编写实用型应用程序的模板,图 3-11 是 Xcode3.2 的模板,其中 Utility Application 选项就是创建实用型应用程序。 图 3-11 实用型应用模板 但是遗憾的是本应用的原作者代码,没有采用 Xcode3.2 模板,我们比较一下它们的 不同,使用 Xcode3.2 生成的类图如图 3-12 所示。 图 3-12 Xcode3.2 生成的类  为了尊重原作者,在本书中如果涉及到由于历史版本引起的问题,我们还是按照原作者应用 始版本介绍和构建应用。 比较类图 3-8 和 3-12 会发现,Xcode3.2 没有 MainView 和 FlipsideView 两个自定义 视图类,而是直接采用 UIView 类,Xcode3.2 中 MainViewController 类还实现了一个 FlipsideViewControllerDelegate 协议,FlipsideViewControllerDelegate 协议是旧版本中没有 的,而 Xcode3.2 中没有 RootViewController 控制器类。这正是两个版本区别的关键,在 旧版本中视图跳转和返回都是通过根控制器 RootViewController 实现控制的,在 Xcode3.2 中是通过 MainViewController 实现 FlipsideViewControllerDelegate 协议视图翻转控制的, MainViewController 即使主视图控制器也是根控制器。 由于我们要构建与旧版本一样的类,所以要添加 MainView、FlipsideView 和 RootViewController 类。先创建 MainView 选择 Main View 文件夹,右键菜单 New File 弹 出对话框如图 3-13 所示,选择 Objective-C class,Subclass of 为 UIView,选择好后点击 Next,在类名中输入 MainView。 图 3-13 创建 UIVew MainView 生成之后还需要修改对应的 nib 文件,在 Xcode 中双击 MainView.xib 然后 Interface Builder 会打开 MainView.xib 窗口如图 3-14 所示, 图 3-14 MainView.xib 窗口 从图 3-14 中可以看到 View 的类型是 UIView,我们需要把它改成为 MainView,为了 实现这一个目的,请选择菜单 Tools->Inspector 打开检查器窗口,选择 标签打开检 查器如图 3-15 所示,选择 MainView 类。 图 3-15 视图检查器 选中双击 Main View 打开设计窗口如图 3-16 所示,该视图是使用 Xcode3.2 工具生成 的,我们需要恢复到默认视图状态,如图 3-17 所示。 图 3-16 Xcode3.2 模板生成视图 图 3-17 修改后视图 为了达到这一效果需要删除 按钮,并把视图背景修改颜色为默认颜色。删除 按 钮过程是用鼠标点中该按钮,使用键盘 delete 就可以删除了,而视图背景修改需要打开 检查器窗口选择标签 后如图 3-18,修改 Background 属性使背景颜色被默认白色。 图 3-18 修改视图背景 按照上面的方法创建 FlipsideView 类,该类创建完成需要修改它对应的 FlipsideView.xib 文件,在 Interface Builder 会打开 FlipsideView.xib 窗口,选中双击 Flipside View 打开设计窗口如图 3-19 所示,该视图是使用 Xcode3.2 工具生成的,我们需要恢复 到默认视图状态,如图 3-20 所示。 图 3-19 Xcode3.2 模板生成视图 图 3-20 修改后视图 为达到图 3-20 效果,需要删除 Title 部分导航栏,在本应用中导航栏是通过代码动态 添加到视图上面去的,而不是在视图设计窗口中拖拽上去的。删除导航栏和修完成背景 后我们会发现视图变成如图 3-21 所示带有状态栏。 图 3-21 状态栏视图 我们需要去掉这个状态栏,打开视图检查器如图 3-22 所示,将 Status Bar 选择为 Unspecified。 图 3-22 视图检查器 并 配 置 它们的创建过程完全一样这里就不再过多介绍了。下面介绍 RootViewController 创建和配置,选择一个文件夹,右键菜单 New File 弹出对话框如图 3-23 所示,选择 UIViewController subclass,并在 Options 部分去掉 With XIB for user interface,就是不创建 nib 文件。选择好后点击 Next,在类名中输入 RootViewController。 图 3-23 创建视图控制器 RootViewController 生成之后还需要修改 MainWindow.xib 文件,在 Xcode 中双击 MainWindow.xib 然后 Interface Builder 会打开 MainWindow.xib 窗口如图 3-24 所示。点中 MainViewController 后选择菜单 Tools->Inspector 打开检查器窗口,选择 标签打开 检查器窗口在 Class 中选择 RootViewController 类。 图 3-24 MainWindow.xib 窗口 3.3.2 视图翻转动画 点击主视图右下角的 按钮,主视图会翻转到背后视图,在背后视图点击 Done 按 钮可以返回到主视图。在 iOS 中这是一种视图跳转的方式,同时还伴有动画效果。在本 应用中采用的是,通过一个根控制器(控制其它控制器的控制器)RootViewController, 来控制主视图控制器(MainViewController)和背后视图控制器(FlipsideViewController)。 本应用是通过 MainWindow.xib 文件加载 RootViewController 控制器,然后在 RootViewController 控制器中创建一个 MainViewController 和 FlipsideViewController。 双击打开 MainWindow.xib 窗口图 3-25 所示。 图 3-25 MainWindow.xib 窗口 在图 3-26 中双击 RootViewController 打开根控制器设计视图如图 3-26 所示。 图 3-26 根控制器设计视图 这个根控制器创建的时候没有对应的视图对象,我们需要从对象库中拖拽一个 UIView 对象,拖拽过程如图 3-27 所示。拖拽完成后视图如图 3-28 所示。 图 3-27 拖拽 View 到根控制器 图 3-28 拖拽完成后视图  打开对象库(Library)是通过菜单 Tools->Library 打开。 我们还需要在图 3-28 视图添加一个 按钮,该按钮是一个特殊的按钮,iOS 提供 了创建该形式按钮的模板,首先要在对象库中找到 Rounded Rect Button 拖拽到视图的左 下角如图 3-29 所示。 图 3-29 拖拽按钮 这 个按钮如何能变成 按钮呢?这需要选中个按钮然后在 通 过 菜 单 Tools->Attributes Inspector 打开按钮检查器,如图 3-30 所示,在图 3-30 中 Type 下拉选项 中选择 Info Light,这样上面的椭圆形按钮就变成 按钮如图 3-31 所示。 图 3-30 属性检查器 图 3-31 Info Light 按钮  在图 3-30 中 Type 下拉选项中有很多种按钮类型,这是开发 iOS 定义好的几个模板按钮。这 些按钮在 iOS 都有这特殊的含义,如果不是真正需要一般情况不用轻易使用。 就是显示 应用信息,一般就是在实用型应用程序中使用。苹果对于这些按钮使用指南和规范。 现在视图翻转的外围工作已经基本完成,我们可以为程序添加代码了,首先添加 RootViewController.h 根控制器 h 文件如 “ 代 码 清单 3-1 Password/Classes/RootViewController.h”所示。 【代码清单3-1】 Password/Classes/RootViewController.h #import @class MainViewController; @class FlipsideViewController; @interface RootViewController : UIViewController { IBOutlet UIButton *infoButton; MainViewController *mainViewController; FlipsideViewController *flipsideViewController; UINavigationBar *flipsideNavigationBar; } @property (nonatomic, retain) UIButton *infoButton; @property (nonatomic, retain) MainViewController *mainViewController; @property (nonatomic, retain) UINavigationBar *flipsideNavigationBar; @property (nonatomic, retain) FlipsideViewController *flipsideViewController; - (IBAction)toggleView; @end 在 h 文件中需要定义成员变量、方法和属性,其中 infoButton 是有关视图中 按钮 对应。  除了在程序中为控件定义输出口外,还需在中把在视图控制器或视图中定义的输出口的控件 成员变量与 nib 文件中的控件连接起来,如图 3-32 所示,infoButton 控件的连接过程是按住 Control 键使用鼠标选中 Root View Controller 拖动连线到 infoButton 控件,然后松开 Control 键选择控件的输出口属性,这是在 MainWindow.xib 的 nib 文件这样连接。如果是一般的 nib 如图 3-33 所示 MainView.xib 文件,其中的控制器的名字是 File’s Owner,因此需要拖动 File’s Owner 到控件就可以了。一般情况下输出口的控件定义在视图控制器中的,但是在本应用中 有些控件是定义在视图中,在这种情况下我们要拖动那个视图对象到控件就可以了,如图 3-34 所示。 图 3-32 根控制器和控件输出口连接 图 3-33 MainView 控制器和控件输出口连接 图 3-34 MainView 和控件输出口连接 mainViewController 属性是主视图控制器,flipsideViewController 属性是背后视图控 制器,flipsideNavigationBar 是在背后视图的导航栏控件。toggleView 方法是个 IBAction (动作)方法,说明它是与控件事件连接在一起的,该方法是对应 按钮的点击事件的。  连接 IBAction 动作事件与输出口方法类似,要用 Interface Builder 把对于的 nib 文件打开, Interface Builder 中把控件和控制器(或视图)连接起来,但是方向与输出口相反,输出口连 线是按住 Control 键拖动鼠标从控制器(或视图)到控件,而 IBAction 动作事件是从控件拖 到控制器(或视图)。 RootViewController 的 h 文件编写完成我们接着编写 RootViewController 的 m 文件如 “代码清单 3-2 Password/Classes/RootViewController.m”所示。 【代码清单3-2】 Password/Classes/RootViewController.m #import "RootViewController.h" #import "MainViewController.h" #import "FlipsideViewController.h" @implementation RootViewController @synthesize infoButton; @synthesize flipsideNavigationBar; @synthesize mainViewController; @synthesize flipsideViewController; - (void)viewDidLoad { MainViewController *viewController = [[MainViewController alloc] initWithNibName:@"MainView" bundle:nil]; self.mainViewController = viewController; [viewController release]; [self.view insertSubview:mainViewController.view belowSubview:infoButton]; } - (void)loadFlipsideViewController { FlipsideViewController *viewController = [[FlipsideViewController alloc] initWithNibName:@"FlipsideView" bundle:nil]; self.flipsideViewController = viewController; [viewController release]; // Set up the navigation bar UINavigationBar *aNavigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)]; aNavigationBar.barStyle = UIBarStyleBlackOpaque; self.flipsideNavigationBar = aNavigationBar; [aNavigationBar release]; UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(toggleView)]; UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Password Generator"]; navigationItem.rightBarButtonItem = buttonItem; [flipsideNavigationBar pushNavigationItem:navigationItem animated:NO]; [navigationItem release]; [buttonItem release]; } - (IBAction)toggleView { /* This method is called when the info or Done button is pressed. It flips the displayed view from the main view to the flipside view and vice-versa. */ if (flipsideViewController == nil) { [self loadFlipsideViewController]; } UIView *mainView = mainViewController.view; UIView *flipsideView = flipsideViewController.view; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:1]; [UIView setAnimationTransition:([mainView superview] ? UIViewAnimationTransitionFlipFromRight : UIViewAnimationTransitionFlipFromLeft) forView:self.view cache:YES]; if ([mainView superview] != nil) { [flipsideViewController viewWillAppear:YES]; [mainViewController viewWillDisappear:YES]; [mainView removeFromSuperview]; [infoButton removeFromSuperview]; [self.view addSubview:flipsideView]; [self.view insertSubview:flipsideNavigationBar aboveSubview:flipsideView]; [mainViewController viewDidDisappear:YES]; [flipsideViewController viewDidAppear:YES]; } else { [mainViewController viewWillAppear:YES]; [flipsideViewController viewWillDisappear:YES]; [flipsideView removeFromSuperview]; [flipsideNavigationBar removeFromSuperview]; [self.view addSubview:mainView]; [self.view insertSubview:infoButton aboveSubview:mainViewController.view]; [flipsideViewController viewDidDisappear:YES]; [mainViewController viewDidAppear:YES]; } [UIView commitAnimations]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview // Release anything that's not essential, such as cached data } - (void)dealloc { [infoButton release]; [flipsideNavigationBar release]; [mainViewController release]; [flipsideViewController release]; [super dealloc]; } @end m 文件中方法很多,值得我们关注有 3 个:viewDidLoad、loadFlipsideViewController 和 toggleView,下分别介绍这个 3 个方法。 viewDidLoad 是视图加载完成还没有显示时候回调的方法,在本应用中该方法是创建 一个主视图控制器,下面语句就是从一个给定 nib 文件名字创建一个控制器的通用方法: MainViewController *viewController = [[MainViewController alloc] initWithNibName:@"MainView" bundle:nil]; 主视图控制器创建完成需要把指针赋值给本类的 mainViewController 属性,因为该属 性设置 retain,这样可以防止内存泄漏,接着再释放刚才创建的 viewController 对象。 self.mainViewController = viewController; [viewController release]; 在 viewDidLoad 该方法最后,通过下面的语句实现,将创建的主视图控制器中的视 图放入到当前视图中作为子视图,并且位于 按钮下面,在 iOS 中视图可以有子视图, 子视图摆放是有顺序的,前后顺序的不同会引起遮挡和影响事件响应。 [self.view insertSubview:mainViewController.view belowSubview:infoButton]; 与– insertSubview:aboveSubview:类似的方法还有:  – addSubview:,直接在前面增加子视图;  – insertSubview:atIndex:,按照索引插入子视图;  – exchangeSubviewAtIndex:withSubviewAtIndex:,交换两个子视 图的顺序,常用于视图切换。 loadFlipsideViewController 方法主要作用就是创建背后视图和上面的导航栏,创建背 后视图控制器方法与主视图控制器方法一样,这里不再多说了。在 iOS 中导航栏比较麻 烦的控件,它涉及到导航栏(Navigation Bar)、导航项目(Navigation Item)和导航按钮 (Bar Button Item),在导航栏中包含导航项目,导航项目包含导航按钮,导航项目可以有 标题,导航按钮可以有左右两个按钮,它们的关系如图 3-35 所示。 图 3-35 导航栏、导航项目和导航按钮的关系 因此我们需要创建这 3 个对象,首先创建导航栏使用下面的语句: UINavigationBar *aNavigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)]; 使用该方法可以通过指定一个矩形轮廓来创建一个导航栏,创建完成导航栏后还要 指定它的样式: aNavigationBar.barStyle = UIBarStyleBlackOpaque; 创建完成导航栏接着创建导航按钮,然后再创建导航项目,再把导航按钮放到导航 项目中。下面语句是创建一个导航按钮: UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(toggleView)]; 在该语句中指定了按钮的样式,同时指定了按钮点击事件触发的时候调用的方法 toggleView。导航项目创建是通过下面语句实现的: UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Password Generator"]; 在该语句中通过指定标题来构造一个导航项目,然后再通过下面的语句把刚才创建 的导航按钮放在到导航项目中,作为它的右按钮。 navigationItem.rightBarButtonItem = buttonItem; 最后还要把导航项目放入到导航栏中,这个目标是通过下面语句实现的,并且要求 动画显示: [flipsideNavigationBar pushNavigationItem:navigationItem animated:NO]; 导航栏维护一个堆栈,通过发出 pushNavigationItem:animated:消息把一个导航项目压 栈,发出 popNavigationItemAnimated:消息把一个导航项目出栈,当前视图显示的是栈顶 的导航项目,所以,压栈可以进入下一级导航项目,而出栈可以返回以上一级导航项目。 toggleView 方法是点击主视图的 按钮和背后视图的 Done 按钮时候触发的方法, 在该方法中实现了两个视图的切换。在该方法中主要涉及到两个知识点,一个是 UIView 切换问题,另一个是 UIView 动画问题。两个问题是相伴而生的,在 UIView 切换的时候 往往伴随着动画发生。下面我们先看看 UIView 切换问题。 在视图切换有很多种方式,可以采用模态视图控制器切换、导航控制器切换和普通 视图控制器切换。在本应用中采用的是普通视图控制器却换,它是定义一个根控制器, 通过根控制器来控制其它视图交替切换。RootViewController 就是视图控制器,其中有自 己的 View 对象(根视图),当主视图加载时候,将主视图作为根视图的子视图放入,当 主视图向背后面翻转的时候,把主视图从根视图中移除掉,把背后视图作为根视图的子 视图放入。当背后面向主视图翻转的时候,把背后视图从根视图中移除掉,把主视图作 为根视图的子视图放入,这样反反复复。  采用模态视图控制器可以参考苹果官方文档 http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ModalVie wControllers/ModalViewControllers.html 。 下面我们看看 toggleView 方法的代码,该方法首先执行下面的语句,实现初始化背 后视图控制器目的,这里有一个判断如果 flipsideViewController 为 nil 时候才去发出 loadFlipsideViewController 消息,可以防止多重加载。 if (flipsideViewController == nil) { [self loadFlipsideViewController]; } 接下来通过下面语句从视图控制器其中获得主视图和背后视图,由于在 viewDidLoad 方法中创建了主视图控制器,在 loadFlipsideViewController 方法中创建了背后视图控制器, 因此在这里获得的视图对象不会是 nil 的。 UIView *mainView = mainViewController.view; UIView *flipsideView = flipsideViewController.view; 在 iOS 中动画有多种形式,这里的视图翻转是属于 UIView 级别动画,UIView 级别 动画必须从[UIView beginAnimations:nil context:NULL]开始到[UIView commitAnimations] 结束。在本应用中与 UIView 级别动画有关代码如下: [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:1]; [UIView setAnimationTransition:([mainView superview] ? UIViewAnimationTransitionFlipFromRight : UIViewAnimationTransitionFlipFromLeft) forView:self.view cache:YES]; … … [UIView commitAnimations]; setAnimationDuration:语句设置动画持续时间,setAnimationTransition: forView: cache: 方法中第一个参数定义动画转变类型,第二个参数是当前视图对象,第三个参数是是否 使用缓冲区。动画转变类型是指动画样式,其取值是 UIViewAnimationTransition 枚举类 型,UIViewAnimationTransition 的成员有:  UIViewAnimationTransitionNone,不指定过渡类型;  UIViewAnimationTransitionFlipFromLeft,指定从左侧翻转;  UIViewAnimationTransitionFlipFromRight,指定从右侧翻转;  UIViewAnimationTransitionCurlUp,指定向上卷起;  UIViewAnimationTransitionCurlDown,指定向下卷起。 虽然,在本应用中没有使用,但是 UIView 级别动画中还有一个重要方法 setAnimationCurve: ,该方法可以设置动画曲线,动画曲线是决定动画进入和退出屏幕的 方式。其它取值是 UIViewAnimationCurve 枚举类型,UIViewAnimationCurve 的成员有:  UIViewAnimationCurveEaseInOut,淡入淡出,开始时候慢,慢 变快,中间最快,然后变慢;  UIViewAnimationCurveEaseIn,淡入,开始时候慢然后越来越 快;  UIViewAnimationCurveEaseOut,淡出,开始快然后越来越慢;  UIViewAnimationCurveLinear,线性匀速,开始和结束是一个 速度。 判断语句 if ([mainView superview] != nil) {}可以判断当前的视图是否为主视图,因为 只有当前视图是主视图情况下主视图才有父视图,否则就是当前视图就是背后视图。如 果当前视图是主视图时候代码如下: [flipsideViewController viewWillAppear:YES]; [mainViewController viewWillDisappear:YES]; [mainView removeFromSuperview]; [infoButton removeFromSuperview]; [self.view addSubview:flipsideView]; [self.view insertSubview:flipsideNavigationBar aboveSubview:flipsideView]; [mainViewController viewDidDisappear:YES]; [flipsideViewController viewDidAppear:YES]; 上面的这段代码就是要实现从主视图向背后面翻转,因此,需要在根视图中移除主 视图[mainView removeFromSuperview] ,同时还移除了 infoButton 对象[infoButton removeFromSuperview],它们两个都是在主视图上显示的。 [self.view addSubview:flipsideView]方法把背后视图添加到当前视图。在通过 [self.view insertSubview:flipsideNavigationBar aboveSubview:flipsideView]语句把导航栏添加到背后 视图上面,使得导航栏与背后视图同是当前视图的子视图,只不过是导航栏在前面。 此外,代码中还用到了控制器中视图出现和消失的几个事件方法:  viewWillAppear:,通知视图控制器,它的视图将要可见;  viewWillDisappear:,通知视图控制器,它的视图将要消失;  viewDidDisappear:,通知视图控制器,它的视图已经消失;  viewDidAppear:,通知视图控制器,它的视图已经可见。 事实上,这几个方法经常在视图控制器中被重写,用于处理视图的不同生命周期中 会触发的事件,它们与 viewDidLoad 和 viewDidUnload 事件类似,它们调用会更加频繁 触发。其中 viewDidLoad 和 viewWillAppear:很类似,viewDidLoad 是视图被创建时候触 发,接着视图变成可见的时候会触发 viewWillAppear:事件,当视图变换到别的视图,视 图从可见变成不可见,再次回来时候,视图从不可见变成可见 viewDidLoad 方法就不会 触发,而 viewWillAppear:事件会触发。 3.3.3 主视图 UI 主视图如图 3-36 所示是 MainView(主视图)设计窗口,我们一步一步介绍一下如何 实现该视图设计和编程。 图 3-36 MainView 视图设计窗口 主视图中的控件进行了编号,视图中的控件内容见表 3-7 所示。 表 3-7MainView 视图中的控件 编号 控件项目 控件类型 1 密码长度标签 UILabel 2 密码长度文本框 UITextField 3 否显示发音标签 UILabel 4 否显示发音开关 UISwitch 5 包括小写标签 UILabel 6 包括小写开关 UISwitch 7 包括大写标签 UILabel 8 包括大写开关 UISwitch 9 包括数字标签 UILabel 10 包括数字开关 UISwitch 11 包括标点符号标签 UILabel 12 包括标点符号开关 UISwitch 13 产生密码按钮 UIButton 14 输出密码文本框 UITextField 15 发音文本框 UITextView 16 发送 E-mail 按钮 UITextField 17 背景图片控件 UIImageView 首先我们需要为工程准备图片,在工程的“Resources”文件夹右键点击菜单 Add-> Existing Files…,弹出添加对话框,选择原作者项目中图片添加到当前工程中。图片添加 到工程中后,可以添加背景图片控件了,选择 MainView.xib 文件双击会用 Interface Builder 打开,然后在菜单 Tool->Library 打开对象库对话框,选择 Image View 把它拖拽到 MainView 设计窗口,如图 3-37 所示。 图 3-37 拖拽 Image View 到主视图设计窗口 然后在菜单 Tools->Attributes Inspector 打开按钮检查器,点中标签 中对话框 的 Image 下拉列表中选择 SafeBack.png 图片,如图 3-38 所示。 图 3-38 选择图片设计窗口 背景图片添加完成之后,我们就可以添加其它的控件了,这个没有先后顺序,从对 象库中拖拽并摆放好就可以了。下面我们添加几个有代表性的,其它的就不再介绍了, 先看密码长度标签如何做的吧,通过对象库拖拽一个 Label 控件到设计视图,然后双击使 其处于编辑状态,输入 Password Length 如图 3-39 所示。再将鼠标点击到其它地方,这样 密码长度标签就添加完成了。 图 3-39 编辑 Label 控件 密码长度文本框是从对象库拖拽一个 Text Field 控件到设计视图如图 3-40 所示。 图 3-40 编辑文本框控件 然后在菜单 Tools->Attributes Inspector 打开检查器,点中标签 打开检查器, 在 Text 项目中输入 8,这可以让文本框显示 8 的初始值,在 Alignment 项目中选择右对齐 按钮 ,如图 3-41 所示。 图 3-41 文本框检查器 其它的控件就不再一一介绍,在视图的下方还有 3 个隐藏控件,这些控件是如何实 现隐藏和显示的呢?在本应用中是通过调整 Alpha(透明度)属性,Alpha 取值范围是 0.0~1.0,0.0 是最弱即完全透明,1.0 是最强即完全不透明。从对象库拖拽一个按钮到设 计视图,双击该按钮,编辑按钮的标签E-Mail Password,然后打开检查器,点中标签 , 找到 Alpha 属性如图 3-42 所示,将划块拖到最左边,就是 0.0 取值 。 图 3-42 Alpha 属性 其它 2 个控件也可以这样设置 Alpha 值。这样整个主视图 UI 部分就完成了。下面我 们介绍主视图代码实现。 3.3.4 主视图代码 在本应用中主视图中使用的视图是 MainView,事实上在很多应用中我们不用为视图 控制器自定义一个视图类的而是直接使用 UIView 基类就可以了。由于本应用是按照旧版 本的 Xcode 模板编写的,我们还是按照旧版本介绍吧。在 Cocoa MVC 设计模式中,视图 中的控件在视图控制器或者视图中都要有对应的控件变量,没有自定义视图类的时候, 这些控件变量是在视图控制器中定义的,有了自定义视图类后,这些控件变量可以在视 图类中定义,本应用就是这样子的。 主 视 图 类是 MainView , 它 的 h 文 件 定 义 请 参 考 “ 代 码 清 单 3-3 Password/Classes/MainView.h”所示。 【代码清单3-3】 Password/Classes/MainView.h #import #import @interface MainView : UIView { IBOutlet UITextField *passwordLength; IBOutlet UITextField *emailAddress; IBOutlet UISwitch *includeLowerCase; IBOutlet UISwitch *includeNumbers; IBOutlet UISwitch *includePunctuation; IBOutlet UISwitch *includeUpperCase; IBOutlet UISwitch *showPhonetics; IBOutlet UITextField *password; IBOutlet UITextView *phoneticPassword; IBOutlet UIButton *emailPasswordButton; } @property (nonatomic, retain) UITextField *emailAddress; @property (nonatomic, retain) UITextView *phoneticPassword; @property (nonatomic, retain) UITextField *passwordLength; @property (nonatomic, retain) UIButton *emailPasswordButton; - (IBAction)setPasssword; - (IBAction)resignResponder; - (IBAction)emailPassword; @end 从“代码清单 3-3 Password/Classes/MainView.h”可以看到定义的控件变量,这需要 注意的是在本应用中这些变量都定义了 IBOutlet(输出口)其实没有这个必要,一个控件 是否定义输出口要看是否需要通过程序修改其属性。此外还定义了 3 个方法,setPasssword 方法是响应 Generate Password 按钮事件,resignResponder 是放弃第一响应者方法,在文 本框等控件中可以输入数据,输入数据过程中会出现键盘,通过文本框放弃第一响应者 后才能关闭键盘。emailPassword 是响应 E-Mail Password 按钮事件把产生的密码通过 E-Mail 发送出去。 主 视 图 类 是 MainView , 它 的 m 文 件 定 义 请 参 考 “ 代 码 清 单 3-4 Password/Classes/MainView.m”所示。 【代码清单3-4】 Password/Classes/MainView.m #import "MainView.h" #define RANDOM_SEED() srandom(time(NULL)) #define RANDOM_INT(__MIN__, __MAX__) ((__MIN__) + random() % ((__MAX__+1) - (__MIN__))) @implementation MainView @synthesize phoneticPassword; @synthesize passwordLength; @synthesize emailPasswordButton; @synthesize emailAddress; - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { } return self; } - (IBAction)setPasssword { [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:1]; [password setAlpha:0.0]; [phoneticPassword setAlpha:0.0]; [emailPasswordButton setAlpha:0.0]; [UIView commitAnimations]; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:1]; [password setAlpha:0.85]; if(showPhonetics.on) {[phoneticPassword setAlpha:0.85];} [emailPasswordButton setAlpha:0.85]; [UIView commitAnimations]; … … } -(IBAction) resignResponder { [password resignFirstResponder]; [passwordLength resignFirstResponder]; } - (BOOL)textFieldShouldReturn:(UITextField *)textField { if (textField == password) { [password resignFirstResponder]; } if (textField == passwordLength) { [passwordLength resignFirstResponder]; } return YES; } -(IBAction) emailPassword { NSString *urlString = @"mailto:?subject=Password%20Generator&body="; urlString = [urlString stringByAppendingString:@"Password:%20"]; urlString = [urlString stringByAppendingString:password.text]; if(showPhonetics.on) { urlString = [urlString stringByAppendingString:@"%20Phonetic:%20"]; urlString = [urlString stringByAppendingString:[phoneticPassword.text stringByReplacingOccurrencesOfString:@" " withString:@"%20"]]; } NSURL* mailURL = [NSURL URLWithString: urlString]; [[UIApplication sharedApplication] openURL: mailURL]; } @end 下面我们分析一下上面的代码,在 m 文件的开始定义了两个宏 RANDOM_SEED 和 RANDOM_INT。RANDOM_SEED 产生随机数种子值,RANDOM_INT 是参数随机数, 其中函数 srandom()、random()和 time()都是 C 语言中的函数。 #define RANDOM_SEED() srandom(time(NULL)) #define RANDOM_INT(__MIN__, __MAX__) ((__MIN__) + random() % ((__MAX__+1) - (__MIN__))) 在主视图控制器 m 文件中最为主要的几个方法是:setPasssword、emailPassword、 resignResponder 和 textFieldShouldReturn:其中前 3 个方法是有关事件处理的,下面我们一 一介绍一下这几个方法。 setPasssword 方法是主要实现了密码的计算和视图中下面 3 个控件的动画显示。关于 如何计算和产生密码,本书就不再介绍了。在本方法里我们重点研究视图中 3 个控件的 动画显示问题,这部分的动画也属于 UIView 级别动画,使用 UIView beginAnimations: 开始 UIView commitAnimations 结束,事实上这是两个动画,先是通过下面的代码将 3 个 控件 Alpha 设置为 0.0,即不可见。 [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:1]; [password setAlpha:0.0]; [phoneticPassword setAlpha:0.0]; [emailPasswordButton setAlpha:0.0]; [UIView commitAnimations]; 然后再通过下面的代码将 3 个控件 Alpha 设置为 0.85,即可见但是还有一点模糊的 效果。这样两个动画分别作用于 3 个控件,如果控件不可见,控件就变成可见,如果控 件已经可见,就先变成不可见,再可见。持续时间都是 1 秒钟。 [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:1]; [password setAlpha:0.85]; if(showPhonetics.on) {[phoneticPassword setAlpha:0.85];} [emailPasswordButton setAlpha:0.85]; [UIView commitAnimations]; emailPassword 方法是将密码通过 E-Mail 发送出去,其中在 iOS 中 E-Mail 发送可以 通过 MFMailComposeViewController 和 MFMailComposeViewControllerDelegate 发送,如 果邮件中没有图片和附件也可以通过本应用采用的简单发送方式: [[UIApplication sharedApplication] openURL: mailURL]; 通过应用程序内部对象的 openURL 方法发送 E-Mail,其中 mailURL 是一个特定格式 的字符串其中,开头是 mailto,其中主题部分 subject,内容部分 body,还有 cc 等参数。 NSString *urlString = @"mailto:?subject=Password%20Generator&body="; iOS 中这个[UIApplication sharedApplication] openURL 方法可以做很多事情,其中可 以:  打开浏览器,格式“http://itunesconnect.apple.com”;  打开 Google 地图,格式 “http://maps.google.com/maps?q=${QUERY_STRING}”, 这是使用 Web 浏览器方式打开地图,其中 q 是查询的参数,例如:经纬度等 信息,在 iOS SDK 中还可以通过本地 API 方式打开地图;  拨打电话,格式“tel://10086”;  发送短信,格式“sms://10086”。  但是要注意的是除了打开浏览器可以在模拟器上打开,其它的几个包括 E-Mail 的发送,都是 不能在模拟器上打开,必须在真机上打开的。 resignResponder 和 textFieldShouldReturn:都是放弃第一响应的,经过测试本应用中的 resignResponder 方法并没有触发,没有实际意义,而 textFieldShouldReturn:方法起到了作 用,该方法是来源于 UITextFieldDelegate 协议中的方法,在 iOS 中 delegate(委托)是一 种设计模式,它通过一个协议(类似于 Java 中的接口)定义,要求它的实现类必须实现 它 的 方 法 , 这 些 方 法 将 在 某 些 事件发生的时候被触发(或回调)。 - (BOOL)textFieldShouldReturn:方法就是在文本框编辑状态时候,点击键盘的回车按钮时候 触发的方法。 事实上我们需要在主视图的 h 文件这样定义 MainView: @interface MainView : UIView {} 而本应用中没有方式定义,这里涉及到 Objective-C 的动态方法调 用问题,与 Java 等面向对象的语言不同的是,Objective-C 不用必须在声明中指定协议, 而直接在 m 文件给出该协议的实现方法也是可以的。 @interface MainView : UIView {} 在 textFieldShouldReturn 方法执行的时候判断当前的文本框是密码还是密码长度,从 而放弃对应文本框的第一响应者。 [password resignFirstResponder]; [passwordLength resignFirstResponder]; 3.3.5 主视图控制器代码 主视图控制器是 MainViewController,由于视图使用的控件都已经在视图 MainView 中定义了,所以在视图控制器 MainViewController 代码很少了,在本应用中还设计了按钮 按下和按钮选择时候的普通和高亮状态效果。这些效果可以在检查器中设定,也可以通 过代码设定,本应用是通过代码设定这些效果。 先看看主视图控制器类 MainViewController,它的 h 文件定义请参考“代码清单 3-5 Password/Classes/MainViewController.h”所示。 【代码清单3-5】 Password/Classes/MainViewController.h #import @interface MainViewController : UIViewController { IBOutlet UIButton *createPassword; IBOutlet UIButton *emailPassword; } @property (nonatomic, retain) UIButton *createPassword; @property (nonatomic, retain) UIButton *emailPassword; @end createPassword 是 Generate Password 对应的控件变量,emailPassword 是 E-Mail Password 对应的控件变量,它们都定义了输出口,这是因为它们都需要在程序中改变控 件状态。 主视图控制器类 MainViewController,它的 m 文件定义请参考“代码清单 3-6 Password/Classes/ MainViewController.m”所示。 【代码清单3-6】 Password/Classes/ MainViewController.m #import "MainViewController.h" #import "MainView.h" @implementation MainViewController @synthesize createPassword; @synthesize emailPassword; - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { } return self; } - (void)viewDidLoad { UIImage *buttonBackground = [[UIImage imageNamed:@"blueButton.png"] stretchableImageWithLeftCapWidth:12.0 topCapHeight:12.0 ]; [createPassword setBackgroundImage:buttonBackground forState:UIControlStateNormal]; [emailPassword setBackgroundImage:buttonBackground forState:UIControlStateNormal]; [createPassword setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [emailPassword setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; UIImage *buttonBackgroundSel = [[UIImage imageNamed:@"whiteButton.png"] stretchableImageWithLeftCapWidth:12.0 topCapHeight:12.0 ]; [createPassword setBackgroundImage:buttonBackgroundSel forState:UIControlStateHighlighted]; [createPassword setBackgroundImage:buttonBackgroundSel forState:UIControlStateSelected]; [emailPassword setBackgroundImage:buttonBackgroundSel forState:UIControlStateHighlighted]; [emailPassword setBackgroundImage:buttonBackgroundSel forState:UIControlStateSelected]; [createPassword setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted]; [createPassword setTitleColor:[UIColor blackColor] forState:UIControlStateSelected]; [emailPassword setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted]; [emailPassword setTitleColor:[UIColor blackColor] forState:UIControlStateSelected]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview // Release anything that's not essential, such as cached data } - (void)dealloc { [emailPassword release]; [createPassword release]; [super dealloc]; } @end 其中 viewDidLoad 方法是我们讨论的重点,其中通过下面的方法定义了一个 UIImage 对象: UIImage *buttonBackground = [[UIImage imageNamed:@"blueButton.png"] stretchableImageWithLeftCapWidth:12.0 topCapHeight:12.0 ]; 该方法是通过拉伸创建一个 UIImage,而边角不拉伸,需要两个参数,第一个是不拉 伸区域和左边框的宽度,第二个参数是不拉伸区域和上边框的宽度。 把这个拉伸的 UIImage 对象作为两个按钮的正常状态时候背景图片: [createPassword setBackgroundImage:buttonBackground forState:UIControlStateNormal]; [emailPassword setBackgroundImage:buttonBackground forState:UIControlStateNormal]; 接 下 来 又 定 义 了 一 个 UIImage 对象,作为按钮其它状态(高亮状态 UIControlStateHighlighted 和选中状态 UIControlStateSelected)时候的背景图片, [createPassword setBackgroundImage:buttonBackgroundSel forState:UIControlStateHighlighted]; [createPassword setBackgroundImage:buttonBackgroundSel forState:UIControlStateSelected]; 然后,又定义了按钮在高亮状态和选中状态时候的文字背景颜色: [createPassword setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted]; [createPassword setTitleColor:[UIColor blackColor] forState:UIControlStateSelected]; 3.3.6 背后视图 UI 背后面视图如图 3-43 所示是 Flipside View(背后视图)设计窗口,我们一步一步介 绍如何实现该视图设计和编程。 图 3-43 Flipside View 视图设计窗口 背后视图中的控件进行了编号,视图中的控件内容见表 3-8 所示。 表 3-8 FlipsideView 视图中的控件 编号 控件项目 控件类型 1 Check out our other applications. UILabel 2 图片控件 UIImageView 3 iFlame is a virtual lighter… UITextView 4 Download UIButton 5 Don't be caught without a light… UITextView  从图 3-42 可以看到这些控件不包含如图 3-44 所示导航栏和 Done,导航栏和 Done 不用在 FlipsideView.xib 文件中设计好,而是通过程序代码动态添加的,代码是在 RootViewController.m 的 loadFlipsideViewController 方法实现的。 图 3-44 Flipside View 视图中的导航栏 我们可以按照表 3-8 一一添加这些控制,需要注意的是 1 号控件是 UILabel,而 3 和 5 号控件是 UITextView,当有很多的文本内容需要显示的时候就要使用 UITextView 控件 而不是 UILabel。这个视图设计过程细节就不再一一介绍了。 3.3.7 背后面视图和视图控制器代码 背后视图主要实现了 2 个功能:导航栏中的 Done 按钮和视图中 Download 按钮,其 中导航栏中的 Done 功能的实现是在 RootViewController.m 类的 toggleView 方法中已经实 现了,而不是在 FlipsideView.m 或 FlipsideViewController.m 中实现的。 Download 按钮是通过浏览器打开在 App Store 上一个 iFlame 应用,该功能是在 FlipsideView.m 中实现的。 我们先看看 FlipsideView.h 代码请参考“代码清单 3-7 Password/Classes/ FlipsideView.h” 所示。 【代码清单3-7】 Password/Classes/ FlipsideView.h #import @interface FlipsideView : UIView { } - (IBAction)openLink; @end 其中的 openLink 方法是响应 Download 按钮事件。FlipsideView.m 代码请参考“代码 清单 3-8 Password/Classes/ FlipsideView.m”所示。 【代码清单3-8】 Password/Classes/ FlipsideView.m #import "FlipsideView.h" @implementation FlipsideView - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { // Initialization code } return self; } - (void)drawRect:(CGRect)rect { // Drawing code } - (void)dealloc { [super dealloc]; } -(IBAction) openLink { // open in Safari //[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://www.apple.com/"]]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware? id=287545019&mt=8"]]; } @end 上面的代码主要的方法是 openLink,通过该方法在 iOS 浏览器中打开一个网页。其 中使用[UIApplication sharedApplication] openURL:方法,该方法介绍 E-Mail 发送功能时候 已经介绍了,它可以打开多种应用程序。 本章小结 通过对本章的学习,读者可以掌握密码生成应用程序(Amuck Password Generator) 应用开发过程,重点是一些基本控件设计和使用过程,这些控件包括:UIView、UIButton 和 UILabel 等,学会使用 Interface Builder,在 Interface Builder 设计这些控件,设定它们 的属性。 读者还可以了解 MVC 设计模式、实用型应用程序模板等概念,Cocoa 和 Cocoa Touch 中 MVC 设计模式最为重要的设计模式,只有能够真正的理解好 MVC 设计模式,才能做 好 iOS 开发,才能理解 nib 文件、视图和视图控制器这些概念。UIView 级别动画是 iOS 比较简单但很常用的动画,UIView 级别动画必须放在[UIView beginAnimations:nil context:NULL]和[UIView commitAnimations]语句之间,其中包括了设定动画持续时间、 动画转变类型和动画曲线等动画属性的设定。 此外,读者还可以掌握[UIApplication sharedApplication] openURL:方法的使用,iOS 中这个[UIApplication sharedApplication] openURL 方法可以做很多事情,其中包括:打开 浏览器、打开 Google 地图、拨打电话、发送短信和发送 Email 等等。

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 5 金币 [ 分享文档获得金币 ] 3 人已下载

下载文档

相关文档