J'ai essayé d'ajouter une fonctionnalité de recherche simple à un TableViewController dans mon application. J'ai suivi le tutoriel de Ray Wenderlich. J'ai une tableView avec quelques données, j'ai ajouté la barre de recherche + le contrôleur d'affichage dans le storyboard, puis j'ai ce code:
#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;
}
Le truc standard, mais lorsque j'entre du texte dans la barre de recherche, il se bloque à chaque fois avec cette erreur:
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'
Je comprends que dans iOS 6, le système de gestion et de mise en file d'attente des cellules a changé, et que la recherche utilise une tableView différente, donc je pensais que le problème était que la table de recherche avec les résultats filtrés ne connaissait pas la cellule, alors j'ai mis ceci à mon avisDidLoad:
[self.searchDisplayController.searchResultsTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"BreedCell"];
Et le tour est joué! Cela a fonctionné ... Seulement la première fois que vous recherchez. Si vous revenez aux résultats d'origine et effectuez une nouvelle recherche, l'application se bloque avec la même erreur. J'ai pensé à peut-être ajouter tous les
if(!cell){//init cell here};
des trucs à la méthode cellForRow, mais cela ne va-t-il pas à l'encontre du but de la méthode dequeueReusableCellWithIdentifier: forIndexPath:? Quoi qu'il en soit, je suis perdu. Qu'est-ce que je rate? Aidez-moi, s'il vous plaît. Merci d'avance pour tout votre temps (:
Alex.
Essayez d'utiliser self.tableView au lieu de tableView dans 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;
}
Ce code fonctionne plutôt bien
Remarque
Si vous avez des cellules de hauteur personnalisées, n'utilisez pas
[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Utilisez-le à la place
[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
La raison pour laquelle cela a très bien fonctionné lors de la première exécution, mais s'est ensuite écrasé si vous avez quitté le tableau des résultats et que vous êtes retourné pour une autre recherche, c'est parce que le contrôleur d'affichage de recherche charge un nouveau UITableView
chaque fois que vous entrez en mode de recherche.
Par mode de recherche, je veux dire, vous avez appuyé sur le champ de texte et vous avez commencé à taper, à quel point une vue de table est générée pour afficher les résultats, en quittant ce mode qu'il a atteint en appuyant sur le bouton Annuler. Lorsque vous appuyez sur le champ de texte la deuxième fois et recommencez à taper - cela passe en "mode de recherche" pour la deuxième fois.
Donc, pour éviter le plantage, vous devez enregistrer la classe de cellules pour la vue de table à utiliser dans le searchDisplayController:didLoadSearchResultsTableView:
méthode déléguée (de UISearchDisplayDelegate
) de au lieu de dans vos contrôleurs viewDidLoad
méthode.
Comme suit:
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
{
[tableView registerClass:[DPContentTableCell class] forCellReuseIdentifier:cellIdentifier];
[tableView registerClass:[DPEmptyContentTableCell class] forCellReuseIdentifier:emptyCellIdentifier];
}
Cela m'a pris par surprise car sur iOS 7 ... la vue tableau est en cours de réutilisation. Vous pouvez donc enregistrer la classe dans viewDidLoad
si vous préférez. Par souci d'héritage, je conserverai mon inscription dans la méthode déléguée que j'ai mentionnée.
Après la recherche, "tableView" de la méthode cellForRowAtIndexPath ne semble pas être une instance de la table que vous définissez. Ainsi, vous pouvez utiliser une instance d'un tableau qui définit la cellule. Au lieu de:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
Utilisation:
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
(N'utilisez pas la méthode tableView de cellForRowAtIndexPath, utilisez self.tableView.)
Retirez la cellule sans utiliser le 'indexPath' et dans le cas où vous obtenez un élément nil, vous devez l'allouer manuellement.
- (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;
}
Essayer d'utiliser self.tableView pour retirer la file d'attente de la cellule peut provoquer des plantages lorsque vous avez une liste principale sectionnée et une liste de recherche simple. Ce code fonctionne à la place dans n'importe quelle situation.
Lorsque j'ai eu ce problème, la solution remplaçait tableView
dequeueReusableCellWithIdentifier: @yourcell par self.tableView
pour Swift 3 vous avez juste besoin de vous ajouter:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = self.tableView.dequeueReusableCell(withIdentifier: "yourCell", for: indexPath) as! YourCell
...
}
Je travaille également sur ce tutoriel. Le TableViewController par défaut a "forIndexPath" et dans son exemple, il n'existe pas. Une fois que je l'ai supprimé, la recherche fonctionne.
//Default code
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//Replace with
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];