Text Programming Guide for iOS - 04 - Managing Text Fields and Text Views

管理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发送给其委托的消息顺序如下:

  1. 就在文本对象成为第一响应者之前- textFieldShouldBeginEditing:(Text field)和textViewShouldBeginEditing:(Text view)。

Delegate可以通过返回YES(默认值)或NO来决定文本对象是否应该成为第一响应者。

  1. 就在文本对象成为第一响应者后- textFieldDidBeginEditing:(Text field)和textViewDidBeginEditing:(Text view)。

Delegate可以通过更新状态信息或(例如)在编辑会话期间显示覆盖视图来响应此消息。

  1. 在编辑阶段------各种各样。

当用户输入和编辑文本时,text对象会调用某些委托方法(如果实现了)。例如,当任何文本发生变化时,Text view的Delegate可以接收textViewDidChange:消息。当用户点击Text field的清除按钮时,Text field的Delegate可以接收textFieldShouldClear:消息;Delegate返回一个布尔值,指示是否应该清除文本。

  1. 就在文本对象注销第一响应者(resigns first responder)之前- textFieldShouldEndEditing:(Text field)和textViewShouldEndEditing:(Text view)。

Delegate实现这些方法的主要原因是验证输入的文本。例如,如果文本应该符合给定的格式,则Delegate在这里验证输入的字符串,如果字符串不符合,则返回NO。默认返回值是YES。关于Text fields的一个相关方法是textFieldShouldReturn:。当用户点击返回键时,Text field类发送一个textFieldShouldReturn:消息给Delegate,询问它是否应该注销第一个响应者。

  1. 就在文本对象注销第一响应值后,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 ReferenceUITextView 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;
}
相关推荐
90后的晨仔1 小时前
RxSwift 源码解析:深入 ObservableType 扩展与订阅机制
ios
90后的晨仔2 小时前
Swift 中的`@dynamicMemberLookup`是什么?
ios
90后的晨仔2 小时前
解析 RxSwift 的响应式基石
ios
90后的晨仔2 小时前
RxSwift 的DisposeBag中的SpinLock()分析
ios
90后的晨仔2 小时前
RxSwift 中的 DisposeBag解析
前端·ios
Swift社区12 小时前
Swift 解法详解:如何在二叉树中寻找最长连续序列
开发语言·ios·swift
小小鱼儿小小林14 小时前
苹果WWDC25重磅发布:Apple Intelligence与Liquid Glass引领未来
ios·wwdc·苹果
I烟雨云渊T14 小时前
2025年的WWDC所更新的内容
macos·ios·wwdc
Fatbobman(东坡肘子)14 小时前
WWDC 2025 开发者特辑 | 肘子的 Swift 周报 #088
开发语言·macos·ios·swiftui·ai编程·swift·wwdc
vastgrassland14 小时前
从WWDC看苹果产品发展的规律
macos·ios·wwdc