Кроме того, вид-предупреждение можно использовать и для текстового ввода, например, запрашивая у пользователя номер кредитной карточки или адрес. Для этого, как было указано ранее, нужно использовать стиль оформления предупреждения UIAlertViewStylePlainTextInput:
 
   – (void) viewDidAppear:(BOOL)animated{
   [super viewDidAppear: animated];
 
   UIAlertView *alertView = [[UIAlertView alloc]
   initWithTitle:@"Credit Card Number"
   message:@"Please enter your credit card number: "
   delegate: self
   cancelButtonTitle:@"Cancel"
   otherButtonTitles:@"OK", nil];
   [alertView setAlertViewStyle: UIAlertViewStylePlainTextInput];
   /* Отобразить для этого текстового поля числовую клавиатуру. */
   UITextField *textField = [alertView textFieldAtIndex:0];
   textField.keyboardType = UIKeyboardTypeNumberPad;
   [alertView show];
 
   }
   Если сейчас запустить приложение в эмуляторе, то мы увидим такое изображение, как на рис. 1.4.
 
   Рис. 1.4. Вид-предупреждение для ввода обычным текстом
 
   В этом коде мы изменяем стиль оформления вида на UIAlertViewStylePlainTextInput, а также делаем еще кое-что. Мы получили ссылку на первое и единственное текстовое поле, которое, как мы знаем, будет присутствовать в виде-предупреждении. Ссылку на текстовое поле применили для того, чтобы изменить тип клавиатуры, связанной с текстовым полем. Подробнее о текстовых полях поговорим в разделе 1.19.
   Кроме обычного текста мы можем попросить пользователя набрать и защищенный текст. Как правило, защищается такой текст, который является для пользователя конфиденциальным, например пароль (рис. 1.5). Рассмотрим пример:
 
   – (void) viewDidAppear:(BOOL)animated{
   [super viewDidAppear: animated];
 
   UIAlertView *alertView = [[UIAlertView alloc]
   initWithTitle:@"Password"
   message:@"Please enter your password: "
   delegate: self
   cancelButtonTitle:@"Cancel"
   otherButtonTitles:@"OK", nil];
 
   [alertView setAlertViewStyle: UIAlertViewStyleSecureTextInput];
   [alertView show];
 
   }
 
   Рис. 1.5. Ввод защищенного текста в окно с предупреждением
 
   Стиль UIAlertViewStyleSecureTextInput очень напоминает UIAlertViewStylePlainTextInput, за исключением того, что вместо символов текста мы подставляем какие-то нейтральные символы.
   Следующий стиль довольно полезный. Он позволяет отобразить два текстовых поля: одно для имени пользователя, а другое – для пароля. Текст в первом поле открыт, а во втором – скрыт:
 
   – (void) viewDidAppear:(BOOL)animated{
   [super viewDidAppear: animated];
 
   UIAlertView *alertView = [[UIAlertView alloc]
   initWithTitle:@"Password"
   message:@"Please enter your credentials: "
   delegate: self
   cancelButtonTitle:@"Cancel"
   otherButtonTitles:@"OK", nil];
 
   [alertView setAlertViewStyle: UIAlertViewStyleLoginAndPasswordInput];
   [alertView show];
 
   }
 
   В результате увидим такое изображение, как на рис. 1.6.
 
   Рис. 1.6. Стиль, позволяющий вводить в вид-предупреждение имя пользователя и пароль

On@2x.png и Off@2x.png (для сетчатого дисплея), а также поместил здесь разновидности изображений для обычного дисплея. Теперь нам предстоит создать переключатель, но присвоить ему заказные изображения «включено» и «выключено». Для этого воспользуемся следующими свойствами UISwitch:
    onImage – как указано ранее, это изображение будет использоваться, когда переключатель включен;
   • offImage – это изображение соответствует переключателю в состоянии «выключено».
   А вот код, позволяющий добиться такого эффекта:
 
   – (void)viewDidLoad
   {
   [super viewDidLoad];
 
   /* Создаем переключатель */
   self.mainSwitch = [[UISwitch alloc] initWithFrame: CGRectZero];
   self.mainSwitch.center = self.view.center;
   /* Убеждаемся, что переключатель не выглядит размытым в iOS-эмуляторе */
   self.mainSwitch.frame = [self roundedValuesInRect: self.mainSwitch.frame];
   [self.view addSubview: self.mainSwitch];
 
   /* Оформляем переключатель */
   self.mainSwitch.onImage = [UIImage imageNamed:@"On"];
   self.mainSwitch.offImage = [UIImage imageNamed:@"Off"];
   }

