【Xcode】TableViewセルの検索機能を実装する

1.TableView と Search Bar and Search Display を使うための下準備をする

1.プロジェクトからシングルビューをつくり、ストーリーボード上の ViewController に TableView と Search Bar and Search Display を配置する。

2.配置した2つの View(TableView と Search Bar and Search Display)を ViewController.mへそれぞれ接続する。

3.プロトコルを追加。

//ViewController.h(m)
----------------------------------------------
@interface ViewController : UIViewController<UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate>

//searchBar を接続(ストーリーボードからドラッグして行う)。
@property (weak, nonatomic) IBOutlet UISearchBar *searchBar;

//TableView を接続(ストーリーボードからドラッグして行う)。
@property (weak, nonatomic) IBOutlet UITableView *tableView;

@end
----------------------------------------------

2.テーブルビューのセルにデータを表示させる

以下の記述をファイルに追加する。

//ViewController.h(m)
----------------------------------------------
@interface ViewController : UIViewController<UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate>

//searchBar を接続
@property (weak, nonatomic) IBOutlet UISearchBar *searchBar;

//TableView を接続
@property (weak, nonatomic) IBOutlet UITableView *tableView;

/**
* さきほど記述した場所の下に追記する。テーブルに表示する情報が入ります
*/
@property (nonatomic, strong) NSArray *dataSourceiPhone;
@property (nonatomic, strong) NSArray *dataSourceAndroid;

@end
----------------------------------------------
これを追記したタイミングで「"Assigning to 'id<~>' from incompatible type '~' "」のようなエラーが出る場合は、プロトコロルがきちんと実装できているか確認する。

//これも記述
----------------------------------------------
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

// デリゲートメソッドをこのクラスで実装する
self.tableView.delegate = self;
self.tableView.dataSource = self;

// テーブルに表示したいデータソースをセット
self.dataSourceiPhone = @[@"iPhone 4", @"iPhone 4S", @"iPhone 5", @"iPhone 5c", @"iPhone 5s"];
self.dataSourceAndroid = @[@"Nexus", @"Galaxy", @"Xperia"];

}
----------------------------------------------

//これもまるまる追記
----------------------------------------------
@implementation ViewController

(略)

//ここから下を全部追記

#pragma mark - TableViewDataSource delegate methods

/**
* テーブルに表示するデータ件数を返します(実装必須)
*
* @param tableView テーブルビュー
* @param section 対象セクション番号
*
* @return データ件数
*/
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger dataCount;

switch (section) {
case 0:
dataCount = self.dataSourceiPhone.count;
break;
case 1:
dataCount = self.dataSourceAndroid.count;
break;
default:
break;
}
return dataCount;
}

/**
* テーブルに表示するセクション(区切り)の件数を返します(任意実装)
*
* @param テーブルビュー
*
* @return セクション件数
*/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}

