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

CHAPTER 8: Introduction to Table Views

255

Implementing a Search Bar

The index is helpful, but even so, we still have a whole lot of names here. If we want to see whether the name Arabella is in the list, for example, we’ll need to scroll for a while even after using the index. It would be nice if we could let the user pare down the list by specifying a search term, wouldn’t it? That would be darn user-friendly. Well, it’s a bit of extra work, but it’s not too bad. We’re going to implement a standard iOS search bar, like the one shown in Figure 8–25.

Figure 8–25. The application with a search bar added to the table

Rethinking the Design

Before we set about adding a search bar, we need to put some thought into our approach. Currently, we have a dictionary that holds a series of arrays, one for each letter of the alphabet. The dictionary is immutable, which means we can’t add or delete values from it, and so are the arrays that it holds. We also need to retain the ability to get back to the original dataset when the user hits cancel or erases the search term.

The solution is to create two dictionaries: an immutable dictionary to hold the full dataset and a mutable copy from which we can remove rows. The delegate and data sources will read from the mutable dictionary, and when the search criteria change or the search

www.it-ebooks.info

256

CHAPTER 8: Introduction to Table Views

is canceled, we can refresh the mutable dictionary from the immutable one. Sounds like a plan. Let’s do it.

CAUTION: This next project is a bit advanced and may cause a distinct burning sensation if

taken too quickly. If some of these concepts give you a headache, retrieve your copy of Learn Objective-C on the Mac by Mark Dalrymple and Scott Knaster (Apress, 2009) and review the bits

about categories and mutability.

A Deep Mutable Copy

To use our new approach, there’s one problem we’ll need to solve. NSDictionary conforms to the NSMutableCopying protocol, which returns an NSMutableDictionary, but that method creates what’s called a shallow copy. This means that when you call the mutableCopy method, it will create a new NSMutableDictionary object that has all the objects that the original dictionary had. They won’t be copies; they will be the same actual objects. This would be fine if, say, we were dealing with a dictionary storing strings, because removing a value from the copy wouldn’t do anything to the original. Since we have a dictionary full of arrays, however, if we were to remove objects from the arrays in the copy, we would also be removing them from the arrays in the original, because both the copies and the original point to the same objects. In this particular case, the original arrays are immutable, so you couldn’t actually remove objects from them anyway, but our intention is to illustrate the point.

In order to deal with this properly, we need to be able to make a deep mutable copy of a dictionary full of arrays. That’s not too hard to do, but where should we put this functionality?

If you said, “in a category,” then great, now you’re thinking with portals! If you didn’t, don’t worry, it takes a while to get used to this language. Categories, in case you’ve forgotten, allow you to add more methods to existing objects without subclassing them. Categories are frequently overlooked by folks new to Objective-C, because they’re a feature most other languages don’t have.

With categories, we can add a method to NSDictionary to do a deep copy, returning an NSMutableDictionary with the same data but not containing the same actual objects.

NOTE: Before you move on to this next series of steps, consider making a backup copy of your

project. This way, you’ll make sure you have a working version to go back to if things go south

with this next set of changes.

In your project window, select the Sections folder, and press N to create a new file. When the new file assistant comes up, select Cocoa Touch from the very top of the iOS section. In the right-hand panel, select Objective-C category, since that’s just what we want to create, and click Next. On the following screen, name your protocol

www.it-ebooks.info

CHAPTER 8: Introduction to Table Views

257

MutableDeepCopy, and enter NSDictionary in the Category on field. Then click Next once again. On the final screen, make sure Sections is selected in the file browser, Group popup, and Target control.

Put the following code in NSDictionary+MutableDeepCopy.h:

#import <Foundation/Foundation.h>

@interface NSDictionary (MutableDeepCopy)

- (NSMutableDictionary *)mutableDeepCopy;

@end

Flip over to NSDictionary+MutableDeepCopy.m, and add the implementation:

#import "NSDictionary+MutableDeepCopy.h"

@implementation NSDictionary (MutableDeepCopy)

- (NSMutableDictionary *)mutableDeepCopy {

NSMutableDictionary *returnDict = [[NSMutableDictionary alloc] initWithCapacity:[self count]];

NSArray *keys = [self allKeys]; for (id key in keys) {

id oneValue = [self valueForKey:key]; id oneCopy = nil;

if ([oneValue respondsToSelector:@selector(mutableDeepCopy)]) oneCopy = [oneValue mutableDeepCopy];

else if ([oneValue respondsToSelector:@selector(mutableCopy)]) oneCopy = [oneValue mutableCopy];

if (oneCopy == nil)

oneCopy = [oneValue copy]; [returnDict setValue:oneCopy forKey:key];

}

return returnDict;

}

@end

This method creates a new mutable dictionary and then loops through all the keys of the original dictionary, making mutable copies of each array it encounters. Since this method will behave just as if it were part of NSDictionary, any reference to self is a reference to the dictionary on which this method is being called. The method first attempts to make a deep mutable copy, and if the object doesn’t respond to the mutableDeepCopy message, it tries to make a mutable copy. If the object doesn’t respond to the mutableCopy message, it falls back on making a regular copy to ensure that all the objects contained in the dictionary are copied. By doing it this way, if we were to have a dictionary that contained dictionaries (or other objects that supported deep mutable copies), the contained ones would also get deep-copied.

For a few of you, this might be the first time you’ve seen this syntax in Objective-C:

for (id key in keys)

Objective-C 2.0 introduced a feature called fast enumeration. Fast enumeration is a language-level replacement for NSEnumerator. It allows you to quickly iterate through a

www.it-ebooks.info

258

CHAPTER 8: Introduction to Table Views

collection, such as an NSArray, without the hassle of creating additional objects or loop variables.

All of the delivered Cocoa collection classes—including NSDictionary, NSArray, and NSSet—support fast enumeration, and you should use this syntax any time you need to iterate over a collection. It will ensure that you get the most efficient loop possible.

If we include the NSDictionary+MutableDeepCopy.h header file in one of our other classes, we’ll be able to call mutableDeepCopy on any NSDictionary object we like. Let’s take advantage of that now.

Updating the Controller Header File

Next, we need to add some outlets to our controller class header file. We’ll add an outlet for the table view. Up until now, we haven’t needed a pointer to the table view outside the data source methods. But for our search bar implementation, we need one to tell the table to reload itself based on the result of the search. We’re also going to add an outlet to a search bar, which is a control used for, well, searching.

In addition to those two outlets, we’ll add another dictionary. The existing dictionary and array are both immutable objects, and we need to change both of them to the corresponding mutable version, so the NSArray becomes an NSMutableArray and the

NSDictionary becomes an NSMutableDictionary.

We won’t need any new action methods in our controller, but we will use a couple of new methods. For now, just declare them, and we’ll talk about them in detail after you enter the code.

We also must conform our class to the UISearchBarDelegate protocol. We’ll need to become the search bar’s delegate in addition to being the table view’s delegate.

Make the following changes to BIDViewController.h:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

<UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate>

@property (strong, nonatomic) NSDictionary *names; @property (strong, nonatomic) NSArray *keys;

@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;

-(void)resetSearch;

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

@end

www.it-ebooks.info

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