2

Я разрабатываю пользовательский UIViewController для iPhone, который эмулирует подмножество MPMediaPickerController для файлов в локальном каталоге документов моего приложения. В частности, я пытаюсь воссоздать вкладку «Песни». Мне удалось создать мой новый контроллер, но я не могу получить названия песен, которые будут сортироваться, как в библиотеке iPod или MPMediaPickerController. Вот пример того, как названия песен должны быть отсортированы:Сортировка NSArray как библиотека MPMediaPickerController/iPod

  1. Удивительный Название песни
  2. крутая песня
  3. The Darkest Song Ever
  4. My Song Название
  5. Действительно крутая песня
  6. Почему я?
  7. 4 часов Wasted

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

ответ

4

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

@interface MyModel : NSObject 
{ 
    NSString* _value; 
    NSString* _sortableValue; 
} 

@property (nonatomic,copy) NSString* value; 

- (NSString*)sortableValue; 
- (NSString*)comparableString:(NSString*)str; 

@end 

Они ключ к модели является метод comparableString, который привыкает для создания sortableValue. Вот реализация модели:

@implementation MyModel 

@synthesize value=_value; 

-(void)dealloc 
{ 
    [_value release]; 
    [_sortableValue release]; 

    [super dealloc]; 
} 

- (void)setValue:(NSString*)value 
{ 
    [_value release]; 
    _value = [value copy]; 
    [_sortableValue release]; 
    _sortableTValue = nil; 
} 

- (NSString*)sortableValue 
{ 
    if (_sortableValue == nil) 
     _sortableValue = [[self comparableString:_value] retain]; 

    return _sortableValue; 
} 

- (NSString*)comparableString:(NSString*)str 
{ 
    if (str == nil) 
     return nil; 
    else if ([str length] == 0) 
     return [NSString stringWithString:str]; 

    NSCharacterSet* numbersSet = [NSCharacterSet decimalDigitCharacterSet]; 
    if ([str rangeOfCharacterFromSet:numbersSet options:0 range:NSMakeRange(0, 1)].location != NSNotFound) 
     return [NSString stringWithString:str]; 

    NSRange range = NSMakeRange(0, [str length]); 

    if ([str compare:@"a " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 2)] == NSOrderedSame) 
     range.location = 2; 
    else if ([str compare:@"an " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 3)] == NSOrderedSame) 
     range.location = 3; 
    else if ([str compare:@"the " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 4)] == NSOrderedSame) 
     range.location = 4; 

    range.length -= range.location; 

    NSCharacterSet* lettersSet = [NSCharacterSet letterCharacterSet]; 
    NSUInteger letterOffset = [str rangeOfCharacterFromSet:lettersSet options:0 range:range].location; 
    if (letterOffset == NSNotFound) 
     return [NSString stringWithString:str]; 

    letterOffset -= range.location; 
    range.location += letterOffset; 
    range.length -= letterOffset; 

    return [str substringWithRange:range]; 
} 

@end 

В дополнении к удаляющим ведущим статьям из строки, он также удаляет любые ведущие символы небуквенных. У меня есть песня в моей библиотеке iPod под названием «$ ell Your $ oul», которая заканчивается в разделе E в MPMediaPickerController. Я не уверен, что это то, что я бы сделал, если бы я обработал исходный алгоритм сортировки, но я собирался согласовать с MPMediaPickerController, так что вы идете.

Последним головоломкой является класс UILocalizedIndexedCollation. Этот удобный небольшой вспомогательный класс поможет вам отсортировать данные, чтобы обеспечить его подключение к UITableView через UITableViewDataSource куском пирога. Вот отрывок о том, как использовать класс UILocalizedIndexedCollation в сочетании с моделью:

