How to search(Predicate) content from list like Xcode suggestion?

前端 未结 1 1659
时光取名叫无心
时光取名叫无心 2020-12-20 08:51

I think everyone notice XCode suggestions list. Check this screenshot

As per Apple document doesn\'t provide what expected ouptut.

NSPredica         


        
相关标签:
1条回答
  • 2020-12-20 09:40

    A possible solution, is to use a Regular Expression for that. We just need to insert .* between each letter (and before and after the string) which means "any character (except for line terminators)". It seems that's what's used for the search on XCode completion.

    NSMutableArray *letters = [[NSMutableArray alloc] init];
    [searchString enumerateSubstringsInRange:NSMakeRange(0, [searchString length])
                                     options:NSStringEnumerationByComposedCharacterSequences
                                  usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
        [letters addObject:substring];
    }];
    NSMutableString *pattern = [[NSMutableString alloc] initWithString:@".*"];
    [pattern appendString:[letters componentsJoinedByString:@".*"]];
    [pattern appendString:@".*"];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.%K MATCHES[C] %@", keyToFilter, pattern];
    

    Sample to play, insert just before:

    NSString *keyToFilter = @"attribute";
    NSArray *array = @[@{keyToFilter:@"UITextField"}, @{keyToFilter:@"nsarray"}, @{keyToFilter:@"uitf"}];
    NSString *searchString = @"uitf";
    

    and just after:

    NSArray *filtered = [array filteredArrayUsingPredicate:predicate];
    NSLog(@"SearchString: %@\nFiltered: %@", searchString, ([filtered count]?[filtered valueForKey:keyToFilter]:@"NO RESULT"));
    

    Output:

    $>SearchString: uitf
    Filtered: (
        UITextField,
        uitf
    )
    

    The code taken for construction the array of letters is from here. Different solutions could have been used to create pattern, it's maybe not the best one, but it's a working one.

    Bonus:

    And for fun (I was intriguing on how doing it), a possible way to color the list (I made an array of NSAttributedString) to adapt (not fully tested or errors free):

    NSMutableArray *filteredColored = [[NSMutableArray alloc] init];
    for (NSDictionary *aDict in filtered) //or for (MyObjectClass *myObject in filtered)
    {
        NSString *aFilteredValue = aDict[keyToFilter]; //or NSString *aFilteredValue = [myObject attribute]
        NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:aFilteredValue attributes:@{}];
        NSInteger currentIndex = 0;
        for (NSString *aLetter in letters)
        {
            NSRange rangeFound = [aFilteredValue rangeOfString:aLetter
                                                       options:NSCaseInsensitiveSearch
                                                         range:NSMakeRange(currentIndex, [aFilteredValue length]-currentIndex)];
            if (rangeFound.location != NSNotFound)
            {
                currentIndex = rangeFound.location+rangeFound.length;
                [attr addAttribute:NSBackgroundColorAttributeName value:[UIColor yellowColor] range:rangeFound];
            }
        }
        [filteredColored addObject:attr];
    }
    
    0 讨论(0)
提交回复
热议问题