Помилка твердження під час використання 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.