J’essaie d’implémenter le code de recherche dans mon application iPhone CoreData. Je ne sais pas comment procéder. L’application possède déjà un NSFetchedResultsController avec un prédicat pour récupérer les données pour la table TableView principale. Je veux m’assurer que je suis sur la bonne voie avant de changer trop de code. Je suis confus parce que beaucoup d’exemples sont basés sur des tableaux plutôt que sur CoreData.
Voici quelques questions:
Dois-je avoir un deuxième NSFetchedResultsController qui récupère uniquement les éléments correspondants ou puis-je utiliser le même que celui de TableView primaire?
Si j’utilise le même, est-ce aussi simple que d’effacer le cache FRC et de modifier le prédicat dans la méthode handleSearchForTerm: searchSsortingng? Le prédicat doit-il contenir le prédicat initial ainsi que les termes de recherche ou se souvient-il d’avoir utilisé un prédicat pour récupérer les données?
Comment puis-je revenir aux résultats originaux? Est-ce que je viens de définir le prédicat de recherche sur zéro? Est-ce que cela ne va pas tuer le prédicat original qui a été utilisé pour récupérer les résultats de la FRC en premier lieu?
Si quelqu’un a des exemples de code utilisant la recherche avec le FRC, je l’apprécierais beaucoup!
Je viens juste de mettre cela en œuvre sur l’un de mes projets (votre question et l’autre mauvaise réponse ont fait allusion à ce qu’il faut faire). J’ai essayé la réponse de Sergio mais j’avais des problèmes d’exception quand je tournais sur un appareil.
Oui, vous créez deux contrôleurs de résultats d’extraction: un pour l’affichage normal et un autre pour la vue de table de UISearchBar.
Si vous n’utilisez qu’un seul FRC (NSFetchedResultsController), les rappels d’origine (UITableView) (pas la vue de la table de recherche active lors de la recherche) peuvent être appelés pendant la recherche et vous essayez d’utiliser la version filtrée de votre FRC. jeté sur le nombre incorrect de sections ou de lignes dans les sections.
Voici ce que j’ai fait: j’ai deux FRC disponibles en tant que propriétés fetchedResultsController et searchFetchedResultsController. Le searchFetchedResultsController ne doit pas être utilisé à moins qu’il y ait une recherche (lorsque la recherche est annulée, vous pouvez voir ci-dessous que cet object est libéré). Toutes les méthodes UITableView doivent déterminer quelle vue de table elle interrogera et quelle FRC applicable extraira les informations. Les méthodes déléguées FRC doivent également déterminer la tableView à mettre à jour.
Il est surprenant de voir combien de ces codes sont normatifs.
Bits pertinents du fichier d’en-tête:
@interface BlahViewController : UITableViewController { // other class ivars // required ivars for this example NSFetchedResultsController *fetchedResultsController_; NSFetchedResultsController *searchFetchedResultsController_; NSManagedObjectContext *managedObjectContext_; // The saved state of the search UI if a memory warning removed the view. NSSsortingng *savedSearchTerm_; NSInteger savedScopeButtonIndex_; BOOL searchWasActive_; } @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain, readonly) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, copy) NSSsortingng *savedSearchTerm; @property (nonatomic) NSInteger savedScopeButtonIndex; @property (nonatomic) BOOL searchWasActive;
bits pertinents du fichier d’implémentation:
@interface BlahViewController () @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, retain) NSFetchedResultsController *searchFetchedResultsController; @property (nonatomic, retain) UISearchDisplayController *mySearchDisplayController; @end
J’ai créé une méthode utile pour récupérer le fichier FRC correct lorsque vous utilisez toutes les méthodes UITableViewDelegate / DataSource:
- (NSFetchedResultsController *)fetchedResultsControllerForTableView:(UITableView *)tableView { return tableView == self.tableView ? self.fetchedResultsController : self.searchFetchedResultsController; } - (void)fetchedResultsController:(NSFetchedResultsController *)fetchedResultsController configureCell:(UITableViewCell *)theCell atIndexPath:(NSIndexPath *)theIndexPath { // your cell guts here } - (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)theIndexPath { CallTableCell *cell = (CallTableCell *)[theTableView dequeueReusableCellWithIdentifier:@"CallTableCell"]; if (cell == nil) { cell = [[[CallTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CallTableCell"] autorelease]; } [self fetchedResultsController:[self fetchedResultsControllerForTableView:theTableView] configureCell:cell atIndexPath:theIndexPath]; return cell; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { NSInteger count = [[[self fetchedResultsControllerForTableView:tableView] sections] count]; return count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { NSInteger numberOfRows = 0; NSFetchedResultsController *fetchController = [self fetchedResultsControllerForTableView:tableView]; NSArray *sections = fetchController.sections; if(sections.count > 0) { id sectionInfo = [sections objectAtIndex:section]; numberOfRows = [sectionInfo numberOfObjects]; } return numberOfRows; }
Déléguer des méthodes pour la barre de recherche:
#pragma mark - #pragma mark Content Filtering - (void)filterContentForSearchText:(NSSsortingng*)searchText scope:(NSInteger)scope { // update the filter, in this case just blow away the FRC and let lazy evaluation create another with the relevant search info self.searchFetchedResultsController.delegate = nil; self.searchFetchedResultsController = nil; // if you care about the scope save off the index to be used by the serchFetchedResultsController //self.savedScopeButtonIndex = scope; } #pragma mark - #pragma mark Search Bar - (void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView; { // search is done so get rid of the search FRC and reclaim memory self.searchFetchedResultsController.delegate = nil; self.searchFetchedResultsController = nil; } - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchSsortingng:(NSSsortingng *)searchSsortingng { [self filterContentForSearchText:searchSsortingng scope:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]; // Return YES to cause the search result table view to be reloaded. return YES; } - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption { [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]; // Return YES to cause the search result table view to be reloaded. return YES; }
assurez-vous d’utiliser la vue de table correcte lors de l’obtention des mises à jour à partir des méthodes de délégation FRC:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView; [tableView beginUpdates]; } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)theIndexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:theIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [self fetchedResultsController:controller configureCell:[tableView cellForRowAtIndexPath:theIndexPath] atIndexPath:theIndexPath]; break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:theIndexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView; [tableView endUpdates]; }
Autres informations de vue:
- (void)loadView { [super loadView]; UISearchBar *searchBar = [[[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 44.0)] autorelease]; searchBar.autoresizingMask = (UIViewAutoresizingFlexibleWidth); searchBar.autocorrectionType = UITextAutocorrectionTypeNo; self.tableView.tableHeaderView = searchBar; self.mySearchDisplayController = [[[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self] autorelease]; self.mySearchDisplayController.delegate = self; self.mySearchDisplayController.searchResultsDataSource = self; self.mySearchDisplayController.searchResultsDelegate = self; } - (void)didReceiveMemoryWarning { self.searchWasActive = [self.searchDisplayController isActive]; self.savedSearchTerm = [self.searchDisplayController.searchBar text]; self.savedScopeButtonIndex = [self.searchDisplayController.searchBar selectedScopeButtonIndex]; fetchedResultsController_.delegate = nil; [fetchedResultsController_ release]; fetchedResultsController_ = nil; searchFetchedResultsController_.delegate = nil; [searchFetchedResultsController_ release]; searchFetchedResultsController_ = nil; [super didReceiveMemoryWarning]; } - (void)viewDidDisappear:(BOOL)animated { // save the state of the search UI so that it can be restored if the view is re-created self.searchWasActive = [self.searchDisplayController isActive]; self.savedSearchTerm = [self.searchDisplayController.searchBar text]; self.savedScopeButtonIndex = [self.searchDisplayController.searchBar selectedScopeButtonIndex]; } - (void)viewDidLoad { // restore search settings if they were saved in didReceiveMemoryWarning. if (self.savedSearchTerm) { [self.searchDisplayController setActive:self.searchWasActive]; [self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex]; [self.searchDisplayController.searchBar setText:savedSearchTerm]; self.savedSearchTerm = nil; } }
Code de création FRC:
- (NSFetchedResultsController *)newFetchedResultsControllerWithSearch:(NSSsortingng *)searchSsortingng { NSArray *sortDescriptors = // your sort descriptors here NSPredicate *filterPredicate = // your predicate here /* Set up the fetched results controller. */ // Create the fetch request for the entity. NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; // Edit the entity name as appropriate. NSEntityDescription *callEntity = [MTCall entityInManagedObjectContext:self.managedObjectContext]; [fetchRequest setEntity:callEntity]; NSMutableArray *predicateArray = [NSMutableArray array]; if(searchSsortingng.length) { // your search predicate(s) are added to this array [predicateArray addObject:[NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", searchSsortingng]]; // finally add the filter predicate for this view if(filterPredicate) { filterPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:filterPredicate, [NSCompoundPredicate orPredicateWithSubpredicates:predicateArray], nil]]; } else { filterPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicateArray]; } } [fetchRequest setPredicate:filterPredicate]; // Set the batch size to a suitable number. [fetchRequest setFetchBatchSize:20]; [fetchRequest setSortDescriptors:sortDescriptors]; // Edit the section name key path and cache name if appropriate. // nil for section name key path means "no sections". NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; aFetchedResultsController.delegate = self; [fetchRequest release]; NSError *error = nil; if (![aFetchedResultsController performFetch:&error]) { /* Replace this implementation with code to handle the error appropriately. abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button. */ NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return aFetchedResultsController; } - (NSFetchedResultsController *)fetchedResultsController { if (fetchedResultsController_ != nil) { return fetchedResultsController_; } fetchedResultsController_ = [self newFetchedResultsControllerWithSearch:nil]; return [[fetchedResultsController_ retain] autorelease]; } - (NSFetchedResultsController *)searchFetchedResultsController { if (searchFetchedResultsController_ != nil) { return searchFetchedResultsController_; } searchFetchedResultsController_ = [self newFetchedResultsControllerWithSearch:self.searchDisplayController.searchBar.text]; return [[searchFetchedResultsController_ retain] autorelease]; }
Certains ont commenté que cela peut être fait avec un seul NSFetchedResultsController
. C’est ce que j’ai fait et voici les détails. Cette solution suppose que vous souhaitiez simplement filtrer la table et gérer tous les autres aspects (ordre de sorting, disposition des cellules, etc.) des résultats de la recherche.
Tout d’abord, définissez deux propriétés dans votre sous-classe UITableViewController
(avec le @synthesize et le dealloc appropriés, le cas échéant):
@property (nonatomic, retain) UISearchDisplayController *searchController; @property (nonatomic, retain) NSSsortingng *searchSsortingng;
Deuxièmement, initialisez la barre de recherche dans la méthode viewDidLoad:
de votre sous-classe UITableViewController
:
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0,0,self.tableView.frame.size.width,44)]; searchBar.placeholder = @"Search"; searchBar.delegate = self; self.searchController = [[[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self] autorelease]; self.searchController.delegate = self; self.searchController.searchResultsDataSource = self; self.searchController.searchResultsDelegate = self; self.tableView.tableHeaderView = self.searchController.searchBar; [searchBar release];
Troisièmement, implémentez les méthodes déléguées UISearchDisplayController
comme ceci:
// This gets called when you start typing text into the search bar -(BOOL)searchDisplayController:(UISearchDisplayController *)_controller shouldReloadTableForSearchSsortingng:(NSSsortingng *)_searchSsortingng { self.searchSsortingng = _searchSsortingng; self.fetchedResultsController = nil; return YES; } // This gets called when you cancel or close the search bar -(void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView { self.searchSsortingng = nil; self.fetchedResultsController = nil; [self.tableView reloadData]; }
Enfin, dans la méthode fetchedResultsController
, modifiez le NSPredicate
fonction de la NSPredicate
self.searchSsortingng
:
-(NSFetchedResultsController *)fetchedResultsController { if (fetchedResultsController == nil) { // removed for brevity NSPredicate *predicate; if (self.searchSsortingng) { // predicate that uses searchSsortingng (used by UISearchDisplayController) // eg, [NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", self.searchSsortingng]; predicate = ... } else { predicate = ... // predicate without searchSsortingng (used by UITableViewController) } // removed for brevity } return fetchedResultsController; }
Il m’a fallu quelques essais pour que ça marche …
La clé de ma compréhension a été de réaliser qu’il existe deux tablesViews au travail. Un géré par mon viewcontroller et un géré par le searchviewcontroller, puis je pourrais tester pour voir ce qui est actif et faire les choses correctement. La documentation était également utile:
Voici ce que j’ai fait –
Ajout du drapeau searchIsActive:
@interface ItemTableViewController : UITableViewController { NSSsortingng *sectionNameKeyPath; NSArray *sortDescriptors; @private NSFetchedResultsController *fetchedResultsController_; NSManagedObjectContext *managedObjectContext_; BOOL searchIsActive; } @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, retain) NSSsortingng *sectionNameKeyPath; @property (nonatomic, retain) NSArray *sortDescriptors; @property (nonatomic) BOOL searchIsActive;
Ajout de la synthèse dans le fichier d’implémentation.
Ensuite, j’ai ajouté ces méthodes à la recherche:
#pragma mark - #pragma mark Content Filtering - (void)filterContentForSearchText:(NSSsortingng*)searchText scope:(NSSsortingng*)scope { NSFetchRequest *aRequest = [[self fetchedResultsController] fetchRequest]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", searchText]; [aRequest setPredicate:predicate]; NSError *error = nil; if (![[self fetchedResultsController] performFetch:&error]) { // Handle error NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } } #pragma mark - #pragma mark UISearchDisplayController Delegate Methods - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchSsortingng:(NSSsortingng *)searchSsortingng { [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:nil]; return YES; } /* - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption { return YES; } */ - (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller { [self setSearchIsActive:YES]; return; } - (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller { NSFetchRequest *aRequest = [[self fetchedResultsController] fetchRequest]; [aRequest setPredicate:nil]; NSError *error = nil; if (![[self fetchedResultsController] performFetch:&error]) { // Handle error NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } [self setSearchIsActive:NO]; return; }
Puis dans controllerWillChangeContent:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { if ([self searchIsActive]) { [[[self searchDisplayController] searchResultsTableView] beginUpdates]; } else { [self.tableView beginUpdates]; } }
Et controllerDidChangeContent:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { if ([self searchIsActive]) { [[[self searchDisplayController] searchResultsTableView] endUpdates]; } else { [self.tableView endUpdates]; } }
Et supprimez le cache lors de la réinitialisation du prédicat.
J’espère que cela t’aides.
Utilisez-vous une recherche en direct?
Si vous n’êtes pas, vous voulez probablement un tableau (ou un NSFetchedResultsController) avec les recherches précédentes que vous avez utilisées, lorsque l’utilisateur appuie sur “rechercher”, vous indiquez à votre FetchedResults de changer son prédicat.
De toute façon, vous devrez reconstruire vos FetchedResults à chaque fois. Je recommande d’utiliser un seul NSFetchedResultsController, car vous devrez dupliquer votre code et vous n’aurez pas à gaspiller de la mémoire dans quelque chose que vous ne montrez pas.
Assurez-vous simplement d’avoir une variable NSSsortingng “searchParameters” et que votre méthode FetchedResults la reconstruit selon vos besoins, en utilisant les parameters de recherche, si disponibles, vous devriez juste faire:
a) définir le “searchParameters” à quelque chose (ou nil, si vous voulez tous les résultats).
b) libérer et définir à zéro l’object NSFetchedResultsController actuel.
c) recharger les données de la table.
Voici un code simple:
- (void)searchSsortingng:(NSSsortingng*)s { self.searchResults = s; [fetchedResultsController release]; fetchedResultsController = nil; [self.tableView reloadData]; } -(NSFetchedResultsController *)fetchedResultsController { if (fetchedResultsController != nil) { return fetchedResultsController; } NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"EntityName" inManagedObjectContext:self.context]; [fetchRequest setEntity:entity]; [fetchRequest setFetchBatchSize:20]; // searchResults is a NSSsortingng* if (searchResults != nil) { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name LIKE %@",searchResults]; [fetchRequest setPredicate:predicate]; } fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.context sectionNameKeyPath:nil cacheName:nil]; fetchedResultsController.delegate = self; [fetchRequest release]; return fetchedResultsController; }
J’ai fait face à la même tâche et j’ai trouvé LA PLUS SIMPLE POSSIBLE de le résoudre. En bref: vous devez définir une autre méthode, très similaire à -fetchedResultsController
avec un prédicat composé personnalisé.
Dans mon cas personnel, mon -fetchedResultsController
ressemble à ceci:
- (NSFetchedResultsController *) fetchedResultsController { if (fetchedResultsController != nil) { return fetchedResultsController; } NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Client" inManagedObjectContext:[[PTDataManager sharedManager] managedObjectContext]]; [fetchRequest setEntity:entity]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"agency_server_id == %@", agency.server_id]; fetchRequest.predicate = predicate; NSSortDescriptor *sortByName1Descriptor = [[NSSortDescriptor alloc] initWithKey:@"lastname" ascending:YES]; NSSortDescriptor *sortByName2Descriptor = [[NSSortDescriptor alloc] initWithKey:@"firstname" ascending:YES]; NSSortDescriptor *sortByName3Descriptor = [[NSSortDescriptor alloc] initWithKey:@"middlename" ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortByName1Descriptor, sortByName2Descriptor, sortByName3Descriptor, nil]; fetchRequest.sortDescriptors = sortDescriptors; fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:[[PTDataManager sharedManager] managedObjectContext] sectionNameKeyPath:nil cacheName:nil]; fetchedResultsController.delegate = self; return fetchedResultsController; }
Comme vous pouvez le voir, je récupère les clients d’une agence filtrée par le prédicat agency.server_id
. En conséquence, je récupère mon contenu dans une tableView
(toutes liées à l’implémentation du code tableView
et fetchedResultsController
est également standard). Pour implémenter searchField
je searchField
une méthode déléguée UISearchBarDelegate
. Je le déclenche avec la méthode de recherche, disons -reloadTableView
:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSSsortingng *)searchText { [self reloadTableView]; }
et bien sûr la définition de -reloadTableView
:
- (void)reloadTableView { NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Client" inManagedObjectContext:[[PTDataManager sharedManager] managedObjectContext]]; [fetchRequest setEntity:entity]; NSSortDescriptor *sortByName1Descriptor = [[NSSortDescriptor alloc] initWithKey:@"lastname" ascending:YES]; NSSortDescriptor *sortByName2Descriptor = [[NSSortDescriptor alloc] initWithKey:@"firstname" ascending:YES]; NSSortDescriptor *sortByName3Descriptor = [[NSSortDescriptor alloc] initWithKey:@"middlename" ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortByName1Descriptor, sortByName2Descriptor, sortByName3Descriptor, nil]; fetchRequest.sortDescriptors = sortDescriptors; NSPredicate *idPredicate = [NSPredicate predicateWithFormat:@"agency_server_id CONTAINS[cd] %@", agency.server_id]; NSSsortingng *searchSsortingng = self.searchBar.text; if (searchSsortingng.length > 0) { NSPredicate *firstNamePredicate = [NSPredicate predicateWithFormat:@"firstname CONTAINS[cd] %@", searchSsortingng]; NSPredicate *lastNamePredicate = [NSPredicate predicateWithFormat:@"lastname CONTAINS[cd] %@", searchSsortingng]; NSPredicate *middleNamePredicate = [NSPredicate predicateWithFormat:@"middlename CONTAINS[cd] %@", searchSsortingng]; NSPredicate *orPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:[NSArray arrayWithObjects:firstNamePredicate, lastNamePredicate, middleNamePredicate, nil]]; NSPredicate *andPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:idPredicate, nil]]; NSPredicate *finalPred = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:orPredicate, andPredicate, nil]]; [fetchRequest setPredicate:finalPred]; } else { [fetchRequest setPredicate:idPredicate]; } self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:[[PTDataManager sharedManager] managedObjectContext] sectionNameKeyPath:nil cacheName:nil]; self.fetchedResultsController.delegate = self; NSError *error = nil; if (![self.fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", [error localizedDescription], [error localizedFailureReason]); }; [self.clientsTableView reloadData]; }
Ce groupe de code est très similaire à la première, “standard” -fetchedResultsController
BUT mais dans if-else la déclaration est ici:
+andPredicateWithSubpredicates:
– en utilisant cette méthode, nous pouvons définir un prédicat pour enregistrer les résultats de notre première extraction principale dans la tableView
+orPredicateWithSubpredicates
– en utilisant cette méthode, nous filtrons la recherche existante par requête de recherche depuis la searchBar
de searchBar
À la fin, je mets un tableau de prédicats en tant que prédicat composé pour cette extraction particulière. ET pour les prédicats requirejs, OU pour facultatif.
Et c’est tout! Vous n’avez pas besoin d’implémenter plus. Heureux codage!
Swift 3.0, UISearchController, NSFetchedResultsController et Core Data
Ce code fonctionnera sur Swift 3.0 avec Core Data
! Vous aurez besoin d’une méthode de délégué unique et de quelques lignes de code pour filtrer et rechercher les objects du modèle. Rien ne sera nécessaire si vous avez implémenté toutes les méthodes FRC
et leurs méthodes delegate
, ainsi que searchController
.
La méthode du protocole UISearchResultsUpdating
func updateSearchResults(for searchController: UISearchController) { let text = searchController.searchBar.text if (text?.isEmpty)! { // Do something } else { self.fetchedResultsController.fetchRequest.predicate = NSPredicate(format: "( someSsortingng contains[cd] %@ )", text!) } do { try self.fetchedResultsController.performFetch() self.tableView.reloadData() } catch {} }
C’est tout! J’espère que ça vous aide! Merci
SWIFT 3.0
Utilisez un textField, UISearchDisplayController est obsolète à partir d’iOS 8, vous devrez utiliser un UISearchController. Au lieu de traiter avec le contrôleur de recherche, pourquoi ne créez-vous pas votre propre mécanisme de recherche? Vous pouvez le personnaliser davantage et mieux le contrôler, sans avoir à vous soucier de la modification de SearchController et / ou de sa dépréciation.
Cette méthode que j’utilise fonctionne très bien et ne nécessite pas beaucoup de code. Cependant, vous devez utiliser Core Data et implémenter NSFetchedResultsController.
Tout d’abord, créez un TextField et enregistrez-le avec une méthode:
searchTextField?.addTarget(self, action: #selector(textFieldDidChange), for: UIControlEvents.editingChanged)
Ensuite, créez votre méthode textFieldDidChange, décrite dans le sélecteur lorsque la cible a été ajoutée:
func textFieldDidChange() { if let querySsortingng = searchTextField.text { filterList(querySsortingng) self.tableView.reloadData() } }
Vous souhaitez ensuite filtrer la liste dans la méthode filterList()
aide du prédicat NSPredicate ou NSCompound si celui-ci est plus complexe. Dans ma méthode filterList, je filtre en fonction du nom de l’entité et du nom de l’object “subCategories” (une relation un à plusieurs).
func filterList(_ querySsortingng: Ssortingng) { if let currentProjectID = Constants.userDefaults.ssortingng(forKey: Constants.CurrentSelectedProjectID) { if let currentProject = ProjectDBFacade.getProjectWithID(currentProjectID) { if (querySsortingng != ""){ let categoryPredicate = NSPredicate(format: "name CONTAINS[c] %@ && project == %@", querySsortingng, currentProject) let subCategoryPredicate = NSPredicate(format: "subCategories.name CONTAINS[c] %@ && project == %@", querySsortingng, currentProject) let orPredicate = NSCompoundPredicate(type: .or, subpredicates: [categoryPredicate, subCategoryPredicate]) fetchedResultsController.fetchRequest.predicate = orPredicate }else{ fetchedResultsController.fetchRequest.predicate = NSPredicate(format: "project == %@", currentProject) } do { try fetchedResultsController.performFetch() } catch { print("Error: Could not fetch fetchedResultsController") } } } }
Je pense que Luka a une meilleure approche pour cela. Voir LargeDataSetSample et sa raison
Il n’utilise pas FetchedResultsController
, mais utilise le cache lors de la recherche. Par conséquent, les résultats de la recherche apparaissent beaucoup plus rapidement lorsque l’utilisateur tape plus dans SearchBar.
J’ai utilisé son approche dans mon application et ça marche bien. Rappelez-vous également si vous souhaitez travailler avec un object Model, rendez-le aussi simple que possible, voir ma réponse à propos de setPropertiesToFetch
Voici un moyen de gérer les résultats d’extraction avec plusieurs ensembles de données, à la fois simples et généraux, à appliquer presque partout. Saisissez simplement vos principaux résultats dans un tableau lorsque certaines conditions sont présentes.
NSArray *results = [self.fetchedResultsController fetchedObjects];
Interrogez le tableau en le parcourant ou ce que vous désirez afin de créer un sous-ensemble de vos principaux résultats récupérés. Et maintenant, vous pouvez utiliser le jeu complet ou le sous-ensemble lorsque certaines conditions sont présentes.
J’ai vraiment apprécié l’approche de @Josh O’Connor où il n’utilise pas UISearchController
. Ce contrôleur encore (Xcode 9) a un bug de mise en page que beaucoup essaient de contourner.
Je suis UISearchBar
à utiliser un UISearchBar
au lieu d’un UITextField
, et ça marche plutôt bien. Mon exigence pour la recherche / filtre est de produire un NSPredicate
. Ceci est transmis au FRC:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate { @IBOutlet var searchBar: UISearchBar! func searchBar(_ searchBar: UISearchBar, textDidChange searchText: Ssortingng) { shouldShowSearchResults = true if let querySsortingng = searchBar.text { filterList(querySsortingng) fetchData() } } func filterList(_ querySsortingng: Ssortingng) { if (querySsortingng == "") { searchPredicate = nil } else { let brandPredicate = NSPredicate(format: "brand CONTAINS[c] %@", querySsortingng) let modelPredicate = NSPredicate(format: "model CONTAINS[c] %@", querySsortingng) let orPredicate = NSCompoundPredicate(type: .or, subpredicates: [brandPredicate, modelPredicate]) searchPredicate = orPredicate } }
…
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext let request = NSFetchRequest(entityName: filmEntity) request.returnsDistinctResults = true request.propertiesToFetch = ["brand", "model"] request.sortDescriptors = [sortDescriptor] request.predicate = searchPredicate
Enfin, connectez le SearchBar à son délégué.
J’espère que cela aide les autres
Approche simple pour filtrer UITableView existant en utilisant CoreData et qui est déjà sortingé comme vous le souhaitez.
C’est littéralement moi 5 minutes pour mettre en place et travailler.
J’avais un UITableView
existant utilisant CoreData
de données provenant d’iCloud et qui avait des interactions utilisateur assez compliquées et je ne voulais pas avoir à reproduire tout cela pour un UISearchViewController
. J’ai simplement pu append un prédicat à la FetchRequest
existante déjà utilisée par FetchResultsController
et filtrer les données déjà sortingées.
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSSsortingng *)searchText { NSPredicate *filterPredicate; if(searchText != nil && searchText.length > 0) filterPredicate = [NSPredicate predicateWithFormat:@"(someField CONTAINS[cd] %@) OR (someOtherField CONTAINS[cd] %@)", searchText, searchText]; else filterPredicate = nil; _fetchedResultsController.fetchRequest.predicate = filterPredicate; NSError *error = nil; [_fetchedResultsController performFetch:&error]; [self.tableView reloadData]; }