Статьи

iOS SDK: создание пользовательских ячеек UITableView

Начиная с iOS 3 разработчикам было доступно несколько предопределенных стилей ячеек. Они удобны и очень полезны для создания прототипов, но во многих ситуациях вам действительно необходимо индивидуальное решение, адаптированное к потребностям проекта, над которым вы работаете. В этом уроке я покажу вам, как настроить ячейки табличного представления, используя статические ячейки и ячейки прототипа, а также подклассы UITableViewCell .


Хотя UITableViewCell напрямую наследуется от UIView , его анатомия более сложна, чем вы могли ожидать. Если вы планируете UITableViewCell подкласс UITableViewCell , необходимо понимать анатомию ячейки табличного представления. По своей сути ячейка табличного представления — это не что иное, как представление с несколькими подпредставлениями, представление фона и выбранного фона, представление содержимого и несколько других более экзотических подпредставлений, таких как вспомогательное представление справа. Давайте посмотрим на различные подпредставления.

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

Дополнительный вид ячейки табличного представления может быть любым типом представления. Вы, вероятно, уже знакомы с представлениями индикаторов раскрытия и раскрытия информации. Дополнительный вид, однако, также может быть кнопкой, ползунком или регулятором шага. Взгляните на приложение « Настройки» на устройстве iOS, чтобы получить представление о некоторых возможностях. Имейте в виду, что пространство вспомогательного представления ограничено для стандартной ячейки табличного представления. Это означает, что не каждый вид может или должен использоваться как дополнительный вид.

Элемент управления для редактирования — это еще одно подпредставление ячейки табличного представления, которое сдвигается в и из вида при изменении режима редактирования табличного представления. Когда табличное представление входит в режим редактирования, источник данных табличного представления спрашивает, какие ячейки табличного представления являются редактируемыми, отправляя ему сообщение tableView:canEditRowAtIndexPath: для каждой видимой в данный момент ячейки. Редактируемые ячейки табличного представления должны войти в режим редактирования, который показывает элемент управления редактирования слева и, если применимо, элемент управления переупорядочением справа. Ячейка табличного представления в режиме редактирования скрывает свое вспомогательное представление, чтобы освободить место для элемента управления переупорядочением и кнопки подтверждения удаления, которая появляется справа, когда строка помечена для удаления.

Фон и выбранные фоновые представления расположены позади всех других подпредставлений ячейки табличного представления. В дополнение к этим фоновым представлениям класс UITableViewCell также определяет фоновое представление ( multipleSelectionBackgroundView ), которое используется для табличных представлений, поддерживающих множественный выбор.


В этом уроке я не буду много говорить о предопределенных стилях ячеек табличного представления. Основное внимание в этом руководстве уделяется тому, как настроить ячейки табличного представления таким образом, что это невозможно при использовании предопределенных стилей ячеек табличного представления. Имейте в виду, однако, что эти предопределенные стили ячеек достаточно эффективны при настройке ячеек табличного представления. Класс UITableViewCell предоставляет первичную ( textLabel ) и вторичную ( detailTextLabel ) метки, а также вид изображения ячейки ( imageView ) слева. Эти свойства предоставляют прямой доступ к подпредставлениям ячейки, что означает, что вы можете напрямую изменять атрибуты метки, такие как шрифт и цвет текста. То же самое верно для представления изображения ячейки.

Несмотря на гибкость, предлагаемую предопределенными стилями ячеек, не рекомендуется перемещать различные подпредставления. Если вы ищете больше контроля с точки зрения расположения ячеек, то вам нужно выбрать другой маршрут.


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

Создайте новый проект в Xcode, выбрав шаблон приложения Single View Application , назовите его Static (рисунок 1) и включите раскадровки и автоматический подсчет ссылок (ARC). На момент написания статические ячейки доступны только в сочетании с раскадровками.

Настройка ячеек табличного представления - статические ячейки - настройка проекта

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

1
2
3
4
5
6
7
8
9
#import <UIKit/UIKit.h>
 
@interface MTViewController : UITableViewController
 
@property (weak, nonatomic) IBOutlet UILabel *firstLabel;
@property (weak, nonatomic) IBOutlet UILabel *secondLabel;
@property (weak, nonatomic) IBOutlet UILabel *thirdLabel;
 
@end

Откройте основную раскадровку, выберите контроллер представления и удалите его. Перетащите контроллер табличного представления из библиотеки объектов и измените его класс на MTViewController в Identity Inspector (рисунок 2). Выберите представление таблицы контроллера представления и установите для его атрибута Content значение « Статические ячейки» в Инспекторе атрибутов (рисунок 3). Без необходимости реализации протокола UITableViewDataSource табличное представление будет размечено так, как определено в раскадровке.

