Monday, December 24, 2012

Делегирование, часть 2: UITableView / UITableViewDelegate http://karonator.ru/cources/ios/1556

http://karonator.ru/cources/ios/1556

Делегирование, часть 2: UITableView / UITableViewDelegate

Здравствуйте товарищи.

Как и обещал, представляю вам вторую статью про делегирование. Речь пойдёт о таблицах. Если вы не знакомы с принципами делегирования, рекомендую вам сначала ознакомиться с первой статьёйэтого цикла, в которой рассказано о том, что собственно такое делегирование, и как его использовать.

Ближе к делу. Стоящая перед нами задача проста: надо оживить UITableView. Для примера сделаем простую табличку, в которой перечислим выдающихся физиков, математиков и программистов. Нам необходимо будет описать класс-контроллер, который будет отвечать за поведение нашей таблички, а так же заполнять её информацией. Для того, чтобы наш класс мог «подружиться» со стандартным элементом интерфейса UITableView, его нужно будет сделать удовлетворяющим протоколамUITableViewDelegate и UITableViewDataSource.

ПОДГОТОВКА ПРОЕКТА

Для начала создадим пустой проект (Simple View Application), я назвал его XSample — не очень логично, зато названия файлов будут покороче. XCode создал нам 5 файлов:

  • XSampleAppDelegate.h / XSampleAppDelegate.m — класс-делегат нашего приложения, в нём описываются функции, которые будут вызваться если наше приложение закроют, свернут и т.п. В нём ничего трогать не надо.
  • XSampleViewController.h / XSampleViewController.m — это класс отвечает за окошко нашего приложения, точнее за его UIView, к нему мы вернёмся позже.
  • XSampleViewController.xib — файл Interface Builder'а, описывающий наш интерфейс.

Самоконтроль:

Теперь нужно добавить в проект класс, который будет отвечать за нашу таблицу (то есть фактически её делегировать). Сделаем этот класс наследником UIViewController, хотя это совершенно не обязательно (его можно спокойно унаследовать от NSObject), и назовём его XTableController. Итак, добавление класса-контроллера:

Вот, проект мы подготовили, давайте теперь опишем класс-контроллер таблицы: XTableController.

КОНТРОЛЛЕР

Итак, сейчас нам нужно подготовить класс, который будет управлять нашей таблицей.
Изначально файл XTableController.h должен выглядеть как-то так:

  1  2  3  4  5  
  @interface XTableController : UIViewController  {  }     @end

Нам нужно сделать две вещи:

1. Объявить о том, что наш класс поддерживает протоколы UITableViewDelegate и UITableViewDataSource. После того как об этом будет заявлено, нам нужно будет в файле XTableController.m описать соответствующие этим протоколам методы. Об этом чуть позже.

2. Подготовить структуры данных для нашей таблицы. С точки зрения данных, таблица это куча ячеек, которые могут быть разделены на разделы. В нашем случае у нас будет 3 раздела (физики, математики и программисты), и ячейки в разделах.

Вообще говоря, хранить данные вы можете как хотите. В нашем примере будет использоваться NSMutableArray. Между прочим, если переименовать файлы *.h и *.m в *.hpp и *.mm, то вы сможете использовать c++, что даст вам возможность использовать std::map и std::vector.

В свете вышесказанного, заголовочный файл нашего класса-контроллера преобразуется:

  1  2  3  4  5  6  7  8  9  10  11  
  @interface XTableController : UIViewController <UITableViewDelegate, UITableViewDataSource>  {    NSMutableArray * group_1;    NSMutableArray * group_2;    NSMutableArray * group_3;    NSMutableArray * group_names;  }     - (void) InitData;     @end

Теперь перейдём к файлу XTableController.m, в котором нам нужно описать ряд методов.
Итак, для начала инициализация структур с данными:

  1  2  3  4  5  6  7  8  
  - (void) InitData  {    group_names = [[NSMutableArray alloc] initWithObjects: @"Физики", @"Maтематики", @"Программисты", nil];       group_1 = [[NSMutableArray alloc] initWithObjects: @"В. Рентген", @"И. Ньютон", @"М. Склодовская-Кюри", @"H. Тесла", nil];    group_2 = [[NSMutableArray alloc] initWithObjects: @"О. Коши", @"К. Вейерштрасс", @"А. Колмогоров", nil];    group_3 = [[NSMutableArray alloc] initWithObjects: @"Д. Ричи", @"Д. Кнут", @"Б. Гейтс", nil];  }

Теперь нам нужно описать некоторые методы, чтобы наш класс мог удовлетворять протоколам, о которых я писал выше. Начинаем с количества секций в таблице (совет — читайте названия методов, они говорящие):

  1  2  3  4  
  - (NSInteger) numberOfSectionsInTableView: (UITableView*) tableView  {    return [group_names count];  }

Далее говорим, сколько ячеек в каждой секции:

  1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  
  - (NSInteger) tableView: (UITableView*) tableView numberOfRowsInSection: (NSInteger) section  {    switch (section)     {      case 0:        return [group_1 count];        break;      case 1:        return [group_2 count];        break;      default:        return [group_3 count];        break;	    }  }

Теперь опишем метод, отвечающий за название каждой секции:

  1  2  3  4  
  - (NSString*) tableView: (UITableView*) tableView titleForHeaderInSection: (NSInteger) section   {      return [group_names objectAtIndex: section];  }

Вот. Ну в целом понятно что происходит, да? Мы, фактически, метод за методом описываем структуру нашей таблицы. Мы задали количество групп, количество ячеек в группах и название групп. Осталось описать сами ячейки.

