Реализовано представление представлений индексированных таблиц в приложении контактов iPhone - PullRequest
0 голосов
/ 04 июня 2010

Мои требования: У меня есть прямое требование перечислять имена людей в алфавитном порядке в индексированном табличном представлении, где заголовки индекса являются начальной буквой алфавита (дополнительно значок поиска в верхней части и знак # для отображения разных значений, которые начинаются с цифры и других символы).

Что я сделал до сих пор: 1. Я использую основные данные для хранения, а «last_name» моделируется как свойство String в сущности «Контакты». 2. Я использую NSFetchedResultsController для отображения отсортированного индексированного табличного представления.

Проблемы с выполнением моего требования: 1. Во-первых, я не мог сделать заголовки указателей разделов первой буквой алфавита. Предложение Дейва в следующем посте помогло мне достичь того же: NSFetchedResultsController с разделами, созданными по первой букве строки

Единственная проблема, с которой я столкнулся при предложении Дейва, заключается в том, что я не могу получить разное имя, сгруппированное под индексом "#".

Что я пробовал: 1. Я попытался добавить пользовательский метод сравнения в NSString (категорию), чтобы проверить, как выполняется сравнение и раздел, но этот пользовательский метод не вызывается при указании в селекторе NSSortDescriptor.

Вот код:

@interface NSString (SortString)

-(NSComparisonResult) customCompare: (NSString*) aStirng;

@end

@implementation NSString (SortString)

-(NSComparisonResult) customCompare:(NSString *)aString
{
 NSLog(@"Custom compare called to compare : %@ and %@",self,aString);
 return [self caseInsensitiveCompare:aString];
}

@end

Код для извлечения данных:

NSArray *sortDescriptors = [NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:@"last_name"
               ascending:YES selector:@selector(customCompare:)] autorelease]];

  [fetchRequest setSortDescriptors:sortDescriptors];
        fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
            managedObjectContext:managedObjectContext sectionNameKeyPath:@"lastNameInitial" cacheName:@"MyCache"];

Можете ли вы дать мне знать, что мне не хватает и как выполнить требование?

Ответы [ 2 ]

4 голосов
/ 04 июня 2010

Это действительно неэффективный первый подход к этой проблеме, который я собираюсь переписать в конце концов. Но, надеюсь, это поможет вам.

