В чем смысл сообщения "no index path for table cell is reused" в iOS 6/7?

С начала компиляции моего приложения с iOS 6 (а также с iOS 7) я начал видеть это сообщение. Я знаю, что способ, которым UITableViews занимается управлением ячейками, отличается в iOS 6, но мне не нужно было изменять код, чтобы он продолжал работать. Но я обеспокоен тем, что это сообщение может указывать на какую-то потенциальную проблему, которую я еще не вижу. Может ли кто-нибудь пролить свет?

Ответ 1

Я начал получать эту ошибку, появляющуюся в журнале с iOS 7 бета-версии 5, в том числе в сборке iOS 7 GM/Release, в то время как это никогда не случалось в моем приложении в iOS 6 или предыдущих бета-версиях iOS 7. После многих экспериментов я нашел причину:

Я использовал объекты UITableViewCell для своих заголовков заголовков разделов и возвращал их в tableView:viewForHeaderInSection:. Это, по-видимому, обычная практика, особенно с iOS 5, когда стало легко спроектировать заголовок заголовка раздела в качестве элемента представления таблицы прототипа в StoryBoard с помощью Interface Builder.

Когда я изменил свое приложение, чтобы использовать только обычные подклассы UIView для моих заголовков заголовков разделов, ошибки ушли и, что более важно, мой табличный вид остановил случайное удаление заголовков разделов!

Похоже, что (поскольку iOS 7 beta 5) UITableView внутренне поддерживает отображение всех объектов UITableViewCell в своей иерархии представлений и их соответствующих дорожках индексов. Поскольку заголовок раздела (или заголовок представления таблицы нижнего колонтитула) не имеет указательного пути, если вы используете объект UITableViewCell для этих представлений, представление таблицы будет запутано, когда оно найдет UITableViewCell, для которого оно выполнено. 't имеет указательный путь, в результате чего ошибка "no index path for table cell is reused" и, если вам не повезло, отображают сбои в представлении таблицы:

ОБНОВЛЕНИЕ: если у вас есть доступ к форуму Apple Dev, вот нить об этом (я начал): https://devforums.apple.com/message/882042#882042

Как было предложено в этом потоке, если вы не хотите многократно перегруппировать, вы можете создать обертку UIView вокруг вашего UITableViewCell и вернуть ее в виде заголовка раздела.

UIView *view = [[UIView alloc] initWithFrame:[cell frame]];
[view addSubview:cell];

return view;

Обратите внимание, что этот подход "обертка" UIView не будет хорошо работать с AutoLayout и вращением устройства, поэтому я предлагаю вам использовать подкласс UIView для ящиков заголовка и нижнего колонтитула, а не подкласс UITableViewCell, как описано в основная часть ответа.

Ответ 2

Я бы вернул contentView из UITableViewCell вместо создания обертки. У меня была проблема с фиксированной привязкой в ​​повествовании.

return cell.contentView;

Ответ 3

У меня была такая же проблема, и мне потребовалось несколько часов, чтобы выследить проблему. Оказывается, я вызывал [textField becomeFirstResponder] при настройке ячеек (здесь textField был частью пользовательской таблицы tableviewcell); [textField becomeFirstResponder] по очереди публикует уведомление о клавиатуреWillShow, которое, в свою очередь, заставляло табличное представление преждевременно загружать себя, тем самым вызывая сообщение об ошибке "no index path for table cell is reused". Как только я удалил этот вызов, проблема исчезла.

Ответ 4

Дополняясь к принятому ответу (mluisbrown), мне нужно было добавить autoverizingMask в ячейку заголовка, так как моя содержала многострочную метку, т.е.

UIView *view = [[UIView alloc] initWithFrame:[cell frame]];
cell.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[view addSubview:cell];
return view;

Ответ 5

Это внутренняя ошибка UIKit - как упоминалось в собственных форумах разработчиков Dev. Предполагается, что он исправлен в новых версиях xcode, хотя я не смог найти информацию о том, какая версия исправляет это.

Ответ 6

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

По-видимому, моя пользовательская ячейка, переопределенная -(void)setEditing:animated:, слишком долго возвращалась.