Вообще говоря, о функции cellForRowAtIndexPath можно смело писать статью, так как она даёт очень широкие возможности по изменению внешнего вида таблицы. Думаю именно об этом будет моя следующая статья, сейчас же не будем выпендриваться, и возьмём самый базовый тип ячеек (UITableViewCellStyleDefault).

Следует обратить внимание на связку dequeueReusableCellWithIdentifier / reuseIdentifier — это стандартный способ создания ячеек, позволяющий оптимизировать работу таблицы.

Итак, наш метод:

  1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  
  - (UITableViewCell*) tableView: (UITableView*) tableView cellForRowAtIndexPath: (NSIndexPath*) index_path  {    static NSString * CellIdentifier = @"Cell";	    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];       if (cell == nil)     {      // существует несколько предопределённых стилей для ячеек таблицы      // UITableViewCellStyleDefault - самый простой из них      cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];    }       switch (index_path.section)     {      case 0:        cell.textLabel.text = [group_1 objectAtIndex: index_path.row];			        break;      case 1:        cell.textLabel.text = [group_2 objectAtIndex: index_path.row];        break;      default:        cell.textLabel.text = [group_3 objectAtIndex: index_path.row];			        break;    }    return cell;  }

На этом описание нашего класса контроллера окончено. Это далеко не все методы, которые можно переопределить, есть много других. Описывая из вы можете добавить в вашу таблицу возможность поиска, удаления ячеек или из сортировки. Нам это пока не нужно. Теперь нужно добавить таблицу на нашу вьюшку (UIView), и связать с контроллером.

ДОБАВЛЕНИЕ ТАБЛИЦЫ

Для того, чтобы добавить таблицу в наше окошко, нужно открыть XSampleViewController.xib и перетащить на нашу UIView элемент UITableView. Вас не должно смущать, что в таблице уже есть секции и текст — это сделано просто для того, чтобы вы знали как примерно она будет выглядеть, если запустить программу то таблица будет пустой. Ну ничего, это не надолго =)
Заодно давайте переключим внешний вид таблицы на grouped.
На всякий случай скриншоты:

Итак у нас есть написанный класс-контроллер (XTableController), и таблица. Нужно их связать. Для этого нам нужно создать объект класса XTableController, и «сказать» нашей таблице, что ею будет управлять этот объект.

НАСТРОЙКА ДЕЛЕГИРОВАНИЯ

Сейчас мы будет работать с классом, отвечающим за нашу основную вьюшку (XSampleViewController). В нём мы свяжем таблицу и контроллер в единое целое.

Дла начала откроем файл XSampleViewController.h и модифицируем его, чтобы он выглядел так:

  1  2  3  4  5  6  7  8  9  10  
  #import <UIKit/UIKit.h>  #import "XTableController.h"     @interface XSampleViewController : UIViewController  {    IBOutlet UITableView * my_table;    XTableController * xcontroller;  }     @end

Мы добавили две строчки. Со второй всё понятно, а вот что такое IBOutlet может быть не вполне ясно. Как вы видите, после IBOutlet идёт обычное объявление указателя на таблицу. Так вот, поставив перед объявлением IBOutlet мы получим возможность связать нашу таблицу в Interface Builder с этим указателем. Фактически, поставленная запись IBOutlet просто говорит XCode, что этот указатель мы определим вручную, связав с каким-то элементом интерфейса. После этого my_table будет указывать на нашу таблицу.

Итак, давайте их таки свяжем. Для этого нужно открыть файл XSampleViewController.xib и кликнуть правой кнопкой мышки по нашей табличке. Далее на плюсик в разделе Reference Outlets, и протянуть линию к File's Owner. XCode предложит вам выбор, выбираем my_table. Теперь my_table указывает на реально существующую таблицу.
Читается это наверное страшновато, на самом деле это очень просто и удобно, посмотрите скриншоты — станет понятнее:

Теперь переменная my_table указывает на реальную таблицу. Нам остался последний шаг — проинициализировать контроллер и указать таблице, кто ею будет управлять. Для этого откроем файл XSampleViewController.m, и внесём изменения в метод ViewDidLoad. Этот метод будет вызван при загрузке нашей вьюшки.
Итак, смотрим:

  1  2  3  4  5  6  7  8  9  10  
  - (void)viewDidLoad  {    xcontroller = [[XTableController alloc] init];    [xcontroller InitData];       [my_table setDelegate: xcontroller];    [my_table setDataSource: xcontroller];       [super viewDidLoad];  }

Вот и всё. Перед тем как любоваться результат, давайте ещё раз вспомним основные шаги:
1. Описали класс для управления таблицей XTableController, удовлетворяющий протоколам UITableViewDelegate и UITableViewDataSource.
2. Добавили таблицу на нашу UIView, через IBOutlet связали её с кодом.
3. Создали объект нашего класса, настроили таблицу, чтобы ею управлял наш объект.

РЕЗУЛЬТАТ

Запускаем приложение, наслаждаемся результатом:

По доброй традиции прикладываю проект и исходный код. В следующей статье будем учиться делать таблицы офигенно красивыми. Успехов вам!

PROJECT & CODE

P.S. Если у вас что-то не получается, падает, зависает, или вам что-то не понятно — пишите вопросы в комментарии, я постараюсь вам помочь.

P.P.S. Если вам помогла эта статья, поставьте лайк, или напишите комментарий — мне будет приятно. Спасибо.

No comments:

Post a Comment