Текст книги "iOS. Приемы программирования"
Автор книги: Вандад Нахавандипур
Жанр: Зарубежная компьютерная литература, Зарубежная литература
сообщить о неприемлемом содержимом
Текущая страница: 20 (всего у книги 59 страниц)
Раздел 1.2.
4.9. Использование UITableViewController для удобства при создании табличных видов
Постановка задачиТребуется возможность быстро создавать табличные виды.
РешениеИспользуйте контроллер вида UITableViewController, который по умолчанию предоставляется с табличным контроллером вида.
ОбсуждениеВ инструментарии iOS SDK есть очень удобный класс UITableViewController, который предоставляется с заранее заготовленным экземпляром табличного вида. Чтобы пользоваться всеми его преимуществами, всего лишь потребуется создать новый класс, наследующий от указанного. Здесь я подробно опишу все этапы создания нового проекта Xcode, использующего табличный контроллер вида.
1. На панели меню Xcode выберите File-New-Project (Файл-Новый-Проект).
2. Убедитесь, что в левой части экрана выбрана категория iOS. Затем перейдите в подкатегорию Application (Приложение). В правой части экрана выберите шаблон Empty Application (Пустое приложение), а потом нажмите кнопку Next (Далее) (рис. 4.19).
Рис. 4.19. Создание нового пустого приложения, в котором позже будет находиться табличный контроллер
3. На следующем экране просто выберите название для вашего проекта. Кроме того, убедитесь, что вся информация у вас на экране, кроме Organization Name (Название организации) и Company Identifier (Идентификатор компании), в точности соответствует той, что приведена на рис. 4.20. Как только все будет готово, нажмите кнопку Next (Далее).
Рис. 4.20. Конфигурирование нового пустого приложения в Xcode
4. На следующем экране вам будет предложено сохранить приложение на диске. Просто сохраните приложение в месте, которое кажется вам целесообразным, и нажмите кнопку Create (Создать).
5. В Xcode выберите меню File-New-File (Файл-Новый-Файл).
6. Убедитесь, что в левой части диалогового окна категория iOS выбрана в качестве основной и при этом также выбрана подкатегория Cocoa Touch. Далее в правой части диалогового окна выберите класс Objective-C (рис. 4.21).
Рис. 4.21. Создание нового класса для табличного вида с контроллером
7. На следующем экране вам будет предложено выбрать суперкласс для нового класса. Это очень важный этап. Убедитесь, что в качестве суперкласса задан UITableViewController. Удостоверьтесь, что все остальные настройки у вас точно такие же, как и у меня на рис. 4.22. Когда все будет готово, нажмите кнопку Next (Далее).
Рис. 4.22. Задаем суперкласс для нового объекта, который станет контроллером табличного вида
8. На следующем экране вы сможете сохранить табличный контроллер вида в проекте. Сохраните его как класс ViewController и нажмите кнопку Create (Создать).
9. В файле реализации делегата вашего приложения обязательно импортируйте заголовочный файл этого контроллера вида, а затем создайте экземпляр этого класса и установите его в качестве корневого контроллера вида приложения, как показано далее:
#import «AppDelegate.h»
#import «ViewController.h»
@implementation AppDelegate
– (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
ViewController *controller = [[ViewController alloc]
initWithStyle: UITableViewStylePlain];
self.window = [[UIWindow alloc]
initWithFrame: [[UIScreen mainScreen] bounds]];
self.window.rootViewController = controller;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Теперь, если вы попытаетесь скомпилировать проект, компилятор выдаст вам следующие предупреждения[3]3
Первая строка: «Потенциально неполная реализация метода». Вторая строка: «Неполная реализация метода». – Примеч. пер.
[Закрыть]:
ViewController.m:47:2: Potentially incomplete method implementation.
ViewController.m:54:2: Incomplete method implementation.
Итак, необходимо иметь в виду, что компилятор выдает определенные предупреждения, об устранении которых придется позаботиться в файле реализации контроллера вида. Открыв этот файл, вы увидите, что Apple вставила в шаблон класса табличного контроллера вида макрокоманды #warning – инструкции для компилятора (именно они приводят к тому, что на экран выводятся показанные ранее предупреждения). Одно из предупреждений находится в методе numberOfSectionsInTableView:, другое – в методе tableView: numberOfRowsInSection:. Мы видим на экране предупреждения, потому что не запрограммировали логику для этих методов. Минимальная информация, необходимая табличному контроллеру вида, – это количество разделов для отображения, количество строк для отображения, а также объект ячейки, который должен отображаться в каждой из строк. Мы не видим никаких предупреждений, связанных с отсутствием реализации объекта ячейки, но только потому, что Apple по умолчанию предоставляет формальную реализацию этого метода, создающую за вас пустые ячейки.
По умолчанию контроллер табличного вида является источником данных и делегатом табличного вида. Вам не придется отдельно указывать источник данных и делегат для этого табличного вида.
Перейдем к файлу реализации табличного контроллера вида и убедимся, что у нас есть массив строк (просто для примера), которыми мы можем заполнить табличный вид:
#import «ViewController.h»
static NSString *CellIdentifier = @"Cell";
@interface ViewController ()
@property (nonatomic, strong) NSArray *allItems;
@end
@implementation ViewController
– (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle: style];
if (self) {
// Специальная инициализация
self.allItems = @[
@"Anthony Robbins",
@"Steven Paul Jobs",
@"Paul Gilbert",
@"Yngwie Malmsteen"
];
[self.tableView registerClass: [UITableViewCell class]
forCellReuseIdentifier: CellIdentifier];
}
return self;
}
– (void) viewDidLoad{
[super viewDidLoad];
}
– (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
– (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section{
return self.allItems.count;
}
– (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier: CellIdentifier
forIndexPath: indexPath];
cell.textLabel.text = self.allItems[indexPath.row];
return cell;
}
@end
Теперь, запустив приложение, мы увидим результат, напоминающий рис. 4.23.
Рис. 4.23. Строки правильно отображаются в табличном виде
Вот практически и все, что следует знать о табличных контроллерах видов. Еще раз напомню, что табличный контроллер вида одновременно является и источником данных, и делегатом табличного вида. Итак, теперь вы можете реализовать методы протокола UITableViewDataSource, а также методы протокола UITableViewDelegate прямо в файле реализации табличного контроллера вида.
См. такжеРаздел 4.1.
4.10. Отображение элемента управления, предназначенного для обновления информации в табличных видах
Постановка задачиТребуется отображать в пользовательском интерфейсе красивый элемент управления для обновления информации. Этот элемент управления должен находиться над табличными видами и служить для пользователя интуитивно понятным инструментом: такой инструмент позволяет временно убрать таблицу с экрана, а потом вновь вывести ее, но уже с обновленной информацией. Пример показан на рис. 4.24.
Рис. 4.24. Элемент управления для обновления информации, расположенный над табличным видом
РешениеСоздайте табличный контроллер вида (так, как описано в разделе 4.9) и задайте в качестве значения его свойства refreshControl новый экземпляр класса UIRefreshControl, как показано далее:
– (id)initWithStyle:(UITableViewStyle)style{
self = [super initWithStyle: style];
if (self) {
[self.tableView registerClass: [UITableViewCell class]
forCellReuseIdentifier: CellIdentifier];
self.allTimes = [NSMutableArray arrayWithObject: [NSDate date]];
/* Создаем элемент управления для обновления информации */
self.refreshControl = [[UIRefreshControl alloc] init];
self.refreshControl = self.refreshControl;
[self.refreshControl addTarget: self
action:@selector(handleRefresh:)
forControlEvents: UIControlEventValueChanged];
}
return self;
}
Элементы управления для обновления информации – это простые визуальные индикаторы, располагающиеся над табличным видом и сообщающие пользователю, что какая-то информация в таблице сейчас обновится. Например, чтобы обновить содержимое почтового ящика в приложении Mail в версиях старше iOS 6, вам приходилось нажимать на специальную кнопку Refresh (Обновить). В новой iOS 7 вы можете просто потянуть список ваших писем вниз, как если бы хотели ознакомиться с какими-то письмами из верхней части списка, которые пока не успели прочитать. Как только iOS зафиксирует такой жест, система инициирует обновление. Круто, правда? Это нововведение впервые появилось в Twitter-клиенте для iPhone, большое спасибо за это его разработчикам. Apple по достоинству оценила всю элегантность и логичность этой возможности обновления видов, поэтому в SDK был добавлен специальный компонент для реализации такой функции. Класс, соответствующий этому компоненту, называется UIRefreshControl.
Чтобы создать новый экземпляр этого класса, достаточно просто вызвать его метод init. Сделав это, добавьте экземпляр к табличному контроллеру вида, как описано в подразделе «Решение» данного раздела.
Итак, вы хотите знать, когда пользователь инициирует обновление информации в табличном виде. Для этого просто вызовите метод экземпляра addTarget: action: forControlEvents: обновляющего элемента и передайте ему целевой объект вместе с селектором этого объекта – остальное система сделает за вас. Передайте событие UIControlEventValueChanged параметру forControlEvents этого метода.
Сейчас я это продемонстрирую. В данном примере имеем табличный контроллер вида, в котором отображаются дата и время в строковом формате. Как только пользователь обновит список, потянув его вниз, мы будем добавлять сюда новые актуальные значения даты и времени, изменяя таким образом таблицу. Итак, всякий раз, когда пользователь опускает таблицу, инициируется обновление, в ходе которого мы можем добавить в список актуальные значения даты и времени, обновив табличный вид. Итак, начнем с файла реализации контроллера вида. Определим элемент управления для обновления информации и источник данных:
#import «ViewController.h»
static NSString *CellIdentifier = @"Cell";
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *allTimes;
@property (nonatomic, strong) UIRefreshControl *refreshControl;
@end
@implementation ViewController
Свойство allTimes – это обычный изменяемый массив, который будет содержать все экземпляры NSDate в момент, когда завершится обновление таблицы. Мы уже рассмотрели инициализацию табличного контроллера вида в подразделе «Решение» данного раздела, поэтому я не буду вновь писать об этом. Но, как вы помните, мы прикрепили событие UIControlEventValueChanged обновляющего элемента управления к методу handleRefresh:. В этом методе мы всего лишь собираемся добавить к массиву дату и время, после чего обновить табличный вид:
– (void) handleRefresh:(id)paramSender{
/* Оставляем небольшую задержку между высвобождением обновляющего элемента
управления и самим моментом обновления. Так весь процесс выглядит
в интерфейсе более плавно, чем при использовании обычной анимации */
int64_t delayInSeconds = 1.0f;
dispatch_time_t popTime =
dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
/* Добавляем актуальную дату к имеющемуся списку дат;
Таким образом, при обновлении табличного вида новый информационный
элемент на экране будет находиться над старым и пользователь увидит
разницу во времени до и после обновления */
[self.allTimes addObject: [NSDate date]];
[self.refreshControl endRefreshing];
NSIndexPath *indexPathOfNewRow =
[NSIndexPath indexPathForRow: self.allTimes.count-1 inSection:0];
[self.tableView
insertRowsAtIndexPaths:@[indexPathOfNewRow]
withRowAnimation: UITableViewRowAnimationAutomatic];
});
}
Последний важный момент: мы записываем дату в табличный вид посредством методов делегата и источника данных табличного вида:
– (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
– (NSInteger) tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section{
return self.allTimes.count;
}
– (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier: CellIdentifier
forIndexPath: indexPath];
cell.textLabel.text = [NSString stringWithFormat:@"%@",
self.allTimes[indexPath.row]];
return cell;
}
Опробуйте в эмуляторе или на устройстве то, что у нас получилось. Открыв приложение, вы сразу заметите только одно значение даты и времени в списке. Но если потянуть таблицу вниз, то постепенно перед вами будут открываться новые элементы (см. рис. 4.24).
См. такжеРаздел 4.9.
Глава 5. Выстраивание сложных макетов с помощью сборных видов
5.0. Введение
Табличные виды очень хороши. Действительно. Тем не менее они отличаются удручающей негибкостью, так как их содержимое всегда ориентировано по вертикали. Это не настоящие таблицы-сетки, поэтому они и функционально не похожи на сетки. Однако программист может оказаться в ситуации, когда требуется отрисовать на экране таблицеподобный компонент со строками и столбцами, а затем поместить в каждую из ячеек различные элементы пользовательского интерфейса и каждый элемент сделать интерактивным. В табличном виде у вас фактически имеется всего один столбец с множеством строк. Если вы хотите создать иллюзию множества столбцов, то придется предоставить собственную специальную ячейку и оформить ее так, как будто она состоит из нескольких столбцов.
Сборные виды, так же как табличные виды, состоят из ячеек, причем каждая ячейка содержит элемент или вид, отображаемый на экране. Ячейки в сборных видах доступны для повторного использования, причем их можно изымать из очереди и возвращать на экран в любой момент, когда это можно и нужно. Но компоновка страницы может быть практически любой вообразимой на двухмерном экране.
Именно поэтому в 6-й версии iOS компания Apple впервые внедрила сборные виды. Сборный вид можно сравнить с сильно усовершенствованным прокручиваемым видом. У него есть источник данных и делегат, как и у табличного вида. Но он обладает одним свойством, делающим его совершенно несхожим с табличным или прокручиваемым видами. Речь идет о макетном объекте.
В сущности, макетный объект вычисляет, где должен быть размещен каждый элемент, входящий в состав сборного вида. Однако Apple немного усложнила такую работу, внедрив конкретный класс для работы со сборными видами, причем этот класс нельзя инстанцировать напрямую. Вместо этого придется инстанцировать подкласс от этого класса, называемый UICollectionViewFlowLayout.
Этот подкласс обеспечивает последовательную компоновку, при которой ячейки из сборного вида распределяются на экране по секциям. Каждая секция – это группа ячеек сборного вида, так же как в табличном виде. Однако в сборном виде любая секция может компоноваться на экране разными способами, не обязательно вертикально. Например, у вас может быть три прямоугольника, в каждом из которых содержится своя маленькая таблица (рис. 5.1).
Рис. 5.1. Типичный макет с последовательной компоновкой в сборном виде
Как правило, секции располагаются на экране в виде таблиц, то есть образуя строки и столбцы. Именно эта задача решается с помощью класса последовательной компоновки. Если вы хотите добиться еще большей свободы действий при компоновке, то попробуйте изменить свойства класса последовательной компоновки. А если желаете сделать нечто, значительно отличающееся от стандартных возможностей последовательной компоновки, создайте для этого собственный класс. Например, такой специальный класс вам потребуется для создания сборного вида, который показан на рис. 5.2. Далее приведен специальный класс компоновки, располагающий соответствующие ячейки совсем не по табличному принципу.
Рис. 5.2. Специальный вариант компоновки для сборного вида
5.1. Создание сборных видов
Постановка задачиТребуется отобразить на экране сборный вид.
РешениеЛибо воспользуйтесь экземпляром UICollectionView, который в таком случае нужно сделать дочерним видом одного из видов вашего приложения (если хотите создать полноэкранный сборный вид), либо примените класс UICollectionViewController.
ОбсуждениеСборный вид, как и табличный, – это элемент, который может быть добавлен в качестве дочернего к другому виду. Итак, создавая приложение, определитесь с тем, должен ли сборный вид быть основным видом в контроллере или представлять собой небольшой фрагмент другого вида.
Сначала рассмотрим вариант с полноэкранным видом.
1. Откройте Xcode.
2. В меню File (Файл) выберите New (Новый), а затем Project (Проект).
3. Слева в качестве основной категории выберите iOS, а под ней – Application (Приложение). В правой части экрана выберите Empty Application (Пустое приложение), после чего нажмите кнопку Next (Далее).
4. На следующем экране введите информацию о вашем проекте и убедитесь, что установлен флажок Use Automatic Reference Counting (Использовать автоматический подсчет ссылок) (рис. 5.3). Как только введете все необходимые значения, нажмите кнопку Next (Далее).
Рис. 5.3. Создание нового проекта Пустое приложение (Empty Application) для сборного вида
5. После этого вам будет предложено сохранить проект на диске. Выберите подходящее для этого место и нажмите кнопку Create (Создать).
6. Теперь, когда проект подготовлен, создайте в нем новый класс и назовите его ViewController. Этот класс должен наследовать от UICollectionViewController. Вам не понадобится. xib-файл для этого контроллера вида, поэтому откажитесь от этой возможности (рис. 5.4).
Рис. 5.4. Добавляем в проект новый класс сборного вида
7. Найдите в проекте файл AppDelegate.m (это файл реализации делегата приложения) и откройте его, после чего создайте экземпляр сборного вида, а затем сделайте этот сборный вид корневым контроллером вида вашего приложения, как показано здесь:
– (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
/* Инстанцируем контроллер сборного вида с нулевым макетным объектом.
Примечание: в результате программа выдаст исключение, но позже мы
изучим, как создавать макетные объекты и предоставлять их нашим сборным
видам */
ViewController *viewController = [[ViewController alloc]
initWithCollectionViewLayout: nil];
self.window = [[UIWindow alloc] initWithFrame:
[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
/* Устанавливаем сборный вид в качестве корневого для нашего окна */
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
return YES;
}
Как только вы запустите ваше приложение, оно аварийно завершится и выдаст сообщение о том, что вы указали нулевой макетный объект для вашего сборного вида. И здесь среда времени исполнения действует совершенно правильно. Так делать нельзя. Но мы просто пока не обсудили, как инстанцировать макетные объекты и передавать их в сборные виды. Поэтому пока оставим все как есть. Далее в этой главе мы подробнее поговорим о макетных объектах сборных видов.
Итак, мы уже научились создавать контроллер сборного вида, и это хорошо, если вы хотите, чтобы такой вид при отображении занимал на устройстве весь экран. Однако если вы разрабатываете специальный компонент, входящий в состав другого, более крупного вида, то попробуйте просто инстанцировать объект типа UICollectionView, воспользовавшись его выделенным инициализатором – методом initWithFrame: collectionViewLayout:.
Чтобы это сделать, требуется просто инстанцировать сборный вид, воспользовавшись указанным инициализатором. После инициализации вы сможете добавить сборный вид в качестве дочернего к другому виду. Например, если вы хотите добавить его к вашему виду контроллера вида, просто вызовите метод addSubview: этого вида с контроллером и передайте экземпляр вашего сборного вида этому методу в качестве параметра. Кроме того, нужно убедиться, что в качестве значений свойств delegate и dataSource сборного вида заданы валидные объекты, соответствующие протоколам UICollectionViewDelegate и UICollectionViewDataSource. Выполнить остальные операции не составляет труда. Далее в этой главе описаны все приемы, используемые для наполнения сборного вида информацией из источника данных и реагирования на события с помощью объекта-делегата.
Правообладателям!
Это произведение, предположительно, находится в статусе 'public domain'. Если это не так и размещение материала нарушает чьи-либо права, то сообщите нам об этом.