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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
89
0
89 601
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

Ответ принят как подходящий

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

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

Где реализовать этот метод?

Krishnan 12.05.2011 16:53

В вашем подклассе UITableViewCell. developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Referen‌ ce /…

DenTheMan 20.06.2011 19:43

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

SK9 04.11.2011 13:10

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

ivanzoid 18.07.2013 16:27

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

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

Shawn Craver 10.02.2009 20:49

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

iwasrobbed 09.05.2010 01:11

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

skorulis 23.10.2010 07:16

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

aryaxt 07.12.2012 02:25

Взгляните на ответ, который я дал на этот вопрос:

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

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

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

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

Nick Forge 24.08.2010 08:17

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

Kendall Helmstetter Gelner 24.08.2010 23:31

У меня сработал метод Луи. Это код, который я использую для создания 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;
}

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

IB_reuse_identifier

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

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

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

Krishnan 12.05.2011 17:41

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

Tim Keating 12.05.2011 18:37

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

Tyler 19.06.2012 03:28

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

Tyler 19.06.2012 04:58

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

Tim Keating 19.06.2012 07:42

@Tyler только что проверил это в XCode 5, и он есть, поэтому я предполагаю, что да в 4.3.3.

Tim Keating 07.11.2013 01:12

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

- (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 ...

alt text

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

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

Подход UIViewController хорош - просто попробовал, и он неплохо работает.

НО...

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

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

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

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

Jon Hess 20.08.2011 12:42

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

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

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

Решение 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 - владелец файла.

Этот метод также работает и не требует фанкового 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];
  }

В архивировании и разархивировании нет необходимости.

Bryan Henry 17.03.2011 22:48

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

Bill Garrison 20.03.2011 01:10

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

Bill Garrison 20.03.2011 01:56

Начиная с 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.

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

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

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

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

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 18.01.2012 06:03

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

Ricard Pérez del Campo 04.01.2013 20:34

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

Jesse 10.12.2013 02:16

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

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

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

Kaelin Colclasure 25.07.2012 22:53

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

Abolfoooud 21.12.2012 21:41

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

marzapower 05.01.2013 16:12

рабочий пример того, как интегрировать это в ваш контроллер: mindfiresolutions.com/…

mblackwell8 06.02.2013 02:34
- (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;
}

Другие вопросы по теме