Завантаження багаторазового 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.)


Що я встановлюю Ідентифікатори, якщо я використовую створені Nib клітини для більш ніж однієї комірки? Це призведе до того, що ми матимемо дві комірки з однаковими ідентифікаторами.
Крішнан

4
Не думайте про це як про унікальний ідентифікатор - думайте про це більше як про назву типу.
Тім Кітінг

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

3
Гаразд, я вирішив свою проблему. Якщо ви починаєте з об’єкта UIView у конструкторі інтерфейсу (у межах Xcode 4) і змінюєте його клас на UITableViewCell, ви не отримуєте властивостей, характерних для клітинки, таких як ідентифікатор повторного використання. Щоб отримати це, вам слід почати з порожнього xib і перетягнути об'єкт комірки таблиці, який потім матиме властивості, специфічні для клітинки, які ви можете редагувати.
Тайлер

1
@Krishnan Подумайте про це так - коли ви створюєте комірку подання таблиці з ідентифікатором 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

працював приклад того, як інтегрувати це у свій контролер: mindfiresolutions.com/…
mblackwell8

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;
}

Приклад налаштування побудови інтерфейсу ...

текст заміщення


12

Подивіться на відповідь, яку я дав на це запитання:

Чи можна розробити підкласи NSCell у Interface Builder?

Це не лише можливість розробити UITableViewCell в IB, це бажано, оскільки в іншому випадку вся ручна проводка та розміщення декількох елементів дуже нудна. Ефективність чудова, якщо ви обережно робите всі елементи непрозорими, коли це можливо. Ідентифікатор повторного використання встановлюється в IB для властивостей UITableViewCell, тоді ви використовуєте відповідний ідентифікатор повторного використання в коді під час спроби зняти чергу.

Минулого року я також чув від деяких доповідачів у WWDC, що не слід робити комірки для перегляду таблиці в IB, але це дуже багато.


2
Ви не повинні робити комірки подання таблиці в IB, якщо вам потрібна прозорість і ви хочете мати хорошу продуктивність прокрутки. Для певних інтерфейсів користувача потрібна прозорість (для відтворення тексту над графікою, наприклад). Іноді єдиним способом досягти гарної прокрутки на старішому (до A4) обладнання є візуалізація в коді, щоб уникнути необхідності складання графічним процесором декількох прозорих шарів.
Нік Фордж

2
Правда, але навіть з урахуванням цього, можливо, вам буде краще дозволити продуктивності трохи постраждати на старих пристроях для легшої ремонтопридатності комірок, вбудованих в IB. Ви також можете використовувати цю техніку як комірку шаблону, з якої потім малюєте елементи, щоб уникнути композиції за допомогою спеціального методу малювання.
Kendall Helmstetter Gelner

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. Я думаю, це те, що насправді шукав оригінальний ор.
Чаршеп

Я сподіваюся, що 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: "Рядок, що ідентифікує об’єкт комірки, який потрібно використовувати повторно. За замовчуванням ідентифікатор багаторазової комірки - це ім’я класу, але ви можете змінити його на будь-яке довільне значення."

Замінити -використовувати ідентифікатор себе ризиковано. Що станеться, якщо у вас є два підкласи вашого підкласу комірок і ви користуєтесь обома ними в одному поданні таблиці? Якщо вони надішлють виклик повторного використання на super, ви виведете з комірки неправильний тип .............. Я думаю, вам потрібно перевизначити метод reuseIdentifier, але нехай він поверне витіснений ідентифікатор рядок. Або, якщо один не вказаний, нехай він повертає клас як рядок.


0

Для чого це варте, я запитав про це інженера iPhone на одному з iPhone Tech Talks. Він відповів: "Так, можна використовувати IB для створення комірок. Але не робіть. Будь ласка, не робіть".


1
Це дивно. Принаймні, дві дискусії на дискусії в Нью-Йорку мали демо-код, який використовував комірки, створені в IB.
Шон Крейвер,

3
Не впевнений, що я в це вірю, оскільки вони використовують IB для створення комірок у прикладі проекту Advanced Table View Cells від Apple.
iwasrobbed

Дякую за це. Кожного разу, коли я це робив, я стикався з проблемами. Це може бути причиною
skorulis

Йому, мабуть, не вистачало про це знань.
aryaxt

0

Я виконував вказівки Apple за посиланням Бена Мошера (дякую!), Але виявив, що Apple пропустила важливий момент. Об'єкт, який вони проектують у IB, - це просто UITableViewCell, як і змінна, яку вони завантажують із нього. Але якщо ви насправді встановили його як власний підклас UITableViewCell і написали файли коду для підкласу, ви можете написати декларації IBOutlet та методи IBAction у коді та підключити їх до своїх власних елементів у IB. Тоді немає необхідності використовувати теги подання для доступу до цих елементів, і ви можете створити будь-яку шалену комірку, яку хочете. Це какао-сенсорне небо.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.