管理Text fields和Text views
Text fields和Text views有两个主要功能:显示文本和允许输入和编辑文本。
一些编程任务,包括配置文本对象、访问当前文本、验证用户输入的内容,以及在Text fields中显示覆盖视图(如书签按钮)。UITextField或UITextView对象的Delegate负责大多数这些任务。Delegate必须采用UITextFieldDelegate或UITextViewDelegate协议,并实现一个或多个协议方法。所有协议方法的实现都是可选的。要调用这些方法,必须以编程方式或在Interface Builder中设置Text fields和Text views的Delegate属性。
发送给Delegate的消息序列
在大多数情况下,对于一个文本对象(Text object),第一响应者(First-responder)状态有变化(或即将发生变化)时,UITextField或UITextView类的实例,向它们的委托发送一系列类似命名的消息。当用户点击文本对象时,它自动成为第一响应者;因此,系统显示键盘,并开始为该文本对象进行编辑会话。当用户点击另一个文本对象或点击按钮结束编辑时,当前文本对象将退出第一响应者状态。如果没有选择其他文本对象,则系统隐藏键盘;另一方面,如果用户选择了另一个文本对象,它将成为第一响应者,并显示该对象的键盘。
这种常见的行为也有一些例外。在iPad上,如果View controller使用"form sheet"样式模态地显示其视图,键盘一旦显示,就不会隐藏,直到用户点击dismiss键或模态View controller以编程方式关闭。这种行为的目的是避免当用户在视图之间移动时产生过多的动画,这些视图大部分是Text fields,但不是全部。另一个例外涉及自定义输入视图(Input view)。输入视图是分配给Text view或自定义视图的inputView属性的系统键盘的替代品。当有输入视图时,即使文本对象是第一响应程序,UIKit也可能会替换键盘,它可能会代表开发人员为非文本对象显示类似键盘的输入视图。(这段看不懂没关系,官方文档想描述一些代码的事情,后面看代码就理解了)。
Text views和Text fields发送给其委托的消息顺序如下:
- 就在文本对象成为第一响应者之前- textFieldShouldBeginEditing:(Text field)和textViewShouldBeginEditing:(Text view)。
Delegate可以通过返回YES(默认值)或NO来决定文本对象是否应该成为第一响应者。
- 就在文本对象成为第一响应者后- textFieldDidBeginEditing:(Text field)和textViewDidBeginEditing:(Text view)。
Delegate可以通过更新状态信息或(例如)在编辑会话期间显示覆盖视图来响应此消息。
- 在编辑阶段------各种各样。
当用户输入和编辑文本时,text对象会调用某些委托方法(如果实现了)。例如,当任何文本发生变化时,Text view的Delegate可以接收textViewDidChange:消息。当用户点击Text field的清除按钮时,Text field的Delegate可以接收textFieldShouldClear:消息;Delegate返回一个布尔值,指示是否应该清除文本。
- 就在文本对象注销第一响应者(resigns first responder)之前- textFieldShouldEndEditing:(Text field)和textViewShouldEndEditing:(Text view)。
Delegate实现这些方法的主要原因是验证输入的文本。例如,如果文本应该符合给定的格式,则Delegate在这里验证输入的字符串,如果字符串不符合,则返回NO。默认返回值是YES。关于Text fields的一个相关方法是textFieldShouldReturn:。当用户点击返回键时,Text field类发送一个textFieldShouldReturn:消息给Delegate,询问它是否应该注销第一个响应者。
- 就在文本对象注销第一响应值后,textFieldDidEndEditing:(Text field)和textViewDidEndEditing:(Text view)。
Delegate可以实现这些方法来获取用户刚刚输入或编辑的文本。
Delegate以外的对象可以通过观察通知来获取Text views和Text fields的第一响应者状态的变化。(然而,他们不能批准或拒绝向新状态的过渡。) 这些通知有:比如UITextFieldTextDidBeginEditingNotification, UITextViewTextDidEndEditingNotification,和UITextViewTextDidChangeNotification。与textFieldDidEndEditing:和textViewDidEndEditing:一样,观察和处理UITextFieldTextDidEndEditingNotification和UITextViewTextDidEndEditingNotification通知的主要目的是访问相关Text field或Text view中的文本。请参阅UITextField Class Reference和UITextView Class Reference以了解有关这些类发出的通知的更多信息。
配置Text fields和Text views
与UIKit框架提供的任何视图对象一样,你通常需要在Text fields和Text views显示之前配置它们。你可以通过编程方式配置它们,也可以使用Interface Builder的属性检查器。无论哪种情况,都是在设置属性。
一些属性是Text views和Text fields的共同属性,而其他属性则是特定于每种类型的对象,包括以下内容:
- 文本特征------文本颜色、对齐方式、Font族、FontTypeface和Font大小。
- keyboard------键盘类型、返回键名、安全文本输入项和auto-enabled返回键,所有这些都由UITextInputTraits协议声明。(请注意,与Text view关联的auto-enabled返回键在点击时充当回车键。)有关更多信息,请参见配置文本对象的键盘。
- 特定于Text field的边框、背景图像、禁用图像、清除按钮和占位符文本。作为UIControl对象,Text fields也具有高亮、选中、启用和其他属性。
- Text view特定-可编辑状态,数据检测器(用于电话号码和URL链接)。因为Text view继承自UIScrollView,所以您还可以通过设置适当的属性来管理滚动视图行为。
跟踪多个Text fields或Text views
文章原文想说明,如果一个对象是多个Text view或Text filed的代理,那么怎么在代理方法里面区分是哪个Text object发送的消息呢?可以通过指针相等,或者tag相等判断
objectivec
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
if (textField == SSN) {
return NO;
}
return YES;
}
objectivec
enum {
NameFieldTag = 0,
EmailFieldTag,
DOBFieldTag,
SSNFieldTag
};
- (void)textFieldDidEndEditing:(UITextField *)textField {
switch (textField.tag) {
case NameFieldTag:
// do something with this text field
break;
case EmailFieldTag:
// do something with this text field
break;
// remainder of switch statement....
}
}
获取输入的文本和设置文本
当用户在Text field或Text view中输入或编辑文本并且编辑会话结束后,Delegate应该获取文本并将其存储在应用程序的数据模型中。访问输入文本的最佳委托方法是textFieldDidEndEditing:(Text fields)和textViewDidEndEditing:(Text views)。
清单3-3演示了如何获取用户在Text field中输入的文本(使用标记区分视图中的多个Text fields)。UITextField或UITextView的text属性保存了当前由text对象显示的字符串。Delegate从此属性获取字符串,并使用为每个字段定义的键将其存储在字典对象中。如果Text field没有字符串值(也就是说,该字段包含一个空字符串),Delegate将简单地返回。
清单3-3获取输入到Text field中的文本
arduino
- (void)textFieldDidEndEditing:(UITextField *)textField {
if ([textField. text isEqualToString:@""])
return;
switch (textField.tag) {
case NameFieldTag:
[thePerson setObject:textField. text forKey:MyAppPersonNameKey];
break;
case EmailFieldTag:
[thePerson setObject:textField. text forKey:MyAppPersonEmailKey];
break;
case SSNFieldTag:
[thePerson setObject:textField. text forKey:MyAppPersonSSNKey];
break;
default:
break;
}
}
清单3-4显示了textViewDidEndEditing:方法的实现,该方法从Text view中获取显示的字符串并将其存储在字典中。这里这个方法没有要求Text view放弃firstresponder。(resignFirstResponder方法是在用户点击视图用户界面中的Done按钮时调用的操作方法中调用的。)
Listing 3-4 Getting the text entered into a text view
ini
- (void)textViewDidEndEditing:(UITextView *)textView {
NSString *theText = textView.text;
if (![theText isEqualToString:@""]) {
[thePerson setObject:theText forKey:MyAppPersonNotesKey];
}
doneButton.enabled = NO;
}
如果需要将字符串写入文本对象(通常是从应用程序的数据模型中取得字符串后),只需将字符串赋值给文本对象的text属性即可。例如:
ini
NSString *storedValue =[个人objectForKey:MyAppPersonEmailKey];
emailField.text = storedValue;
对Text fields使用格式化
Formatter对象自动解析特定格式的字符串,并将字符串转换为表示数字、日期或其他值的对象;它们也可以反过来工作,将NSDate、NSNumber等类似对象转换为表示这些对象值的格式化字符串。Foundation框架提供了抽象基类NSFormatter以及该类的两个具体子类NSDateFormatter和NSNumberFormatter。使用这些类,用户可以在Text field中输入如下值:
diff
11/15/2010
-1348 .09
你的应用程序可以使用formatter对象将字符串分别转换为NSDate对象和NSNumber对象。
下面的代码清单使用一个date-formatter对象来说明格式化器的用法。(当然,您可以使用UIDatePicker对象而不是Text field进行日期输入,但是带附加日期格式化程序的Text field是另一种选择。)清单3-5中的代码创建了一个NSDateFormatter对象,并将其赋值给一个实例变量。它将日期格式化程序配置为对日期使用"短样式",但以一种能够响应日历、地区和时区变化的方式。它还以指定格式将今天的日期指定为占位符字符串,这样用户在输入日期时就有了一个模型。
Listing 3-5 Configuring a date formatter
ini
- (void)viewDidLoad {
[super viewDidLoad];
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setGeneratesCalendarDates:YES];
[dateFormatter setLocale:[NSLocale currentLocale]];
[dateFormatter setCalendar:[NSCalendar autoupdatingCurrentCalendar]];
[dateFormatter setTimeZone:[NSTimeZone defaultTimeZone]];
[dateFormatter setDateStyle:NSDateFormatterShortStyle]; // example: 4/13/10
DOB.placeholder = [NSString stringWithFormat:@"Example: %@", [dateFormatter stringFromDate:[NSDate date]]];
// code continues....
}
在配置了日期格式化器之后,Delegate可以调用格式化器上的dateFromString:方法将输入的日期字符串转换为NSDate对象,如清单3-6所示。
Listing 3-6 Using an NSDateFormatter
object to convert a date string to a date object
arduino
- (void)textFieldDidEndEditing:(UITextField *)textField {
[textField resignFirstResponder];
if ([textField.text isEqualToString:@""])
return;
switch (textField.tag) {
case DOBField:
NSDate *theDate = [dateFormatter dateFromString:textField.text];;
if (theDate)
[inputData setObject:theDate forKey:MyAppPersonDOBKey];
break;
// more switch case code here...
default:
break;
}
}
有关NSDateFormatter和NSNumberFormatter的更多信息, see Data Formatting Guide.
验证输入的文本
字符串可能必须是某种格式,或者值(在转换为数值之后)必须在某个范围内。如果不先验证值,应用程序有时无法接受Text fields和Text views中输入的字符串。验证输入字符串的最佳委托方法是textFieldShouldEndEditing:(用于Text fields)和textViewShouldEndEditing:(用于Text views)。这些方法将在Text field或Text view放弃第一响应者状态之前调用。返回NO文本对象仍然是编辑的焦点。这是还应该显示一个警告框,通知用户有错误。
清单3-7使用一个正则表达式来验证在"Social Security Number"字段中输入的字符串符合该数字的格式。
Listing 3-7 Validating the format of a text field's string using a regular expression
ini
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
if (textField == SSN) { // SSN is an outlet
NSString *regEx = @"[0-9]{3}-[0-9]{2}-[0-9]{4}";
NSRange r = [textField. text rangeOfString:regEx options:NSRegularExpressionSearch];
if (r.location == NSNotFound) {
UIAlertView *av = [[[UIAlertView alloc] initWithTitle:@"Entry Error"
message:@"Enter social security number in 'NNN-NN-NNNN' format"
delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease];
[av show];
return NO;
}
}
return YES;
}
清单3-8中的textViewShouldEndEditing:实现对Text view中输入的文本实施Character限制。
Listing 3-8 Validating a text view's string for allowable length
objectivec
- (BOOL)textViewShouldEndEditing:(UITextView *)textView {
if (textView.text.length > 50) {
UIAlertView *av = [[[UIAlertView alloc] initWithTitle:@"Entry Error"
message:@"You must enter less than 50 characters." delegate:self cancelButtonTitle:@"OK"
otherButtonTitles:@"Clear", nil] autorelease];
[av show];
return NO;
}
return YES;
}
通过实现textField:shouldChangeCharactersInRange:replacementString:方法,Delegate还可以在输入Text field时验证每个Character。清单3-9中的代码验证每个输入的Character(字符)是否表示一个数字。(你可以通过为Text field指定一个UIKeyboardTypeNumberPad键盘来实现同样的目标。)
Listing 3-9 Validating each character as it's entered
objectivec
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
if ([string isEqualToString:@""]) return YES;
if (textField. tag == SalaryFieldTag) {
unichar c = [string characterAtIndex:0];
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:c]) {
return YES;
} else {
return NO;
}
}
return YES;
}
还可以实现textField:shouldChangeCharactersInRange:replacementString:方法,在用户输入文本时提供可能的单词补全或更正功能。
在Text fields中使用叠加视图(Overlay Views)
叠加视图(Overlay Views)是插入到Text field的左右角的小视图。当用户点击它们(通常是按钮)时,它们充当控件,并作用于Text field的当前内容。搜索和添加书签是覆盖视图的两个常见任务

