UISearchDisplayController 調査

2010年7月14日水曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

NSFetchedResultController を使った UITableView に検索機能を付加したい。今回はまず UISearchDisplayController について調べてみた。

サンプル - TableSearch


UISearchDisplayController を使ったサンプルが iPhone Dev Center で提供されている。
TableSearch

こんな画面。検索窓に入力するとリアルタムに検索結果が表示される。


Core Data は使っていない。
今回はこのソースコードを読んでみる。


MainView.xib


UITableView が配置されていてそのヘッダに UISearchBar が入っている。Interface Builder でこんなことができるのか。Libraryウィンドウを見ると "Search Bar and Search Display Controller"というのがある。

これを UITableView の上へドラッグするとヘッダの辺りが反応して配置ができることがわかる。

UISearchBar が UITableViewのサブビュー(ヘッダ)に配置され、UISearchDisplayController のインスタンスが準備される。

さてサンプルへ戻り各オブジェクトの接続状況を見てみる。まずは UITableView から。
dataSource と delegate が接続されていないことがわかる。あとで見るようにこれらは UISearchDisplayController 側で管理するようだ。

次に UISearchBar
delegate(UISearchBarDelegate)が File's Owner へ接続されている。

UISearchDisplayController
様々な Outlets が用意されている。これらは Interface Builder で配置すると自動的に接続されるようだ。

最後に File's Owner 。これは UITableViewController のサブクラス。
searchDisplayController への Outlet が接続されている。これは UIViewController が持っているプロパティ(つまり UIViewController 自体が UISearchDisplayController に対応しているということか)。


MainViewController.h

メインとなるコントローラで UITableViewController のサブクラス。ソースはこちら。
TableSearch: MainViewController.h

定義部:
@interface MainViewController : UITableViewController 


MainViewController.m

実装コード。ソースはこちら。
TableSearch: MainViewController.m

メソッドはこんな感じ。
大きくは UIViewController の処理(Lifecycle methods)、UITableView 向けのデータソース/デリゲート、検索絞り込み、UISearchDisplayControllerのデリゲートから構成されている。

気になった部分をピックアップしてみる。

まず viewDidDisapear
- (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];
}
ビューが隠れるときに UISearchDisplayController関連のプロパティを保存している。これは viewDidLoad で再利用される。メモリ不足時に viewが再作成されることを想定したパターン。
- (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;
    }
  :
  :

次は UITableViewDataSource 関連
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
 if (tableView == self.searchDisplayController.searchResultsTableView)
 {
        return [self.filteredListContent count];
    }
 else
 {
        return [self.listContent count];
    }
}

tableView によって返す値を分けている。通常時と検索時で渡される UITableView のインスタンスが異なるようだ。試しに self.tableView と 引数で渡ってくる tabaleView をデバッグ表示してみた。

通常(非検索)
self.tableView: <UITableView: 0x6829a00;
2: <UITableView: 0x6829a00;
UITableView のインスタンスが渡ってきている。

検索時
self.tableView: <UITableView: 0x6829a00;
1: <UISearchResultsTableView: 0x6044600;
self.tableView: <UITableView: 0x6829a00;
1: <UISearchResultsTableView: 0x6044600;
    :
UISearchResultsTableView のインスタンスが渡ってきている。このクラスは非公開で UIKit 内部だけで使われるクラスのようだ。

他のメソッドでも同様に tableView を元に表示処理の振り分けを行っている。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
 static NSString *kCellID = @"cellID";
 
 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
 if (cell == nil)
 {
            :
    }
 
 Product *product = nil;
 if (tableView == self.searchDisplayController.searchResultsTableView)
 {
        product = [self.filteredListContent objectAtIndex:indexPath.row];
    }
 else
 {
        product = [self.listContent objectAtIndex:indexPath.row];
    }
            :
            :

UITableViewDelegate も同様。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIViewController *detailsViewController = [[UIViewController alloc] init];
    
 Product *product = nil;
 if (tableView == self.searchDisplayController.searchResultsTableView)
 {
        product = [self.filteredListContent objectAtIndex:indexPath.row];
    }
 else
 {
        product = [self.listContent objectAtIndex:indexPath.row];
    }
 detailsViewController.title = product.name;
    
    [[self navigationController] pushViewController:detailsViewController animated:YES];
    [detailsViewController release];
}

残りは UISearchDisplayControllerDelegate
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
    [self filterContentForSearchText:searchString scope:
   [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[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 scopeButtonTitles] objectAtIndex:searchOption]];
    
    // Return YES to cause the search result table view to be reloaded.
    return YES;
}
どちらもこのクラスで定義した filterContentForSearchText:scope: を呼び出す。このメソッドは検索条件に応じて self.filteredListContent をつくり直す。


- - - -
大まか理解できた。次は NSFetchedResultController との連携について調べる。

Responses

Leave a Response

人気の投稿(過去 30日間)