Настройка ячеек табличного представления - статические ячейки - добавление контроллера табличного представления
Настройка ячеек табличного представления - статические ячейки - установка свойства содержимого табличных представлений в статические ячейки

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

Настройка ячеек табличного представления - статические ячейки - расположение статических ячеек
1
2
3
4
5
6
7
8
— (void)viewDidLoad {
    [super viewDidLoad];
 
    // Configure Labels
    [self.firstLabel setText:@»First Label»];
    [self.secondLabel setText:@»Second Label»];
    [self.thirdLabel setText:@»Third Label»];
}

Создайте и запустите приложение в iOS Simulator, чтобы увидеть результат. Статические ячейки достаточно мощные, особенно для прототипирования приложений. Они весьма полезны, когда макет табличного представления не изменяется, например, в настройках приложения или в представлении. Помимо указания количества строк и разделов табличного представления, вы также можете вставить собственные верхние и нижние колонтитулы разделов.


Еще одно большое преимущество использования раскадровок — это ячейка прототипа, которая была представлена ​​вместе с раскадровками в iOS 5. Ячейки прототипа — это динамически заполняемые шаблоны. Каждая ячейка прототипа идентифицируется идентификатором повторного использования, посредством которого на ячейку прототипа можно ссылаться в коде. Давайте посмотрим на другой пример.

Создайте новый проект Xcode на основе шаблона приложения Single View . Назовите проект Prototype и включите раскадровки и ARC для проекта (рисунок 5). Как и в предыдущем примере, измените подкласс контроллера представления ( MTViewController ) на UITableViewController . В этом примере нет необходимости объявлять торговые точки.

Настройка ячеек табличного представления - Ячейки прототипа - Настройка проекта
1
2
3
4
5
#import <UIKit/UIKit.h>
 
@interface MTViewController : UITableViewController
 
@end

Как и в предыдущем примере, удалите существующий контроллер представления в основной раскадровке и перетащите контроллер представления таблицы из библиотеки объектов . Измените класс нового контроллера табличного представления на MTViewController в Identity Inspector .

Как я упоминал ранее, каждая ячейка прототипа имеет идентификатор повторного использования, по которому на нее можно ссылаться. Выберите ячейку прототипа в табличном представлении и установите для ее идентификатора значение CellIdentifier, как показано на рисунке ниже (рисунок 6).

Настройка ячеек табличного представления - Ячейки прототипа - Установка идентификатора повторного использования ячейки прототипа

Как и в случае статических ячеек, вы можете добавить подпредставления в представление содержимого ячейки прототипа. Нет необходимости указывать количество строк и разделов, как мы делали в предыдущем проекте. При использовании прототипа ячеек необходимо реализовать протокол источника данных табличного представления ( UITableViewDataSource ). Вам может быть интересно, как мы ссылаемся на подпредставления представления содержимого прототипа ячейки? Есть два варианта. Самый простой вариант — назначить каждому представлению тег и запросить представление содержимого ячейки для подпредставления с определенным тегом. Посмотрим, как это работает.

Добавьте метку в ячейку прототипа и установите для ее тега значение 10 в Инспекторе атрибутов (рисунок 7). В классе MTViewController мы реализуем протокол источника данных табличного представления, как показано ниже. Идентификатор повторного использования ячейки объявлен как статическая строковая константа.

Настройка ячеек табличного представления - Ячейки прототипа - Установка тега подпредставления в представлении содержимого ячейки прототипа
1
static NSString *CellIdentifier = @»CellIdentifier»;
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
— (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 5;
}
 
— (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 5;
}
 
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 
    // Configure Cell
    UILabel *label = (UILabel *)[cell.contentView viewWithTag:10];
    [label setText:[NSString stringWithFormat:@»Row %i in Section %i», [indexPath row], [indexPath section]]];
 
    return cell;
}
 
— (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}
 
— (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}

Запустите приложение в iOS Simulator, чтобы увидеть результат. Хотя кажется, что работать с ячейками-прототипами легко, используя теги для идентификации подпредставлений, это быстро становится неудобным, когда компоновка ячейки является более сложной. Лучшим подходом является использование подкласса UITableViewCell . Создайте новый класс Objective C, назовите его MTTableViewCell и сделайте его подклассом UITableViewCell . Откройте файл заголовка нового класса и создайте выход типа UILabel именем mainLabel .

1
2
3
4
5
6
7
#import <UIKit/UIKit.h>
 
@interface MTTableViewCell : UITableViewCell
 
@property (weak, nonatomic) IBOutlet UILabel *mainLabel;
 
@end

