ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C часть 2

Сегментированная таблица (Grouped UITableView)

Перед тем как писать этот пример, я пролистал много интернет-страничек в поисках подобного примера. Но к сожалению — ничего достойного не смог найти. Этот факт и подтолкнул меня на решение написать действительно хороший пример работы с группированными таблицами.

 

 

В предыдущем примере мы убедились в простоте работы с таблицами. Сегодня я вас научу выводить сгруппированные элементы. Чтобы не создавать новую таблицу давайте воспользуемся кодом с предыдущего примера.

 

В одном из уроков я рассказывал как удобно пользоваться словарями. В самом начале я приводил пример, когда два массива хранятся в одном словаре (каждый под своим ключом). Я предлагаю использовать этот метод в нашем примере. То есть, источником данных у нас будет словарь (NSDictionary), в котором храниться два массива. Как раз эти два массива мы и разобьем на секции, а ключи под которыми эти массивы хранятся буду именами секций.

 

Для начала нам нужно изменить тип источника данных для таблицы. Делать это следует в интерфейсе нашего класса, все сводится к указанию переменной students типа NSDictionary (при этом не забудьте изменить тип этой переменной в ее свойствах). Кроме этого я предлагаю объявить функцию

 

— (NSArray*)curentStudents:(NSInteger)index;

 

Она нам будет возвращать текущий массив по номеру индекса. Необходимость этой функции связана с тем, что ее нам нужно вызывать в двух частях программы. Иначе говоря, без функции нам пришлось бы дублировать код. Дублирование кода — это плохой пример программирования. Первое правило чистоты кода гласит, что код должен успешно компилироваться, а второе — в коде не должно быть дублированного кода. Иначе, если мы захотим исправить что-то в части кода — нам прийдется исправлять это во всех частях дубликатов.

 

Теперь перейдем к реализации класса. Давайте добавим реализацию функции curentStudents:

 

<code data-result="[object Object]">- (NSArray*)curentStudents:(NSInteger)index {
    NSArray *keys = [students allKeys];
    NSString *curentKey = [keys objectAtIndex:index];
    NSArray *curentStudents = [students objectForKey:curentKey];
    return curentStudents;
}</code>

 

Сначала мы получаем массив всех ключей нашего словаря, затем по индексу получаем ключ массива, под которым в словаре храниться массив студентов (мальчиков или девочек). В конце — по известному ключу получаем сам массив и возвращаем его.

 

Как я уже говорил, с урока NSDictionary (словари) возьмем часть второго примера:

 

<code data-result="[object Object]">NSArray *girls = [NSArray arrayWithObjects:@"Amanda", @"Ira", @"Natali",nil];
NSArray *boys = [NSArray arrayWithObjects:@"Sergei", @"Vova", @"Ivan", nil];
NSDictionary *children = [NSDictionary dictionaryWithObjectsAndKeys:girls,@"girls", boys, @"boys", nil];
NSLog(@"girls: %@", [children objectForKey:@"girls"]);
NSLog(@"boys: %@", [children objectForKey:@"boys"]);</code>

 

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

 

<code data-result="[object Object]">#define titleGirls @"girls"
#define titleBoys @"boys"</code>

 

Теперь, если мы захотим изменить имена секций — нам не прийдется их искать по всему коду. Они будут на самом видном месте (после импортирования библиотек).

 

Теперь изменим метод viewDidLoad:

 

<code data-result="[object Object]">- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"Students";

    NSArray *girls = [NSArray arrayWithObjects:@"Amanda", @"Ira", @"Natali", nil];
    NSArray *boys = [NSArray arrayWithObjects:@"Tom", @"Bill", @"Tom", @"Joe", @"Bob", nil];

    self.students = [NSDictionary dictionaryWithObjectsAndKeys:girls, titleGirls, boys, titleBoys, nil];
}</code>

 

Как уже и оговаривалось мы создаем два массива, затем добавляем их в наш словарь, который объявлен как переменная нашего класса, и для которого мы создали свойства. В качестве ключей словаря мы используем только что объявленые дэфайны.

 

После создания источника следует указать количество секций в таблице, делается это в методе numberOfSectionsInTableView:

 

<code data-result="[object Object]">- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [students count];
}</code>

 

NSDictionary как и NSArray имеет метод count, только в случае с NSDictionary он возвращает количество записей словаря.

 

Теперь необходимо указать сколько строк будет в каждой секции. Как мы уже знаем — делается это в методе numberOfRowsInSection:

 

<code data-result="[object Object]">- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSArray *curentStudents = [self curentStudents:section];
    return [curentStudents count];
}</code>

 

Вот тут как раз и пригодилась нам функция curentStudents. Мы передаем в нее номер текущей секции, а она нам возвращает массив записей, которые будут расположены в этой секции. Затем мы узнаем количество этих записей и возвращаем его.

 

В методе titleForHeaderInSection мы указываем имя секции:

 

<code data-result="[object Object]">- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {  
    return [[students allKeys] objectAtIndex:section];
}</code>

 

Здесь мы просто получаем массив всех ключей, которые имеет словарь students, затем по индексу получаем значение нужного нам ключа и возвращаем его. Это значение и будет отображено в имени секции.

 

Теперь перейдем к самому интересному — заполнение ячеек. Как уже известно, выполняется это в методе cellForRowAtIndexPath. Давайте изменим его с учетом поставленной задачи:

 

<code data-result="[object Object]">- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                       reuseIdentifier:CellIdentifier] autorelease];
    }

    NSArray *curentStudents = [self curentStudents:indexPath.section];
    cell.textLabel.text = [curentStudents objectAtIndex:indexPath.row];

    return cell;
}</code>

 

Как видите, наш метод претерпел не значительные изменения. Код, который добавил Xcode мы оставили неизменным, добавив лишь одну строку. В этой строке мы снова используем метод curentStudents. Мы снова передаем в него индекс текущей секции (indexPath.section), а он как и раньше, возвращает массив записей, которыми будет заполняться эта секция. Затем каждую строку таблицы заполняем значениями с полученного массива.

 

В результате у нас получился компактный, удобный и чистый код. Поэкспериментируйте со стилем таблицы, чтобы привести ее к такому виду как на картинке в начале этого примера.

 

 

Это один из способов отображения информации в таблице по группам, но этого более чем достаточно для базового понимания сегментированных таблиц.

 

Исходный код можно скачать здесь.