TextKit框架详细解析 (八) —— 文本编程指南之管理Text Fields and Text Views(四)

版本记录

版本号 时间
V1.0 2018.09.01

前言

TextKit框架是对Core Text的封装,用简洁的调用方式实现了大部分Core Text的功能。 TextKit是一个偏上层的开发框架,在iOS7以上可用,使用它可以方便灵活处理复杂的文本布局,满足开发中对文本布局的各种复杂需求。TextKit实际上是基于CoreText的一个上层框架,其是面向对象的。接下来几篇我们就一起看一下这个框架。感兴趣的看下面几篇文章。
1. TextKit框架详细解析 (一) —— 基本概览和应用场景(一)
2. TextKit框架详细解析 (二) —— 基本概览和应用场景(二)
3. TextKit框架详细解析 (三) —— 一个简单布局示例(一)
4. TextKit框架详细解析 (四) —— 一个简单布局示例(二)
5. TextKit框架详细解析 (五) —— 文本编程指南之简介(一)
6. TextKit框架详细解析 (六) —— 文本编程指南之展示文本内容(二)
7. TextKit框架详细解析 (七) —— 文本编程指南之排版概念(三)

Managing Text Fields and Text Views - 管理Text Fields and Text Views

Text fields and text views有两个主要功能:显示文本和启用文本的输入和编辑。 几个编程任务与这些简单目的相关联,包括配置文本对象,访问当前文本,验证用户输入的内容以及在Text fields中显示叠加视图(如书签按钮)。

UITextFieldUITextView对象的委托负责大多数这些任务。 委托必须采用UITextFieldDelegateUITextViewDelegate协议并实现一个或多个协议方法。 所有协议方法的实现都是可选的。 要调用这些方法,必须以编程方式或在Interface Builder中设置text fields and text views的代理属性。


The Sequence of Messages to the Delegate - 代理的消息序列

在大多数情况下,当给定文本对象的第一响应者状态发生更改(或即将发生的更改)时,UITextFieldUITextView类的实例会向其委托发送一系列命名相似的消息。当用户点击文本对象时,它自动成为第一响应者;结果,系统显示键盘并开始为该文本对象编辑会话。当用户点击另一个文本对象或点击按钮以结束编辑时,当前文本对象将退出第一响应者状态。如果未选择其他文本对象,系统将隐藏键盘;另一方面,如果用户选择另一个文本对象,则它成为第一响应者并显示该对象的键盘。

这种常见行为有几个例外。在iPad上,如果视图控制器使用“form sheet”样式以模态方式显示其视图,则键盘一旦显示,就不会隐藏,直到用户点击关闭键或模式视图控制器以编程方式解除。此行为的目的是避免在用户在大部分(但不完全是)text fields的视图之间移动时过多的动画。另一个例外是自定义输入视图。输入视图可替代分配给text view or a custom viewinputView属性的系统键盘。当存在输入视图时,即使文本对象是第一响应者,UIKit也可能交换键盘,并且可能代表开发人员为非文本对象显示类似键盘的输入视图。

text views and text fields发送给其代理的消息序列如下:

  • 1)Just before a text object becomes first responder - 就在文本对象成为第一个响应者之前 - textFieldShouldBeginEditing:(text field)textViewShouldBeginEditing:(text view)。代理可以通过返回YES(默认值)或NO来验证文本对象是否应成为第一响应者。
  • 2)Just after a text object becomes first responder - 在文本对象成为第一个响应者之后 -textFieldDidBeginEditing:(text field)textViewDidBeginEditing:(text view)。代理可以通过更新状态信息或例如在编辑会话期间显示覆盖视图来响应此消息。
  • 3)During the editing session - 在编辑会话期间 - 各种。当用户输入和编辑文本时,文本对象会调用某些代理方法(如果已实现)。例如,text view的代理可以在任何文本更改时接收textViewDidChange:消息。当用户点击文本字段的清除按钮时,text field的代理可以接收textFieldShouldClear:消息;代理返回一个布尔值,指示是否应清除该文本。
  • 4)Just before a text object resigns first responder - 就在文本对象撤消第一个响应者 - textFieldShouldEndEditing:(text field)textViewShouldEndEditing:(text view)之前。代理实施这些方法的主要原因是验证输入的文本。例如,如果文本应符合给定格式,则代理在此处验证输入的字符串,如果字符串不符合则返回NO。默认返回值为YES。text field的相关方法是textFieldShouldReturn:。当用户点击return键时,text field类向委托发送textFieldShouldReturn:消息,询问是否应该重新签名第一响应者。
  • 5)Just after text a object resigns first responder - 在文本对象取消第一响应者 - textFieldDidEndEditing:(text field)textViewDidEndEditing:(text view)。代理可以实现这些方法来获取用户刚刚输入或编辑的文本。

