Помилка твердження під час використання UISearchDisplayController в UITableViewController


80

Я намагався додати просту функцію пошуку до 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, тому я думав, що проблема полягала в тому, що пошук tableView із відфільтрованими результатами не знав про комірку, тому я поставив на мій поглядDidLoad:

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

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

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

речі до методу cellForRow, але чи не суперечить це цілі призначення методу dequeueReusableCellWithIdentifier: forIndexPath:? У всякому разі, я загубився. Чого мені не вистачає? Допоможіть, будь ласка. Заздалегідь дякую за весь ваш час (:

Алекс.

Відповіді:


194

Спробуйте використовувати 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];

4
чи не просто це створюватиме інстанції нових комірок з однієї таблиці в іншу на невизначений час? якщо скидання клітин з одного столу і
передача

1
Я думав, це теж спрацює. Це не так. У моєму випадку спроба зняти комірку з головної таблиці з таблиці пошуку іноді спричиняє збій.
Lee Probert

1
Не працює. Спробуйте здійснити пошук, а потім скасувати (вийти з режиму накладання результатів), а потім здійснити пошук ще раз. Аварії.
Даніель

8
Запропоноване рішення все ще викликає для мене виняток. Однак, здається, це працює, як очікувалося, якщо я заміню dequeueReusableCellWithIdentifier:forIndexPath:на dequeueReusableCellWithIdentifier:.
jweyrich

3
Дякую! Проблемою став self.tableview замість tableview! рятувальник життя!
adamteale

14

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

Під режимом пошуку я маю на увазі, що ви натиснули текстове поле і почали вводити текст, після чого створюється подання таблиці для відображення результатів, вихід із цього режиму досягнутий натисканням кнопки скасування. Коли ви натискаєте текстове поле вдруге і починаєте вводити текст знову - це входить у "режим пошуку" вдруге.

Отже, щоб уникнути збою, вам слід зареєструвати клас комірок для подання таблиці для використання в searchDisplayController:didLoadSearchResultsTableView:методі делегатів (from UISearchDisplayDelegate), а не в viewDidLoadметоді контролерів .

Наступне:

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

Це застало мене зненацька, оскільки на iOS 7 ... подання таблиці використовується повторно. Тож ви можете зареєструвати клас, viewDidLoadякщо хочете. Заради застарілості я буду зберігати реєстрацію за методом делегування, про який я згадав.


2
На жаль, це не працює з динамічними прототипами комірок Storyboard.
Ortwin Gentz

Але він чудово працює з клітинами, зареєстрованими nib. Хороший обхідний шлях.
Томас Вербек,

12

Після пошуку метод 'tableView' методу cellForRowAtIndexPath здається не екземпляром таблиці, яку ви визначаєте. Отже, ви можете використовувати екземпляр таблиці, що визначає клітинку. Замість:

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

Використання:

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

(Не використовуйте Tableview методу cellForRowAtIndexPath, використання self.tableView .)


2
+1. Потрібен self.tableView під час моєї швидкої реалізації цієї концепції.
davidethell

3

Зняти з комірки комірку, не використовуючи '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 для зняття черги з комірки може спричинити збій, коли у вас є розділений основний список і простий список пошуку. Цей код замість цього працює в будь-якій ситуації.


1
Це не спрацює, якщо ваша комірка є спеціальною в Раскадровці, і її потрібно створити за допомогою екземпляра, а не її класу.
Lee Probert

3

Коли у мене виникла ця проблема, рішення замінило tableViewdequeueReusableCellWithIdentifier: @yourcell наself.tableView


2

Я також працюю над цим підручником. За замовчуванням TableViewController має "forIndexPath", і в його прикладі він не існує. Після того, як я його видалив, пошук працює.

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

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

1
На жаль, це не працює з динамічними прототипами комірок Storyboard.
Ortwin Gentz

2

для swift 3 вам просто потрібно додати self:

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

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