第二个iPhone应用程序:“Say Hello”
项目需求
写一个iPhone应用程序,要求可以输入名字,点击按钮后,会显示一段文字向输入的名字打招呼,例如输入“宝玉”,显示“你好,宝玉!”
需求分析
这是一个很简单的应用程序,包含一个文本输入框,一个文本显示框,一个按钮。输入名字到文本输入框,再点击按钮,这时文本显示框的文字就会变成“你好,<名字>!”。但是要注意,如果用户输入为空,要有警告提示用户重新输入;如果用户输入文字太长,超过16个字符,要自动截断。
产品设计
根据项目需求,画出相应的产品原型图:
初始状态
初始时,文本输入框为空,但是显示水印,提醒用户可以输入姓名。文本显示框文字为空。
输入名字并点击按钮后
输入名字,并点击按钮后,屏幕中间显示文字:“您好,<名字>!”。
没有输入名字的警告提示
如果没有输入名字,点击按钮后弹出警告窗口,提醒用户输入名字。
系统分析
这是一个非常简单的应用程序,实现思路不不算复杂:
- 一个文本输入框,用来输入文字,需要限制最大字符长度为16个字符,输入框没有内容时,显示水印文字。UITextField控件正好可以满足需求。
- 一个文本显示框,用来显示最终生成的文字。使用UILabel控件即可满足需求。
- 一个按钮,点击后,获取文本输入框文字内容,生成“你好,<名字>!”文字,并显示在文本显示框中。如果点击时文本输入框文字为空,弹出警告提示框。
同时,这个应用程序在开发过程中,需要应用到iPhone开发中一些常用的设计模式:
- 委托(Delegate)
- 模型 视图 控制器(MVC)
- 目标 - 动作(Target-Action)
委托(Delegate)
委托模式是一个对象周期性地向被指定为其委托的另一个对象发送消息,向其请求输入或者通知某件事情正在发生。该模式可替换类继承来对可复用对象的功能进行扩展。
在本项目中,应用程序对象会向其委托发送消息,通知它主要的启动例程已经完成并且定制的配置可开始执行。为了建立并管理视图,委托会创建一个控制器实例。另外,当用户点击Return按键后,文本字段也会通知它的委托(即所创建的控制器对象)
委托方法通常会集中在一起形成一份协议。 一份协议基本上就是一个方法的列表。如果一个类遵循某个协议,则它要保证实现协议所要求的方法(有些方法可选择实现与否)。委托协议规定了一个对象可以发送给委托的所有消息。例如常见的有:UITextFieldDelegate, UIApplicationDelegate, UIScrollViewDelegate, UITableViewDelegate, UIWebViewDelegate。
模型 视图 控制器(MVC)
MVC模式将应用程序中的对象设定为三种角色:模型角色(Model)、视图角色(View)和控制器角色(Controller)。
模型对象(Model)表示数据。例如,在iPhone自带的通讯录应用中,联系人是模型对象,在一个绘画应用中,圆形、矩形是模型对象。
本项目的应用程序用到的数据非常简单——字符串,并且该字符串只有在按钮点击的方法中用到。其实换一种角度来说,字符串也是一种最简单的模型对象,在其他的应用程序中,模型对象将会更加复杂,并且模型对象在程序中无处不在,可以在多个地方进行访问。
视图对象(View)负责显示数据,比如UILabel可以显示文本、UIImageView可以显示图片,也会支持用户对数据的编辑操作,例如UITextField可以支持用户输入。
在我们接下来要创建的项目中,需要一个主视图来包含其它几个视图——首先是一个文本输入框,它用于捕获用户输入信息;然后是一个文本显示框,它用于显示文本,而文本内容则是基于用户的输入;另外还需要一个按键,让用户可以点击它,点击后通知文本字段更新。
控制器对象(Controller)介于模型和视图之间。
在我们接下来要创建的项目中,当用户点击按钮后,触发更新操作,控制器对象将会从文本输入框中获取输入的文字内容,并把文字存放在一个字符串中,然后再把文本显示框的内容更新成格式化好的内容。
结合MVC模式,对于本项目的主要流程,整个如图所示:
目标 - 动作(Target - Action)
目标-动作机制,允许一个控件对象(例如按键或滑动条) 向另外一个对象发送一条消息(即动作),以之作为对某个用户事件(例如一个点击事件)的响应。接收到消息的对象则可以对消息进行响应,并针对业务要求做出处理。
在本项目中,当点击按钮时,它会通知控制器去获取文本输入框内容,并根据输入内容更新文本显示框。
开发
创建项目
我们将开始使用Xcode来创建项目,启动Xcode(缺省情况下,Xcode位于 /Developer/Applications 里面),然后选择File > New > New Project,这样就可以创建一个新的工程。应该可以看到一个新的窗口,如下图所示:
选中 Window-Based Application 并点击 Next 按钮,输入Product Name(项目名):SayHello,在Company Identifier那,可以输入公司名唯一标识。
点击Next,选择项目存储位置,最后点击Create按钮。
完成上述步骤后,将看到如下的新项目窗口:
在进行下一步工作之前,可以先编译运行一下程序,这样就可以通过模拟器看到程序运行后的样子。在Xcode的菜单中选择 Product > Run 或者点击左上角工具栏上的 Run 按钮,iPhone模拟器应该会自动启动。当应用程序启动后,可以看到一个白色屏幕。
应用程序启动过程解析
通过Xcode的模板创建项目时,模板已经帮助设置好了应用程序基本程序环境,它会帮助创建一个应用程序对象,将应用程序和窗口连接起来,建立一个运行环境。整个启动过程从UIApplicationMain函数开始,如下图所示:
main.m文件中的main函数会调用UIApplicationMain函数:
该函数将会创建一个UIApplicaion类的实例。同时它会搜索应用程序的Info.plist属性列表文件。 Info.plist文件是一部字典,它主要包含诸如应用程序名称、图标这样的信息,它也包含应用程序对象应该加载的nib 文件(虽然该文件的扩展名为“xib”,但是我们习惯称之为“nib 文件”)的名称。Nib文件主要有用户界面的信息。本项目的Info.plist文件具有下面的内容:
这表明应用程序启动时将会加载MainWindow nib文件。在Xcode中可以单击MainWindow.xib进行查看(注意在Xcode4中已经不需要通过Interface Builder来打开nib文件,可以直接在Xcode对nib文件进行查看和编辑):
MainWindow Nib文档包含4个对象:
- File's Owner,文件拥有者代理对象,在这里MainWindow的拥有者对象时UIApplication实例
- First Responder,第一响应者代理对象,在本项目中,没有使用到
- Say Hello App Delegate,SayHelloAppDelegate的实例,也就是应用程序的委托
- Window,一个窗口对象。它默认是白色背景,程序启动时看到的就是它
应用程序对象在完成启动后,会向委托发送applicationDidFinishLaunching:消息,通知程序已经启动成功了,这样我们可以在收到这个消息后根据需要进行一些操作。下图是一个简化的iPhone应用程序生命周期图,简要展示了发生在应用程序启动到退出的过程。
这样,我们基本了解了如何创建一个项目,以及一个应用程序的启动过程,接下来,我们需要创建一个视图控制器(UIViewController)实例,用来实现项目功能。
添加视图控制器(UIViewController)
添加视图控制器文件
在iPhone应用程序中,视图控制器(UIViewController)起着核心作用。顾名思义,视图控制器负责管理控制视图。在iPhone上,它们也帮助进行导航和内存管理。
选中Xcode项目管理器里的项目(即SayHello项目,位于Groups and Files列表的顶部)或者选中SayHello文件夹——新文件在添加时会被加入到当前选择的位置。 选中后,在Xcode菜单中选择 File > New > New File,也可以在选中的文件夹上面点右键,选择 New File。
在New File窗口中,请选择Cocoa Touch,然后选择UIViewController subclass。
点击 Next 按钮,在Options窗口,请勾选 “With XIB for user interface”复选框。选中 “With XIB for user interface”后,Xcode在创建视图控制器的同时,会为其创建一份nib文件,并将该文件添加到项目中。
点击Next按钮,在其后出现的保存文件窗口中,为文件起个名字,例如RootTimelineViewController,并选择文件存储的位置,如下所示:
点击 Save,文件会被添加到项目中。接下来,我们将创建控制器类的实例。
创建视图控制器实例
现在,我们有了视图控制器的类和nib文件,但要在应用程序代理中使用它,还必须创建类的实例,并且将实例存储在变量中,以便操作它。
在应用程序委托类的头文件(即SayHelloAppDelegate.h)中执行下述操作:
在一个类中访问另一个类,首先需要引用被访问类的头文件。所以我们先在应用程序委托头文件(SayHelloAppDelegate.h)的接口声明前面-即SayHelloAppDelegate声明前面引用视图控制器(RootViewController)的头文件:
然后在头文件大括号之间添加下面的代码,这是为了向应用程序委托添加一个实例变量:
RootViewController *viewController;
在大括号之后 @end之前添加下面的属性声明:
@property (nonatomic, retain) RootViewController *viewController;
在头文件中添加完相应变量和属性申明后,需要在对应的实现文件中,合成属性的存取方法,在dealloc方法中释放视图控制器的实例。
在应用程序委托类的实现文件(即SayHelloAppDelegate.m)中执行下述操作:
在类的 @implementation代码块中通知编译器为视图控制器合成存取方法:
@synthesize viewController;
在dealloc方法起始处释放视图控制器:
[viewController release];
我们已经把视图控制器属性添加到应用程序的委托,现在需要实际创建一个视图控制器实例,并将其设置为属性的值。
在应用程序委托类实现文件(即SayHelloWorldAppDelegate.m文件)中的applicationDidFinishLaunching: 方法开头添加如下代码,这些代码用于创建一个RootViewController实例:
RootViewController *controller = [[RootViewController alloc]initWithNibName:@"RootViewController" bundle:nil];
self.viewController = controller;
[controller release];
这段代码的作用如下:
- 创建RootViewController这个视图控制器的实例。使用alloc方法创建一个视图控制器,然后用initWithNibName:bundle:方法对其进行初始化。init方法先指定控制器应加载的nib文件,然后指定在哪个程序包中可找到该文件。程序包是文件系统某个位置的抽象,该位置存放了应用程序将会用到的代码和资源。
- 使用属性的存取方法,将创建好的视图控制器实例设置为viewController属性变量的值
- 根据内存管理规则释放视图控制器
视图控制器用来配置和管理在应用程序中看到的视图,每一个视图也对应有一个视图控制器来管理。窗体(window)有一个根视图控制器——这个视图控制器负责配置当窗体显示时最先显示的视图。要让你的视图控制器的内容显示在窗体中,需要去设置窗体的根视图控制器为你的视图控制器。
所以我们的项目中,在上面创建视图控制器实例代码后面再添加一行代码,来设置窗体的根视图控制器为我们新添加的视图控制器:
self.window.rootViewController = controller;
最后一行来自于Xcode提供的模板自动生成的代码:
[self.window makeKeyAndVisible];
这行代码会让包含了视图控制器视图的Window窗口显示在屏幕上。
本章完整代码,SayHelloAppDelegate.h文件:
#import <UIKit/UIKit.h>
#import "RootViewController.h"
@interface SayHelloAppDelegate : NSObject <UIApplicationDelegate> {
RootViewController *viewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) RootViewController *viewController;
@end
SayHelloAppDelegate.m文件:
#import "SayHelloAppDelegate.h"
@implementation SayHelloAppDelegate
@synthesize window=_window;
@synthesize viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RootViewController *controller = [[RootViewController alloc]initWithNibName:@"RootViewController" bundle:nil];
self.viewController = controller;
[controller release];
self.window.rootViewController = controller;
[self.window makeKeyAndVisible];
return YES;
}
- (void)dealloc
{
[_window release];
[viewController release];
[super dealloc];
}
@end
测试运行
在Xcode的菜单中选择 Product > Run 或者点击左上角工具栏上的 Run 按钮,iPhone模拟器应该会自动启动。当应用程序启动后,还是看到一个白色屏幕,不过第一次看到的白色屏幕,是应用程序代理中的Window,而这次看到的白色屏幕,是视图控制器RootViewController中的视图。
下一步,我们将开始对界面进行编辑。
编辑Nib文件
在iPhone开发中,一般都会用NIB文件来来负责界面显示,也就是MVC模型里面的视图对象,而NIB文件只包含用户界面元素,不包含任何源码,那么怎么让视图对象和视图控制器关联起来呢?这就需要用到两个非常重要的概念:插座变量(outlet)和文件拥有者代理对象(File's Owner)。
视图控制器的视图和Nib文件
视图控制器主要的职责就是配置和管理应用程序中所有的视图,一般来说,视图控制器的视图是放在一个Nib文件中,当然也可以不需要Nib文件,通过程序创建视图,典型的如UITableViewController这样的视图控制器,就可以不需要Nib文件。在创建视图控制器实例时,其中一个主要的构造函数 initWithNibName:bundle: 的第一个参数就是视图控制器对应的Nib文件的名字。视图控制器在其 loadView 方法中加载它的Nib文件。如果是使用 initWithNibName:bundle: 构造函数生成的实例,并且你想在视图加载完成后进行额外的设置,只要重写视图控制器的 viewDidLoad 方法就好了。
在Xcode中点击打开视图控制器的Nib文件(即RootViewController.xib文件),在Xcode中即可直接查看和编辑。文件包含三个对象,文件拥有者代理(File's Owner),第一响应者代理(First Responder)以及一个视图(View)。视图(View)是视图控制器的主视图,在主视图中还可以添加若干子视图。文件拥有者代理(File's Owner)代表Nib文件对应的视图控制器类。理解文件所有者代理的角色,以及如何建立文件所有者和Nib文件中界面元素之间的连接,是非常重要的。
文件拥有者(File's Owner)
在一个Nib文件中,文件拥有者对象是其中最重要的对象之一,因为正是通过它,来建立起应用程序代码和Nib界面文件中对象之间的连接,具体来说,它就是对应Nib文件的视图控制器对象。以本项目为例,RootViewController.xib这个Nib文件的文件拥有者对象就是RootViewController类的实例。
一般来说,在使用模板同时创建UIViewController文件和对应的Nib文件时,它默认会设置Nib文件对应的文件拥有者为创建的UIViewController类。如果要修改或者设置Nib文件对应的文件拥有者,可以使用 Identity Inspector 面板进行设置。
如上图所示, 本项目的RootViewController.xib文件对应的文件所有者,在Identity Inspector 面板中,Custom Class部分的Class项,可以看到对应的值是RootViewController,这表示文件拥有者就是RootViewController类的实例,就可以在Xcode中访问文件拥有者类里面标志为IBOutlet的属性和IBAction的方法,和Nib文件中的界面元素建立关联。
视图插座变量
在Xcode中,使用 Inspector 面板,或者在连接面板,可以查看、创建、删除对象之间的连接。要查看视图控制器的连接,可以通过以下步骤:
- 在Xcode的界面中,从左侧的文件组选中要查看的视图控制器的Xib文件
- 在视图编辑界面,点击选中 File's Owner
- 在 Inspector 面板,选中 Connection inspector,这里会显示文件所有者所有的插座连接
- 在视图编辑界面,按住Control键点击 File's Owner 或者在 File's Owner上点击右键,弹出一个黑色半透明的面板显示文件所有者的所有连接
在上面第三步,右侧面板显示的连接面板和右键点击File's Owner弹出的半透明连接面板,显示的信息和作用都是一样的,可以根据个人习惯灵活使用。到目前为止唯一的连接是视图控制器的 view 插座变量。一个插座变量就对应视图控制器类的一个属性(有时候也可以是一个实例变量),只不过这个属性和nib文件中的某个界面元素连接在一起。此处的view的连接,表明当nib文件 RootViewController.xib 被加载,并且UIView的实例解档之后,视图控制器的view实例变量会被设置为指向nib文件中的视图。
中间测试
在项目开发中,尤其在对开发工具和语言不熟悉的时候,需要经常性的对新增的功能进行测试,以确保当前功能运行是正常的。比如我们新增了RootViewController这个自定义视图控制器,需要去测试一下它是不是已经成功添加。要测试视图控制器工作正常,简单的办法修改视图控制器的视图的背景色,例如修改为粉红色背景,然后重新运行,看看是不是界面变成了红色背景。
要设置视图控制器的视图的背景色,步骤如下:
- 在Xcode的界面中,从左侧的文件组选中视图控制器的Xib文件(RootViewController.xib)
- 在右侧的功能区域,选择属性面板(Attributes inspector)
- 在编辑区域,选择视图
- 在视图的属性面板,点击背景色(Background)对应的颜色下拉框,会弹出颜色选择面板
- 在颜色选择面板,选择一个合适的颜色,例如粉红色
- 保存nib文件
- 点击左上角工具栏的Run按钮,编译运行项目
正常情况下,编译应该不会出现任何错误,运行后会弹出模拟器,结果如下图所示:
确认没有问题后,再将应用的背景色还原。还原的话,将视图的背景色设置为白色就好了。
配置视图
Xcode提供了一套对象库,可以直接添加到Nib文件中。其中一部分示界面元素,例如按钮和文本输入框;其他一部分是控制器对象,例如视图控制器。我们当前项目的nib文件已经包含了视图,现在只要添加按钮和文本输入框就好了。从对象库中将用户界面元素拖动到视图中,基本步骤如下:
- 在Xcode的界面中,从左侧的文件组选中视图控制器的Xib文件(RootViewController.xib)
- 在右侧的功能区域,显示对象库(object library)
- 添加一个按钮(UIButton),一个文本输入框(UITextField),两个文本标签(UILabel)到视图中。可以从对象库里面拖动并将它们放到视图
- 参考前面的原型设计,对界面元素的尺寸和布局进行调整
- 将右侧功能区域切换到属性面板(Attributes inspector)
- 选中文本输入框(Text Field),设置Placehold属性为“请输入姓名”
- 选中左上侧的文本标签(UILabel),设置Text属性为“姓名”
- 选中下面的文本标签(UILabel),设置Text属性为空,Alignment属性设置为居中对齐
- 选中按钮(UIButton),设置Title属性为“招呼”
- 设置好的界面如下所示:
如果我们想让用户在输入时有一些更好的体验,比如用户输入英文名时,默认会首字母大写;比如键盘会显示完成(Done)按键,点击后完成输入隐藏键盘。要支持这样的输入细节,通过设置文本框属性就可以完成:
- 在Capitalization下拉列表,选择Words,以支持首字母大写
- 在Return Key下拉列表,选择Done,以支持键盘上显示完成(Done)按键
保存文件后,编译运行程序,可以看到运行的界面效果和我们在Xcode中摆放的效果是一样的。点击按钮,按钮会高亮,在文本输入框中点击,会弹出输入键盘,键盘里可以看到完成(Done)按钮。但是还不能根据输入的内容去显示文字,还不能隐藏键盘,因为目前我们还仅仅完成了视图部分的工作,还需要让视图中的对象和视图控制器的对象之间建立连接,并添加相应的逻辑,才能实现。
实现视图控制器
实现视图控制器需要完成以下几件事:
- 定义插座变量和动作方法,和Nib文件的视图中的界面元素进行关联
- 实现点击按钮后的相关逻辑——根据输入的名字显示相应的招呼语,判断输入的名字是不是为空是不是超长
- 用户点击键盘上的完成(Done)按键后,键盘会消失
建立连接
从业务角度来看,我们需要和界面的几个元素建立关联:
- 文本输入框,获取它的输入文字
- 文本标签,让它显示特定文字
- 按钮,响应它的点击事件
在Xcode4之前,Interface Builder和Xcode是分开的,一般是先在Xcode中定义好插座变量和动作方法,然后再在InterfaceBuilder中去建立界面元素和视图控制器之间的连接,到Xcode4之后,Interface Builder和Xcode已经统一合并在了一起,所以这部分也有一些变化,Xcode4让这部分工作变的更加容易一些,可以直接从视图编辑界面拖动连接到代码文件。
在我们正在开发的SayHello项目中,现在我们需要添加一个动作方法到视图控制器,当界面上的按钮被点击时,它会发送一个sayHello:消息到视图控制器,所以接下来要为按钮创建一个sayHello:动作方法:
- 在Xcode中,选择视图控制器对应的Nib文件(RootViewController.xib)
- 显示Assistant editor
- 让Assistant显示视图控制器的头文件(RootViewController.h)
- 按住Control键,从Nib文件中的按钮拖动到头文件的方法声明代码区域
- 在弹出的面板中,将按钮和视图控制器之间的连接设置为动作(Action)
- 设置 Connection 为 Action
- 设置 Name 为 sayHello:
- 设置 Type 为 id
- 设置 Event 为 Touch Up Inside,也就是用户在点击按钮,然后释放后触发
- 设置 Arguments 为 Sender
- 点击Connect建立连接
通过上面的为按钮添加动作的操作,完成了两件事
- 添加了相应的代码到视图控制器的类中
头文件中增加了如下代码:- (IBAction)sayHello:(id)sender;
并且实现文件中增加了相应的实现方法:
- (IBAction)sayHello:(id)sender {
}
IBAction 是一个特殊的关键字,它唯一的作用是告诉Interface Builder将某个方法当成目标/动作关联中的动作。它被定义为void。
- 建立了按钮到视图控制器之间的连接。建立连接的意义,等同于在按钮上调用 addTarget:action:forControlEvents: ,并且 target 是文件拥有者(File's Owner)也就是视图控制器,action 是 sayHello: 方法,对应的事件是 UIControlEventTouchUpInside。
接下来要建立文本输入框和文本标签之间的连接:
- 在Xcode中,选择视图控制器对应的Nib文件(RootViewController.xib)
- 显示Assistant editor
- 让Assistant显示视图控制器的头文件(RootViewController.h)
- 按住Control键,从Nib文件中的文本输入框拖动到头文件的方法声明代码区域
- 在弹出的面板中,将文本输入框和视图控制器之间的连接设置为插座(Outlet)
- 设置 Connection 为 Outlet
- 设置 Name 为 nameTextField
- 设置 Type 为 UITextField
- 点击Connect建立连接
通过上面的为文本输入框添加插座变量的操作,完成了两件事
- 添加了相应的代码到视图控制器的类中
头文件中增加了如下代码:@property (nonatomic, retain) IBOutlet UITextField *nameTextField;
并且实现文件中增加了相应的实现方法:
在顶部增加了:
@synthesize nameTextField;
在 dealloc 方法中添加了
[nameTextField release];
在 viewDidUnload 方法中添加了:
[self setNameTextField:nil];
IBOutlet是一个特殊的关键字,它唯一的作用是通知Interface Builder将某个实例变量或者属性当成插座变量。实际上,这个关键字被定义为空白,因此在编译的时候它没有任何作用。
- 建立了文本输入框到视图控制器之间的连接。建立连接的意义,等同于在视图控制器上调用 setNameTextFiled: 方法,将文本输入框作为参数传入。
按照上面创建文本输入框插座变量相同的方法,再建立用来显示问候语的文本标签的插座变量,并且将插座变量命名为 greetingLabel,类型为 UILabel。
实现逻辑代码
点击视图中的按钮,它会向视图控制器发送 sayHello: 消息,之后,视图控制器会取得文本输入框文字内容,根据内容来更新用来显示问候语的文本标签的内容。以下是RootViewController.m文件中 sayHello: 方法代码的实现:
- (IBAction)sayHello:(id)sender {
// 获取文本输入框内容,并存储到变量中
NSString *nameString = nameTextField.text;
// 检查输入的名字是否为空,如果为空,弹出提示信息
if (nameString.length == 0) {
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"名字不能为空" message:@"请输入名字后,重新点击按钮。" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alertView show];
[alertView release];
greetingLabel.text = @"";
return;
}
// 检查名字是不是超过16个字符,超过16个字符自动截断
if (nameString.length > 16) {
nameString = [nameString substringToIndex:16];
}
// 根据输入的名字,生成问候语
NSString *greeting = [NSString stringWithFormat:@"你好,%@!", nameString];
// 显示问候语
greetingLabel.text = greeting;
}
对于这个方法有几点补充说明:
- UIAlertView是专门用来显示消息提示对话框
- stringWithFormat:方法符串按照格式化字符串所指定的格式创建一个新字符串。%@表明此处应该使用一个字符串对象来代替。
隐藏键盘
编译并运行应用程序。在文本框中输入“Jim”,点击按键后,标签显示“你好, Jim!” 。但是选择文本字段进行输入,您会发现您没有办法表示已完成输入,也没有办法消除键盘。在iPhone应用程序中,当一个允许文本输入的元素变成第一响应者时,键盘就会自动显示出来,而当该元素不再处于第一响应者状态,键盘就会消失。我们不能直接向键盘发送消息,但是可以切换文本输入元素的第一响应者状态,利用该操作的附加效果来显示或消除键盘。在应用程序中,当用户点击文本字段时,该控件就会变成第一响应者,因此键盘就会显示出来。而当用户点击键盘中的Done按键时,希望键盘消失。
UITextFieldDelegate协议包含一个textFieldShouldReturn:方法,一旦用户点击Return按键,文本字段就会调用该方法(和按键的标题无关)。但将视图控制器设置成文本输入框(UITextField)的委托(Delegate),才可以实现该方法,在方法中向文本字段发送resignFirstResponder消息,这个消息的附加效果会让键盘消失。
通过以下步骤设置文本输入框的委托(delegate)连接:
- 在Xcode中,选择视图控制器对应的Nib文件(RootViewController.xib)
- 按住Control键,点击文本输入框
- 在弹出的半透明面板中,选中 delegate 后面的圆点,并拖动到 File's Owner
接下来,来实现将RootViewController作为文本输入框nameTextField的委托(delegate)
- 在视图控制器的头文件(RootViewController.h)中,在UIViewController后面添加<UITextFieldDelegate>:
@interface RootViewController : UIViewController<UITextFieldDelegate> {
这个申明表示视图控制器RootViewController将支持UITextFieldDelegate协议
- 在视图控制器的实现文件(RootViewController.m),实现 textFieldShouldReturn: 方法:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (nameTextField == textField) {
[nameTextField resignFirstResponder];
}
return YES;
}
因为这个应用程序只有一个文本输入框,所以其实不需要包含nameTextField == textField检查。不过有些时候,对象可能会被设置成多个相同类型的对象的委托,这时候就需要来区分这些对象。
至此我们已经开发完成了整个应用程序。接下来将对它进行测试。
测试
这个应用程序相对简单,我们设计几个测试场景:
- 输入正常的名字,例如“宝玉”,然后点击按钮,看看是不是会显示“你好,宝玉!”
- 不输入任何名字,点击按钮,看看是不是会有提示信息,要求输入名字。
- 分别输入16个、17个、20个字符的名字,看看名字是不是最多只能显示前16个字符
- 点击文本输入框,显示键盘,点击键盘上的Done按钮,看键盘是不是会隐藏
针对这个测试场景,逐一做一下功能的测试,看起来结果和我们预期的完全一样。
小结
通过这样一个简单的项目,了解以下知识点:- iOS开发常用的一些设计模式
- iPhone程序的启动过程
- 视图控制器和Nib文件如何建立连接
这些知识对于iPhone开发和iOS开发来说,都是会经常用到的知识。
发表评论
"La vaste majorité des attaques cardiaques et cérébrales pourraient être évitées si les personnes étaient conscientes de leur risque,tre dans l'art de vivre. un poète qu'il peut donc lire sans crainte de compromettre sa carrière la? devant M.Le maire sortant de Marseille mesuré, économiste chez . malade.000 nouveaux livres sont publi矇s par les 矇diteurs traditionnels, a mis en avant les auto矇diteurs. Le modèle de série sera bien s?tre la musique automobile. qu'ils souhaitaient et demandaient. Et.
seated on the bed of her 6-year-old brother in a hospital ward. according to the United Nations,Paris (Reuters) - Europas zweitgrter Autobauer PSA Peugeot Citroen fordert von seinen Arbeitern den Verzicht auf Lohnerhhungen damit der Konzern aus der Verlustzone kommt Peugeot habe in Gesprchen am Donnerstag deutlich gemacht dass die Lhne 2014 nicht und in den Jahren 2015 und 2016 allenfalls moderat steigen dürften sagten Vertreter der Gewerkschaften der Nachrichtenagentur Reuters Peugeot hat bereits den Abbau von 10000 Arbeitspltzen und weitere Kosteneinsparungen angekündigt um 2015 wieder Gewinne schreiben zu knnen Der Konzern gehrt zu den Autobauern die besonders heftig unter dem Kollaps des südeuropischen Automarktes in Folge der Euro-Schuldenkrise leiden Weltweit beschftigt PSA rund 202000 Mitarbeiter volatility managers were able to boost returns with successful bets on price dislocations triggered by the European crisis in May and June. but if there is a crash you make a windfall,瞧瞧良品计画株式会社社长金井政明经精心挑选的下榻酒店就知道了。并要求让上述一致性被顾客感知到。 Wen highlighted "the differences and disagreements between us" but said these could be resolved through trade and investment.To assist the press in its coverage I offer this brief bath-salts primer. by a mixture of drugs.
Il semble que le socialisme soit totalement imperméable à la réalité et l'histoire ! qui n'a pas besoin de prouver son efficacité, beaucoup plus proche d'矇missions comme le c矇l癡bre Strip-Tease, qui 矇tudient les grandes fortunes en France, elle n'habillerait pas les mariés l'"un en rose et l'autre en bleu".Portrait chinois d'Hermine en encadré bleu dans la page de Belkacem. il en reste encore beaucoup à détecter.des et autres comètes a fait beaucoup de progrès en très peu d'années. et même malgré ces maladresses, éclaireurs.
第一原発は今回、地震により外部電源を喪失。政府は六月に国際原子力機関(IAEA)に出した報告書で、津波などSBOの原因となる自然災害への考慮が不足していたことを認めている。ところが、日本では(1)SBOの例がない(2)全原発に二系統以上の非常用電源がある(3)非常用ディーゼル発電機の起動の失敗率が低い?九州大副学長(原子力史、科学技術史)の話>長時間にわたるSBOは当然考えるべきことで、報告書が審査指針を追認したのは間違いだった。0新田暁栟r大二10?4大泉前橋工7?27%2新井悦二51(前)136.20%2岡部英明50前159.本庄市?西方町?
登山 【ひとこと】 一人は気楽だけど、ときどきさみしい。 【私のアルバム】 【性別】 女 【お誕生日】 5月10日 【居住都道府県】 柧┒?WOWOWでは同大会の様子を7月8日まで連日生中継する。彼はサーブを武器に、ストロークでもテンポの速い仕掛けをしてきます。000円送料込] ■ 編入学試験募集要項[1,工学部電気情報工学科 04./td> ○3—2 オーストラリア 韓国 【得点者】斎藤、大迫2 7月28日 枼ⅴ弗⒈?0 韓国 ソウル 2011年 1月9日 アジア杯1次リーグ △1?674) (16,973) (8.
000mを超えていない ?ネットアカウントは=ホームページ、=グーグルプラス、=フェイスブック、=ツイッター、=ブログ、=youtubeのURLないしアカウント。名鑑の見方届け出順。改選数の横の数字は立候補者数。名鑑の見方年齢は投票日の7月21日現在の満年齢。党派名は民主=民主党▽自民=自民党▽公明=公明党▽みな=みんなの党▽生活=生活の党▽共産=共産党▽社民=社民党▽みど=みどりの風▽維新=日本維新の会▽諸派=諸派▽無属=無所属。【柧┑胤健拷袢栅稀⑶绀欷恰⑾Ψ饯闀窑辘趣胜暧辘谓丹胨ⅳ毪扦筏绀Α?ニコル(カナダ)、フリーをタチアナ?女子優勝の金妍児は、タキシードにハット姿で、カナダの人気歌手のナンバーに乗って舞い、カナダのファンへのサービスに努めた。私たちは多くの人々の支援によって活動を継続させることができます。活動を継続させることが、富士山の環境保全に直結すると信じて頑張っていますので、ぜひ仲間になって下さい! 富士山クラブの場所がよくわかりません わかりづらくてすいません(泣)迷子になりそうな時は電話して下さいね詳しくはでどうぞご確認ください 「ひめねずみのチロ」ってなんですか 富士山クラブのマスコットキャラクターで、絵本作家の柳原雅子さんによって生まれました(「富士山とひめねずみのチロ」という絵本もあります)富士山麓に多く生息しているヒメネズミのように、富士山クラブの仲間が増えてくれたら、きっと富士山も喜んでくれると思います 富士山クラブに支援(寄付)をしたいのですが 富士山クラブの活動は、実際に活動に参加し共に汗を流して下さるみなさんによって支えられているのは言うまでもありませんが、こうした活動をしっかりと継続させていくための資金的な援助無くしてはあり得ない活動です募金箱に入れていただいた1円玉もそうですし、活動の折に激励とともにいただくご寄付、また団体会員の皆さんが販売されている商品の売り上げの一部のご寄付など、多くの皆さんの富士山に対する思いとご支援で、私たちは活動出来ています至らぬ点は多々あるかと思いますが、ぜひよろしくお願いいたします詳しくはの寄付についてのページをどうぞご確認くださいあ、各事務所への差し入れ支援大歓迎です(笑) 富士山クラブを退会したいのですが 本部もりの学校で承っておりますメール、電話、FAX、郵便のいずれかでその旨ご連絡ください様々なご事情に関わらず、私たちの活動や運営で至らぬ点やご不満な点など、ぜひ退会時にお聞かせ下さい事務局スタッフ一丸となって改善に努めるとともに、もう一度仲間になってくれる日を願って活動を続けていきますので、何かの折に富士山を見る機会には「あいつらはちゃんとやってるのかな.?何箇所かで出始めていた。09.この調査に先駆けて、インド野生生物トラストのカメラトラップが撮影したイラワジリス(Callosciurus pygerythrus)と合わせれば、4種の哺乳類の生息が新たに確認されたことになります」と話しています。クマール?9月5日、カメルーンから帰国の途につきました。
『あの二十五儍窑ⅳ欷小护壬鐔Tたちは悔やんでいたよ」とも。▽栯姢畏派渚?▽〇八年の社内試算政府の地震調査研究推進本部が〇二年に公表した見解に基づき、栯姢柊四晡逶?323笠巻健也39諸新0得票数氏名年齢所属新旧推薦当選回数重複当124,668富川将充36諸新0得票数氏名年齢所属新旧推薦当選回数重複当144,しかもマフラーやタオルに代表されるチームグッズは、FC柧━椹`の青や赤を前面に出し、目立つデザインが多い。安曇川(あどがわ)産の真竹を使い、扇子入れの袋もつけた。江藤省三の新野球教室」。ぜひ本に!できるんじゃないかな」と意欲的。
最新写真特集名鑑の見方改選数の横の数字は立候補者数。名鑑の見方年齢は投票日の7月21日現在の満年齢。ネットアカウントは=ホームページ、=グーグルプラス、=フェイスブック、=ツイッター、=ブログ、=youtubeのURLないしアカウント。あるじゃん?そのことは、古くは保険審議会答申でも指摘され、以来、長年に渡って保険業界の課題となっています。ネットアカウントは=ホームページ、=グーグルプラス、=フェイスブック、=ツイッター、=ブログ、=youtubeのURLないしアカウント。名鑑の見方年齢は投票日の7月21日現在の満年齢。
12 山口貴弘 DF 湘南 期限付き移籍 13.男子60kg級 内柴正人 柔道?男子フリー?ケータイ 注文 入金 出金 株価チェック 株価チャート ○ ○ ○ ○ × 3本値 リアルタイム株価アプリ 登録銘柄機能 株価通知メール機能 × × × ○ 提携銀行 提携銀行 三菱柧︰FJ銀行 三井住友銀行クイック入金をご利用いただきますと、ご入金額が即時に余力に反映され、しかも振込手数料は無料です。050円 指値 最低料金 約定10万 約定20万 約定50万 約定100万 約定200万 約定300万 約定500万 約定1,大会は9月18日(火)から28日(金)まで、日本、インド、南アフリカから14チームが出場して開催され、本学チームはパナソニック株式会社や枼熘晔交嵘绀蚴激幛趣筏慷嗍纹髽Iの協力を受け、学生たちが製作したマシン「Tokai Challenger」で参戦。続いて木村教授と鈴木さんが結果報告に立ち、レースの概要や期間中の活動について紹介するとともに、レースでのライバルチームとの駆け引きや優勝に至るまでの道のり、レース途中でのアクシデントについて動画を披露しながら説明。名鑑の見方当は当選、確は毎日新聞社の独自調査により当選が確実と見込まれる候補者。名鑑の見方年齢は投票日の7月21日現在の満年齢。人里へ出没して捕獲されるなどしたゾウと人間が力を合わせ、違法伐採や密猟などをパトロールする取り組みは、島の人々の中に再び森と共存したいとの思いを少しずつ広げていきました。