通过观察通知,可以向代理以外的对象通知text views and text fields的第一响应者状态的变化。 (但是,它们不能批准或拒绝转换到新状态。)通知的名称包括UITextFieldTextDidBeginEditingNotificationUITextViewTextDidEndEditingNotificationUITextViewTextDidChangeNotification。 与textFieldDidEndEditing:textViewDidEndEditing:一样,观察和处理UITextFieldTextDidEndEditingNotificationUITextViewTextDidEndEditingNotification通知的主要原因是访问相关text field or text view中的文本。 请参阅UITextField Class ReferenceUITextView Class Reference以了解有关这些类发布的通知的更多信息。


Configuring Text Fields and Text Views - 配置Text Fields and Text Views

与UIKit框架提供的任何视图对象一样,您通常需要在显示text fields and text views之前对其进行配置。您可以通过编程方式或使用Interface Builder的属性检查器来配置它们。在任何一种情况下,您都要设置文本对象的property

某些属性对于text fields and text views是通用的,而其他属性特定于每种类型的对象,包括以下内容:

  • Text characteristics - 文本特征 - 文本颜色,对齐方式,字体系列,字体字体和字体大小。
  • Keyboard - 键盘 - 键盘类型,返回键名称,安全文本条目和自动启用的返回键,所有这些都由UITextInputTraits协议声明。 (请注意,与文本视图关联的自动启用的返回键在轻触时充当回车键。),有关详细信息,请参阅Configuring the Keyboard for Text Objects
  • Text-field specific - 特定于文本字段 - 边框,背景图像,禁用图像,清除按钮和占位符文本。作为UIControl对象,文本字段还具有highlighted, selected, enabled和其他属性。
  • Text-view specific - 特定于文本视图 - 可编辑状态,数据检测器(用于电话号码和URL链接)。由于文本视图继承自UIScrollView,因此您还可以通过设置适当的属性来管理滚动视图行为。

Tracking Multiple Text Fields or Text Views - 跟踪多个Text Fields or Text Views

UITextFieldDelegateUITextViewDelegate协议的所有方法都有一个参数,用于标识text field or text view,其中包括第一响应者状态的更改,值的更改或作为代理消息的原因的任何其他更改。如果当前显示的视图中只有一个文本对象,则该参数引用的文本对象的标识是显而易见的。但是,如果当前显示的视图具有多个text field or text view,则代理必须找到一种方法来标识作为代理消息主题的文本对象。

您可以使用以下两种方法之一进行此确定:outlets or tags。对于outlets方法,声明一个outlets实例变量(使用IBOutlet关键字),然后make an outlet connection.。在您的代理方法中,使用指针比较测试传入的文本对象是否与outlet引用的对象相同。例如,假设您声明并连接名为SSNoutlet。您的代码可能类似于Listing 3-1

// Listing 3-1  Identifying the passed-in text object using an outlet

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
    if (textField == SSN) {
            // .....
            return NO;
        }
    return YES;
}

当您需要将字符串值写入这些对象时,定义视图中文本对象的outlet连接特别有用,甚至是必不可少的,而不仅仅是获取它们。

对于tag方法,声明一组枚举常量,每个标记一个常量。

