Копирование UITableViewCell

Я читаю пользовательскую ячейку таблицы в tableView:cellForRowAtIndexPath: из файла пера. Это отлично работает для моих целей, за исключением того, что это довольно медленно.

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

На данный момент я был бы счастлив, если бы я читал перо только один раз в подклассе UIViewController, а затем tableView:cellForRowAtIndexPath: делал его копии. Я предполагаю, что копирование будет быстрее, чем чтение пера.

Вот что я использую для загрузки пера, которое я вызываю из viewDidLoad:retain после)

-(id)loadFromNamed:(NSString*)name {
    NSArray *objectsInNib = [[NSBundle mainBundle] loadNibNamed:name
                                                          owner:self
                                                        options:nil];
    assert( objectsInNib.count == 1 );
    return [objectsInNib objectAtIndex:0];
}

Пока все хорошо. Но вопрос в том, как мне копировать это снова и снова? Это вообще возможно?

Я пробовал [_cachedObject copy] и [_cachedObject mutableCopy], но UITableViewCell не поддерживает ни один из протоколов копирования.

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

Любые идеи?


person Steven Fisher    schedule 20.02.2009    source источник


Ответы (5)


Используйте клонирование ячеек, встроенное в табличное представление. Apple знала, что создание большого количества ячеек таблицы происходит медленно. Ознакомьтесь с документацией для этого метода:

- (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier

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

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


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

person Alex Wayne    schedule 20.02.2009
comment
Спасибо. Я забыл о dequeueReusableCellWithIdentifier. Сейчас пользуюсь, но все равно медленнее, чем хотелось бы. Это говорит о том, что узкое место находится в рисовании, которое придется подождать, пока у меня не будет времени применить другие приемы быстрого стола. Но теперь я знаю, что происходит! - person Steven Fisher; 21.02.2009
comment
Ну, ваш подход был бы еще медленнее, поскольку повторное использование объектов намного быстрее, чем клонирование объектов. Рад, что смог помочь. - person Alex Wayne; 21.02.2009
comment
Попробуйте выполнить профилирование на устройстве, чтобы наверняка увидеть, что работает медленно. Некоторые люди говорят, что хитрость заключается в том, чтобы использовать только один вид на ячейку и рисовать все самостоятельно. - person Chris Lundie; 21.02.2009
comment
На самом деле это не правильный ответ на этот вопрос. При первоначальной настройке удаление из очереди будет возвращать ноль для каждой видимой ячейки. Итак, в этом блоке if (cell == nil) вы захотите создать новый UITableViewCell, возможно, загрузив его из пера. Я также предпочел бы скопировать UITableViewCell из IBOutlet в моем контроллере, который я подключил к разработанному UITableViewCell в nib. Но копировать не удается, говоря, что он не поддерживает copyWithZone. - person dlamblin; 16.06.2009
comment
dlamblin, вы находитесь именно в той ситуации, которая навеяла мой вопрос. Но я принял это как правильный ответ, потому что понял, что если я использую кеш, чаще всего я буду запускать loadNibNamed один раз для изначально видимой строки. Скажем, 5 раз. Мне просто не нужно это оптимизировать; настоящее узкое место должно быть в чертеже или в том, что я переделал ячейку для разных строк позже в таблице. (Еще не решил это, так как я был в другом проекте.) - person Steven Fisher; 16.06.2009
comment
Есть ли способ создать экземпляр прототипа UITableViewCell в Interface Builder и просто клонировать столько, сколько необходимо UITableView ? Это гораздо проще, чем настраивать ячейку по коду. Спасибо. - person adib; 16.04.2010
comment
@adib Существует класс SDK 4.0, который помогает в этом, под названием UINib. Но сделать это в доступной в настоящее время ОС не так-то просто. - person Alex Wayne; 16.04.2010
comment
Для потомков это теперь легко с iOS 5 Storyboards. - person Steven Fisher; 08.08.2012

Я думаю, что копирование ячейки таблицы можно использовать вместе с механизмом удаления из очереди, который позволит создать ячейку один раз (из nib или программно или автоматически загрузить ее из другого nib и связать как выход в IB), а затем клонировать ее или удалить из очереди. при необходимости.

UITableViewCell не соответствует протоколу NSCopying, но поддерживает механизм архивирования/разархивирования с ключом, поэтому его можно использовать для клонирования.

На основе ответа " Как дублировать UIButton в Objective C ? " мой метод делегата источника данных выглядит так:

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

    if (!cell) {
        NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:self.tableViewCell];
        cell = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
    }

    // ... config ...

    return cell;
}

И в моем случае self.tableViewCell — это ячейка, которая была загружена один раз из файла пера представления.

Я не проверял, что будет быстрее: «архивировать + разархивировать» для клонирования или «загрузить nib-файл + разархивировать», что будет делать фреймворк в случае -loadNibNamed:owner:options:, я использовал это метод только из соображений удобства, но есть хорошие шансы, что операция с памятью по сравнению с операцией с файлом будет быстрее.

РЕДАКТИРОВАТЬ: Кажется, это не так просто, как казалось сначала. Поскольку UIImage не соответствует NSCoding, ячейки с настроенными UIImageViews нельзя просто скопировать без дополнительного кода. Да, копирование всего изображения определенно не является хорошей практикой, спасибо Apple за указание на это.

person zubko    schedule 17.03.2011
comment
Вау, я бы никогда не понял, как дублировать UITableViewCell, если бы не этот пост. Большое спасибо. - person Joe C; 24.10.2015
comment
ограничения не прошли архивацию/разархивацию :-( - person Anton Tropashko; 10.11.2017

Не горжусь этим решением, но оно работает с максимальным количеством возможных привязок IB:

Интерфейс (AlbumTableViewCell является подклассом UITableViewCell, экземпляр которого определен в XIB-файле AlbumViewController):

@interface AlbumsViewController : UITableViewController {
    IBOutlet AlbumTableViewCell *tableViewCellTrack;
}

@property (nonatomic, retain) AlbumTableViewCell *tableViewCellTrack;

Реализация (разархивирует/архивирует/копирует/клонирует ячейку табличного представления):

@implementation AlbumsViewController

@synthesize tableViewCellTrack;

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

    if (cell == nil) {
        AlbumsViewController *albumsViewController = [[[AlbumsViewController alloc] init] autorelease];
        [[NSBundle mainBundle] loadNibNamed: @"AlbumsViewController" owner: albumsViewController options: nil];

        cell = albumsViewController.tableViewCellTrack;
    }

    cell.labelTitle.text = ...;
    cell.labelArtist.text = ...;

    return cell;
}
person pfo    schedule 08.12.2009

Ну, я не уверен, почему все учебники не указывают этот шаг.

При использовании собственного пользовательского UITableViewCell из Nib вызова dequeueReusableCellWithIdentifier недостаточно. Вы должны указать «Идентификатор» в ИБ, только для этого в разделе «Ячейка представления таблицы».

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

person Teo Choong Ping    schedule 17.06.2009

Вот это в Свифте

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    var cell : UITableViewCell?
    let cellId = String(format: "Cell%d", indexPath.row)
    cell = alertTable!.dequeueReusableCellWithIdentifier(cellId) as! UITableViewCell?

    if cell == nil {
        let archivedData = NSKeyedArchiver.archivedDataWithRootObject(masterTableCell!)
        cell = NSKeyedUnarchiver.unarchiveObjectWithData(archivedData) as! UITableViewCell?
    }

    // do some stuff

    return cell!
}
person Joe C    schedule 24.10.2015