延續Core Data-Part2的文章,現在要加入的是搜尋,當Table View中有越來越多的資料後,要尋找某比資料的話,用眼睛尋找是一件很累人的事情!所以可以透過輸入關鍵字來搜尋,接下來就來完成這個範例。
Step 1.加入Search Bar and Search Display Controller
加入Search Bar and Search Display Controller
到Table View
上方。
Step 2.編輯MasterViewController.h檔案
MasterViewController.h1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #import <UIKit/UIKit.h> #import <CoreData/CoreData.h>
#import "Phone.h"
@interface MasterViewController : UITableViewController <NSFetchedResultsControllerDelegate, UISearchDisplayDelegate, UISearchBarDelegate> { BOOL savedSearchTerm; }
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController; @property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain) NSMutableArray *searchResults;
@property (nonatomic, retain) NSIndexPath *indexP;
@end
|
引入<code>Phone.h</code>。
加入<code>UISearchDisplayDelegate</code>、<code>UISearchBarDelegate</code>兩個協定。
<code>saveSearchTerm</code>用來判斷是否為搜尋狀態。
<code>searchResults</code>陣列是用來儲存搜尋的結果。
<code>indexP</code>是用來取得搜尋結果,點選cell的索引值。
Step 3.編輯MasterViewController.m
補上synthesize
。
MasterViewController.m1 2 3 4 5 6 7 8 9 10 11 12 13 14
| - (void)viewDidLoad { [super viewDidLoad]; self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]]; [self.tableView reloadData]; }
- (void)viewDidUnload { [super viewDidUnload]; self.searchResults = nil; }
|
在`viewDidLoad`方法內對`searchResults`初始化,並且重新整理`Table View`。
`viewDidUnload`方法內,將`searchResults`釋放掉。
MasterViewController.m1 2 3 4 5 6 7 8 9 10
| - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (tableView == self.searchDisplayController.searchResultsTableView) { return [self.searchResults count]; } else { id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section]; return [sectionInfo numberOfObjects]; } }
|
Table View
的cell
個數,在搜尋的時候會不一樣,因此tableView:numberOfRowsInSection:
方法需要做修改。
如上當if
判斷式條件成立時,會回傳searchResults
陣列的個數,否則就回傳原本fetchedResultsController
的個數。
MasterViewController.m1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; } Phone *phone = nil; if (tableView == self.searchDisplayController.searchResultsTableView) { phone = [self.searchResults objectAtIndex:indexPath.row]; } else { phone = [self.fetchedResultsController objectAtIndexPath:indexPath]; }
cell.textLabel.text = phone.name; return cell; }
|
這邊需要額外使用程式碼的方式定義一個`cell`,並且初始化,因為在搜尋的時候並不知道`Storyboard`中的`cell`。
當`tableView`是在搜尋的狀態時,指定`searchResults`搜尋結果給`phone`,反之將`fetchedResultsController`指定給`phone`。
這邊改成直接指定`cell`的文字為`phone.name`。
MasterViewController.m1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #pragma mark - #pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString { [self filterContentForSearchText:searchString scope:@"All"]; savedSearchTerm = TRUE; return YES; }
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption { [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:@"All"]; savedSearchTerm = TRUE; return YES; } -(void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller{ savedSearchTerm = TRUE; }
|
加入上述三個UISearchDisplayController
的方法,並且指定savedSearchTerm
為TRUE
,其中可以看到filterContentForSearchText:scope:
這個方法,該方法是用來根據您輸入的文字做搜尋的。
MasterViewController.m1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #pragma mark - #pragma mark Content Filtering
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope { [self.searchResults removeAllObjects]; NSLog(@"search text: %@", searchText); for (Phone *phone in [self.fetchedResultsController fetchedObjects]) { if ([scope isEqualToString:@"All"] || [phone.name isEqualToString:scope]) { NSComparisonResult result = [phone.name compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])]; if (result == NSOrderedSame) { [self.searchResults addObject:phone]; } } } }
|
該方法會根據您輸入的字串,尋找到符合的結果!
MasterViewController.m1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| -(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (savedSearchTerm) { indexP = indexPath; [self performSegueWithIdentifier:@"showDetail" sender:self]; } }
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@"showDetail"]) { if (savedSearchTerm) { NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexP]; [[segue destinationViewController] setDetailItem:object]; } else { NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath]; [[segue destinationViewController] setDetailItem:object]; } } }
|
當你搜尋完畢後要可以點選`cell`,然後看到該`cell`的細部資訊,所以透過`tableView:didSelectRowAtIndexPath:`方法可以取得你點選`cell`的`indexPath`索引值,將該值指定給`indexP`,在呼叫到`perforSegueWithIdentifier:sender:`方法。
該方法就是要用來傳遞參數到`DetailViewController`的,一樣需要判斷是否在搜尋的狀態,然後再傳遞對應的索引值(`indexPath`或`indexP`)。