Как сделать UITableViewCell с различными подпредставлениями повторно используемыми? - PullRequest
5 голосов
/ 21 апреля 2011

У меня есть UITableView, в котором я отображаю, естественно, UITableViewCells, которые принадлежат к одному и тому же классу, назовем его MyCell. Итак, у меня есть один MyCell.xib, один MyCell.h и один MyCell.m.

К сожалению, эти ячейки содержат одно подпредставление, которое содержит различное содержимое, например, train subview и car subview. Так что, если UITableView нужна новая ячейка, это всегда MyCell, но иногда он содержит подпредставление поезда, а иногда и автомобильное подпредставление.

Теперь вот моя проблема: как сделать MyCell правильно многоразовым? Сама ячейка может использоваться повторно по назначению (в .xib я определил ее идентификатор), но ее подпредставление необходимо создавать снова и снова для каждой ячейки. Моей первой идеей было изменить идентификатор MyCell в зависимости от его содержимого, но, к сожалению, reuseIdentifier нельзя изменить во время выполнения. Я мог бы, однако, реализовать свой собственный - (NSString *) reuseIdentifier {}, который, я думаю, сработал бы, хотя я бы не считал его отличным стилем. Есть ли лучший способ сделать это?

Заранее большое спасибо!

РЕДАКТИРОВАТЬ: я понимаю, что мне нужно добавить, что подпредставления хранятся в своих собственных классах / XIBs, чтобы сохранить их код отделенным.

Ответы [ 4 ]

11 голосов
/ 22 апреля 2011

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

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

В Руководстве по программированию Apple Table View для iOS / Характеристики объектов ячеек , 3-я параграфа дает некоторое представление о значении идентификатора повторного использования.

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

Прежде всего небольшой пример использования cellForRowAtIndexPath и заводских настроек, а также настройки содержимого ячейки.

Я создаю новую ячейку с фабрикой, которой нужен tableView, чтобы он мог обрабатывать логику повторного использования.Следующее, что нужно сделать, - метод заполнить содержимое ячейки.В данном случае это ячейка, в которой показан видеоклип с некоторым текстом.

Метод делегата источника данных и помощник

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)anIndexPath
{
    VideoClipTableViewCell *cell = [TableViewCellFactory videoClipTableViewCellWithTableView:aTableView];

    [self configureVideoClipCellWithCell:cell andIndexPath:anIndexPath];

    // code to decide what kind of cell not shown, but it could be here, just move the model
    // access code from the configure cell up here and decide on what you get

    return cell;
}

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

- (void)configureVideoClipCellWithCell:(VideoClipTableViewCell *)aCell andIndexPath:(NSIndexPath *)anIndexPath
{
    VideoClip *videoClip = [videoClips objectAtIndex:anIndexPath.row];

    aCell.videoTitleLabel.text = videoClip.title;
    aCell.dateLabel.text = videoClip.date;

    // more data setting ...
}

TableViewFactory

Этот класс состоит в основном из вспомогательных методов и некоторых стандартных методов для выполнения реальной работы..

// Convenience static method to create a VideoClipTableViewCell
+ (VideoClipTableViewCell *)videoClipTableViewCellWithTableView:(UITableView *)aTableView
{
    return [self loadCellWithName:@"VideoClipTableViewCell" tableView:aTableView];
}

// method to simplify cell loading
+ (id)loadCellWithName:(NSString *)aName tableView:(UITableView *)aTableView
{
    return [self loadCellWithName:aName 
                        className:aName
                       identifier:aName
                        tableView:aTableView];
}

// method with actually tries to create the cell
+ (id)loadCellWithName:(NSString *)aName 
             className:(NSString *)aClassName 
            identifier:(NSString *)anIdentifier 
             tableView:(UITableView *)aTableView
{
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:anIdentifier];

    if (cell == nil) {
        UINib * nib = [UINib nibWithNibName:aName bundle:nil];  

        NSArray * nibContent = nil;

        nibContent = [nib instantiateWithOwner:nil options:nil];

        for (id item in nibContent) {
            if ([item isKindOfClass:NSClassFromString(aClassName)]) {
                cell = item;
            }
        }
    }
    return cell;
}

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

