2017-06-28 1 views
1

localizedCompare:を使用して、NSFetchedResultsControllerを使用して人名を返し、並べ替え順にUITableViewを入力しようとしています。 UI(各セクションの最初の文字の右の列)にセクションインデックスを提供しようとしています。 NSFetchedResultsControllerには、各エンティティが属するべきセクション(具体的には、人物の名前の最初の文字、大文字)を提供するエンティティのセレクタが用意されています。NSString localizedCompare:長い文字列を指定した結果が一致しません

ユニコードコードポイントを使用する人名を扱うときは、問題が発生しました。 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

@MartinR "en_US" – levigroker

+1

これは同様の問題であるようですhttps://stackoverflow.com/questions/2167857/non-us-characters-in-section-headers-for-a-uitableview、おそらくそれらの回答の1つが役立ちます。 –

答えて

0

localizedCompare:は、この現象の原因となっているNSStringCompareOptionsフラグの特定の組み合わせを使用しています。 https://developer.apple.com/documentation/foundation/nsstringcompareoptions?preferredLanguage=occ

compare:options:を使用し、NSDiacriticInsensitiveSearchをオンにすると、結果が得られる場合があります。

セクションインデックスを生成するには、最初にすべての拡張文字の値を削除してから最初の文字を取得するのが最適です。以下のような何か:

[[str1 stringByFoldingWithOptions:NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch] substringToIndex:1] 

あなたはセクションの最初の文字を取る前に、このような「エドワード」などのアクセント付き文字で始まる名前は「エドワード」に変換されますこの方法。

+1

ありがとうございました。悲しいことに、 'compare:options:'やそれに類するものを使うことはできません。私たちはSQLiteストアをサポートしているので、オプションは限られています。具体的には、「SQLiteのサポートされているソートセレクタはcompare:caseInsensitiveCompare :, localizedCompare :, localizedCaseInsensitiveCompare :, localizedStandardCompare:です。後者はFinderのような並べ替えで、ほとんどの人が大部分の時間を使用する必要があります。 SQLiteストアを使用する一時的なプロパティ。 https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/PersistentStoreFeatures.html – levigroker

+0

を参照してください。同様に、 'stringByFoldingWithOptions:locale:'は ''Østerhus ''に対して ''Ø "を生成し、"Østerhus "をNSOrderedSameとして "Ø"と "O"を扱うのは、localizedCompare:と同じ奇妙な問題を引き起こします。 :( – levigroker

0

ええ、そこにいました。私が見つけた唯一の解決策は、文字を簡略化する検索のための第2のフィールドを作成し、それを検索に使用される第2フィールドとして格納することでした。スーパーエレガントではなく、それは働いた。

+0

ありがとう。唯一の堅牢なアプローチは、データベースに正規化されたセクション名を格納して、FRCがそれらを並べ替えることができると思います。 – levigroker

0

最終的にこれを解決したアプローチは、正規化されたセクション名をデータベースに格納することでした。

@MartinRは、このアプローチについて話しており、それを解決する鍵 "ああ、ha"の瞬間だったhttps://stackoverflow.com/a/13292767/397210に投稿しました。

localizedCompare:の曖昧な振る舞いを説明しているわけではありませんが、NSOrderedSameとして「Ø」と「O」を追加文字が続く場合は、IMHOという、すべてのUnicodeコードポイントで機能するより堅牢で完全な解決策です。私たちのテスト。

具体的には、アプローチは次のとおりです。

  1. が作成(または既存の利用)エンティティの正規化されたセクション名を受け取るためにあなたのエンティティのフィールドを(のはそれsectionNameを呼びましょう)。
  2. このフィールド(sectionName)には、正規化されたセクション名*を最初に入力し、必要に応じて(人名が変更されたときなど)入力します。 NSFetchedResultsControllerに渡されたフェッチ要求で使用されるソート記述子のNSFetchedResultsController-initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:
  3. sectionNameKeyPathため

  4. 使用このセクション名フィールド(sectionNameを)の内容をソートする方法により、セクション名で最初にソートすることを確認してください比較セレクタのローカライズされたバージョンの使用に注意を払うセクション(人名など)例:

    [NSSortDescriptor sortDescriptorWithKey:@"sectionName" ascending:YES selector:@selector(localizedStandardCompare:)], 
    [NSSortDescriptor sortDescriptorWithKey:@"personName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)] 
    
  5. テスト。

*正規化されたセクション名

我々は、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]; 

うまくいけば、このアプローチは明確で他の人に役立ち、および支援に感謝、すべてのです。

関連する問題