Мой предыдущий код:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{   
    [super setEditing:editing animated:animated];
    [self someAdditionalCode];
}

Мне удалось исправить это, изменив его на:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{   
    [super setEditing:editing animated:animated];

    // DRM: we want to perform the actions from this block in the main thread, but
    // asynchronously to avoid excessive delays which were causing issues.
    //
    dispatch_async(dispatch_get_main_queue(), ^void()
    {
        [self someAdditionalCode];
    });
}

Ответ 7

У меня была такая же проблема с появлением сообщения об ошибке. Насколько я вижу, это вызвано перезагрузкой табличного представления из функции, называемой текстовым полем, как часть его протокола делегирования. Т.е. textFieldDidEndEditing → [controller.tableview reload...]

Ответ 8

Выполнение моих endupdates после resignfirstresponder решило мою проблему (у меня есть UITextFIeld в моей пользовательской ячейке)

-(void)textfieldEditDone
{
....

    [textField resignFirstResponder];
    [self.tableView endUpdates];

Ответ 9

Для записи я тоже столкнулся с этим сообщением при работе под iOS 6. Кажется, что какой-то код, унаследованный или импортированный, имел что-то вроде этого:

(NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section {
    NSInteger rows = 0;
    if ([delegate respondsToSelector:@selector(numberOfItemsInSection:)]) {
        rows = [delegate numberOfItemsInSection:section];

        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

Когда последовательность beginUpdate:/endUpdate: была удалена, проблема волшебным образом исчезла.

Ответ 10

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

Я использовал PINRemoteImage для асинхронного скачивания изображения в UIImageView, который был внутри пользовательского UITableViewCell.

Чтобы правильно изменить размер строки (ячейки динамической высоты с использованием автоматической компоновки) после загрузки изображения, я вызывал:

self.tableView beginUpdates;
self.tableView endUpdates;

Затем я получал сообщение "no index path for table cell is reused", и приложение терпело крах. Я думал, что блок PINRemoteImageManagerResult находится в основном потоке, однако оказывается, что он не был - поэтому гарантировал, что начальные/конечные обновления были вызваны в основном потоке, исправлена ​​проблема.

dispatch_async(dispatch_get_main_queue(), ^(void){
                            [self.tableView beginUpdates];
                            [self.tableView endUpdates];
});

Ответ 11

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

У меня был табличный вид, который иногда отправлял это сообщение при загрузке. Оказалось, что это вызвано уведомлениями KVO для объектов объектов Core Data, когда загрузка была загружена. (При наблюдении за изменением мой контроллер попытался вызвать reloadData в рассматриваемом представлении таблицы. Исправлено, не наблюдая объекты до тех пор, пока представление не закончило загрузку (ранее я начал наблюдать объект, как только он был назначен через accessor)

TL;DR: проверьте, можете ли вы пытаться перезагрузить данные из чего-то другого, кроме основного.

Ответ 12

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

NSIndexPath *reloadRow = [NSIndexPath indexPathForRow:1 inSection:2];
[self._mainTableView reloadRowsAtIndexPaths:@[reloadWebViewRow]
                           withRowAnimation:UITableViewRowAnimationFade];

Но случайно, я набрал

NSIndexPath *reloadRow = [NSIndexPath indexPathForItem:1 inSection:2];

Обратите внимание на разницу между двумя индексами: один создается с помощью indexPathForItem (неправильный), другой - с indexPathForRow (правильный). Все это привело к очень странному поведению tableView и сообщению об ошибке в заголовке.

Ответ 13

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

void runOnMainQueueWithoutDeadlocking(void (^block)(void)){
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

Я называю это следующим образом внутри блока успеха моего фонового веб-вызова.

runOnMainQueueWithoutDeadlocking(^{
    [self.tableView beginUpdates];
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:2] withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.tableView endUpdates];
});

Ответ 14

Еще одно условие...

Это произошло, когда, не желая заголовка, я вернулся nil.

Fix:

func tableView(tableView: UITableView,
               titleForHeaderInSection section: Int) -> String? {
    return ""
}