Прежде чем мы сможем использовать наш подкласс, нам нужно внести некоторые изменения в основную раскадровку. Откройте основную раскадровку, выберите ячейку прототипа, а затем установите для ее класса значение MTTableViewCell в Identity Inspector (рисунок 8). Откройте Инспектор соединений и подключите розетку mainLabel с меткой, которую мы добавили к ячейке прототипа (рисунок 9).

Настройка ячеек представления таблицы - ячейки прототипа - изменение класса ячейки прототипа
Настройка ячеек табличного представления - Ячейки-прототипы - Подключение выхода ячейки-прототипа

Изменения, которые мы внесли в раскадровку, позволяют нам реорганизовать tableView:cellForRowAtIndexPath: как показано ниже. Не забудьте импортировать заголовочный файл класса MTTableViewCell . Я надеюсь, что вы согласны с тем, что это изменение делает наш код более читабельным и понятным.

1
#import «MTTableViewCell.h»
1
2
3
4
5
6
7
8
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MTTableViewCell *cell = (MTTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 
    // Configure Cell
    [cell.mainLabel setText:[NSString stringWithFormat:@»Row %i in Section %i», [indexPath row], [indexPath section]]];
 
    return cell;
}

Запустите приложение в iOS Simulator. Клетки-прототипы — замечательный компонент раскадровки. Они делают настройку ячеек табличного представления невероятно легкой без особых усилий.


В предыдущем примере мы создали пользовательский подкласс UITableViewCell . Тем не менее, мы не использовали силу подклассов. Вместо этого мы полагались на универсальность прототипа клеток. В третьем и последнем варианте я покажу вам, как создать собственный подкласс UITableViewCell без использования прототипа ячеек. Существует несколько стратегий создания подкласса UITableViewCell и та, которую я покажу вам в следующем примере, ни в коем случае не является единственным способом. В этом примере я хочу показать, чем подклассы отличаются от первых двух вариантов, в которых мы использовали Interface Builder и раскадровки.

Создайте новый проект Xcode на основе шаблона приложения Single View , назовите его Subclass и включите ARC для нового проекта. Убедитесь, что флажок « Использовать раскадровки» не установлен (рисунок 10).

Настройка ячеек табличного представления - создание подклассов UITableViewCell - настройка проекта

Как мы делали в двух предыдущих примерах, начнем с изменения суперкласса контроллера представления ( MTViewController ) на UITableViewController . Откройте файл XIB контроллера представления, удалите представление контроллера представления и перетащите представление таблицы из библиотеки объектов . Выберите табличное представление и установите его dataSource и delegate выходы Владельцу файла , то есть контроллеру представления. Выберите владельца файла и установите выход его view в виде таблицы (рисунок 11).

1
2
3
4
5
#import <UIKit/UIKit.h>
 
@interface MTViewController : UITableViewController
 
@end
Настройка ячеек табличного представления - создание подклассов UITableViewCell - настройка файла XIB контроллеров представления

Прежде чем мы создадим пользовательский подкласс UITableViewCell , давайте сначала реализуем протокол источника данных табличного представления, чтобы убедиться, что все работает как положено. Как мы делали ранее, рекомендуется объявлять идентификатор повторного использования ячейки как статическую строковую константу. Чтобы упростить повторное использование ячейки (и инициализацию), мы отправляем табличному представлению сообщение registerClass:forCellReuseIdentifier: и передаем имя класса и идентификатор повторного использования ячейки в качестве первого и второго параметра. Это дает табличному представлению всю информацию, необходимую для создания новых ячеек, когда нет доступных для повторного использования ячеек. Что это нам дает? Это означает, что мы никогда не должны явно создавать экземпляр ячейки. Табличное представление позаботится об этом за нас. Все, что нам нужно сделать, это попросить табличное представление освободить для нас ячейку. Если доступна повторно используемая ячейка, табличное представление возвращает ее нам. Если ячейки недоступны для повторного использования, табличное представление автоматически создает закулисное представление. Хорошим местом для регистрации класса для повторного использования ячеек является метод viewDidLoad контроллера представления (см. Ниже).

1
static NSString *CellIdentifier = @»CellIdentifier»;
1
2
3
4
5
6
— (void)viewDidLoad {
    [super viewDidLoad];
 
    // Register Class for Cell Reuse Identifier
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifier];
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
— (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 5;
}
 
— (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 5;
}
 
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
 
    // Configure Cell
    [cell.textLabel setText:[NSString stringWithFormat:@»Row %i in Section %i», [indexPath row], [indexPath section]]];
 
    return cell;
}
 
— (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}
 
— (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}

