Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Beginning iOS5 Development.pdf
Скачиваний:
7
Добавлен:
09.05.2015
Размер:
15.6 Mб
Скачать

264

CHAPTER 8: Introduction to Table Views

These buttons don’t do anything on their own (except toggle when the user taps them), but you could use them to let the delegate set up some different display content depending on the status of the toggle buttons.

Leave those first two unchecked, but do check the box that says Shows Cancel Button. A Cancel button will appear to the right of the search field. The user can tap this button to cancel the search. The final checkbox is for enabling the scope bar, which is a series of connected buttons designed to let the user pick from various categories of searchable things (as specified by the series of Scope Titles beneath it). We’re not going to use the scope functionality, so just leave those parts alone.

Below the checkboxes and Scope Titles, set the Correction popup menu to No to indicate that the search bar should not try to correct the user’s spelling.

Switch to the connections inspector by pressing 6, and drag from the delegate connection to the File’s Owner icon to tell this search bar that our view controller is also the search bar’s delegate.

That should be everything we need here, so make sure to save your work before moving on. Now, let’s dig into some code.

Modifying the Controller Implementation

The changes to accommodate the search bar are fairly drastic. Make the following modifications to BIDViewController.m:

#import "BIDViewController.h"

#import "NSDictionary+MutableDeepCopy.h"

@implementation ViewController @synthesize names;

@synthesize keys;

@synthesize table; @synthesize search; @synthesize allNames;

#pragma mark -

#pragma mark Custom Methods - (void)resetSearch {

self.names = [self.allNames mutableDeepCopy]; NSMutableArray *keyArray = [[NSMutableArray alloc] init]; [keyArray addObjectsFromArray:[[self.allNames allKeys]

sortedArrayUsingSelector:@selector(compare:)]]; self.keys = keyArray;

}

- (void)handleSearchForTerm:(NSString *)searchTerm {

NSMutableArray *sectionsToRemove = [[NSMutableArray alloc] init]; [self resetSearch];

for (NSString *key in self.keys) {

NSMutableArray *array = [names valueForKey:key]; NSMutableArray *toRemove = [[NSMutableArray alloc] init];

www.it-ebooks.info

CHAPTER 8: Introduction to Table Views

265

for (NSString *name in array) {

if ([name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location == NSNotFound)

[toRemove addObject:name];

}

if ([array count] == [toRemove count]) [sectionsToRemove addObject:key];

[array removeObjectsInArray:toRemove];

}

[self.keys removeObjectsInArray:sectionsToRemove]; [table reloadData];

}

.

.

.

- (void)viewDidLoad { [super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib. NSString *path = [[NSBundle mainBundle] pathForResource:@"sortednames"

ofType:@"plist"];

NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];

self.names = dict; self.allNames = dict;

NSArray *array = [[names allKeys] sortedArrayUsingSelector: @selector(compare:)];

self.keys = array;

[self resetSearch]; [table reloadData];

[table setContentOffset:CGPointMake(0.0, 44.0) animated:NO];

}

- (void)viewDidUnload { [super viewDidUnload];

//Release any retained subviews of the main view.

//e.g. self.myOutlet = nil;

self.table = nil; self.search = nil; self.allNames = nil; self.names = nil; self.keys = nil;

}

.

.

.

#pragma mark -

#pragma mark Table View Data Source Methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [keys count];

return ([keys count] > 0) ? [keys count] : 1;

}

- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {

www.it-ebooks.info

266 CHAPTER 8: Introduction to Table Views

if ([keys count] == 0) return 0;

NSString *key = [keys objectAtIndex:section]; NSArray *nameSection = [names objectForKey:key]; return [nameSection count];

}

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

NSUInteger section = [indexPath section]; NSUInteger row = [indexPath row];

NSString *key = [keys objectAtIndex:section];

NSArray *nameSection = [names objectForKey:key];

static NSString *sectionsTableIdentifier = @"SectionsTableIdentifier";

UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier: sectionsTableIdentifier];

if (cell == nil) {

cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier: sectionsTableIdentifier] autorelease];

}

cell.textLabel.text = [nameSection objectAtIndex:row]; return cell;

}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { if ([keys count] == 0)

return nil;

NSString *key = [keys objectAtIndex:section]; return key;

}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { return keys;

}

