Загрузка многоразового UITableViewCell из пера


89

Я могу создавать собственные UITableViewCells и загружать их, используя технику, описанную в ветке http://forums.macrumors.com/showthread.php?t=545061 . Однако использование этого метода больше не позволяет вам инициализировать ячейку с помощью reuseIdentifier, что означает, что вам нужно создавать совершенно новые экземпляры каждой ячейки при каждом вызове. Кто-нибудь придумал хороший способ кэшировать определенные типы ячеек для повторного использования, но при этом иметь возможность создавать их в Interface Builder?

Ответы:


74

Просто реализуйте метод с соответствующей сигнатурой метода:

- (NSString *) reuseIdentifier {
  return @"myIdentifier";
}

Где реализовать этот метод?
— Кришнан

2
В вашем подклассе UITableViewCell. developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/…
— DenTheMan,

5
Это рискованно. Что произойдет, если у вас есть два подкласса подкласса ячейки, и вы используете оба из них в одном табличном представлении? Если они отправят вызов идентификатора повторного использования на super, вы удалите из очереди ячейку неправильного типа .............. Я думаю, вам нужно переопределить метод reuseIdentifier, но он вернет замененный идентификатор строка.
— SK9

3
Чтобы убедиться, что он уникален, вы можете сделать:return NSStringFromClass([self class]);
— ivanzoid

119

Фактически, поскольку вы создаете ячейку в Interface Builder, просто установите там идентификатор повторного использования:

IB_reuse_identifier

Или, если вы используете Xcode 4, проверьте вкладку инспектора атрибутов:

введите описание изображения здесь

(Изменить: после того, как ваш XIB сгенерирован XCode, он содержит пустой UIView, но нам нужен UITableViewCell; поэтому вам нужно вручную удалить UIView и вставить ячейку представления таблицы. Конечно, IB не будет отображать какие-либо параметры UITableViewCell для UIView.)


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

4
Не думайте об этом как об уникальном идентификаторе - думайте о нем больше как об имени типа.
— Тим Китинг

Я не вижу возможности установить идентификатор через встроенный построитель интерфейса в Xcode 4.3.3. Я определенно устанавливаю класс для своего подкласса UITableViewCell. Я просто скучаю по нему или его больше нет?
— Тайлер

3
Хорошо, я решил свою проблему. Если вы начнете с объекта UIView в построителе интерфейса (в Xcode 4) и измените его класс на UITableViewCell, вы не получите специфичных для ячейки свойств, таких как идентификатор повторного использования. Чтобы получить это, вы должны начать с пустого xib и перетащить объект ячейки таблицы, который затем будет иметь свойства, специфичные для ячейки, которые вы можете редактировать.
— Тайлер

1
@Krishnan Подумайте об этом так - когда вы создаете ячейку табличного представления с идентификатором X, вы говорите: «Дайте мне ячейку из пула с меткой X». Если пул существует, и в нем есть свободная ячейка, то он отдает ее вам. В противном случае он создает пул (при необходимости), затем обновляет ячейку, маркирует ее «X» и передает ее вам. Таким образом, ячейки МОГУТ быть уникальными - например, вы можете создать пул только из одной ячейки с определенным идентификатором - но библиотека использует стратегию, подобную свободной списку, чтобы избежать выделения / освобождения памяти.
— Тим Китинг

66

Теперь в iOS 5 для этого есть подходящий метод UITableView:

- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier

10
Другие ответы в этой ветке, включая принятый, содержат устаревшие рекомендации.
— Kaelin Colclasure

это обратно совместимо? Я имею в виду, что если я разрабатываю приложение с SDK 5.0 и целевой минимум 4.0, будет ли приложение работать, например, на устройствах с iOS 4.0?
— Abolfoooud

1
Нет, это не имеет обратной совместимости, как любой новый API в iOS 5.0.
— marzapower 05


47