Подкласс, который мы собираемся создать, довольно прост. Моя цель — показать вам, что происходит под капотом и что требуется для создания подкласса UITableViewCell , в отличие от использования статических или прототипных ячеек. Создайте новый класс Objective C, назовите его MTTableViewCell и сделайте его подклассом UITableViewCell . Откройте файл заголовка класса и добавьте открытое свойство типа UILabel с именем mainLabel .

1
2
3
4
5
6
7
#import <UIKit/UIKit.h>
 
@interface MTTableViewCell : UITableViewCell
 
@property (strong, nonatomic) UILabel *mainLabel;
 
@end

Как вы можете видеть ниже, реализация MTTableViewCell не сложна. Все, что мы делаем, это initWithStyle:reuseIdentifier: суперкласс initWithStyle:reuseIdentifier: метод. Этот метод вызывается табличным представлением, когда он создает для нас класс. Недостатком предоставления разрешения табличного представления для создания экземпляров ячеек является то, что вы не можете указать первый аргумент этого метода, стиль ячейки. Вы можете прочитать больше об этом в переполнении стека .

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
— (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
 
    if (self) {
        // Helpers
        CGSize size = self.contentView.frame.size;
 
        // Initialize Main Label
        self.mainLabel = [[UILabel alloc] initWithFrame:CGRectMake(8.0, 8.0, size.width — 16.0, size.height — 16.0)];
 
        // Configure Main Label
        [self.mainLabel setFont:[UIFont boldSystemFontOfSize:24.0]];
        [self.mainLabel setTextAlignment:NSTextAlignmentCenter];
        [self.mainLabel setTextColor:[UIColor orangeColor]];
        [self.mainLabel setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
 
        // Add Main Label to Content View
        [self.contentView addSubview:self.mainLabel];
    }
 
    return self;
}

Чтобы использовать наш новый класс, импортируйте его файл заголовка в MTViewController.m , обновите метод viewDidLoad контроллера viewDidLoad и tableView:cellForRowAtIndexPath: метод протокола источника данных табличного представления, как показано ниже.

1
#import «MTTableViewCell.h»
1
2
3
4
5
6
— (void)viewDidLoad {
    [super viewDidLoad];
 
    // Register Class for Cell Reuse Identifier
    [self.tableView registerClass:[MTTableViewCell class] forCellReuseIdentifier:CellIdentifier];
}
1
2
3
4
5
6
7
8
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MTTableViewCell *cell = (MTTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
 
    // Configure Cell
    [cell.mainLabel setText:[NSString stringWithFormat:@»Row %i in Section %i», [indexPath row], [indexPath section]]];
 
    return cell;
}

UITableViewCell подклассов UITableViewCell — гораздо более сложная тема, чем то, что я обсуждал в этом руководстве. Если вы хотите, чтобы я больше писал об этой теме, дайте мне знать в комментариях ниже. Не забудьте запустить приложение в iOS Simulator, чтобы увидеть подкласс в действии.


Каковы преимущества использования пользовательского подкласса по сравнению с использованием ячеек-прототипов? Простой ответ — гибкость и контроль. Несмотря на свою полезность, клетки-прототипы имеют свои пределы. Основным препятствием, с которым сталкиваются многие разработчики при создании подкласса UITableViewCell является тот факт, что это утомительно. Написание кода пользовательского интерфейса утомительно, и немногие люди — если таковые имеются — наслаждаются этим. Apple создала Interface Builder по уважительной причине. Можно также создать настраиваемые ячейки табличного представления с помощью Interface Builder и загрузить файл XIB во время выполнения. Я обычно создаю сложные ячейки табличного представления в Интерфейсном Разработчике и преобразовываю проект в код, когда я доволен результатом. Этот трюк сэкономит вам много времени.

Следует ли вам использовать Interface Builder для создания пользовательских ячеек табличного представления или разработки пользовательских подклассов UITableViewCell с нуля, действительно зависит от ситуации и ваших предпочтений. Однако очевидно, что Interface Builder стал более мощным, и появление Xcode 4 означало еще один большой шаг вперед — несмотря на ранние ошибки и проблемы, с которыми он столкнулся.

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

Я надеюсь, вам понравился этот урок. Если вы все время работаете с iOS, почему бы не проверить ассортимент более чем 1000 шаблонов приложений iOS на Envato Market? Со всем, от утилит iOS и элементов пользовательского интерфейса до шаблонов аудио и видео приложений , вы обязательно найдете что-то, что может ускорить ваш рабочий процесс.

iOS app templates on Envato Market
Шаблоны приложений для iOS на Envato Market

Или вы можете отправиться в Envato Studio, где вы найдете разработчиков Android, готовых работать над любым проектом, большим или маленьким.

App developers on Envato Studio
Разработчики приложений на Envato Studio