Идея этого заключается в том, чтобы "гарантировать" получение реального индекса раздела таблицы при нажатии на "стандартное" представление индекса раздела. Стандартный индексный раздел должен иметь значок увеличительного стекла для поиска, хэш-знак (#) для неалфавитных разделов и буквы от A до Z для алфавитных разделов.

Это стандартное представление представлено независимо от того, сколько существует реальных секций или из чего они сделаны.

В конечном счете, этот код отображает индексы представления раздела в реально существующие алфавитные пути имен разделов в контроллере извлеченных результатов, или в реально существующие не алфавитные (числовые) разделы, или в поле поиска в заголовке таблицы.

Пользователь будет лишь иногда воссоздавать массив отображения индекса раздела (_idxArray) при каждом касании индекса раздела, но воссоздание массива при каждом касании, очевидно, неэффективно и может быть настроено для кэширования предварительно вычисленных результатов.

Есть много мест, где можно начать сжимать: например, я мог бы сделать sectionIndexTitleLetters статическую строку полностью заглавной с начала. Это достаточно быстро на телефоне 3GS, так что я не обращался к этому в последнее время.

В шапке:

static NSString *sectionIndexTitleLetters = @"abcdefghijklmnopqrstuvwxyz";

В реализации табличного представления источника данных:

- (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tv {
    if (tv != searchDisplayController.searchResultsTableView) {
        NSMutableArray *_indexArray = [NSMutableArray arrayWithCapacity:([sectionIndexTitleLetters length]+2)];

        [_indexArray addObject:@"{search}"];
        [_indexArray addObject:@"#"];

        for (unsigned int _charIdx = 0; _charIdx < [sectionIndexTitleLetters length]; _charIdx++) {
            char _indexChar[2] = { toupper([sectionIndexTitleLetters characterAtIndex:_charIdx]), '\0'};
            [_indexArray addObject:[NSString stringWithCString:_indexChar encoding:NSUTF8StringEncoding]];
        }

        return _indexArray;
    }
    return nil;
}

- (NSInteger) tableView:(UITableView *)tv sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    if (tv != searchDisplayController.searchResultsTableView) {
        if (index == 0) {
            //
            // This is the search bar "section"
            //
            [currentTableView scrollRectToVisible:[[currentTableView tableHeaderView] bounds] animated:YES];
            return -1;
        }
        else if (index == 1) {
            //
            // This is the "#" section, which covers non-alphabetic section headers (e.g. digits 0-9)
            //
            return 0;
        }
        else {
            //
            // This is a bit more involved because the section index array may contain indices that do not exist in the 
            // fetched results controller's sections->name info.
            //
            // What we are doing here is building a "fake-index" array that will return a real section index regardless of 
            // whether the section index title being touched exists or not. 
            //
            // The fake array will be of length of the section index title array, and each index will contain an unsigned 
            // integer from 1 to {numOfRealSections}. 
            //
            // The value this array returns will be "nearest" to the real section that is in the fetched results controller.
            //

            NSUInteger _alphabeticIndex = index-2;

            unsigned int _idxArray[26];
            for (unsigned int _initIdx = 0; _initIdx < [sectionIndexTitleLetters length]; _initIdx++) {
                _idxArray[_initIdx] = [[fetchedResultsController sections] count] - 1;
            }

            unsigned int _previousChunkIdx = 0;
            NSNumberFormatter *_numberFormatter = [[NSNumberFormatter alloc] init];
            NSLocale *_enUSLocale = [[NSLocale alloc] initWithLocaleIdentifier: @"en_US"];
            [_numberFormatter setLocale:_enUSLocale];
            [_enUSLocale release];

            for (unsigned int _sectionIdx = 0; _sectionIdx < [[fetchedResultsController sections] count]; _sectionIdx++) {
                NSString *_sectionTitle = [[[fetchedResultsController sections] objectAtIndex:_sectionIdx] name];
                if (![_numberFormatter numberFromString:_sectionTitle]) {
                    // what's the index of the _sectionTitle across sectionIndexTitleLetters?
                    for (unsigned int _titleCharIdx = 0; _titleCharIdx < [sectionIndexTitleLetters length]; _titleCharIdx++) {
                        NSString *_titleCharStr = [[sectionIndexTitleLetters substringWithRange:NSMakeRange(_titleCharIdx, 1)] uppercaseString];
                        if ([_titleCharStr isEqualToString:_sectionTitle]) {
                            // put a chunk of _sectionIdx into _idxArray
                            unsigned int _currentChunkIdx;
                            for (_currentChunkIdx = _previousChunkIdx; _currentChunkIdx < _titleCharIdx; _currentChunkIdx++) {
                                _idxArray[_currentChunkIdx] = _sectionIdx - 1;
                            }
                            _previousChunkIdx = _currentChunkIdx;
                            break;
                        }
                    }               
                }
            }

            [_numberFormatter release];

            return (NSInteger)_idxArray[_alphabeticIndex];
        }
    }
    return 0;
}
1 голос
/ 29 апреля 2012

Я мог бы быть наивным, но я не понимаю, почему эти решения так барокко. Я сделал это:

В моей модели я добавил метод:

-(NSString *)lastInitial {
    return [self.lastname substringToIndex:1];
}

И в моем контроллере таблиц я установил контроллер fetchedresults для использования этого метода:

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"lastInitial" cacheName:@"Master"];

Кажется, работает - есть ли причина, по которой это плохая идея? Или я пользуюсь новыми функциями в ios5 или чем-то еще?

...