UITableView中的节标题中出现非美国字符

13

我为一个简单的Core Data iPhone应用程序添加了一个部分列表。

我按照这个stackoverflow问题创建它- 如何将第一个字符用作部分名称,但是我的列表还包含以A-Z之外的字符开头的项,特别是在瑞典使用的Å,Ä和Ö。

问题现在是当表格视图显示部分列表时,最后三个字符会被错误地绘制。请参见下面的图片

现在似乎我的最佳选择是让这些项目在“Z”下排序。

if ([letter isEqual:@"Å"] ||
    [letter isEqual:@"Ä"] ||
    [letter isEqual:@"Ö"]) 
    letter = @"Z";

有人解决了这个问题吗?

顺便提一下... 'Å'、'Ä'和'Ö'应该按照这个顺序排序,但是Core Data的NSSortDescriptor将它们排序为'Ä'、'Å'和'Ö'。我尝试将选择器设置为localizedCaseInsensitiveCompare:,但是这会导致out of order section name 'Ä. Objects must be sorted by section name'错误。你见过这种情况吗?


请看我对类似问题的答案在这里,其中我描述了如何创建本地化的sectionKey索引。 - Scott Gardner
对于仍然遇到此问题的人,请在 willDisplayHeaderView 中使用 localizedCapitalizedStringlocalizedUppercaseStringlocalizedLowercaseString。(API_AVAILABLE ios 9.0) - ACAkgul
7个回答

15

所以,我不能放弃这个问题,并找到了以下内容:

在这种情况下,你需要使用的是“Unicode规范化形式D”。该文档在http://unicode.org/reports/tr15/中有更详细的解释(警告:篇幅长且枯燥)。

这里有一个函数可以进行分解,然后过滤掉所有的变音符号。你可以使用它将Äpple转换为Apple,然后使用第一个字母建立索引。

- (NSString*) decomposeAndFilterString: (NSString*) string
{
    NSMutableString *decomposedString = [[string decomposedStringWithCanonicalMapping] mutableCopy];
    NSCharacterSet *nonBaseSet = [NSCharacterSet nonBaseCharacterSet];
    NSRange range = NSMakeRange([decomposedString length], 0);

    while (range.location > 0) {
        range = [decomposedString rangeOfCharacterFromSet:nonBaseSet
            options:NSBackwardsSearch range:NSMakeRange(0, range.location)];
        if (range.length == 0) {
            break;
        }
        [decomposedString deleteCharactersInRange:range];
    }

    return [decomposedString autorelease];
}

(我在一个邮件列表上找到了这段代码,忘记了来源,但我对其进行了一些修复)


2
+1 鼓励你的研究和兴趣。但我认为现在已经掌握了排序方法。我使用 localizedCaseInsensitiveCompare: 作为排序描述符传递给获取结果控制器,这将根据设备上的语言设置保持顺序。但是,索引标题等由CoreData处理,就像gerry3在这里所描述的那样:https://dev59.com/O3I-5IYBdhLWcg3wqqTg#1741131 - epatel

4

在我的控制器中添加这个代码解决了我的原始问题

- (NSString *)controller:(NSFetchedResultsController *)controller sectionIndexTitleForSectionName:(NSString *)sectionName {
return sectionName;}

列表中 Å、Ä 和 Ö 显示正确


1
请看我上面的评论,substringToIndex:1 对于UTF字符无效。请使用以下代码:[[sectionName substringWithRange:[sectionName rangeOfComposedCharacterSequenceAtIndex:0]] uppercaseString]; - Felix Lamouroux
这对我有效吗? NSString *s = @"ÉÎØÜ"; NSLog(@"%@", [s substringToIndex:1]); NSLog(@"%@", [[s substringWithRange:[s rangeOfComposedCharacterSequenceAtIndex:0]] uppercaseString]); 2014-08-19 15:01:15.054 test[53092:3703902] É 2014-08-19 15:01:15.055 test[53092:3703902] É除非还有其他字符会失败... - codepoet

4

我遇到了与原问题中报告的相同问题,即索引栏中未正确显示扩展字符(Unicode第0页之外的字符)。

尽管我似乎使用正确的单个Unicode字符字符串来填充NSFetchedResultsController,但当我访问“sectionIndexTitles”属性时,返回的是同样的字符,但高位字节设置为0;例如,字符\u30a2(片假名字母A)变成了\u00a2(美分符号)。

我不确定这是否是NSFetchedResultsController的方法“sectionIndexTitleForSectionName:”中的错误,还是我的其他地方出了问题。然而,我的解决方法是重写它并执行文档中所述的内部操作:

- (NSString *)sectionIndexTitleForSectionName:(NSString *)sectionName
{
    NSString    *outName;

    if ( [sectionName length] )
        outName = [[sectionName substringToIndex:1] uppercaseString];
    else
        outName = [super sectionIndexTitleForSectionName:sectionName];

    return outName;
}

这将产生预期的输出。

这听起来很有前途。必须尝试一下...如果我能再次找到项目文件的话 ;) - epatel
1
这是一个非常好的线索,但对于像泰语这样更复杂的字符集来说,这还不够。在这种情况下,您应该使用[[sectionName substringWithRange:[sectionName rangeOfComposedCharacterSequenceAtIndex:0]] uppercaseString]; - Felix Lamouroux

3
当我遇到这种情况时,我首先问自己的问题是“苹果公司会怎么做”。
作为一个实验,我刚刚在我的iPhone通讯录中添加了“Joe Äpple”,他出现在普通A下面。我认为这很有意义。
所以,与其将它们放在Z或Ä下面,你应该做同样的事情。一定有一些方法可以获取Unicode字符的“基本”字母来进行分组。

+1 好的观点。我在这里也进行了一些测试,实际上它会根据设置中选择的语言而有所不同。我现在想到了排序方法。但是我仍然无法正确绘制... - epatel

2
你可以使用UTF-8编码。
const char *cLetter = (const char *)[ tmp cStringUsingEncoding:NSUTF8StringEncoding]; 
NSString *original = [NSString stringWithCString:cLetter encoding:NSUTF8StringEncoding];

1

你解决了这个问题吗?

通过实现sectionIndexTitlesForTableView:来构建自己的部分标题数组,我已经成功地使部分标题索引正确显示:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {

    NSMutableArray *indexKeys = [NSMutableArray arrayWithCapacity:30];
    NSArray *fetchedResults = [fetchedResultsController fetchedObjects];
    NSString *currKey = @"DEFAULT";

    for (NSManagedObject *managedObject in fetchedResults) {
        NSString *indexKey = [managedObject valueForKey:@"indexKey"];
        if (![indexKey isEqualToString:currKey]) {
            [indexKeys addObject:indexKey];
            currKey = indexKey;
        }
    }

    return indexKeys;
}

这里,indexKey是名称的第一个字母。

然而,在sectionForSectionIndexTitle中,这会创建两个问题之一:

如果我只返回该部分的索引,那么现在这是未排序的索引,不再与fetchResultController中的排序顺序对应:

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    return index;
}

另外,如果我将调用传递给fetchedResultsController,则会在非美国索引标题上中断,因为这些不再是fetchedResultsController使用的奇怪字符。
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    return [fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}

当导航到“Ø”索引标题时,后面的代码会生成以下类型的错误:

由于未捕获的异常'NSInternalInconsistencyException',应用程序终止,原因是:索引标题在24处不等于'Ø'

解决此问题的方法是将有问题的字符转换回它们奇怪的形式:

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    if ([title isEqualToString:@"Æ"]) {
        title = @"\u2206";
    } else if ([title isEqualToString:@"Ø"]) {
        title = @"\u0178";
    } else if ([title isEqualToString:@"Å"]) {
        title = @"\u2248";
    }
    return [fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}

您可以在调试器中使用“打印描述到控制台”操作来查找Unicode值。

然而,更好的解决方案是找出为什么会发生这种奇怪的编码并加以防止。


1

你有按照我的答案去回答那个问题吗?

我正在进行一些测试(重新获取我的对象),并且看到奇怪的间歇性行为。它确实正确地排序它们('Å'在'A'之后,但在'Ä'之前),但有时候会出现这种情况。其他时候,即使我没有做任何特殊处理,它也会将字符放在A-Z之外的末尾(在'Z'之后)。

此外,我注意到如果返回小写形式,'Å'会被正确绘制。你是否尝试过覆盖sectionIndexTitleForSectionName:以保持顺序,但更改绘制的字符?


+1,我根据您的建议制定了解决方案,谢谢!我尽可能地信任Core Data。我将研究子类化NSFetchedResultsController,第一次尝试失败了。 - epatel
这不是子类化,而是您可以在表视图委托中实现的一种方法。 - gerry3
阅读文档时,您是否认为(sectionIndexTitleForSectionName:)是控制器上的一个方法? - epatel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接