UITableViewのレスポンシブ検索



この記事では、「検索」ボタンがクリックされるのを待たずに、検索文字列に入力されたテキストに基づいて検索結果を動的に生成する必要がある場合、ユーザーがクエリをすばやく入力するときに、DataSource UITableViewで検索を実装する方法を紹介します。



そのため、検索用のUISearchBarを持つテーブルがあります。 この例では、SQLiteデータベースはDataSourceとして機能します(たとえば、APIアクセスを備えた外部データソースにすることもできます)。 データベースには多くのレコード(数千)が含まれており、検索には約0.5秒かかります。



ユーザークエリを入力したときに検索結果を動的に生成するには、 -(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchTextデリゲートUISearchBar:を実装する必要があります。



- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { __weak ProductPickerTableViewController *weakSelf = self; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ const char *label = "ru.example.unique.search"; weakSelf.searchDispatchQueue = dispatch_queue_create(label, DISPATCH_QUEUE_SERIAL); }); dispatch_async(self.searchDispatchQueue, ^{ NSArray *searchProducts = nil; if ([searchText length]) { searchProducts = [self.food productsBySearchPhrase:searchText]; } dispatch_async(dispatch_get_main_queue(), ^{ weakSelf.searchProducts = searchProducts; [weakSelf.tableView reloadData]; }); }); }
      
      







コントローラーのデータソースはメインスレッドで満たされていません。そうでないと、インターフェイスの速度が低下します。 データを受信した後、テーブルビューを更新します(常にメインスレッドで)。



このようなアプローチには1つの欠点があります-検索文字列が変更されるたびに、データベース検索メソッドが呼び出されます(またはAPIを介したネットワーク要求が送信されます)。ユーザーがクエリをすばやく入力するか、Backspaceキーを押して削除する場合、これはまったく不要です。







私が最初に思いついたのは、たとえばエントリ間で0.1秒が経過した場合にのみ検索メソッドを呼び出すこと(データソースを更新すること)でした。 githubのオープンスペースで、キャンセルされたブロックの実装が見つかりました。



 // // dispatch_cancelable_block.h // sebastienthiebaud.us // // Created by Sebastien Thiebaud on 4/9/14. // Copyright (c) 2014 Sebastien Thiebaud. All rights reserved. // typedef void(^dispatch_cancelable_block_t)(BOOL cancel); NS_INLINE dispatch_cancelable_block_t dispatch_after_delay(NSTimeInterval delay, dispatch_block_t block) { if (block == nil) { return nil; } // First we have to create a new dispatch_cancelable_block_t and we also need to copy the block given (if you want more explanations about the __block storage type, read this: https://developer.apple.com/library/ios/documentation/cocoa/conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW6 __block dispatch_cancelable_block_t cancelableBlock = nil; __block dispatch_block_t originalBlock = [block copy]; // This block will be executed in NOW() + delay dispatch_cancelable_block_t delayBlock = ^(BOOL cancel){ if (cancel == NO && originalBlock) { originalBlock(); } // We don't want to hold any objects in the memory originalBlock = nil; }; cancelableBlock = [delayBlock copy]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ // We are now in the future (NOW() + delay). It means the block hasn't been canceled so we can execute it if (cancelableBlock) { cancelableBlock(NO); cancelableBlock = nil; } }); return cancelableBlock; } NS_INLINE void cancel_block(dispatch_cancelable_block_t block) { if (block == nil) { return; } block(YES); block = nil; }
      
      







キャンセルされたブロックのこの実装を使用して、UISearchBarデリゲートメソッドを次のように書き換えることができます。



 - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { __weak ProductPickerTableViewController *weakSelf = self; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ static const char *label = "ru.example.unique.search"; weakSelf.searchDispatchQueue = dispatch_queue_create(label, DISPATCH_QUEUE_SERIAL); }); double searchDelay = 0.1; if (self.searchBlock != nil) { //We cancel the currently scheduled block cancel_block(self.searchBlock); } self.searchBlock = dispatch_after_delay(searchDelay, ^{ //We "enqueue" this block with a certain delay. It will be canceled if the user types faster than the delay, otherwise it will be executed after the specified delay dispatch_async(self.searchDispatchQueue, ^{ NSArray *searchProducts = nil; if ([searchText length]) { searchProducts = [self.food productsBySearchPhrase:searchText]; } dispatch_async(dispatch_get_main_queue(), ^{ weakSelf.searchProducts = searchProducts; [weakSelf.tableView reloadData]; }); }); }); }
      
      







変数searchDelayは、検索文字列に2文字を入力(または削除)する時間間隔に対応します。 バックスペースキーで検索文字列を消去するときに検索メソッドを繰り返し呼び出さないようにするには0.1秒で十分です。クエリをすばやく入力するには0.2〜0.3秒で十分です。



その結果、ユーザーの意見に即応する検索が得られます。






All Articles