objective-c 如何使用第一个字符作为节名称
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1741093/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use the first character as a section name
提问by nevan king
I'm using Core Data for a table view, and I'd like to use the first letter of each of my results as the section header (so I can get the section index on the side). Is there a way to do this with the key path? Something like below, where I use name.firstLetteras the sectionNameKeyPath(unfortunately that doesn't work).
我将 Core Data 用于表格视图,并且我想使用每个结果的第一个字母作为部分标题(这样我就可以在侧面获得部分索引)。有没有办法用关键路径做到这一点?像下面这样的东西,我在这里name.firstLetter用作sectionNameKeyPath(不幸的是这不起作用)。
Do I have to grab the first letter of each result manually and create my sections like that? Is it better to put in a new property to just hold the first letter and use that as the sectionNameKeyPath?
我是否必须手动抓取每个结果的第一个字母并像这样创建我的部分?放入一个新属性来保存第一个字母并将其用作sectionNameKeyPath?
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:@"name.firstLetter"
cacheName:@"Root"];
Thanks.
谢谢。
**EDIT: ** I'm not sure if it makes a difference, but my results are Japanese, sorted by Katakana. I want to use these Katakana as the section index.
**编辑:** 我不确定它是否有区别,但我的结果是日语,按片假名排序。我想使用这些片假名作为章节索引。
回答by gerry3
You should just pass "name" as the sectionNameKeyPath. See this answerto the question "Core Data backed UITableView with indexing".
您应该只将“名称”作为 sectionNameKeyPath 传递。请参阅“Core Data backed UITableView with indexing”问题的答案。
UPDATE
That solution only works if you only care about having the fast index title scroller. In that case, you would NOT display the section headers. See below for sample code.
更新
该解决方案仅在您只关心快速索引标题滚动条时才有效。在这种情况下,您不会显示部分标题。请参阅下面的示例代码。
Otherwise, I agree with refulgentis that a transient property is the best solution. Also, when creating the NSFetchedResultsController, the sectionNameKeyPathhas this limitation:
否则,我同意 refulgentis 的观点,即瞬态属性是最佳解决方案。此外,在创建NSFetchedResultsController时,sectionNameKeyPath有以下限制:
If this key path is not the same as that specified by the first sort descriptor in fetchRequest, they must generate the same relative orderings. For example, the first sort descriptor in fetchRequest might specify the key for a persistent property; sectionNameKeyPath might specify a key for a transient property derived from the persistent property.
如果此键路径与 fetchRequest 中的第一个排序描述符指定的不同,则它们必须生成相同的相对顺序。例如,fetchRequest 中的第一个排序描述符可能指定持久属性的键;sectionNameKeyPath 可以为从持久属性派生的瞬态属性指定一个键。
Boilerplate UITableViewDataSource implementations using NSFetchedResultsController:
使用 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];
//}
UPDATE 2
更新 2
For the new 'uppercaseFirstLetterOfName' transient property, add a new string attribute to the applicable entity in the model and check the "transient" box.
对于新的 'uppercaseFirstLetterOfName' 瞬态属性,将新的字符串属性添加到模型中的适用实体并选中“瞬态”框。
There are a few ways to implement the getter. If you are generating/creating subclasses, then you can add it in the subclass's implementation (.m) file.
有几种方法可以实现 getter。如果您正在生成/创建子类,那么您可以将其添加到子类的实现 (.m) 文件中。
Otherwise, you can create a category on NSManagedObject (I put this right at the top of my view controller's implementation file, but you can split it between a proper header and implementation file of its own):
否则,您可以在 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
Also, in this version, don't forget to pass 'uppercaseFirstLetterOfName' as the sectionNameKeyPath:
此外,在此版本中,不要忘记将 'uppercaseFirstLetterOfName' 作为 sectionNameKeyPath 传递:
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext
sectionNameKeyPath:@"uppercaseFirstLetterOfName" // this key defines the sections
cacheName:@"Root"];
And, to uncomment tableView:titleForHeaderInSection:in the UITableViewDataSource implementation:
并且,tableView:titleForHeaderInSection:在 UITableViewDataSource 实现中取消注释:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo name];
}
回答by refulgentis
There may be a more elegant way to do this, but I recently had the same problem and came up with this solution.
可能有一种更优雅的方法来做到这一点,但我最近遇到了同样的问题并提出了这个解决方案。
First, I defined a transient property on the objects I was indexing called firstLetterOfName, and wrote the getter into the .m file for the object. e.x.
首先,我在索引的对象上定义了一个名为 firstLetterOfName 的瞬态属性,并将 getter 写入对象的 .m 文件。前任
- (NSString *)uppercaseFirstLetterOfName {
[self willAccessValueForKey:@"uppercaseFirstLetterOfName"];
NSString *stringToReturn = [[self.name uppercaseString] substringToIndex:1];
[self didAccessValueForKey:@"uppercaseFirstLetterOfName"];
return stringToReturn;
}
Next, I set up my fetch request/entities to use this property.
接下来,我设置我的获取请求/实体以使用此属性。
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];
Side note, apropos of nothing: Be careful with NSFetchedResultsController — it's not exactly fully baked yet IMHO, and any situation beyond the simple cases listed in the documentation, you will probably be better off doing it the 'old fashioned' way.
旁注,无中生有:小心使用 NSFetchedResultsController — 恕我直言,它还没有完全成熟,并且除了文档中列出的简单案例之外的任何情况,您可能最好以“老式”方式来做。
回答by Brent Priddy
I solved this using the UILocalizedIndexCollation as mentioned in the NSFetchedResultsController v.s. UILocalizedIndexedCollationquestion
我使用 NSFetchedResultsController vs UILocalizedIndexedCollation问题中提到的 UILocalizedIndexCollation 解决了这个问题
回答by Corey Floyd
The elegant way is to do make the "firstLetter" a transient property, HOWEVER in practice that is slow.
优雅的方法是让“firstLetter”成为一个瞬态属性,但实际上这很慢。
It is slow because for a transient property to be calculated, the entire object needs to be faulted into memory. If you have a lot of records, it will be very, very slow.
它很慢,因为要计算瞬态属性,需要将整个对象错误地放入内存。如果你有很多记录,它会非常非常慢。
The fast, but inelegant way, is to create a non-transient "firstLetter" property which you update each time you set your "name" property. Several ways to do this: override the "setName:" assessor, override "willSave", KVO.
快速但不优雅的方法是创建一个非瞬态的“firstLetter”属性,每次设置“name”属性时都会更新该属性。有几种方法可以做到这一点:覆盖“setName:”评估器,覆盖“willSave”,KVO。
回答by Scott Gardner
See my answer to a similar question here, in which I describe how to create localized sectionKey indexes that are persisted (because you cannot sort on transient attributes in an NSFetchedResultsController).
在此处查看我对类似问题的回答,其中我描述了如何创建持久化的本地化 sectionKey 索引(因为您无法对 NSFetchedResultsController 中的瞬态属性进行排序)。
回答by SimonTheDiver
Here is the simple solution for obj-c, several years and one language late . It works and works quickly in a project of mine.
这是obj-c的简单解决方案,几年和一种语言延迟。它可以在我的一个项目中快速运行。
First I created a category on NSString, naming the file NSString+Indexing. I wrote a method that returns the first letter of a string
首先,我在 NSString 上创建了一个类别,将文件命名为 NSString+Indexing。我写了一个返回字符串首字母的方法
@implementation NSString (Indexing)
- (NSString *)stringGroupByFirstInitial {
if (!self.length || self.length == 1)
return self;
return [self substringToIndex:1];
}
Then I used that method in the definition of the fetched result controller as follows
然后我在获取的结果控制器的定义中使用了该方法,如下所示
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"name.stringGroupByFirstInitial"
cacheName:nil];
The above code in conjunction with the two stringIndex methods will work with no need for messing about with transient properties or storing a separate attribute that just holds the first letter in core data
上面的代码与两个 stringIndex 方法结合使用,无需弄乱瞬态属性或存储仅包含核心数据中第一个字母的单独属性
However when I try to do the same with Swift it throws an exception as it doesn't like having a function as part key path (or a calculated string property - I tried that too) If anyone out there knows how to achieve the same thing in Swift I would dearly like to know how.
但是,当我尝试对 Swift 执行相同操作时,它会引发异常,因为它不喜欢将函数作为部分键路径(或计算出的字符串属性 - 我也尝试过)如果那里有人知道如何实现相同的目标在 Swift 中,我非常想知道如何使用。

