Iphone 对NSArray(如MPMediaPickerController/iPod库)进行排序
我正在为iPhone开发一个自定义UIViewController,它模拟我的应用程序的本地文档目录中的文件的MPMediaPickerController的子集。特别是,我正在尝试重新创建“歌曲”选项卡。我已经成功地创建了我的新控制器,只是我不能像iPod库或MPMediaPickerController中那样对歌曲标题进行排序。以下是歌曲名称需要如何排序的示例:Iphone 对NSArray(如MPMediaPickerController/iPod库)进行排序,iphone,cocoa-touch,sorting,nsarray,mpmediapickercontroller,Iphone,Cocoa Touch,Sorting,Nsarray,Mpmediapickercontroller,我正在为iPhone开发一个自定义UIViewController,它模拟我的应用程序的本地文档目录中的文件的MPMediaPickerController的子集。特别是,我正在尝试重新创建“歌曲”选项卡。我已经成功地创建了我的新控制器,只是我不能像iPod库或MPMediaPickerController中那样对歌曲标题进行排序。以下是歌曲名称需要如何排序的示例: 真棒的歌名 酷歌 有史以来最黑暗的歌曲 我的歌名 一首很酷的歌 为什么是我 浪费4小时 如您所见,排序排除了歌曲标题中的前导文章,
如您所见,排序排除了歌曲标题中的前导文章,并将以数值开头的歌曲放在列表的末尾。有人能建议一个有效的排序功能,将这些tules考虑在内吗?既然看起来没有人能提供解决方案,我想我会发布我提出的解决方案。首先,我为我的数据创建了一个模型:
@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
// 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];
}];
除了从字符串中删除前导项目外,它还删除所有前导非字母字符。我的iPod库中有一首歌名为“$ell Your$oul”,它最终出现在MPMediaPickerController的E部分。我不确定如果我把最初的排序算法装箱,我会做什么,但我要与MPMediaPickerController保持一致,所以你就这样做了
拼图的最后一块是UILocalizedIndexedCollation类。这个方便的小助手类将帮助您对数据进行排序,使通过UITableViewDataSource向UITableView提供数据变得轻而易举。下面是一个关于如何将UILocalizedIndexedCollation类与模型结合使用的片段:
@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
// 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的一个简要说明(来自苹果的文档):
如果应用程序提供
的Localizable.strings文件
当前的语言偏好
索引排序规则对象本地化
方法返回的每个字符串
由选择器标识
因此,请确保为要支持的每种语言提供一个Localizable.strings,否则您的表视图将只包含a-Z和#部分
我花了一段时间才弄清楚这方面的所有细节,所以我希望它对其他人有用。如果你看到我可以改进的方法,请告诉我>P>你可能需要考虑某些带有重音的字符,例如,艾,欧,,,,,, 因此,我稍微修改了您的代码以合并此。您的代码对我们所有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;
}
+一个伟大的工人。我见过你的问题一次,但我没有时间找到解决办法。干得好,回答得真棒。您能否建议我如何使用
MPMediaQuery
实现此功能?我如何将其包括在UITableViewController
类中,我是否需要为节创建一个单独的包含字母列表的数组?这由UILocalizedIndexedCollation在调用SortedArray FromArray:collationStringSelector:时自动处理,从我提供的最后一个代码片段开始。此外,您的添加只考虑第一个字符,如果字符串中的其他字符有重音符号,则会产生意外的结果。