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

CHAPTER 8: Introduction to Table Views

269

returned by this method is an NSRange struct with two members: location and length. If the search term was not found, the location will be set to NSNotFound, so we just check for that. If the NSRange that is returned contains NSNotFound, we add the name to the array of objects to be removed later.

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

[toRemove addObject:name];

}

After we’ve looped through all the names for a given letter, we check to see whether the array of names to be removed is the same length as the array of names. If it is, we know this section is now empty, and we add it to the array of keys to be removed later.

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

Next, we actually remove the nonmatching names from this section’s arrays.

[array removeObjectsInArray:toRemove];

}

Finally, we remove the empty sections, release the array used to keep track of the empty sections, and tell the table to reload its data.

[self.keys removeObjectsInArray:sectionsToRemove]; [sectionsToRemove release];

[table reloadData];

}

Changes to viewDidLoad

Down in viewDidLoad, we made a few changes. First, we now load the property list into the allNames dictionary instead of the names dictionary and delete the code that loads the keys array, because that is now done in the resetSearch method. We then call the resetSearch method, which populates the names mutable dictionary and the keys array for us. After that, we call reloadData on our tableView. In the normal flow of the program, reloadData will be called before the user ever sees the table, so most of the time it’s not necessary to call it in viewDidLoad:. However, in order for the line after it, setContentOffset:animated:, to work, we need to make sure that the table is all set up, which we do by calling reloadData on the table.

[table reloadData];

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

So, what does setContentOffset:animated: do? Well, it does exactly what it sounds like. It offsets the contents of the table—in our case, by 44 pixels, the height of the search bar. This causes the search bar to be scrolled off the top when the table first comes up. In effect, we are “hiding” the search bar up there at the top, to be discovered by users the first time they scroll all the way up. This is similar to the way that Mail, Contacts, and other standard iOS applications support searching. Users don’t see the search bar at first, but can bring it into view with a simple downward swipe.

www.it-ebooks.info

270

CHAPTER 8: Introduction to Table Views

Hiding the search bar bears a certain risk in that the user might not discover the search functionality at first, or perhaps not at all! However, this is a risk shared among a wide variety of iOS apps, and this usage of the search bar is now so common that there’s no real reason to show anything more explicit. We’ll talk more about this in the “Adding a Magnifying Glass to the Index” section, coming up soon.

Changes to Data Source Methods

If you skip down to the data source methods, you’ll see we made a few minor changes there. Because the names dictionary and keys array are still being used to feed the data source, these methods are basically the same as they were before.

We did need to account for the fact that table views always have a minimum of one section, and yet the search could potentially exclude all names from all sections. So, we added a little code to check for the situation where all sections were removed. In those cases, we feed the table view a single section with no rows and a blank name. This avoids any problems and doesn’t give any incorrect feedback to the user.

Adding a Table View Delegate Method

Below the data source methods, we’ve added a single delegate method. If the user clicks a row while using the search bar, we want the keyboard to go away. We accomplish this by implementing tableView:willSelectRowAtIndexPath: and telling the search bar to resign first responder status, which will cause the keyboard to retract. Next, we return indexPath unchanged.

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

return indexPath;

}

We could also have done this in tableView:didSelectRowAtIndexPath:, but because we’re doing it here, the keyboard retracts a bit sooner.

Adding Search Bar Delegate Methods

The search bar has a number of methods that it calls on its delegate. When the user taps return or the search key on the keyboard, searchBarSearchButtonClicked: will be called. Our version of this method grabs the search term from the search bar and calls our search method, which will remove the nonmatching names from names and the empty sections from keys.

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

[self handleSearchForTerm:searchTerm];

}

The searchBarSearchButtonClicked: method should be implemented any time you use a search bar.

www.it-ebooks.info

CHAPTER 8: Introduction to Table Views

271