Я не могу вспомнить, где я изначально нашел этот код, но до сих пор он отлично работал у меня.

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"CustomTableCell";
    static NSString *CellNib = @"CustomTableCellView";

    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellNib owner:self options:nil];
        cell = (UITableViewCell *)[nib objectAtIndex:0];
    }

    // perform additional custom work...

    return cell;
}

Пример настройки Interface Builder ...

альтернативный текст


12

Посмотрите, какой ответ я дал на этот вопрос:

Можно ли разработать подклассы NSCell в Интерфейсном Разработчике?

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

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


2
Не следует создавать ячейки табличного представления в IB, если вам нужна прозрачность и хорошая скорость прокрутки. Для определенных пользовательских интерфейсов вам нужна прозрачность (например, для визуализации текста поверх графики). Иногда единственный способ добиться хорошей прокрутки на старом оборудовании (до A4) - это выполнить рендеринг в коде, чтобы графическому процессору не приходилось объединять несколько прозрачных слоев.
— Ник Фордж,

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

7

Начиная с iOS около 4.0, в документации iOS есть специальные инструкции, которые делают эту работу сверхбыстрой:

http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7

Прокрутите вниз до того места, где говорится о создании подкласса UITableViewCell.


6

Вот еще вариант:

NSString * cellId = @"reuseCell";  
//...
NSArray * nibObjects = [[NSBundle mainBundle] loadNibNamed:@"CustomTableCell" owner:nil options:nil];

for (id obj in nibObjects)
{
    if ([obj isKindOfClass:[CustomTableCell class]])
    {
        cell = obj;
        [cell setValue:cellId forKey:@"reuseIdentifier"];
        break;
    }
}

Обратите внимание, что это единственное решение, опубликованное на данный момент, которое не требует UITableViewCellсоздания подкласса для вашего пользовательского класса , чтобы установить уникальное значение для reuseIdentifer. Я думаю, это то, что на самом деле искала оригинальная операция.
— charshep

Я надеюсь, что Apple не откажет моему приложению в использовании этого ... Я использую это для получения "статических" ячеек, потому что в моем табличном представлении процесс заполнения ячейки довольно медленный. Таким образом, мне просто нужно выполнить его один раз (давая разные идентификаторы каждой строке). Спасибо!
— Рикард Перес дель Кампо,

Очень полезно, так как нет метода UITableViewCell для ручной настройки!
— Джесси

2

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

[nib objectAt...]Подход чувствителен к изменениям в позиции элементов в массиве.

UIViewControllerПодход хорош - просто попытался его, и он работает достаточно хорошо.

НО...

Во всех случаях initWithStyleконструктор НЕ вызывается, поэтому инициализация по умолчанию не выполняется.

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

Помимо явного вызова некоторого метода инициализации в cellForRowAtIndexPath методе, я еще не нашел на это ответа.


awakeFromNib - это правильный способ реагировать на объект, загружаемый из NIB.
— Джон Хесс,

2

Некоторое время назад я нашел отличное сообщение в блоге по этой теме на blog.atebits.com , и с тех пор начал использовать класс Loren Brichter ABTableViewCell для выполнения всех моих UITableViewCells.

В итоге вы получаете простой контейнер UIView для размещения всех ваших виджетов, а прокрутка выполняется молниеносно.

Надеюсь, это будет полезно.


2