См. также

   Раздел 1.2.

1.4. Выбор значений с помощью UIPickerView

Постановка задачи

   Необходимо предоставить пользователю приложения возможность выбирать значения из списка.

Решение

   Воспользуйтесь классом UIPickerView.

Обсуждение

   Вид выбора (Picker View) – это элемент графического интерфейса, позволяющий отображать для пользователей списки значений, из которых пользователь затем может выбрать одно. В разделе Timer (Таймер) приложения Clock (Часы) в iPhone мы видим именно такой пример (рис. 1.10).
 
   Рис. 1.10. Вид выбора, расположенный в верхней части экрана
 
   Как видите, в отдельно взятом виде выбора содержится два независимых визуальных элемента, один слева, другой справа. В левой части вида отображаются часы (0, 1, 2 и т. д.), а в правой – минуты (18, 19, 20, 21, 22 и т. д.). Два этих элемента называются компонентами. В каждом компоненте есть строки (Rows). На самом деле любой элемент в любом компоненте представлен строкой, как мы вскоре увидим. Например, в левом компоненте 0 hours – это строка, 1 – это строка и т. д.
   Создадим вид выбора в виде нашего контроллера. Если вы не знаете, где находится исходный код того вида, в котором расположен контроллер, обратитесь к разделу 1.2, где обсуждается этот вопрос.
   Сначала перейдем к файлу реализации. m контроллера нашего вида и определим в нем вид выбора:
 
   @interface ViewController ()
   @property (nonatomic, strong) UIPickerView *myPicker;
   @end
 
   @implementation ViewController
 
   …
   А теперь создадим вид выбора в методе viewDidLoad контроллера нашего вида:
   – (void)viewDidLoad{
   [super viewDidLoad];
 
   self.view.backgroundColor = [UIColor whiteColor];
 
   self.myPicker = [[UIPickerView alloc] init];
   self.myPicker.center = self.view.center;
   [self.view addSubview: self.myPicker];
 
   }
 
   В данном примере необходимо отметить, что вид выбора выравнивается по центру того вида, в котором находится. Если мы запустим это приложение в эмуляторе iOS 7, то увидим пустой экран. Дело в том, что в iOS 7 сам элемент для выбора белый и мы видим фон контроллера вида.
   Вид выбора отображается в виде сплошного белого поля потому, что мы еще не наполнили его какими-либо значениями. Сделаем это. Итак, нам потребуется указать источник данных для вида выбора, а потом убедиться в том, что контроллер вида соответствует протоколу, требуемому источником данных. Источник данных экземпляра UIPickerView должен подчиняться протоколу UIPickerViewDataSource, так что обеспечим соответствие данного вида условиям этого протокола в файле. m:
 
   @interface ViewController () <UIPickerViewDataSource, UIPickerViewDelegate>
   @property (nonatomic, strong) UIPickerView *myPicker;
   @end
 
   @implementation ViewController
 
   …
 
   Хорошо. Теперь изменим наш код в файле реализации, чтобы гарантировать, что актуальный контроллер вида выбран в качестве источника данных для вида выбора:
 
   – (void)viewDidLoad{
   [super viewDidLoad];
 
   self.myPicker = [[UIPickerView alloc] init];
   self.myPicker.dataSource = self;
   self.myPicker.center = self.view.center;
   [self.view addSubview: self.myPicker];
 
   }
 
   После этого, попытавшись скомпилировать приложение, вы увидите, что компилятор начинает выдавать предупреждения. Эти предупреждения сообщают, что вы еще не реализовали некоторые методы, внедрения которых требует протокол UIPickerViewDataSource. Чтобы исправить эту ситуацию, нужно нажать Command+Shift+O, ввести UIPickerViewDataSource и нажать Enter. Так вы попадете к тому месту в вашем коде, где определяется данный протокол, и увидите нечто подобное:
 
   @protocol UIPickerViewDataSource<NSObject>
   @required
 
   // Возвращает количество столбцов для отображения
   – (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
 
   // Возвращает количество строк в каждом компоненте
   – (NSInteger)pickerView:(UIPickerView *)pickerView
   numberOfRowsInComponent:(NSInteger)component;
   @end
 
   Вы заметили здесь ключевое слово @required? Оно означает, что любой класс, желающий стать источником данных для вида выбора, обязан реализовывать эти методы. Напишем их в файле реализации контроллера нашего вида:
 
   – (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
 
   if ([pickerView isEqual: self.myPicker]){
   return 1;
   }
 
   return 0;
 
   }
   – (NSInteger) pickerView:(UIPickerView *)pickerView
   numberOfRowsInComponent:(NSInteger)component{
 
   if ([pickerView isEqual: self.myPicker]){
   return 10;
   }
 
   return 0;
   }
 
   Итак, что здесь происходит? Рассмотрим, какие данные предполагает каждый из методов источника:
    numberOfComponentsInPickerView: – этот метод передает объект вида выбора в качестве параметра, а в качестве возвращаемого значения ожидает целое число, указывающее, сколько компонентов вы хотели бы отобразить в этом виде выбора;
   • pickerView: numberOfRowsInComponent: – для каждого компонента, добавляемого в вид выбора, необходимо указать системе, какое количество строк вы хотите отобразить в данном компоненте. Этот метод передает вам экземпляр вида выбора, а в качестве возвращаемого значения ожидает целое число, сообщающее среде времени исполнения, сколько строк вы хотели бы отобразить в этом компоненте.
   Итак, мы приказываем системе отобразить один компонент всего с 10 строками для вида выбора, который мы создали ранее и назвали myPicker.
   Скомпилируйте приложение и запустите его в эмуляторе iPhone (рис. 1.11). Хм-м-м, и что же это?
 
   Рис. 1.11. Вот как выглядит вид выбора, когда неизвестно, какую информацию в нем отображать
 
   По всей видимости, наш вид выбора знает, сколько компонентов в нем должно быть и сколько строк он должен отображать в интересующем нас компоненте, но не знает, какой текст должен содержаться в каждой строке. Этот вопрос обязательно следует прояснить, и мы решим данную проблему, предоставив делегат для вида выбора. Делегат экземпляра UIPickerView должен подчиняться протоколу UIPickerViewDelegate и реализовывать все методы, помеченные как @required.
   Нас интересует только один метод делегата UIPickerViewDelegate, а именно pickerView: titleForRow: forComponent:. Этот метод передает вам индекс актуального раздела и индекс актуальной строки в данном разделе вида выбора и в качестве возвращаемого значения ожидает экземпляр NSString. Строка, представленная NSString, отобразится в заданном ряду внутри компонента. В рассматриваемом случае я предпочитаю просто отобразить первую строку как «Строка 1», а затем продолжить: «Строка 2», «Строка 3» и т. д. Не забывайте, что потребуется также установить свойство delegate нашего вида выбора:
 
   self.myPicker.delegate = self;
 
   А теперь обработаем только что изученный метод делегата:
 
   – (NSString *)pickerView:(UIPickerView *)pickerView
   titleForRow:(NSInteger)row
   forComponent:(NSInteger)component{
 
   if ([pickerView isEqual: self.myPicker]){
 
   /* Строка имеет нулевое основание, а мы хотим, чтобы первая строка
   (с индексом 0) отображалась как строка 1. Таким образом, нужно
   прибавить +1 к индексу каждой строки. */
   result = [NSString stringWithFormat:@"Row %ld", (long)row + 1];
 
   }
   return nil;
   }
 
   Теперь запустим приложение и посмотрим, что происходит (рис. 1.12).
 
   Рис. 1.12. Вид выбора с одним разделом и несколькими строками
 
   Виды с возможностью выбора в iOS 6 и старше могут подсвечивать выбранный вариант с помощью свойства showsSelectionIndicator, по умолчанию имеющего значение NO. Вы можете либо напрямую изменить значение этого свойства на YES, либо воспользоваться методом setShowsSelectionIndicator: вида выбора, чтобы включить этот индикатор:
 
   self.myPicker.showsSelectionIndicator = YES;
 
   Снова предположим, что мы создаем вид выбора в окончательной версии нашего приложения. Какая польза от вида выбора, если мы не можем определить, что именно пользователь выбрал в каждом из компонентов? Да, хорошо, что Apple уже позаботилась о решении этой проблемы и предоставила нам возможность спрашивать вид выбора о выбранном варианте. Вызовем метод selectedRowInComponent: класса UIPickerView и передадим индекс компонента (с нулевым основанием), а в качестве возвращаемого значения получим целое число. Это число будет представлять собой индекс с нулевым основанием, сообщающий строку, которая в данный момент выбрана в интересующем нас компоненте.
   Если во время исполнения вам потребуется изменить значения, содержащиеся в вашем виде выбора, необходимо гарантировать, что вид выбора сможет перегружать данные, заменяя старую информацию новой, получаемой из источника и от делегата. Для этого нужно либо принудительно заставить все компоненты перезагрузить содержащиеся в них данные (это делается с помощью метода reloadAllComponents), либо приказать конкретному компоненту перезагрузить содержащиеся в нем данные. Во втором случае применяется метод reloadComponent:. Ему передается индекс компонента, который необходимо перезагрузить.

См. также

   Раздел 1.2.

1.5. Выбор даты и времени с помощью UIDatePicker

Постановка задачи

   Необходимо предоставить пользователям вашего приложения возможность выбирать дату и время. Для этого нужен интуитивно понятный и уже готовый пользовательский интерфейс.

Решение

   Воспользуйтесь классом UIDatePicker.

Обсуждение

   Класс UIDatePicker очень напоминает класс UIPickerView. Фактически UIDatePicker – это уже заполненный вид выбора. Хорошим примером такого вида является программа Calendar (Календарь) в iPhone (рис. 1.13).
 
   Рис. 1.13. Вид для выбора даты показан в нижней части экрана
 
   Для начала объявим свойство типа UIDatePicker, а потом выделим и инициализируем это свойство и добавим его в вид, в котором находится контроллер нашего вида:
 
   #import "ViewController.h"
 
   @interface ViewController ()
   @property (nonatomic, strong) UIDatePicker *myDatePicker;
   @end
 
   @implementation ViewController
 
   …
 
   А теперь, как и планировалось, инстанцируем вид для выбора даты:
 
   – (void)viewDidLoad{
   [super viewDidLoad];
   self.myDatePicker = [[UIDatePicker alloc] init];
   self.myDatePicker.center = self.view.center;
   [self.view addSubview: self.myDatePicker];
   }
 
   После этого запустим приложение и посмотрим, как оно выглядит (рис. 1.14).
 
   Рис. 1.14. Простой вид для выбора даты
 
   Как видите, по умолчанию в виде выбора даты ставится сегодняшняя дата. Начиная работать с такими инструментами, первым делом нужно уяснить, что они могут иметь различные стили оформления и режимы работы. Режим можно изменить, работая со свойством datePickerMode, тип которого – UIDatePickerMode:
 
   typedef enum {
   UIDatePickerModeTime,
   UIDatePickerModeDate,
   UIDatePickerModeDateAndTime,
   UIDatePickerModeCountDownTimer,
   } UIDatePickerMode;
 
   В зависимости от конкретной задачи, стоящей перед вами, для режима вида выбора даты можно задать любое из значений, перечисленных в списке UIDatePickerMode. Далее по мере обсуждения данной темы мы рассмотрим некоторые из этих значений.
   Теперь, когда вы успешно смогли отобразить на экране вид для выбора даты, можно попытаться получить дату, которая выведена в нем в настоящий момент. Для получения этой информации используется свойство date данного вида. Другой способ – применить метод date к виду выбора даты:
 
   NSDate *currentDate = self.myDatePicker.date;
   NSLog(@"Date = %@", currentDate);
 
   Подобно классу UISwitch, вид для выбора даты также посылает своим целям инициирующие сообщения (Action Messages) всякий раз, когда отображаемая в виде дата изменяется. Чтобы иметь возможность реагировать на эти сообщения, получатель должен добавить себя в список целей вида выбора даты. Для этого используется метод addTarget: action: forControlEvents: следующим образом:
 
   – (void) datePickerDateChanged:(UIDatePicker *)paramDatePicker{
 
   if ([paramDatePicker isEqual: self.myDatePicker]){
   NSLog(@"Selected date = %@", paramDatePicker.date);
   }
 
   }
 
   – (void)viewDidLoad{
   [super viewDidLoad];
   self.myDatePicker = [[UIDatePicker alloc] init];
   self.myDatePicker.center = self.view.center;
   [self.view addSubview: self.myDatePicker];
   [self.myDatePicker addTarget: self
   action:@selector(datePickerDateChanged:)
   forControlEvents: UIControlEventValueChanged];
 
   }
 
   Теперь всякий раз, когда пользователь изменяет дату, вы будете получать сообщение от вида выбора даты.