/**
* テーブルに表示するセルを返します(実装必須)
*
* @param tableView テーブルビュー
* @param indexPath セクション番号・行番号の組み合わせ
*
* @return セル
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";

// 再利用できるセルがあれば再利用する
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (!cell) {
// 再利用できない場合は新規で作成
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}

switch (indexPath.section) {
case 0:
cell.textLabel.text = self.dataSourceiPhone[indexPath.row];
break;
case 1:
cell.textLabel.text = self.dataSourceAndroid[indexPath.row];
break;
default:
break;
}

return cell;
}

@end
----------------------------------------------

セルにデータが表示てきていることを確認する。

3.検索バーをタップした時のTableViewControllerに検索結果を表示する準備をする

//検索結果を格納するNSArrayをプロパティで宣言する
----------------------------------------------
@interface ViewController ()

@property (nonatomic, strong) NSArray *dataSourceiPhone;
@property (nonatomic, strong) NSArray *dataSourceAndroid;

//ここを追記
//検索結果を格納する配列
@property (nonatomic, strong) NSArray *dataSourceSearchResultsiPhone;
@property (nonatomic, strong) NSArray *dataSourceSearchResultsAndroid;

@end
----------------------------------------------

結果を表示するViewControllerはStoryboardで 「Search Bar and Search Display」を紐づけした時に自動で参照が追加されて Self(UIViewController)にsearchDisplayController として紐づけられている。

//numberOfRowsInSectionを変更
----------------------------------------------
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger dataCount;

// ここのsearchDisplayControllerはStoryboardで紐付けされたsearchBarに自動で紐づけられている
//検索キーワードが入力されているか判定
if (tableView == self.searchDisplayController.searchResultsTableView) {
// 検索中のテーブルビューはこちらで処理
//セクションが複数ある場合はセクションごとに
switch (section) {
case 0:
dataCount = self.dataSourceSearchResultsiPhone.count;
break;
case 1:
dataCount = self.dataSourceSearchResultsAndroid.count;
break;
default:
break;
}
} else {
// 通常時のテーブルビューはこちらで処理
switch (section) {
case 0:
dataCount = self.dataSourceiPhone.count;
//もし、検索前のセルにデータを表示したくない場合はデータの数を0にする
//dataCount = 0;
break;
case 1:
dataCount = self.dataSourceAndroid.count;
break;
default:
break;
}
}
NSLog(@"%ld", (long)dataCount);
return dataCount;
}
----------------------------------------------

//cellForRowAtIndexPathを変更
----------------------------------------------
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
// 再利用できるセルがあれば再利用する
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (!cell) {
// 再利用できない場合は新規で作成
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}

// ここのsearchDisplayControllerはStoryboardで紐付けされたsearchBarに自動で紐づけられている
//検索キーワードが入力されているか判定
if (tableView == self.searchDisplayController.searchResultsTableView) {
// 検索中のテーブルビューはこちらで処理
//セクションが複数ある場合はセクションごとに
switch (indexPath.section) {
case 0:
cell.textLabel.text = self.dataSourceSearchResultsiPhone[indexPath.row];
break;
case 1:
cell.textLabel.text = self.dataSourceSearchResultsAndroid[indexPath.row];
break;
default:
break;
}
} else {
// 通常時のテーブルビューはこちらで処理
switch (indexPath.section) {
case 0: // iOS
cell.textLabel.text = self.dataSourceiPhone[indexPath.row];
break;
case 1: // Android
cell.textLabel.text = self.dataSourceAndroid[indexPath.row];
break;
default:
break;
}
}

return cell;
}
----------------------------------------------

4.データを絞り込む

----------------------------------------------
// 検索バーに入力された文字列を引数に、絞り込みをかけます
- (void)filterContainsWithSearchText:(NSString *)searchText
{
//CONTAINS右辺値が含まれているか,cは大文字小文字の区別なしオプション
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@", searchText];

self.dataSourceSearchResultsiPhone = [self.dataSourceiPhone filteredArrayUsingPredicate:predicate];
self.dataSourceSearchResultsAndroid = [self.dataSourceAndroid filteredArrayUsingPredicate:predicate];
}
----------------------------------------------

NSPredicate の詳細はこちら

//検索バーの文字を編集する度に呼ばれるデリゲートメソッド
----------------------------------------------
- (BOOL)searchDisplayController:controller shouldReloadTableForSearchString:(NSString *)searchString
{
// 検索バーに入力された文字列を引数に、絞り込みをかけます
[self filterContainsWithSearchText:searchString];

// YESを返すとテーブルビューがリロードされます。
// リロードすることでdataSourceSearchResultsiPhoneとdataSourceSearchResultsAndroidからテーブルビューを表示します
return YES;
}
----------------------------------------------

//セルが選択されたら
----------------------------------------------
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

NSLog(@"セルが選択されました");

}
----------------------------------------------
↑これで、1度目のタップでうまく反応しない場合は、didSelectRowAtIndexPath が didDeselectRowAtIndexPath になっていないか確認すること。

参考ページ