我们尝试使用NSFetchedResultsController
返回人名,并使用UITableView
按排序顺序填充localizedCompare:
。我们还尝试在UI中提供节索引(每个节的第一个字符的右列)。我们在我们的实体上为NSFetchedResultsController
提供了一个选择器,它提供了每个实体应该属于的部分(具体地说,是该人名的第一个字符,大写)。
在处理使用Unicode代码点的人名时,我们遇到了一个问题。 NSFetchedResultsController
抱怨实体未按部分排序。
具体做法是:
reason=The fetched object at index 103 has an out of order section name 'Ø. Objects must be sorted by section name'}, {
reason = "The fetched object at index 103 has an out of order section name '\U00d8. Objects must be sorted by section name'";
问题似乎是localizedCompare:
返回的比较值对于整个“单词”与主要字符不同。
以下测试传递虽然我希望(“Ø”和“O”)与(“Østerhus”和“Osypowicz”)之间的比较结果一致。
- (void)testLocalizedSortOrder300
{
NSString *str1 = @"Osowski";
NSString *str2 = @"Østerhus";
NSString *str3 = @"Osypowicz";
NSString *letter1 = @"O";
NSString *letter2 = @"Ø";
//localizedCompare:
//"Osowski" < "Østerhus"
NSComparisonResult res = [str1 localizedCompare:str2];
XCTAssertTrue(res == NSOrderedAscending, @"(localizedCompare:) Expected '%@' and '%@' to be NSOrderedAscending, but got %@", str1, str2, res == NSOrderedSame ? @"NSOrderedSame" : @"NSOrderedDescending");
//"Østerhus" < "Osypowicz"
res = [str2 localizedCompare:str3];
XCTAssertTrue(res == NSOrderedAscending, @"(localizedCompare:) Expected '%@' and '%@' to be NSOrderedAscending, but got %@", str2, str3, res == NSOrderedSame ? @"NSOrderedSame" : @"NSOrderedDescending");
//"O" < "Ø"
res = [letter1 localizedCompare:letter2];
XCTAssertTrue(res == NSOrderedAscending, @"(localizedCompare:) Expected '%@' and '%@' to be NSOrderedAscending, but got %@", letter1, letter2, res == NSOrderedSame ? @"NSOrderedSame" : @"NSOrderedDescending");
}
所以,问题最终是,给定一个使用Unicode代码点的人名(或任何其他字符串),我们如何正确地(以本地化的方式)返回一个部分名称,该部分名称将与排序顺序相对应,如下所示: localizedCompare:
?
此外,localizedCompare:
显然将“Ø”和“O”视为NSOrderedSame
,后面跟着其他字符会发生什么?
答案 0 :(得分:0)
我希望localizedCompare:
正在使用导致此行为的NSStringCompareOptions
标志的特定组合。
https://developer.apple.com/documentation/foundation/nsstringcompareoptions?preferredLanguage=occ
您可以使用compare:options:
并启用NSDiacriticInsensitiveSearch
来获得所需的结果。
为了生成节索引,最好先删除所有扩展字符的值,然后取第一个字母。类似的东西:
[[str1 stringByFoldingWithOptions:NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch] substringToIndex:1]
这样,以“Édward”等重音字母开头的名字将在您获取该部分的第一个字母之前转换为“Edward”。
答案 1 :(得分:0)
答案 2 :(得分:0)
最终解决此问题的方法是将规范化的部分名称存储在数据库中。
@MartinR建议SO帖子引导我到https://stackoverflow.com/a/13292767/397210谈论这种方法,并且是解决它的关键“啊哈”时刻。
虽然这并不能解释localizedCompare:
显然将“Ø”和“O”视为NSOrderedSame的愚蠢行为,然后是其他字符,恕我直言,这是一个更强大,更完整的解决方案,适用于所有Unicode代码点,在我们的测试中。
具体来说,方法是:
sectionName
)。sectionName
),并根据需要(例如,当人名更改时)。sectionName
)用于sectionNameKeyPath
NSFetchedResultsController
-initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:
对于传递给NSFetchedResultsController
的获取请求所使用的排序描述符,请务必先按节名称排序,然后按如何对节的内容进行排序(例如,人名),付款注意使用本地化版本的比较选择器。 e.g:
[NSSortDescriptor sortDescriptorWithKey:@"sectionName" ascending:YES selector:@selector(localizedStandardCompare:)],
[NSSortDescriptor sortDescriptorWithKey:@"personName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]
测试。
*标准化部分名称
我们需要注意在处理unicode时假设第一个“字符”是什么。 “字符”可以由多个字符组成。 见https://www.objc.io/issues/9-strings/unicode/ 还有Compare arabic strings with special characters ios
这是我用来生成规范化部分名称的方向:
NSString *decomposedString = name.decomposedStringWithCanonicalMapping;
NSRange firstCharRange = [decomposedString rangeOfComposedCharacterSequenceAtIndex:0];
NSString *firstChar = [decomposedString substringWithRange:firstCharRange];
retVal = [firstChar localizedUppercaseString];
希望这种方法对其他人来说是明确和有用的,并且感谢所有人的帮助。