// tableData will contain an NSArray for each populated section in the table view 
    NSMutableDictionary* tableData = [NSMutableDictionary dictionary]; 

    NSMutableArray* myArray = [NSMutableArray array]; 
    // Populate myArray with instances of MyModel 

    UILocalizedIndexedCollation* indexer = [UILocalizedIndexedCollation currentCollation]; 
    for (MyModel* data in myArray) 
    { 
     NSInteger index = [indexer sectionForObject:data collationStringSelector:@selector(sortableValue)]; 
     NSNumber* key = [[NSNumber alloc] initWithInteger:index]; 
     NSMutableArray* array = [tableData objectForKey:key]; 
     if (array == nil) 
     { 
     array = [NSMutableArray new]; // Will be released after creating a sorted array in the following section 
     [tableData setObject:array forKey:key]; 
     } 

     [array addObject:data]; 
     [key release]; 
    } 

    [tableData enumerateKeysAndObjectsUsingBlock:^(id key, id array, BOOL* stop) 
    { 
     NSMutableArray* sortedArray = [[indexer sortedArrayFromArray:array collationStringSelector:@selector(sortableValue)] mutableCopy]; 
     [tableData setObject:sortedArray forKey:key]; 
     [array release]; 
    }]; 

Одно быстрое примечание о UILocalizedIndexedCollation (из документации компании Apple):

Если приложение содержит файл Localizable.strings для нужного языка , объект индексированного сопоставления локализует каждую строку, возвращаемую методом , идентифицированную селектором.

Поэтому убедитесь, что вы предоставили Localizable.strings для каждого языка, который хотите поддерживать, или в вашем представлении таблицы будут только разделы A-Z и #.

Мне потребовалось некоторое время, чтобы разобраться во всех подробностях, поэтому я надеюсь, что это станет полезным для других людей. Если вы видите какие-либо способы улучшить это, пожалуйста, дайте мне знать!

+1

+1 Отличный рабочий человек. Однажды я видел ур, но у меня нет времени, чтобы найти решение. Хорошая работа. – KingofBliss

+0

Удивительный ответ. Не могли бы вы предложить, как я могу реализовать это с помощью 'MPMediaQuery'? Как бы включить это в класс 'UITableViewController', мне нужно было бы создать отдельный массив со списком букв для разделов? – sooper

-2

Возможно, вам необходимо будет рассмотреть некоторые персонажи с акцентами, такими как è, é, ò, à, ù, ì.

Поэтому я немного изменил ваш код, чтобы включить это. Ваш код является отличным вкладом для всех нас разработчиков iphone

- (NSString*)comparableString:(NSString*)str 
{ 
    if (str == nil) 
     return nil; 
    else if ([str length] == 0) 
     return [NSString stringWithString:str]; 

    NSCharacterSet* numbersSet = [NSCharacterSet decimalDigitCharacterSet]; 
    if ([str rangeOfCharacterFromSet:numbersSet options:0 range:NSMakeRange(0, 1)].location != NSNotFound) 
     return [NSString stringWithString:str]; 

    NSRange range = NSMakeRange(0, [str length]); 

    if ([str compare:@"a " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 2)] == NSOrderedSame) 
     range.location = 2; 
    else if ([str compare:@"an " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 3)] == NSOrderedSame) 
     range.location = 3; 
    else if ([str compare:@"the " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 4)] == NSOrderedSame) 
     range.location = 4; 

    range.length -= range.location; 

    NSCharacterSet* lettersSet = [NSCharacterSet letterCharacterSet]; 
    NSUInteger letterOffset = [str rangeOfCharacterFromSet:lettersSet options:0 range:range].location; 
    if (letterOffset == NSNotFound) 
     return [NSString stringWithString:str]; 

    letterOffset -= range.location; 
    range.location += letterOffset; 
    range.length -= letterOffset; 



//my modification starts here......... 

    NSString * finalString = [str substringWithRange:range]; 
    NSString * firstCharString = [finalString substringToIndex:1]; 

    NSData * encodedData = [firstCharString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; 
    NSString * encodedString = [[NSString alloc] initWithBytes:[encodedData bytes] length:[encodedData length] encoding:NSASCIIStringEncoding]; 
    if ([encodedString isEqualToString:@"?"]) { 
     return finalString; 
    } 

    NSString * finalProcessedString = [finalString stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:encodedString]; 
    [encodedString release]; 
    return finalProcessedString; 
} 
+1

Это обрабатывается автоматически с помощью UILocalizedIndexedCollation при вызове sortedArrayFromArray: collationStringSelector: из последнего предоставленного фрагмента. Кроме того, ваше добавление учитывает только первый символ, что даст неожиданные результаты, если другие символы в строках имеют знаки акцента. –