#pragma mark -

#pragma mark Table View Delegate Methods

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { [search resignFirstResponder];

return indexPath;

}

#pragma mark -

#pragma mark Search Bar Delegate Methods

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { NSString *searchTerm = [searchBar text];

[self handleSearchForTerm:searchTerm];

}

www.it-ebooks.info

CHAPTER 8: Introduction to Table Views

267

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchTerm { if ([searchTerm length] == 0) {

[self resetSearch]; [table reloadData]; return;

}

[self handleSearchForTerm:searchTerm];

}

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { search.text = @"";

[self resetSearch]; [table reloadData];

[searchBar resignFirstResponder];

}

@end

Wow, are you still with us after all that typing? Let’s break it down and see what we just did. We’ll start with the first new method we added.

Copying Data from allNames

Our new resetSearch method will be called any time the search is canceled or the search term changes.

- (void)resetSearch {

self.names = [self.allNames mutableDeepCopy]; NSMutableArray *keyArray = [[NSMutableArray alloc] init]; [keyArray addObjectsFromArray:[[self.allNames allKeys]

sortedArrayUsingSelector:@selector(compare:)]]; self.keys = keyArray;

}

All this method does is create a mutable copy of allNames, assign it to names, and then refresh the keys array so it includes all the letters of the alphabet.

We need to refresh the keys array because if a search eliminates all values from a section, we need to get rid of that section, too. Otherwise, the screen would be filled up with headers and empty sections, and it wouldn’t look good. We also don’t want to provide an index to something that doesn’t exist, so as we cull the names based on the search terms, we also cull the empty sections.

Implementing the Search

The other new method we added is the actual search.

- (void)handleSearchForTerm:(NSString *)searchTerm {

NSMutableArray *sectionsToRemove = [[NSMutableArray alloc] init]; [self resetSearch];

for (NSString *key in self.keys) {

NSMutableArray *array = [names valueForKey:key];

www.it-ebooks.info

268 CHAPTER 8: Introduction to Table Views

NSMutableArray *toRemove = [[NSMutableArray alloc] init]; for (NSString *name in array) {

if ([name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location == NSNotFound)

[toRemove addObject:name];

}

if ([array count] == [toRemove count]) [sectionsToRemove addObject:key];

[array removeObjectsInArray:toRemove];

}

[self.keys removeObjectsInArray:sectionsToRemove]; [table reloadData];

}

Although we’ll kick off the search in the search bar delegate methods, we pulled handleSearchForTerm: into its own method, since we’re going to need to use the exact same functionality in two different delegate methods. By embedding the search in the handleSearchForTerm: method, we consolidate the functionality into a single place so it’s easier to maintain, and then we just call this new method as required. Since this is the real meat (or tofu, if you prefer) of this section, let’s break this method down into smaller chunks.

First, we create an array that’s going to hold the empty sections as we find them. We use this array to remove those empty sections later, because it is not safe to remove objects from a collection while iterating through that collection. Since we are using fast enumeration, attempting to do that will raise an exception. So, since we won’t be able to remove keys while we’re iterating through them, we store the sections to be removed in an array, and after we’re finished enumerating, we remove all the objects at once. After allocating the array, we reset the search.

NSMutableArray *sectionsToRemove = [[NSMutableArray alloc] init]; [self resetSearch];

Next, we enumerate through all the keys in the newly restored keys array.

for (NSString *key in self.keys) {

Each time through the loop, we grab the array of names that corresponds to the current key and create another array to hold the values we need to remove from the names array. Remember that we’re removing names and sections, so we must keep track of which keys are empty as well as which names don’t match the search criteria.

NSMutableArray *array = [names valueForKey:key];

NSMutableArray *toRemove = [[NSMutableArray alloc] init];

Next, we iterate through all the names in the current array. So, if we’re currently working through the key of A, this loop will enumerate through all the names that begin with A.

for (NSString *name in array) {

Inside this loop, we use one of NSString‘s methods that returns the location of a substring within a string. We specify an option of NSCaseInsensitiveSearch to tell it we don’t care about the search term’s case—in other words, A is the same as a. The value

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]