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

Раскрывающаяся таблица

Сегментированная таблица с возможностью раскрытия секций — один из самых распространенных элементов интерфейса. В трех из шести своих приложениях я использую именно такой вид таблицы. (Для примера можете посмотреть на Накачай-ка). В этом уроке я хочу рассказать о том как создать такую таблицу. В его основу положен урок сегментированной таблицы (Сегментированная таблица (Grouped UITableView)), но для чистоты эксперимента я решил создать новый проект на основе шаблона View Based Application и назвал я его DropdownTable.

 

Исходными данными для таблицы будет массив students. Объявим его в интерфейсе главного контроллера представления:

 

<code data-result="[object Object]">@interface ViewController : UITableViewController

@property (strong, nonatomic) NSArray *students;

@end</code>

 

Элементами массива students будут словари. Каждый из элемента будет содержать массив имен студентов, заголовок группы и состояния сексции. Формировать вышеупомянутый массив мы будем в методе viewDidLoad, а выглядит это так:

 

<code data-result="[object Object]">- (void)viewDidLoad
{
    [super viewDidLoad];

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

    NSMutableDictionary *girlsSection = [NSMutableDictionary dictionary];
    [girlsSection setObject:girls forKey:@"items"];
    [girlsSection setObject:@"Girls" forKey:@"title"];

    NSMutableDictionary *boysSection = [NSMutableDictionary dictionary];
    [boysSection setObject:boys forKey:@"items"];
    [boysSection setObject:@"Boys" forKey:@"title"];

    self.students = @[girlsSection, boysSection];
}</code>

 

Теперь методы таблицы:

 

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

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSDictionary *currentSection = [self.students objectAtIndex:section];
    if ([[currentSection objectForKey:@"isOpen"] boolValue]) {
        NSArray *items = [currentSection objectForKey:@"items"];
        return items.count;
    }
    return 0;
}

- (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];
    }

    NSDictionary *currentSection = [self.students objectAtIndex:indexPath.section];
    NSArray *items = [currentSection objectForKey:@"items"];
    NSString *currentItem = [items objectAtIndex:indexPath.row];
    cell.textLabel.text = currentItem;

    return cell;
}</code>

 

Рассмотрим подробнее метод numberOfRowsInSection. В нем мы по индексу секции получаем ее текущий элемент. Затем следует проверка, если данная секця раскрыта — возвращаем ее количество элементов, в противном случае — возвращаем число ноль. Таким образом, мы решаем вопрос отображения элементов в зависимости от состояния секции. В cellForRowAtIndexPath такой проверки мы не делаем, в нем мы просто получаем текущую секцию и выводим значения ее элементов. Это связано с тем, что если в метод numberOfRowsInSection для какой-то секции мы вернули нулевое значение элементов — метод cellForRowAtIndexPath для этой секции не вызовится.

 

Теперь добавим возможность раскрывать и сворачивать эти секции. Поможет нам в этом метод viewForHeaderInSection:

 

<code data-result="[object Object]">- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    NSDictionary *currentSection = [self.students objectAtIndex:section];
    NSString *sectionTitle = [currentSection objectForKey:@"title"];
    BOOL isOpen = [[currentSection objectForKey:@"isOpen"] boolValue];
    NSString *arrowNmae = isOpen? @"arrowUp":@"arrowDown";

    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(0.0f, 0.0f, 320.0f, 50.0f);
    button.tag = section;
    button.backgroundColor = [UIColor brownColor];
    [button setTitle:sectionTitle forState:UIControlStateNormal];
    [button addTarget:self action:@selector(didSelectSection:)
     forControlEvents:UIControlEventTouchUpInside];
    [button setImage:[UIImage imageNamed:arrowNmae] forState:UIControlStateNormal];
    return button;
}</code>

 

Как и в numberOfRowsInSection, мы по индексу получаем текущий элемент секции. В отдельные переменные выводим ее заголовок и состояние. В зависимости от состояния секции формируем имя файла картинки. После подготовки всех переменных создаем кнопку, которой устанавливаем имя секции и картинку. Елси кто-то забыл (или не знал) как программно создать кнопку — можно подсмотреть вшпаргалке. Особое внимание при создании кнопки следует обратить на установку значения tag. Таким образом, мы оставляем метку, чтобы потом знать к какой секции относиться эта кнопка. При нажатии на кнопку вызывается метод didSelectSection, добавим же его реализацию:

 

<code data-result="[object Object]">- (void)didSelectSection:(UIButton*)sender {
    //Получение текущей секции
    NSMutableDictionary *currentSection = [self.students objectAtIndex:sender.tag];

    //Получение элементов секции
    NSArray *items = [currentSection objectForKey:@"items"];

    //Создание массива индексов
    NSMutableArray *indexPaths = [NSMutableArray array];
    for (int i=0; i&lt;items.count; i++) {
        [indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:sender.tag]];
    }

    //Получение состояния секции
    BOOL isOpen = [[currentSection objectForKey:@"isOpen"] boolValue];

    //Установка нового состояния
    [currentSection setObject:[NSNumber numberWithBool:!isOpen] forKey:@"isOpen"];

    //Анимированное добавление или удаление ячеек секции
    if (isOpen) {
        [self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
    } else {
        [self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
    }

    //Обновление картинки кнопки
    NSString *arrowNmae = isOpen? @"arrowDown.png":@"arrowUp.png";
    [sender setImage:[UIImage imageNamed:arrowNmae] forState:UIControlStateNormal];
}</code>

 

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

 

Картинки состояния секции можете использовать свои, либо взять с проекта урока.