We also implement another search bar delegate method, which requires a bit of caution. This next method implements a live search. Every time the search term changes, regardless of whether the user has selected the search button or tapped return, we redo the search. This behavior is very user-friendly, as the users can see the results change while typing. If users pare the list down far enough on the third character, they can stop typing and select the row they want.

You can easily hamstring the performance of your application by implementing live search, especially if you’re displaying images or have a complex data model. In this case, with 2,000 strings and no images or accessory icons, things actually work pretty well, even on a first-generation iPhone or iPod touch.

CAUTION: Do not assume that snappy performance in the simulator translates to snappy performance on your device. If you’re going to implement a live search like this, you need to test

extensively on actual hardware to make sure your application stays responsive. When in doubt, don’t use the live search feature. Your users will likely be perfectly happy tapping the search

button.

To handle a live search, implement the search bar delegate method searchBar:textDidChange: like so:

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

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

}

[self handleSearchForTerm:searchTerm];

}

Notice that we check for an empty string. If the string is empty, we know all names are going to match it, so we simply reset the search and reload the data, without bothering to enumerate over all the names.

Last, we implement a method that allows us to be notified when the user clicks the Cancel button on the search bar.

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

[self resetSearch]; [table reloadData];

[searchBar resignFirstResponder];

}

When the user clicks Cancel, we set the search term to an empty string, reset the search, and reload the data so that all names are showing. We also tell the search bar to yield first responder status, so that the keyboard drops away and the user can resume working with the table view.

www.it-ebooks.info

272 CHAPTER 8: Introduction to Table Views

If you haven’t done so already, fire up our app and try out the search functionality. Remember that the search bar is scrolled just off the top of the screen, so drag down to bring it into view. Click in the search field and start typing. The name list should trim to match the text you type (Figure 8–32). It works, right?

Figure 8–32. Our Sections app in all its glory. As promised, the index no longer steps on the Cancel button. Nice!

For our next bit of tinkering, how about making that index disappear when you tap on the search field? This is not mandatory—it’s strictly a design decision—but worth knowing how to do.

First, let’s add a property variable to keep track of whether the user is currently using the search bar. Add the following to BIDViewController.h:

@interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate>

@property (strong, nonatomic) IBOutlet UITableView *table; @property (strong, nonatomic) IBOutlet UISearchBar *search; @property (strong, nonatomic) NSDictionary *allNames; @property (strong, nonatomic) NSMutableDictionary *names; @property (strong, nonatomic) NSMutableArray *keys;

@property (assign, nonatomic) BOOL isSearching;

-(void)resetSearch;

-(void)handleSearchForTerm:(NSString *)searchTerm; @end

www.it-ebooks.info

CHAPTER 8: Introduction to Table Views

273

Save the file, and let’s shift our attention to BIDViewController.m. First, add a method synthesizer for the new property:

@implementation ViewController @synthesize names;

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

@synthesize isSearching;

Next, we need to modify the sectionIndexTitlesForTableView: method to return nil if the user is searching:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { if (isSearching)

return nil; return keys;

}

We need to implement a new delegate method to set isSearching to YES when searching begins. Add the following method to the search bar delegate methods section of BIDViewController.m:

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { isSearching = YES;

[table reloadData];

}

This method is called when the search bar is tapped. In it, we set isSearching to YES, and then tell the table to reload itself, which causes the index to disappear. We also need to remember to set isSearching to NO when the user is finished searching. There are two ways a user can finish searching: by pressing the Cancel button or by tapping a row in the table. Therefore, we need to add code to the searchBarCancelButtonClicked: method:

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { isSearching = NO;

search.text = @""; [self resetSearch]; [table reloadData];

[searchBar resignFirstResponder];

}

We also need to make that change to the tableView:willSelectRowAtIndexPath: method:

-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {

[search resignFirstResponder];

isSearching = NO; search.text = @""; [tableView reloadData]; return indexPath;

}

www.it-ebooks.info

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