【Objective-C】インクリメンタルサーチ(検索中連動検索結果表示)の実装について【Xcode10.2対応】

こういう人に向けて発信しています。
・インクリメンタルサーチのコードをみたい人
・ポップオーバーで検索結果を表示する方法がみたい人
・Objective-C初心者向け

インクリメンタルサーチとは?

インクリメンタルサーチ(英語: incremental search)は、アプリケーションにおける検索方法のひとつ。 検索したい単語をすべて入力した上で検索するのではなく、入力のたびごとに即座に候補を表示させる。

アプリ完成イメージ

インクリメンタルサーチ:仕様

今回はインクリメンタルサーチは前方一致にしました。
たとえばAで打った時に「EDCBA」はヒットせず、
「ABCDE」と出ます。

現場でインクリメンタルサーチを実装した時に検索結果数が、
膨大になってしまった事があって以来、
インクリメンタルサーチでは前方一致でなるべく数を減らして、
表示してますね。

ちなみにですが、ポップオーバー上のTableViewを押下すると
テキストフィールドに検索結果の候補が反映されます。
(例:AAAタップしたら、テキストフィールドにAAA)

実装の構成

(1)ViewController(h/m/xib)
(2)TableViewController(h/m)

上記で実装していますね。

ViewController.h

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (nonatomic) NSMutableArray *dataArray;  //外部から渡されたマスタデータを格納できるようにhファイルに記載
@end

ViewController.m

#import "ViewController.h"
#import "TableViewController.h"

@interface ViewController ()<UIPopoverPresentationControllerDelegate,UITextFieldDelegate>
@property (weak,nonatomic) IBOutlet UITextField *textField;
@property (nonatomic)  UIPopoverPresentationController *presentationController;
@end

@implementation ViewController

//testDemo
- (void)viewDidLoad {
    [super viewDidLoad];
    self.textField.delegate = self;  //UITextFieldのデリゲートを指定している。
    if(!self.dataArray){
        self.dataArray = @[@"AAAA",@"BBBB",@"CCCC",@"DDDD",@"EEEE"].mutableCopy;
    }
    
    [self.textField addTarget:self
                  action:@selector(textFieldDidChange:)
        forControlEvents:UIControlEventEditingChanged];
}

#pragma mark インクリメンタルサーチ関連処理

-(NSMutableArray *)checkIncrementalSearch{
    NSMutableArray *incrementalArray = @[].mutableCopy;
    for(NSString *str in self.dataArray){
        if([str hasPrefix:self.textField.text]){
            [incrementalArray addObject:str];
        }
    }
    return incrementalArray;
}

#pragma mark UIPopoverPresentationController

- (void)presentPopOverWithViewController:(UIViewController *)viewController sourceView:(UIView *)sourceView
{
    viewController.modalPresentationStyle = UIModalPresentationPopover;
    viewController.preferredContentSize = CGSizeMake(120.0, [self checkIncrementalSearch].count*44);
    self.presentationController = viewController.popoverPresentationController;
    self.presentationController.delegate = self;
    self.presentationController.permittedArrowDirections = UIPopoverArrowDirectionUp;
    self.presentationController.sourceView = sourceView;
    self.presentationController.sourceRect = sourceView.bounds;
    //吹き出しの根本が色変わるのでおすすめ。
    self.presentationController.backgroundColor = [UIColor whiteColor];
    
    [self presentViewController:viewController animated:YES completion:NULL];
}

#pragma mark UITextField

//TextFieldの値が変わるたびに呼び出されるメソッド(TextFieldの値は常に更新後で拾える)
-(void)textFieldDidChange:(UITextField *)sender{
    //本来であればpopOverが開かれている時のみ、という処理が好ましい。後に更新します。
    if(self.presentationController){
        self.presentationController = nil;
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    TableViewController *tableVC = [[TableViewController alloc]init];
    if([self checkIncrementalSearch].count > 0){     //検索結果が1件以上ある
        tableVC.receivedArray = [self checkIncrementalSearch];
        tableVC.customBlock = ^(NSString *string){
            self.textField.text = string;
            [self dismissViewControllerAnimated:YES completion:nil];
        };
        //sourceView = ポップオーバーを出すオブジェクトという認識
        [self presentPopOverWithViewController:tableVC sourceView:self.textField];
    }
}

@end

TableViewController.h

#import <UIKit/UIKit.h>

typedef void(^MyCustomBlock)(NSString *);

@interface TableViewController : UITableViewController
@property (nonatomic) NSMutableArray *receivedArray;
@property (nonatomic, copy) MyCustomBlock customBlock;

@end

TableViewController.m

#import "TableViewController.h"

@interface TableViewController ()

@end

@implementation TableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    //必ずNSInteger型を返してあげている。
    return self.receivedArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //標準で用意されているTableViewを利用する場合。
    NSString *cellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    
    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    }
    
    cell.textLabel.text = self.receivedArray[indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
    self.customBlock(selectedCell.textLabel.text);
    
}

@end

この記事が気に入ったらサポートをしてみませんか?