Загрузка многоразового 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.