Этот метод также работает и не требует фанкового ivar в вашем контроллере представления для управления памятью. Здесь ячейка настраиваемого табличного представления находится в xib с именем «CustomCell.xib».

 static NSData *sLoadedCustomCell = nil;

 cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
 if (cell == nil) 
 {
   if (sLoadedCustomCell == nil) 
   {        
      // Load the custom table cell xib
      // and extract a reference to the cell object returned
      // and cache it in a static to avoid reloading the nib again.

      for (id loadedObject in [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:nil options:nil]) 
      {
        if ([loadedObject isKindOfClass:[UITableViewCell class]]) 
        {
          sLoadedCustomCell = [[NSKeyedArchiver archivedDataWithRootObject: loadedObject] retain];
          break;
        }
    }
    cell = (UITableViewCell *)[NSKeyedUnarchiver unarchiveObjectWithData: sLoadedCustomCell];
  }

1
В архивации и разархивировании нет необходимости.
— Брайан Генри

1
В архивировании / разархивировании нет необходимости, если вы нормально загружаете ячейку из ее пера, когда ее нельзя удалить из очереди. Однако, если вы хотите загрузить ячейку из пера ровно один раз , вам необходимо кэшировать ее в памяти. Я выполняю это кеширование с помощью NSKeyedArchiving, потому что UITableViewCell не реализует NSCopying.
— Билл Гаррисон,

1
При этом использование UINib для загрузки ячейки дает тот же эффект: загрузка с диска один раз, а затем загрузка из памяти.
— Билл Гаррисон,

2

У меня сработал метод Луи. Это код, который я использую для создания UITableViewCell из пера:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{   
    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"CustomCellId"];

    if (cell == nil) 
    {
        UIViewController *c = [[UIViewController alloc] initWithNibName:@"CustomCell" bundle:nil];
        cell = (PostCell *)c.view;
        [c release];
    }

    return cell;
}

2
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *simpleTableIdentifier = @"CustomCell";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
    cell = [nib objectAtIndex:0];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}         

return cell;
}

1

Решение gustavogb у меня не работает, я пробовал:

ChainesController *c = [[ChainesController alloc] initWithNibName:@"ChainesController" bundle:nil];
[[NSBundle mainBundle] loadNibNamed:@"ChaineArticleCell" owner:c options:nil];
cell = [c.blogTableViewCell retain];
[c release];

Вроде работает. BlogTableViewCell - это IBOutlet для ячейки, а ChainesController - владелец файла.


1

Из документов UITableView относительно dequeueWithReuseIdentifier : «Строка, идентифицирующая объект ячейки, который будет использоваться повторно. По умолчанию идентификатором повторно используемой ячейки является имя ее класса, но вы можете изменить его на любое произвольное значение».

Самостоятельное переопределение -reuseIdentifer рискованно. Что произойдет, если у вас есть два подкласса подкласса ячейки, и вы используете оба из них в одном табличном представлении? Если они отправят вызов идентификатора повторного использования на super, вы удалите из очереди ячейку неправильного типа .............. Я думаю, вам нужно переопределить метод reuseIdentifier, но он вернет замещенный идентификатор строка. Или, если он не был указан, вернуть класс в виде строки.


0

Как бы то ни было, я спросил об этом инженера по iPhone на одном из iPhone Tech Talks. Его ответ был: «Да, можно использовать IB для создания ячеек. Но не делайте этого. Пожалуйста, не надо».


1
Это странно. По крайней мере, на двух выступлениях на конференции в Нью-Йорке был представлен демонстрационный код, использующий ячейки, созданные в IB.
— Шон Крейвер,

3
Не уверен, что верю в это, поскольку они используют IB для создания ячеек в примере проекта Apple Advanced Table View Cells.
— iwasrobved

Спасибо за это. Каждый раз, когда я это делал, я сталкивался с проблемами.
— Может

Вероятно, он не знал об этом достаточно.
— aryaxt 06

0

Я следовал инструкциям Apple, по ссылке Бена Мошера (спасибо!), Но обнаружил, что Apple упустила важный момент. Объект, который они создают в IB, - это просто UITableViewCell, как и переменная, которую они загружают из него. Но если вы на самом деле настроите его как настраиваемый подкласс UITableViewCell и напишете файлы кода для подкласса, вы можете написать объявления IBOutlet и методы IBAction в коде и связать их со своими настраиваемыми элементами в IB. Тогда нет необходимости использовать теги просмотра для доступа к этим элементам, и вы можете создать любую сумасшедшую ячейку, какую захотите. Это рай какао Touch.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.