web-dev-qa-db-ja.com

最初の文字をセクション名として使用する方法

テーブルビューにCore Dataを使用していて、各結果の最初の文字をセクションヘッダーとして使用したいので(セクションインデックスを側面で取得できます)。キーパスでこれを行う方法はありますか?以下のようなもの、私はname.firstLettersectionNameKeyPathとして(残念ながら機能しません)。

各結果の最初の文字を手動で取得し、そのようなセクションを作成する必要がありますか?最初の文字を保持するだけの新しいプロパティを挿入し、それをsectionNameKeyPathとして使用する方が良いですか?

NSFetchedResultsController *aFetchedResultsController = 
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
            managedObjectContext:managedObjectContext
            sectionNameKeyPath:@"name.firstLetter"
            cacheName:@"Root"];

ありがとう。

**編集:**違いがあるかどうかはわかりませんが、結果はカタカナで並べ替えられた日本語です。これらのカタカナをセクションインデックスとして使いたい。

38
nevan king

"name"をsectionNameKeyPathとして渡すだけです。これを参照 answer 質問「Core Data Backed UITableView with indexing」へ。

[〜#〜]更新[〜#〜]
この解決策は、高速なインデックスタイトルスクローラーを使用することにのみ関心がある場合にのみ機能します。その場合、セクションヘッダーは表示されません。サンプルコードについては、以下を参照してください。

それ以外の場合は、一時的なプロパティが最良のソリューションであるというrefulgentisに同意します。また、NSFetchedResultsControllerを作成する場合、 sectionNameKeyPath には次の制限があります。

このキーパスがfetchRequestの最初のソート記述子で指定されたものと同じでない場合、それらは同じ相対順序を生成する必要があります。たとえば、fetchRequestの最初のソート記述子は、永続プロパティのキーを指定する場合があります。 sectionNameKeyPathは、永続プロパティから派生した一時プロパティのキーを指定する場合があります。

NSFetchedResultsControllerを使用したボイラープレートUITableViewDataSource実装:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [[fetchedResultsController sections] count];
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    return [fetchedResultsController sectionIndexTitles];
}

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

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}

// Don't implement this since each "name" is its own section:
//- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
//    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
//    return [sectionInfo name];
//}

更新2

新しい 'uppercaseFirstLetterOfName'一時プロパティの場合、モデル内の該当するエンティティに新しい文字列属性を追加し、[一時]ボックスをオンにします。

ゲッターを実装する方法はいくつかあります。サブクラスを生成/作成している場合は、サブクラスの実装(.m)ファイルに追加できます。

それ以外の場合は、NSManagedObjectにカテゴリを作成できます(私はこれをビューコントローラの実装ファイルの先頭に配置しましたが、適切なヘッダーと独自の実装ファイルに分割できます)。

@interface NSManagedObject (FirstLetter)
- (NSString *)uppercaseFirstLetterOfName;
@end

@implementation NSManagedObject (FirstLetter)
- (NSString *)uppercaseFirstLetterOfName {
    [self willAccessValueForKey:@"uppercaseFirstLetterOfName"];
    NSString *aString = [[self valueForKey:@"name"] uppercaseString];

    // support UTF-16:
    NSString *stringToReturn = [aString substringWithRange:[aString rangeOfComposedCharacterSequenceAtIndex:0]];

    // OR no UTF-16 support:
    //NSString *stringToReturn = [aString substringToIndex:1];

    [self didAccessValueForKey:@"uppercaseFirstLetterOfName"];
    return stringToReturn;
}
@end

また、このバージョンでは、sectionNameKeyPathとして「uppercaseFirstLetterOfName」を渡すことを忘れないでください。

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext
sectionNameKeyPath:@"uppercaseFirstLetterOfName" // this key defines the sections
cacheName:@"Root"];

そして、コメントを解除するtableView:titleForHeaderInSection: UITableViewDataSource実装:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo name];
}
79
gerry3

これを行うにはもっとエレガントな方法があるかもしれませんが、私は最近同じ問題があり、この解決策を思いつきました。

最初に、firstLetterOfNameと呼ばれる、インデックスを付けるオブジェクトの一時的なプロパティを定義し、オブジェクトの.mファイルにゲッターを書き込みました。 e.x.

- (NSString *)uppercaseFirstLetterOfName {
    [self willAccessValueForKey:@"uppercaseFirstLetterOfName"];
    NSString *stringToReturn = [[self.name uppercaseString] substringToIndex:1];
    [self didAccessValueForKey:@"uppercaseFirstLetterOfName"];
    return stringToReturn;
}

次に、このプロパティを使用するようにフェッチリクエスト/エンティティを設定します。

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:dataContext];
[request setEntity:entity];
[NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];

余計なことを言っておきますが、NSFetchedResultsControllerには十分注意してください。完全に焼き付けられているわけではありませんが、IMHOと、ドキュメントに記載されている単純なケース以外の状況では、おそらく「旧式」の方法で行うほうがよいでしょう。

11
refulgentis

NSFetchedResultsController v.s. UILocalizedIndexedCollat​​ion の質問で述べたように、UILocalizedIndexCollat​​ionを使用してこれを解決しました

4
Brent Priddy

同様の質問 here への私の回答を参照してください。ここでは、永続化されたローカライズされたsectionKeyインデックスを作成する方法について説明します(NSFetchedResultsControllerで一時属性をソートできないため)。

1
Scott Gardner

エレガントな方法は、「firstLetter」を一時的なプロパティにすることですが、実際には低速です。

一時的なプロパティを計算するには、オブジェクト全体をメモリにフォールトする必要があるため、処理が遅くなります。レコードが多いと、非常に遅くなります。

高速ですが洗練されていない方法は、「name」プロパティを設定するたびに更新する非一時的な「firstLetter」プロパティを作成することです。これを行ういくつかの方法: "setName:"評価者をオーバーライドする、 "willSave"、KVOをオーバーライドする。

1
Corey Floyd

これがobj-cの簡単な解決策です。数年後、1つの言語が遅れます。それは機能し、私のプロジェクトですぐに機能します。

まず、NSStringにカテゴリを作成し、ファイルにNSString + Indexingという名前を付けました。文字列の最初の文字を返すメソッドを書きました

@implementation NSString (Indexing)

- (NSString *)stringGroupByFirstInitial {
    if (!self.length || self.length == 1)
        return self;
    return [self substringToIndex:1];
}

次に、フェッチされた結果コントローラーの定義でそのメソッドを次のように使用しました

_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"name.stringGroupByFirstInitial"
                                                                               cacheName:nil];

上記のコードを2つのstringIndexメソッドと組み合わせて使用​​すると、一時的なプロパティをいじったり、コアデータの最初の文字だけを保持する別の属性を保存したりする必要はありません。

しかし、Swiftで同じことを実行しようとすると、関数が一部のキーパス(または計算された文字列プロパティ-私も試した)として持つのが好きではないため、例外がスローされます) Swiftで同じことを達成する方法を知っています。どうすればよいか知りたいです。

0
SimonTheDiver