Динамический поиск строки в iOS

  • Tutorial


Недавно появилась задача — встроить в приложение динамический поиск информации, то есть выдача должна генерироваться параллельно вводу пользователем символов. Вся информация хранится в SQLite базе данных. Все бы ничего, но в каждом слове выдачи искомые символы должны подсвечиваться.
Было принято волевое решение не использовать внешние библиотеки, а попробовать обойтись своими силами.

Начнем с внешней формы.



В качестве поисковой строки используется UISearchBar. Результаты выводятся в обычный UITableView (в данном случае, с ячейками, кроме текста включающими в себя и картинку).
Обработчиком для события изменения текста в строке является:

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
    _data = [SBHotspotData findHotspot:searchText];
    [self.searchResultsTableView reloadData];
}

где _data — NSArray, хранящий результаты выдачи.

На этом этапе возникла проблема — во всех названиях объектов, по которым проводится поиск, есть как заглавные буквы, так и строчные, которые SQLite воспринимает по-разному, а функции lower() и upper(), в отличии от SQL, не поддерживаются. Путем наименьшего сопротивления решили добавить в базу данных отдельный столбец, хранящий в себе все те же заголовки, но уже в нижнем регистре, тем более, что в данном случае увеличение объема базы было практически незаметно. Метод поиска (для работы с БД используется библиотека FMDatabase):

+ (NSArray *) findHotspot: (NSString *)partOfTitle
{
    SBHotspotData *item;
    NSMutableArray *result = [NSMutableArray array];
    
    NSString *path = [self getDatabasePath];
    FMDatabase *database;
    database = [FMDatabase databaseWithPath:path];
    [database open];
    
    NSString *query = [NSString stringWithFormat:@"select * from hotspots where TitleLow like '%%%@%%'", [partOfTitle lowercaseString]];
    FMResultSet *results = [database executeQuery:query];
    
    while([results next]) {
        item = [[SBHotspotData alloc ]init];
        item.hotspotIdentity = [results stringForColumn:@"Identity"];
        item.hotspotTitle = [results stringForColumn:@"Title"];
        item.hotspotDescription = [results stringForColumn:@"Description"];
        [result addObject:item];
    }
        
    [database close];

    return [result sortedArrayUsingSelector:@selector(compare:)];
}

- (NSComparisonResult)compare:(SBHotspotData *)otherObject {
    return [self.hotspotTitle compare:otherObject.hotspotTitle];
}

При формировании запроса используется %%%@%%, где знаками процента обозначается, что перед и после строки может идти любое количество символов. Метод возвращает массив объектов из базы данных, отсортированный по алфавиту.
Самая интересная часть задания — подсветка введенных символов. Реализуется она следующим образом:

NSMutableAttributedString *attributedString =
[[NSMutableAttributedString alloc] initWithString:item.hotspotTitle];

NSRange range = [item.hotspotTitle rangeOfString:_searchHotspotBar.text];
[attributedString addAttribute:NSBackgroundColorAttributeName
                   value:[UIColor colorWithRed:216/255.0f green:87/255.0f blue:23/255.0f alpha:1.0f]
                   range:range];

cell.cellLabel.attributedText = attributedString;

Для подсветки определенных символов используется NSMutableAttributedString и его метод addAttribute:value:range:.

Ну и результат работы программы:



Задача получилась достаточно тривиальной, но, надеюсь, полученный опыт пригодится кому-либо из хабровчан.
Поделиться публикацией

Комментарии 7

    –2
    Задача получилась достаточно тривиальной

    А теперь сделайте то же для iOS 4.3
      0
      Не стояло такой задачи, разработка ведется под iOS 6+. Но подозреваю, что было бы посложнее :)
      и, да: david-smith.org/iosversionstats/
        +2
        В чем сложности? Ну заменится в ячейке UILabel на NIAttributedLabel или TTTAttributedLabel.
      • НЛО прилетело и опубликовало эту надпись здесь
          0
          Насколько быстро работает поиск? Есть мнение, что на большом наборе данных он будет занимать порядочное количество времени, ведь на каждую добавленную букву вы открываете и закрываете базу, прогоняете запрос и полностью перегружаете таблицу.
            0
            С базой в 200 записей работает отлично, без каких-либо видимых задержек. На больших объемах не тестировали, но есть подозрение, что с парой тысяч работать будет тоже нормально.
              0
              Использую такой же поиск в приложении. В базе 60,000 записей, правда использую CoreData. На iPad 3 задержка практически не видна.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое