Вандад Нахавандипур - iOS. Приемы программирования
Вид выбора отображается в виде сплошного белого поля потому, что мы еще не наполнили его какими-либо значениями. Сделаем это. Итак, нам потребуется указать источник данных для вида выбора, а потом убедиться в том, что контроллер вида соответствует протоколу, требуемому источником данных. Источник данных экземпляра 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: