Читать книгу iOS. Приемы программирования - Вандад Нахавандипур - Страница 39
Глава 1. Реализация контроллеров и видов
1.19. Прием пользовательского текстового ввода с помощью UITextField
Обсуждение
ОглавлениеТекстовое поле очень похоже на подпись тем, что в нем также можно отображать текстовую информацию. Но текстовое поле, в отличие от подписи, может принимать текстовый ввод и во время исполнения. На рис. 1.49 показаны два текстовых поля в разделе Twitter приложения Settings (Настройки) в iPhone.
Рис. 1.49. Текстовые поля, в которые можно вводить текст
В текстовом поле можно вводить и отображать только одну строку текста. Именно поэтому стандартная высота текстового поля, задаваемая по умолчанию, – всего 31 пункт. Эту высоту нельзя изменить в конструкторе интерфейса, но если вы создаете текстовое поле прямо в коде, то сделать это можно. Тем не менее при изменении высоты не изменяется количество строк, которые можно записать в текстовом поле, – строка всегда всего одна.
Чтобы определить наше текстовое поле, начнем работу с файла реализации контроллера вида:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UITextField *myTextField;
@end
@implementation ViewController
…
А потом создадим это текстовое поле:
– (void)viewDidLoad{
[super viewDidLoad];
CGRect textFieldFrame = CGRectMake(0.0f,
0.0f,
200.0f,
31.0f);
self.myTextField = [[UITextField alloc]
initWithFrame: textFieldFrame];
self.myTextField.borderStyle = UITextBorderStyleRoundedRect;
self.myTextField.contentVerticalAlignment =
UIControlContentVerticalAlignmentCenter;
self.myTextField.textAlignment = NSTextAlignmentCenter;
self.myTextField.text = @"Sir Richard Branson";
self.myTextField.center = self.view.center;
[self.view addSubview: self.myTextField];
}
Прежде чем подробно рассматривать код, взглянем на результат его выполнения (рис. 1.50).
При создании этого текстового поля мы использовали различные свойства класса UITextField:
• borderStyle – свойство имеет тип UITextBorderStyle и указывает, как должны отображаться границы текстового поля;
• contentVerticalAlignment – это значение типа UIControlContentVerticalAlignment, сообщающее текстовому полю, как текст должен отображаться по вертикали в границах этого поля. Если не выровнять текст по центру по вертикали, он по умолчанию отобразится в левом верхнем углу поля;
• textAlignment – это свойство имеет тип UITextAlignment и указывает выравнивание текста в текстовом поле по горизонтали. В данном примере текст выровнен в текстовом поле по центру и по горизонтали;
• text – это свойство доступно как для считывания, так и для записи. То есть можно не только получать из него информацию, но и записывать туда новые данные. Функция считывания возвращает текст, который в данный момент находится в текстовом поле, а функция записи задает для текстового поля то значение, которое вы в ней указываете.
Рис. 1.50. Простое текстовое поле, текст в котором выровнен по центру
Текстовое поле посылает сообщения-делегаты своему объекту-делегату. Такие сообщения отправляются, например, когда пользователь начинает изменять (редактировать) информацию в текстовом поле (как-либо изменяет его содержимое) и когда он прекращает взаимодействовать с полем (покидает его). Чтобы получать уведомления об этих событиях, задайте ваш объект в качестве значения свойства delegate текстового поля. Делегат текстового поля должен соответствовать протоколу UITextFieldDelegate, так что позаботимся об этом:
@interface ViewController () <UITextFieldDelegate>
@property (nonatomic, strong) UITextField *myTextField;
@end
@implementation ViewController
Нажав и удерживая клавишу Command, щелкните на протоколе UITextFieldDelegate в Xcode. Вы увидите методы, которыми позволяет управлять этот протокол. Рассмотрим эти методы, а также укажем, когда они вызываются.
• textFieldShouldBeginEditing: – возвращает логическое значение, сообщающее текстовому полю (текстовое поле является параметром этого метода), может ли пользователь редактировать содержащуюся в нем информацию (то есть разрешено это или нет). Возвратите здесь значение NO, если не хотите, чтобы пользователь изменял текст в этом поле. Метод запускается, как только пользователь касается этого поля, намереваясь его редактировать (при условии, что в поле допускается редактирование).
• textFieldDidBeginEditing: – вызывается, когда пользователь начинает редактировать текстовое поле. Этот метод запускается уже после того, как пользователь коснулся текстового поля, а метод делегата текстового поля textFieldShouldBeginEditing: возвратил значение YES, сообщив таким образом, что пользователь может редактировать содержимое этого поля.
• textFieldShouldEndEditing: – возвращает логическое значение, сообщающее текстовому полю, закончен текущий акт редактирования или нет. Этот метод запускается перед тем, как пользователь собирается покинуть текстовое поле, или после того, как статус активного объекта (First Responder) переходит к другому полю для ввода текста. Если возвратить NO от этого метода, то пользователь не сможет перейти в другое текстовое поле и начать вводить текст в него. Виртуальная клавиатура останется на экране.
• textFieldDidEndEditing: – вызывается, когда текущий акт редактирования конкретного текстового поля завершается. Это происходит, когда пользователь решает перейти к редактированию какого-то другого текстового поля или нажимает кнопку, предоставленную автором приложения, чтобы убрать с экрана клавиатуру, предназначенную для ввода текста в текстовое поле.
• textField: shouldChangeCharactersInRange: replacementString: – вызывается всякий раз, когда текст в текстовом поле изменяется. Возвращаемое значение этого метода – логическое. Если возвращается YES, это означает, что текст можно изменить. Если возвращается NO, то любые изменения текста в этом поле приняты не будут и даже не произойдут.
• textFieldShouldClear: – в каждом текстовом поле есть кнопка очистки – обычно это круглая кнопка с крестиком. Когда пользователь нажимает эту кнопку, все содержимое текстового поля автоматически стирается. Если вы предоставляете кнопку для очистки текста, но возвращаете от этого метода значение NO, то пользователь может подумать, что ваша программа не работает. Поэтому в данном случае вы должны отдавать себе отчет в том, что делаете. Если пользователь видит кнопку «Стереть», нажимает ее, а текст в поле не исчезает, это очень плохо характеризует программу.
• textFieldShouldReturn: – вызывается после того, как пользователь нажимает клавишу Return/Enter, пытаясь убрать клавиатуру с экрана. Текстовое поле должно быть присвоено этому методу в качестве активного элемента.
Объединим этот раздел с разделом 1.17 и создадим динамическую текстовую подпись под нашим текстовым полем. Кроме того, отобразим общее количество символов, введенных в текстовое поле. Начнем с файла реализации:
@interface ViewController () <UITextFieldDelegate>
@property (nonatomic, strong) UITextField *myTextField;
@property (nonatomic, strong) UILabel *labelCounter;
@end
@implementation ViewController
Теперь создадим текстовое поле с подписью и нужные нам методы делегата текстового поля. Обойдемся без реализации многих методов UITextFieldDelegate, так как в этом примере они нам не требуются:
– (void) calculateAndDisplayTextFieldLengthWithText:(NSString *)paramText{
NSString *characterOrCharacters = @"Characters";
if ([paramText length] == 1){
characterOrCharacters = @"Character";
}
self.labelCounter.text = [NSString stringWithFormat:@"%lu %@",
(unsigned long)[paramText length],
characterOrCharacters];
}
– (BOOL) textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string{
if ([textField isEqual: self.myTextField]){
NSString *wholeText =
[textField.text stringByReplacingCharactersInRange: range
withString: string];
[self calculateAndDisplayTextFieldLengthWithText: wholeText];
}
return YES;
}
– (BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
– (void)viewDidLoad{
[super viewDidLoad];
CGRect textFieldFrame = CGRectMake(38.0f,
30.0f,
220.0f,
31.0f);
self.myTextField = [[UITextField alloc]
initWithFrame: textFieldFrame];
self.myTextField.delegate = self;
self.myTextField.borderStyle = UITextBorderStyleRoundedRect;
self.myTextField.contentVerticalAlignment =
UIControlContentVerticalAlignmentCenter;
self.myTextField.textAlignment = NSTextAlignmentCenter;
self.myTextField.text = @"Sir Richard Branson";
[self.view addSubview: self.myTextField];
CGRect labelCounterFrame = self.myTextField.frame;
labelCounterFrame.origin.y += textFieldFrame.size.height + 10;
self.labelCounter = [[UILabel alloc] initWithFrame: labelCounterFrame];
[self.view addSubview: self.labelCounter];
[self calculateAndDisplayTextFieldLengthWithText: self.myTextField.text];
}
Мы делаем важное вычисление в методе textField: shouldChangeCharactersInRange: replacementString:. Здесь мы объявляем и используем переменную wholeText. Когда вызывается этот метод, параметр replacementString указывает строку, которую пользователь ввел в текстовое поле. Вы, возможно, полагаете, что пользователь может вводить по одному символу в каждый момент времени, поэтому почему бы не присвоить данному полю значение char? Но не забывайте, что пользователь может вставить в текстовое поле целый фрагмент текста, по этой причине данный параметр должен быть строковым. Параметр shouldChangeCharactersInRange указывает место в текстовом поле, с которого пользователь начинает вводить текст. Итак, с помощью двух этих параметров мы создаем строку, которая сначала считывает весь текст из текстового поля, а потом использует заданный диапазон, чтобы разместить новый текст рядом со старым. Итак, получается, что вводимый нами текст будет появляться в поле после того, как метод textField: shouldChangeCharactersInRange: replacementString: возвратит YES. На рис. 1.51 показано, как приложение будет выглядеть в эмуляторе.
Рис. 1.51. Реагирование на сообщения-делегаты текстового поля
В текстовом поле может отображаться не только текст, но и подстановочные (джокерные) символы. Подстановочный текст отображается до того, как пользователь введет в это поле какой-нибудь собственный текст, пока свойство text текстового поля является пустым. В качестве подстановочного текста вы можете использовать любую строку, какую хотите, но лучше этим текстом подсказать пользователю, для ввода какой именно информации предназначено данное поле. Многие программисты указывают в подстановочном тексте, значения какого типа может принимать данное поле. Например, на рис. 1.49 в двух текстовых полях (для ввода имени пользователя и пароля) стоит подстановочный текст Required (Обязательно). Можно использовать свойство placeholder текстового поля для установки или получения актуального подстановочного текста:
CGRect textFieldFrame = CGRectMake(38.0f,
30.0f,
220.0f,
31.0f);
self.myTextField = [[UITextField alloc]
initWithFrame: textFieldFrame];
self.myTextField.delegate = self;
self.myTextField.borderStyle = UITextBorderStyleRoundedRect;
self.myTextField.contentVerticalAlignment =
UIControlContentVerticalAlignmentCenter;
self.myTextField.textAlignment = UITextAlignmentCenter;
self.myTextField.placeholder = @"Enter text here…";
[self.view addSubview: self.myTextField];
Результат показан на рис. 1.52.
У текстовых полей есть два очень приятных свойства, которые называются leftView и rightView. Они относятся к типу UIView и доступны как для чтения, так и для записи. Они проявляются, как понятно из названий, в левой (left) и правой (right) частях текстового поля, когда вы присваиваете им определенный вид. Первое свойство (левый вид) может использоваться, например, при показе курсов валют. В этом случае слева отображается курс валюты страны, в которой проживает пользователь. Поле с этими данными относится к типу UILabel. Вот как можно решить такую задачу:
Рис. 1.52. Подстановочный текст отображается, когда пользователь еще ничего не ввел в поле
UILabel *currencyLabel = [[UILabel alloc] initWithFrame: CGRectZero];
currencyLabel.text = [[[NSNumberFormatter alloc] init] currencySymbol];
currencyLabel.font = self.myTextField.font;
[currencyLabel sizeToFit];
self.myTextField.leftView = currencyLabel;
self.myTextField.leftViewMode = UITextFieldViewModeAlways;
Если просто присвоить вид свойству leftView или rightView текстового поля, то эти виды не появятся автоматически. То, когда они появятся на экране, зависит от режима, управляющего их внешним видом. Данный режим контролируется свойствами leftViewMode и rightViewMode соответственно. Эти режимы относятся к типу UITextFieldViewMode:
typedef NS_ENUM(NSInteger, UITextFieldViewMode) {
UITextFieldViewModeNever,
UITextFieldViewModeWhileEditing,
UITextFieldViewModeUnlessEditing,
UITextFieldViewModeAlways
}
Итак, например, если задать UITextFieldViewModeWhileEditing в качестве режима левого вида и присвоить ему значение, то этот вид будет отображаться только в то время, как пользователь редактирует текстовое поле. И наоборот, если задать здесь значение UITextFieldViewModeUnlessEditing, левый вид будет отображаться, только пока пользователь не редактирует текстовое поле. Как только редактирование начнется, левый вид исчезнет. Теперь запустим наш код в эмуляторе (рис. 1.53).
Рис. 1.53. Текстовое поле с левым видом