要实现覆盖视图,创建一个大小适合Text field高度的视图,并为该视图提供一个适当大小的图像。如果视图是按钮或其他控件,则指定目标对象、操作选择器和触发的控件事件。通常你想要一个覆盖视图在它的Text field是编辑的焦点时出现,所以在Delegate的textFieldDidBeginEditing:方法中将它分配给Text field的leftView或rightView属性。你可以通过给leftViewMode或rightViewMode属性赋一个UITextFieldViewMode常量来控制覆盖视图在编辑会话期间何时出现------例如,在用户开始输入文本之前或之后。清单3-10展示了如何实现覆盖视图。
Listing 3-10 Displaying an overlay view in a text field
ini
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if (textField.tag == NameField && self.overlayButton) {
textField.leftView = self.overlayButton;
textField.leftViewMode = UITextFieldViewModeAlways;
}
}
@dynamic overlayButton;
- (UIButton *)overlayButton {
if (!overlayButton) {
overlayButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
UIImage *overlayImage = [UIImage imageNamed:@"bookmark.png"];
if (overlayImage) {
[overlayButton setImage:overlayImage forState:UIControlStateNormal];
[overlayButton addTarget:self action:@selector(bookmarkTapped:)
forControlEvents:UIControlEventTouchUpInside];
}
}
return overlayButton;
}
要删除覆盖视图,只需在textFieldDidEndEditing: delegation方法中把leftView或rightView属性设置为nil,如代码清单3-11所示。
Listing 3-11 Removing the overlay view
ini
- (void)textFieldDidEndEditing:(UITextField *)textField {
if (textField.tag == NameFieldTag) {
textField.leftView = nil;
}
// remainder of implementation....
}
跟踪Text views中的选择
UITextViewDelegate的textViewDidChangeSelection:方法让你跟踪用户在Text view中所做选择的变化。你可以实现这个方法来获取选中的子字符串并对其进行一些操作。清单3-12是一个奇怪的例子,它使所选子字符串中的所有Characters都大写。
Listing 3-12 Getting the selected substring and changing it
ini
- (void)textViewDidChangeSelection:(UITextView *)textView {
NSRange r = textView.selectedRange;
if (r.length == 0) {
return;
}
NSString *selText = [textView.text substringWithRange:r];
NSString *upString = [selText uppercaseString];
NSString *newString = [textView.text stringByReplacingCharactersInRange:r withString:upString];
textView.text = newString;
}