如何使用UISearchDisplayController / UISearchBar过滤NSFetchedResultsController(CoreData)
我试图在我的基于CoreData的iPhone应用程序中实现search代码。 我不知道如何继续。 该应用程序已经有一个NSFetchedResultsController与谓词来检索主TableView的数据。 在更改太多的代码之前,我想确保自己走在正确的道路上。 我很困惑,因为这么多的例子是基于数组而不是CoreData。
这里有一些问题:
-
我是否需要第二个NSFetchedResultsController检索只匹配的项目,或者我可以使用相同的主要TableView?
-
如果我使用相同的,就像清除FRCcaching,然后改变handleSearchForTerm:searchString方法中的谓词一样简单? 谓词是否必须包含初始谓词和search词,还是记得它首先使用谓词来检索数据?
-
我如何回到原来的结果? 我只是设置search谓词为零? 这不会杀死最初用来检索FRC结果的谓词吗?
如果任何人有任何使用FRCsearch代码的例子,我将不胜感激!
我其实只是在我的一个项目上实现了这个(你的问题和其他错误的答案暗示了该怎么做)。 我尝试了Sergio的回答,但在设备上运行时遇到了exception问题。
是的,您创build了两个提取结果控制器:一个用于正常显示,另一个用于UISearchBar的表格视图。
如果您只使用一个FRC(NSFetchedResultsController),那么原始UITableView(不是在search时处于活动状态的search表视图)可能会在search时调用callback,并尝试错误地使用FRC的过滤版本,您将看到exception在部分中抛出不正确的节数或行数。
这是我做了什么:我有两个FRC可用属性fetchedResultsController和searchFetchedResultsController。 searchFetchedResultsController不应该被使用,除非有一个search(当search被取消时,你可以看到下面这个对象被释放)。 所有的UITableView方法必须找出它将要查询的表格视图以及哪个可用的FRC来从中提取信息。 FRC委托方法还必须找出哪个tableView要更新。
这是多less是样板代码是令人惊讶的。
头文件的相关位:
@interface BlahViewController : UITableViewController <UISearchBarDelegate, NSFetchedResultsControllerDelegate, UISearchDisplayDelegate> { // 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. NSString *savedSearchTerm_; NSInteger savedScopeButtonIndex_; BOOL searchWasActive_; } @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain, readonly) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, copy) NSString *savedSearchTerm; @property (nonatomic) NSInteger savedScopeButtonIndex; @property (nonatomic) BOOL searchWasActive;
实施文件的相关部分:
@interface BlahViewController () @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, retain) NSFetchedResultsController *searchFetchedResultsController; @property (nonatomic, retain) UISearchDisplayController *mySearchDisplayController; @end
我使用所有的UITableViewDelegate / DataSource方法创build了一个有用的方法来检索正确的FRC:
- (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 <NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:section]; numberOfRows = [sectionInfo numberOfObjects]; } return numberOfRows; }
为search栏委派方法:
#pragma mark - #pragma mark Content Filtering - (void)filterContentForSearchText:(NSString*)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 shouldReloadTableForSearchString:(NSString *)searchString { [self filterContentForSearchText:searchString 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; }
请确保从FRC委托方法获取更新时使用正确的表视图:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView; [tableView beginUpdates]; } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)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]; }
其他视图信息:
- (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; } }
FRC创build代码:
- (NSFetchedResultsController *)newFetchedResultsControllerWithSearch:(NSString *)searchString { 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(searchString.length) { // your search predicate(s) are added to this array [predicateArray addObject:[NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", searchString]]; // 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]; }
有些人评论说,这可以用一个NSFetchedResultsController
。 这就是我所做的,这里是细节。 这个解决scheme假设你只是想过滤掉表格并且维护search结果的所有其他方面(sorting顺序,单元布局等)。
首先,在UITableViewController
子类中定义两个属性(如果适用的话,使用适当的@synthesize和dealloc):
@property (nonatomic, retain) UISearchDisplayController *searchController; @property (nonatomic, retain) NSString *searchString;
其次,初始化UITableViewController
子类的viewDidLoad:
方法中的search栏:
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];
第三,实现像这样的UISearchDisplayController
委托方法:
// This gets called when you start typing text into the search bar -(BOOL)searchDisplayController:(UISearchDisplayController *)_controller shouldReloadTableForSearchString:(NSString *)_searchString { self.searchString = _searchString; self.fetchedResultsController = nil; return YES; } // This gets called when you cancel or close the search bar -(void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView { self.searchString = nil; self.fetchedResultsController = nil; [self.tableView reloadData]; }
最后,在fetchedResultsController
方法中,根据是否定义了fetchedResultsController
更改NSPredicate
:
-(NSFetchedResultsController *)fetchedResultsController { if (fetchedResultsController == nil) { // removed for brevity NSPredicate *predicate; if (self.searchString) { // predicate that uses searchString (used by UISearchDisplayController) // eg, [NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", self.searchString]; predicate = ... } else { predicate = ... // predicate without searchString (used by UITableViewController) } // removed for brevity } return fetchedResultsController; }
我花了几个尝试才得到这个工作…
我理解的关键是意识到有两个tableViews在这里工作。 一个由我的viewcontrollerpipe理,一个由searchviewcontrollerpipe理,然后我可以testing看哪个是活动的,做正确的事情。 文档也是有帮助的:
这就是我所做的 –
添加了searchIsActive标志:
@interface ItemTableViewController : UITableViewController <NSFetchedResultsControllerDelegate, UISearchDisplayDelegate, UISearchBarDelegate> { NSString *sectionNameKeyPath; NSArray *sortDescriptors; @private NSFetchedResultsController *fetchedResultsController_; NSManagedObjectContext *managedObjectContext_; BOOL searchIsActive; } @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, retain) NSString *sectionNameKeyPath; @property (nonatomic, retain) NSArray *sortDescriptors; @property (nonatomic) BOOL searchIsActive;
在实现文件中添加了合成。
然后,我添加了这些方法进行search:
#pragma mark - #pragma mark Content Filtering - (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)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 shouldReloadTableForSearchString:(NSString *)searchString { [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; }
然后在controllerWillChangeContent中:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { if ([self searchIsActive]) { [[[self searchDisplayController] searchResultsTableView] beginUpdates]; } else { [self.tableView beginUpdates]; } }
和controllerDidChangeContent:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { if ([self searchIsActive]) { [[[self searchDisplayController] searchResultsTableView] endUpdates]; } else { [self.tableView endUpdates]; } }
重置谓词时删除caching。
希望这可以帮助。
你正在使用实时search?
如果你不是,你可能需要一个数组(或一个NSFetchedResultsController)以前的search,当用户按下“search”,你告诉你的FetchedResults改变它的谓词。
无论哪种方式,您将需要每次重buildFetchedResults。 我build议只使用一个NSFetchedResultsController,因为你必须重复你的代码,你不需要浪费你没有显示的内存。
只要确保你有一个NSString“searchParameters”variables,你的FetchedResults方法根据需要使用search参数为你重build它,你应该这样做:
a)将“searchParameters”设置为某个值(或nil,如果您想要所有的结果)。
b)释放并设置为零当前的NSFetchedResultsController对象。
c)重新加载表格数据。
这是一个简单的代码:
- (void)searchString:(NSString*)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 NSString* 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; }
我面临着同样的任务,并find了最简单的方法来解决它。 -fetchedResultsController
:您需要定义一个更多的方法,与使用自定义复合谓词的-fetchedResultsController
类似。
在我个人的情况下,我的-fetchedResultsController
看起来像这样:
- (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; }
正如你所看到的,我正在提取由agency.server_id
谓词过滤的一个代理的客户端。 因此,我在tableView
检索我的内容(所有与tableView
和fetchedResultsController
代码的实现相关的标准都是相当标准的)。 为了实现searchField
我定义了一个UISearchBarDelegate
委托方法。 我用search方法触发它,说-reloadTableView
:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { [self reloadTableView]; }
当然还有-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]; NSString *searchString = self.searchBar.text; if (searchString.length > 0) { NSPredicate *firstNamePredicate = [NSPredicate predicateWithFormat:@"firstname CONTAINS[cd] %@", searchString]; NSPredicate *lastNamePredicate = [NSPredicate predicateWithFormat:@"lastname CONTAINS[cd] %@", searchString]; NSPredicate *middleNamePredicate = [NSPredicate predicateWithFormat:@"middlename CONTAINS[cd] %@", searchString]; 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]; }
这一堆代码是非常类似于第一,“标准” -fetchedResultsController
但在里面的if-else语句是:
+andPredicateWithSubpredicates:
– 使用这个方法,我们可以设置一个谓词来保存我们在tableView
第一次获取的结果
+orPredicateWithSubpredicates
– 使用这种方法,我们正在过滤从searchsearchBar
search查询现有的抓取
最后,我将谓词数组设置为此特定提取的复合谓词。 AND用于所需的谓词,OR用于可选。
就这样! 你不需要执行更多的事情。 快乐的编码!
Swift 3.0,UISearchController,NSFetchedResultsController和Core Data
这个代码将工作在Swift 3.0与Core Data
! 您需要一个委托方法和几行代码来过滤和search模型中的对象。 如果您已经实现了所有FRC
及其delegate
方法以及searchController
。
UISearchResultsUpdating
协议方法
func updateSearchResults(for searchController: UISearchController) { let text = searchController.searchBar.text if (text?.isEmpty)! { // Do something } else { self.fetchedResultsController.fetchRequest.predicate = NSPredicate(format: "( someString contains[cd] %@ )", text!) } do { try self.fetchedResultsController.performFetch() self.tableView.reloadData() } catch {} }
而已! 希望它可以帮助你! 谢谢
我认为卢卡有更好的方法。 请参阅LargeDataSetSample 及其原因
他不使用FetchedResultsController
,但在search时使用caching,因此当用户在SearchBar中input更多内容时,search结果显得更快
我在我的应用程序中使用了他的方法,它工作正常。 还记得如果你想使用模型对象,使其尽可能简单,请参阅我有关setPropertiesToFetch的答案
这是一种处理fetchedResults的方法,它具有多个数据集,既简单又通用,几乎可以在任何地方应用。 当存在某种情况时,只需将主要结果抓取到数组中即可。
NSArray *results = [self.fetchedResultsController fetchedObjects];
通过遍历它或任何你想要的来查询数组,以创build主fetchedResults的一个子集。 现在,您可以在出现某种情况时使用全套或子集。
SWIFT 3.0
使用textField,UISearchDisplayController从iOS 8开始已弃用,您将不得不使用UISearchController。 为什么不创build自己的search机制,而不是处理search控制器? 您可以自定义更多,并有更多的控制,而不必担心SearchController更改和/或被弃用。
我使用这种方法工作得很好,并不需要太多的代码。 但是,它确实需要使用核心数据并实现NSFetchedResultsController。
首先,创build一个TextField并用一个方法注册它:
searchTextField?.addTarget(self, action: #selector(textFieldDidChange), for: UIControlEvents.editingChanged)
然后创build你的textFieldDidChange方法,在添加目标时在select器中描述:
func textFieldDidChange() { if let queryString = searchTextField.text { filterList(queryString) self.tableView.reloadData() } }
然后,如果更复杂, filterList()
使用NSPredicate或NSCompound谓词筛选filterList()
方法中的列表。 在我的filterList方法中,我是基于实体名称和实体“subCategories”对象(一对多关系)的名称进行过滤。
func filterList(_ queryString: String) { if let currentProjectID = Constants.userDefaults.string(forKey: Constants.CurrentSelectedProjectID) { if let currentProject = ProjectDBFacade.getProjectWithID(currentProjectID) { if (queryString != ""){ let categoryPredicate = NSPredicate(format: "name CONTAINS[c] %@ && project == %@", queryString, currentProject) let subCategoryPredicate = NSPredicate(format: "subCategories.name CONTAINS[c] %@ && project == %@", queryString, 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") } } } }