Некоторые важные вещи об использовании:

  • Имя подключенного класса, идентификатор повторного использования и имя пера:все равно, поэтому ячейка может быть создана только с одной строковой константой, в противном случае необходимо использовать длинную loadCellWithName.

  • Не забудьте установить идентификатор повторного использования в конструкторе интерфейса,

  • Перо должно содержать только одну TableViewCell (хотя может быть изменено с помощью некоторого кодирования)

  • Не устанавливать выходы владельца файла,используйте те из tableViewCell

  • Установите идентификатор класса ячейки для соответствующего класса, который должен быть создан в первую очередь

  • Посмотрите на скриншот

enter image description here

Мысли о создании подклассов собственных пользовательских ячеек

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

Основная проблема - сам интерфейс.Нелегко сделать разные типы ячеек на основе пользовательской ячейки в конструкторе интерфейсов.Первый подход заключается в том, чтобы скопировать nib-файл, переименовать его и использовать со всеми существующими ссылками и связать новые с различными выходами.Но что произойдет, если базовая ячейка должна быть заменена?Прохождение всех видов наследующих клеток может быть утомительной задачей.

Я только что наткнулся на Пользовательские представления в Интерфейсном Разработчике, использующие IBPlugins на Какао с Любовью.Это хороший урок, как расширить библиотеку компонентов в IB.Наша пользовательская базовая ячейка может стать элементом в библиотеке и стать шаблоном, который мы искали.Я думаю, что такой подход - правильный выбор.Тем не менее, рассматривая необходимые шаги, это не просто делается в течение 5 минут.

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

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

РЕДАКТИРОВАТЬ

С другой стороны, Apple предупреждает о чрезмерном использовании подпредставлений в ячейке:

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

В настоящее время любой подход имеет свои недостаткии преимущества:

  • Слишком большое количество человеко-просмотров снизит производительность, что легко сделать с помощью IB

  • Рисование с помощью кода приведет к ухудшению обслуживания базы кодано будет работать лучше

  • Пропуск IB позволяет упростить подклассы классов шаблонных ячеек

  • Иерархия с помощью подклассов труднодостижима с IB с файлами nib

3 голосов
/ 21 апреля 2011

Есть несколько способов сделать это. Вам нужен способ получить доступ к этому подпредставлению и сбросить или изменить его при повторном использовании.

  1. Вы можете создать подкласс UITableViewCell с вашим собственным классом ячеек, который имеет свойство для поезда или вагона. Таким образом, вы можете получить доступ и изменить это представление при повторном использовании ячейки.

  2. Назначьте разные идентификаторы для каждого типа ячеек:

`

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        static NSString *CarCellIdentifier = @"CarCell";
    static NSString *TrainCellIdentifier = @"TrainCell";
        if(indexPath == carCellNeeded) { 
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CarCellIdentifier];
             if (cell == nil) {
                 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CarCellIdentifier] autorelease]; 
             [cell addSubview:carView];
        }
        } else if(indexPath == trainCellNeeded){ 
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TrainCellIdentifier];
                if (cell == nil) {
                  cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TrainCellIdentifier] autorelease]; 
            [cell addSubview:trainView];
             }
        }
      return cell; 
}
  1. Или назначьте специальный тег для этого добавляемого подпредставления, и когда ячейка возвращается снова для повторного использования, вы можете получить доступ к этому конкретному подпредставлению по его тегу.
1 голос
/ 21 апреля 2011

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"CellIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = /* load from nib */
    }
    if (/*indexPath conditionForTrainCell*/) {
       cell.trainSubview.hidden = NO;
        cell.carSubview.hidden = YES;
        // configure train cell
    }
    else {
       cell.trainSubview.hidden = YES;
        cell.carSubview.hidden = NO;
        // configure car cell
    }
    return cell;
}
0 голосов
/ 08 сентября 2013

самое простое - создать пользовательский подкласс UITableViewCell и создать для него xib. Установите корневое представление в xib как uitableviewCell и установите его класс в свой подкласс UITableViewCell. Установите класс владельца файла в качестве подкласса TableViewController и добавьте в него все необходимые подпредставления. Тогда вы можете просто:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * CellIdentifier = @"cellIdentifier";

    TableViewMessageCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil)
    {
        cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([TableViewMessageCell class])
                                              owner:self
                                            options:nil] lastObject];
    }

    Message * message = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.message      = message;

    return cell;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...