enum {
    NameFieldTag = 0,
    EmailFieldTag,
    DOBFieldTag,
    SSNFieldTag
};

然后以编程方式或在Interface Builder的属性检查器中将整数值分配给文本对象的tag属性。 (tag属性由UIView声明。)在委托方法中,您可以使用switch语句来评估传入文本对象的tag值并相应地继续(如Listing 3-2所示)。

// Listing 3-2  Identifying the passed-in text object using tags

- (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....
    }
}

Getting the Entered Text and Setting Text - 获取输入的文本和设置文本

用户在text field or text view中输入或编辑文本并且编辑会话结束后,委托应获取文本并将其存储在应用程序的数据模型中。 获取输入文本的最佳代理方法是textFieldDidEndEditing:(text fields)textViewDidEndEditing:(text views)

Listing 3-3说明了如何获取用户在text field中输入的文本(使用标记区分视图中的多个text field)。 UITextFieldUITextView的text属性保存文本对象当前显示的字符串。 代理从此属性获取字符串,并使用为每个字段定义的键将其存储在字典对象中。 如果text field没有字符串值 - 也就是说,该text field包含空字符串 - 代理只返回。

// Listing 3-3  Getting the text entered into a text field

- (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;
    }
}

Listing 3-4显示了textViewDidEndEditing:方法的实现,该方法从文本视图中获取显示的字符串并将其存储在字典中。 这里该方法不要求text view取消第一响应者。 (当用户在视图的用户界面中点击“完成”按钮时,调用的resignFirstResponder方法在之前调用的操作方法中被调用。)

// Listing 3-4  Getting the text entered into a text view

- (void)textViewDidEndEditing:(UITextView *)textView {
    NSString *theText = textView.text;
    if (![theText isEqualToString:@""]) {
        [thePerson setObject:theText forKey:MyAppPersonNotesKey];
    }
    doneButton.enabled = NO;
}

如果需要将字符串值写入文本对象 - 通常在从应用程序的数据模型中检索它们之后 - 只需将字符串分配给文本对象的text属性即可。 例如:

NSString *storedValue = [thePerson objectForKey:MyAppPersonEmailKey];
emailField.text = storedValue;

为此,为每个要写入字符串值的text field or text view定义outlets
很有用(在此示例中为emailField)。


Using Formatters with Text Fields

Formatter objects自动解析特定格式的字符串,并将字符串转换为表示数字,日期或其他值的对象;它们也可以反向工作,将NSDateNSNumber和类似对象转换为表示这些对象值的格式化字符串。 Foundation框架提供了抽象基类NSFormatter和该类的两个具体子类NSDateFormatterNSNumberFormatter。 使用这些类,用户可以在文本字段中输入以下值:

11/15/2010
-1,348.09

并且您的应用程序可以使用formatter对象将字符串分别转换为NSDate对象和NSNumber对象。

以下代码清单使用日期格式化程序对象来说明格式化程序的使用。 (当然,您可以使用UIDatePicker对象进行日期输入而不是text field,但是带有附加日期格式化程序的text field是另一种选择。)Listing 3-5中的代码创建了一个NSDateFormatter对象并将其分配给实例变量。 它将日期格式化程序配置为使用日期的“short style”,但是以对日历,区域设置和时区的更改作出响应的方式。 它还以给定格式将今天的日期指定为占位符字符串,以便用户在输入日期时可以使用模型。

// Listing 3-5  Configuring a date formatter

- (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....
}

配置日期格式化程序后,代理可以在格式化程序上调用dateFromString:方法,将输入的日期字符串转换为NSDate对象,如Listing 3-6所示。

// Listing 3-6  Using an NSDateFormatter object to convert a date string to a date object

- (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;
    }
}

