概要
-
NSSearchField
を使用し、入力した単語でデータを検索して、絞り込み表示するものを作成しました。- 今回のものは最低限だけですが、公式のドキュメントをざっと見た所、検索履歴を残したり色々できそうでした。
GitHub
コード全体
// AppDelegate.m
// NSSearchFieldSample
# import "AppDelegate.h"
# import "Function.h"
@interface AppDelegate ()
@property NSArray *allPokemonNames; // 全てのポケモン名
@property NSArray *pokemonNames; // テーブルビューに表示するポケモン名
@property (weak) IBOutlet NSSearchField *searchField; // 検索窓
@property (weak) IBOutlet NSTableView *tableView;
@property (weak) IBOutlet NSWindow *window;
@end
@implementation AppDelegate
- (void)awakeFromNib {
[self loadPokemonNames]; // テーブルビューに表示するデータを作成する
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
// テキストからポケモン名のデータを取得する
// またテーブルビューに表示するデータを作成する
- (void)loadPokemonNames {
NSURL *pokemonList = [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:@"PokemonList"];
NSMutableArray *pokemonNames = [NSMutableArray arrayWithArray:[Function loadFile:pokemonList]];
_allPokemonNames = [NSArray arrayWithArray:pokemonNames];
[self fontSearchFieldIsChanged:nil];
}
# pragma mark - NSSearch Field Method
// 検索窓のテキストに変更があった場合に呼ばれる
- (IBAction)fontSearchFieldIsChanged:(id)sender {
_pokemonNames = [self createListWithSearchWord:_searchField.stringValue list:_allPokemonNames];
[_tableView reloadData];
}
/**
@brief 対象文字列の含まれる要素の配列を作成する
@parama word 検索する文字列(※ひらがな・カタカナを区別しない)
@param oldList 検索対象の配列
@return 対象文字列が含まれる要素の配列
*/
- (NSArray *)createListWithSearchWord:(NSString *)word list:(NSArray<NSString *> *)oldList {
NSString *searchWord = [word stringByApplyingTransform:NSStringTransformHiraganaToKatakana reverse:NO]; // ひらがなをカタカナに変換(検索時の統一のため)
searchWord = [searchWord uppercaseString]; // 大文字へキャスト
if (searchWord.length == 0) {
return oldList; // 検索文字列がない場合は、元の配列をそのまま返す
}
NSMutableArray *newList = [NSMutableArray array];
[oldList enumerateObjectsUsingBlock:^(NSString *element, NSUInteger idx, BOOL * _Nonnull stop) {
NSString *fixedElement = [element stringByApplyingTransform:NSStringTransformHiraganaToKatakana reverse:NO]; // 日本語フォント名(ひらがなをカタカナに変換)
fixedElement = [fixedElement uppercaseString]; // 大文字へキャスト
NSRange searchResult = [fixedElement rangeOfString:searchWord];
if (searchResult.location != NSNotFound) {
[newList addObject:element]; // 対象文字列が含まれる場合
}
}];
return newList;
}
# pragma mark - NSTableView data source
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return _pokemonNames.count;
}
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row{
NSString *identifier = tableColumn.identifier;
NSTableCellView *cellView = [tableView makeViewWithIdentifier:identifier owner:self];
cellView.textField.stringValue = _pokemonNames[row];
return cellView;
}
@end
/**
@brief テキストファイルを読み込んで内容を配列で返す
@param sourceUrl テキストファイルのパスURL
@return 行区切りの配列
*/
+ (NSArray *)loadFile:(NSURL *)sourceUrl {
NSError *loadError = nil;
// ファイルの生データ
NSString *source = [[NSString alloc] initWithContentsOfURL:sourceUrl
encoding:NSUTF8StringEncoding
error:&loadError];
if (loadError) {
NSLog(@"%@", [loadError localizedDescription]);
return [NSArray array];
}
NSMutableArray *contents = [NSMutableArray array]; // return value
[source enumerateLinesUsingBlock:^(NSString *line, BOOL * _Nonnull stop) {
[contents addObject:line];
}];
return contents;
}
-
別クラス
Function.h/m
には、上記のメソッドを定義しているだけである。 -
データはこちらからお借りしました。
PokemonList
をプロジェクト内に作成しています。
コード説明
TableView(ViewBase)の設定(StoryBoard)
-
dataSource
とdelegate
をバインドしておく
NSSearchFieldのテキストが変更されたとき
@property (weak) IBOutlet NSSearchField *searchField; // 検索窓
- テキストを取得するため
@IBOutlet
を作成しバインディングをしておく
# pragma mark - NSSearch Field Method
// 検索窓のテキストに変更があった場合に呼ばれる
- (IBAction)fontSearchFieldIsChanged:(id)sender {
_pokemonNames = [self createListWithSearchWord:_searchField.stringValue list:_allPokemonNames];
[_tableView reloadData];
}
-
NSSearchField
をアクションにバインドした場合、中のテキストが変更されたときにアクションが呼ばれる・ - 下記のように少し一般化したメソッドを定義した。
- 結局やっていることとしては、リストと検索文字列を渡して新たに表示するデータをプロパティに格納し、テーブルビューを更新する、というだけ。
特定の文字列が含まれる要素の配列を作成するメソッド
/**
@brief 対象文字列の含まれる要素の配列を作成する
@parama word 検索する文字列(※ひらがな・カタカナを区別しない)
@param oldList 検索対象の配列
@return 対象文字列が含まれる要素の配列
*/
- (NSArray *)createListWithSearchWord:(NSString *)word list:(NSArray<NSString *> *)oldList {
NSString *searchWord = [word stringByApplyingTransform:NSStringTransformHiraganaToKatakana reverse:NO]; // ひらがなをカタカナに変換(検索時の統一のため)
searchWord = [searchWord uppercaseString]; // 大文字へキャスト
if (searchWord.length == 0) {
return oldList; // 検索文字列がない場合は、元の配列をそのまま返す
}
NSMutableArray *newList = [NSMutableArray array];
[oldList enumerateObjectsUsingBlock:^(NSString *element, NSUInteger idx, BOOL * _Nonnull stop) {
NSString *fixedElement = [element stringByApplyingTransform:NSStringTransformHiraganaToKatakana reverse:NO]; // 日本語フォント名(ひらがなをカタカナに変換)
fixedElement = [fixedElement uppercaseString]; // 大文字へキャスト
NSRange searchResult = [fixedElement rangeOfString:searchWord];
if (searchResult.location != NSNotFound) {
[newList addObject:element]; // 対象文字列が含まれる場合
}
}];
return newList;
}
- 検索する場合にひらがな・カタカナを区別しないようにするため、文字列比較時にはカタカナに統一している。
- 今回は関係ないが、同様に大文字・小文字を区別しないようにするため、文字列比較時には大文字に統一している。
- 文字列検索には
rangeOfString
を用いる