Skip to content

Instantly share code, notes, and snippets.

@monmon
Last active December 21, 2015 02:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save monmon/6237760 to your computer and use it in GitHub Desktop.
Save monmon/6237760 to your computer and use it in GitHub Desktop.
AdvancedTableSearch サンプルコード勉強会

http://developer.apple.com/library/ios/samplecode/AdvancedTableSearch/Introduction/Intro.html

ReadMe.txt

  • このサンプルは UISearchDisplayController の使い方ですよ
  • テーブル内部のデータでもfilteringできますよ
  • 単純な文字列比較ではなくfilteredArrayUsingPredicateを使って比較をしていますよ
    • その方法はNSPredicateを使うということですよ
      • このサンプルではNSObjectのサブクラスの複数のフィールドの横断検索のためにNSCompoundPredicateとNSExpressionsを使っていますよ

このサンプルではobjectは

  1. title
  2. year introduced
  3. price

の3つを持っていますよ

ユーザが"2007"と入れたら2007年発表のものが表示され、"2007 i"と入れたら2007年発表かつ"i"から始まるobjectが表示されますよ

APLViewController内にロジックがある

まず前提としてUIViewControllerはsearchDisplayControllerをpropertyに持ってる UISearchDisplayDelegateプロトコルに準拠するため以下2つのメソッドを実装(293行目から)

  • searchDisplayController:shouldReloadTableForSearchString:
  • searchDisplayController:shouldReloadTableForSearchScope:

この中で updateFilteredContentForSearchString を呼び出していて、ここが検索のロジックになっている

181行目 updateFilteredContentForSearchString

  • 横断検索の方法: NSPredicate(NSComparisonPredicate)を作ってArrayのfilteredArrayUsingPredicateに投げるだけ
    • self.searchResults = [self.products mutableCopy]; でテーブルの全データをコピーしていて、updateFilteredContentForSearchStringの最後でこのArrayのfilteredArrayUsingPredicateにNSPredicateを投げている
  • NSPredicateについては以下のドキュメント

200行目 入力されたそれぞれのkeywordに対してNSComparisonPredicateを構築していく

以下のコメントの通り、nameのkeyからcase-insensitive([c])でcontainsかどうか、year, priceのkeyからisEqualかどうかを検索するという実装

        // each searchString creates an OR predicate for: name, yearIntroduced, introPrice
        //
        // example if searchItems contains "iphone 599 2007":
        //      name CONTAINS[c] "iphone"
        //      name CONTAINS[c] "599", yearIntroduced ==[c] 599, introPrice ==[c] 599
        //      name CONTAINS[c] "2007", yearIntroduced ==[c] 2007, introPrice ==[c] 2007
        //

NSComparisonPredicateを使った比較の表現

        NSPredicate *finalPredicate = [NSComparisonPredicate
                                       predicateWithLeftExpression:lhs
                                       rightExpression:rhs
                                       modifier:NSDirectPredicateModifier
                                       type:NSContainsPredicateOperatorType
                                       options:NSCaseInsensitivePredicateOption];
  • lhs(left hand side):左辺
  • rhs(right hand side):右辺
  • modifier:NSDirectPredicateModifier:直接比較(後述)
  • type:NSContainsPredicateOperatorType:左辺が右辺を含んでいればtrue(後述)
  • options:NSCaseInsensitivePredicateOption:case-insensitive

NSDirectPredicateModifierの他にALLとANYがある

// Describes how the operator is modified: can be direct, ALL, or ANY
typedef NS_ENUM(NSUInteger, NSComparisonPredicateModifier) {
    NSDirectPredicateModifier = 0, // Do a direct comparison
    NSAllPredicateModifier, // ALL toMany.x = y
    NSAnyPredicateModifier // ANY toMany.x = y
};

NSContainsPredicateOperatorTypeの他に色々ある

// Type basic set of operators defined. Most are obvious; NSCustomSelectorPredicateOperatorType allows a developer to create an operator which uses the custom selector specified in the constructor to do the evaluation.
typedef NS_ENUM(NSUInteger, NSPredicateOperatorType) {
    NSLessThanPredicateOperatorType = 0, // compare: returns NSOrderedAscending
    NSLessThanOrEqualToPredicateOperatorType, // compare: returns NSOrderedAscending || NSOrderedSame
    NSGreaterThanPredicateOperatorType, // compare: returns NSOrderedDescending
    NSGreaterThanOrEqualToPredicateOperatorType, // compare: returns NSOrderedDescending || NSOrderedSame
    NSEqualToPredicateOperatorType, // isEqual: returns true
    NSNotEqualToPredicateOperatorType, // isEqual: returns false
    NSMatchesPredicateOperatorType,
    NSLikePredicateOperatorType,
    NSBeginsWithPredicateOperatorType,
    NSEndsWithPredicateOperatorType,
    NSInPredicateOperatorType, // rhs contains lhs returns true
    NSCustomSelectorPredicateOperatorType,
    NSContainsPredicateOperatorType NS_ENUM_AVAILABLE(10_5, 3_0) = 99, // lhs contains rhs returns true
    NSBetweenPredicateOperatorType NS_ENUM_AVAILABLE(10_5, 3_0)
};

yearIntroducedとpriceについても同様なものを作る 数字は完全一致の比較にしたいのでNSContainsPredicateOperatorTypeではなくNSEqualToPredicateOperatorTypeを使っている

最後にこれら3つのPredicateををor条件にする

        // at this OR predicate to our master AND predicate
        NSCompoundPredicate *orMatchPredicates = (NSCompoundPredicate *)[NSCompoundPredicate orPredicateWithSubpredicates:searchItemsPredicate];

257行目 typeの比較は単純な比較なのでNSPredicateのpredicateWithFormatを使っている

finalCompoundPredicate =
                (NSCompoundPredicate *)[NSPredicate predicateWithFormat:@"(SELF.type == %@)", type];

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html

287行目 最後にfilteredArrayUsingPredicateに投げて検索

self.searchResults = [[self.searchResults filteredArrayUsingPredicate:finalCompoundPredicate] mutableCopy];

filteredArrayUsingPredicateはNSPredicate.hによってNSArrayに生えたメソッド

@interface NSArray (NSPredicateSupport)
- (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate;    // evaluate a predicate against an array of objects and return a filtered array
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment