3 回答
TA贡献1831条经验 获得超4个赞
我实际上只是在我的一个项目上实现了这个(你的问题和另一个错误的答案暗示了该做什么)。我尝试了Sergio的回答但在实际运行在设备上时遇到异常问题。
是的,您创建了两个获取结果控制器:一个用于正常显示,另一个用于UISearchBar的表视图。
如果您只使用一个FRC(NSFetchedResultsController),则原始UITableView(不是搜索时处于活动状态的搜索表视图)可能会在您搜索时调用回调并尝试错误地使用FRC的过滤版本,您将看到异常抛出部分中不正确数量的部分或行。
这是我做的:我有两个FRC可用作属性fetchedResultsController和searchFetchedResultsController。除非有搜索,否则不应使用searchFetchedResultsController(当取消搜索时,您可以在下面看到该对象已被释放)。所有UITableView方法都必须弄清楚它将查询哪个表视图以及从中提取信息的适用FRC。FRC委托方法还必须确定要更新的tableView。
令人惊讶的是,这有多少是样板代码。
头文件的相关位:
@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方法时,我创建了一个有用的方法来检索正确的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;}
委托搜索栏的方法:
#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创建代码:
- (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];}
TA贡献1772条经验 获得超5个赞
有人评论说,这可以用一个单独完成NSFetchedResultsController
。这就是我所做的,这里是细节。此解决方案假设您只想过滤表格并维护搜索结果的所有其他方面(排序顺序,单元格布局等)。
首先,在UITableViewController
子类中定义两个属性(使用适当的@synthesize和dealloc,如果适用):
@property (nonatomic, retain) UISearchDisplayController *searchController;@property (nonatomic, retain) NSString *searchString;
其次,初始化子类viewDidLoad:
方法中的搜索栏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];
第三,实现这样的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
方法中更改NSPredicate
依赖if self.searchString
是否定义:
-(NSFetchedResultsController *)fetchedResultsController { if (fetchedResultsController == nil) { // removed for brevity NSPredicate *predicate; if (self.searchString) { // predicate that uses searchString (used by UISearchDisplayController) // e.g., [NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", self.searchString]; predicate = ... } else { predicate = ... // predicate without searchString (used by UITableViewController) } // removed for brevity } return fetchedResultsController;}
- 3 回答
- 0 关注
- 573 浏览
添加回答
举报