格式化程序的使用并不保证输入的字符串包含有效值 - 例如,用户可以在公历中为月份编号输入13。为确保用户输入了正确的值,代理必须按照Validating Entered Text中的说明验证字符串。并且因为验证通常需要已知格式和有效值范围,如果您配置日期格式化程序,如Listing 3-5所示,以便它对不同的日历和区域设置敏感,则无法确定地知道格式。要指定已知的日期格式,请通过调用setDateFormat:来配置日期格式化程序,传入Unicode标准定义的格式模式。

您还可以反转上面显示的过程:通过调用NSDateFormatter方法stringFromDate:将日期对象转换为给定格式的字符串,然后将该字符串分配给text field, text view, or labeltext属性。

有关NSDateFormatterNSNumberFormatter的详细信息,请参阅Data Formatting Guide


Validating Entered Text - 验证输入的文本

应用程序有时在验证该值之前无法接受在text fields and text views中输入的字符串。 也许字符串必须是某种格式,或者值(在转换为数值后)必须在一定范围内。 验证输入字符串的最佳代理方法是textFieldShouldEndEditing:用于text fieldstextViewShouldEndEditing:用于text views。 在text field or text view退出第一响应者状态之前调用这些方法。 返回NO可防止发生这种情况,因此文本对象仍然是编辑的焦点。 如果输入的字符串无效,您还应显示警告以通知用户该错误。

Listing 3-7使用正则表达式来验证在“Social Security Number”字段中输入的字符串是否符合这些号码的格式。

// Listing 3-7  Validating the format of a text field’s string using a regular expression

- (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;
}

Listing 3-8textViewShouldEndEditing:的实现对文本视图中输入的文本强制执行字符限制。

// Listing 3-8  Validating a text view’s string for allowable length

- (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:方法,代理还可以在输入文本字段时验证每个字符。 Listing 3-9中的代码验证每个输入的字符(字符串)代表一个数字。 (您可以通过为文本字段指定UIKeyboardTypeNumberPad键盘来实现相同的目标。)

// Listing 3-9  Validating each character as it’s entered

- (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:方法,以便在用户输入文本时为其提供可能的单词补全或更正。


Using Overlay Views in Text Fields - Text Fields中使用叠加视图

叠加视图是插入text field左右角的小视图。 当用户点击它们(通常是按钮)并作用于text field的当前内容时,它们充当控件。 搜索和书签是叠加视图的两个常见任务,但其他任务都是可能的。 此叠加视图使用text field中的(部分)URL加载Web浏览器:

要实现叠加视图,请创建一个大小适合text field高度的视图,并为视图提供适当大小的图像。 如果视图是按钮或其他控件,请指定目标对象,操作选择器和触发控件事件。 通常,当text field是编辑焦点时,您希望显示叠加视图,因此请将其分配给代理的textFieldDidBeginEditing:方法中的text fieldleftViewrightView属性。 您可以控制在编辑会话期间何时显示叠加视图 - 例如,在用户开始输入文本之前或仅在用户开始输入文本之后 - 通过向leftViewModerightViewMode属性分配UITextFieldViewMode常量。 Listing 3-10说明了如何实现叠加视图。

// Listing 3-10  Displaying an overlay view in a text field

- (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;
}

如果对覆盖视图使用控件,请确保实现action方法。

要删除覆盖视图,只需在textFieldDidEndEditing:代理方法中将leftViewrightView属性设置为nil,如Listing 3-11所示。

// Listing 3-11  Removing the overlay view

- (void)textFieldDidEndEditing:(UITextField *)textField {
 
    if (textField.tag == NameFieldTag) {
        textField.leftView = nil;
    }
    // remainder of implementation....
}

Tracking the Selection in Text Views - 跟踪Text Views中的选择

UITextViewDelegatetextViewDidChangeSelection:方法允许您跟踪用户在text view中所做选择的更改。 您可以实现该方法以获取所选子字符串并对其执行某些操作。Listing 3-12是一个示例,它使所选子字符串中的所有字符都为大写。

// Listing 3-12  Getting the selected substring and changing it

- (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;
}

后记

本篇主要讲述了管理Text Fields and Text Views,感兴趣的给个赞或者关注~~~

`

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容