Ошибка утверждения при использовании UISearchDisplayController в UITableViewController

Я пытаюсь добавить простые функции поиска в TableViewController в моем приложении. Я последовал за руководством Рэя Вендерлиха. У меня есть tableView с некоторыми данными, я добавил панель поиска + контроллер отображения в раскадровку, а затем у меня есть этот код:

#pragma mark - Table View
     - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BreedCell" forIndexPath:indexPath];

        //Create PetBreed Object and return corresponding breed from corresponding array
        PetBreed *petBreed = nil;

        if(tableView == self.searchDisplayController.searchResultsTableView)
            petBreed = [_filteredBreedsArray objectAtIndex:indexPath.row];
        else
            petBreed = [_breedsArray objectAtIndex:indexPath.row];

        cell.accessoryType  = UITableViewCellAccessoryDisclosureIndicator;
        cell.textLabel.text = petBreed.name;

        return cell;
    }

#pragma mark - Search
    -(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
        [_filteredBreedsArray removeAllObjects];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.name contains[c] %@",searchString];
        _filteredBreedsArray = [[_breedsArray filteredArrayUsingPredicate:predicate] mutableCopy];

        return YES;
    }

    -(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
        // Tells the table data source to reload when scope bar selection changes

        [_filteredBreedsArray removeAllObjects];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.name contains[c] %@",self.searchDisplayController.searchBar.text];
        _filteredBreedsArray = [[_breedsArray filteredArrayUsingPredicate:predicate] mutableCopy];
        return YES;
    }

Стандартный материал, но когда я ввожу текст в строку поиска, он каждый раз сбой с этой ошибкой:

2013-01-07 19:47:07.330 FindFeedo[3206:c07] *** Assertion failure in -[UISearchResultsTableView dequeueReusableCellWithIdentifier:forIndexPath:], /SourceCache/UIKit_Sim/UIKit-2372/UITableView.m:4460
2013-01-07 19:47:07.330 FindFeedo[3206:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier BreedCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

Я понимаю, что в iOS 6 изменилась система обработки и удаления очереди для ячеек, а также что поиск использует другой tableView, поэтому я думал, что проблема заключается в том, что таблица поиска с результатами фильтрации не знала о ячейке, поэтому я помещаю это в свой viewDidLoad:

[self.searchDisplayController.searchResultsTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"BreedCell"];

И вуаля! Это сработало... Только в первый раз, когда вы ищете. Если вы вернетесь к исходным результатам и снова выполните поиск, приложение выйдет с той же ошибкой. Я подумал о том, что возможно добавить все

if(!cell){//init cell here};

для метода cellForRow, но разве это не противоречит целям использования метода dequeueReusableCellWithIdentifier: forIndexPath: method? Во всяком случае, я потерян. Что мне не хватает? Помоги пожалуйста. Заранее благодарю вас за все ваше время (:

Алекс.

Ответ 1

Попробуйте использовать self.tableView вместо tableView в dequeueReusableCellWithIdentifier:

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

    //Create PetBreed Object and return corresponding breed from corresponding array
    PetBreed *petBreed = nil;

    if(tableView == self.searchDisplayController.searchResultsTableView)
        petBreed = [_filteredBreedsArray objectAtIndex:indexPath.row];
    else
        petBreed = [_breedsArray objectAtIndex:indexPath.row];

    cell.accessoryType  = UITableViewCellAccessoryDisclosureIndicator;
    cell.textLabel.text = petBreed.name;

    return cell;
}

Этот код работает очень хорошо

Примечание

Если у вас есть пользовательские ячейки высоты, не используйте

[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

Используйте это вместо

[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

Ответ 2

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

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

Итак, чтобы избежать сбоя, вы должны зарегистрировать класс ячейки для представления таблицы для использования в методе делегата searchDisplayController:didLoadSearchResultsTableView: (от UISearchDisplayDelegate) вместо вашего метода viewDidLoad.

Как следует:

- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
{
    [tableView registerClass:[DPContentTableCell class] forCellReuseIdentifier:cellIdentifier];
    [tableView registerClass:[DPEmptyContentTableCell class] forCellReuseIdentifier:emptyCellIdentifier];
}

Это застало меня врасплох, потому что на iOS 7... табличное представление используется повторно. Поэтому вы можете зарегистрировать класс в viewDidLoad, если хотите. Для наследства я продолжу регистрацию в методе делегата, о котором я упоминал.

Ответ 3

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

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

Использование:

UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

(Не используйте tableView метода cellForRowAtIndexPath, используйте self.tableView.)

Ответ 4

Выгрузите ячейку без использования "indexPath", и в случае получения элемента nil вы должны выделить его вручную.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"YourCellId"];
    if (!cell)
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"YourCellId"];

    // fill your cell object with useful stuff :)

    return cell;
}

Попытка использовать self.tableView для деактивации ячейки может привести к сбоям, если у вас есть разделенный основной список и простой список поиска. Этот код работает в любой ситуации.

Ответ 5

Когда у меня возникла эта проблема, решение заменило tableView dequeueReusableCellWithIdentifier: @yourcell с self.tableView

Ответ 6

Я тоже работаю над этим учебником. По умолчанию TableViewController имеет "forIndexPath", и в его примере его не существует. Как только я удалю его, поиск будет выполнен.

//Default code
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

//Replace with
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

Ответ 7

для быстрого 3 вам просто нужно добавить self:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cell = self.tableView.dequeueReusableCell(withIdentifier: "yourCell", for: indexPath) as! YourCell

    ...
}