2009-11-16 54 views
38

我正在使用核心数据作为表视图,并且我想将每个结果的第一个字母用作部分标题(这样我可以得到部分指数在旁边)。有没有办法做到这一点的关键路径?像下面这样,我使用name.firstLetter作为sectionNameKeyPath(不幸的是,这不起作用)。如何使用第一个字符作为部分名称

我必须手动抓住每个结果的第一个字母并创建我的部分吗?把一个新的房产放在第一个字母上并用作sectionNameKeyPath是否更好?

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

谢谢。

**编辑:**我不知道它是否有所作为,但我的结果是日语,按片假名排序。我想用这些片假名作为部分索引。

回答

78

您应该只传递“name”作为sectionNameKeyPath。查看此answer的问题“带有索引的核心数据支持的UITableView”。

UPDATE
也就是说,如果你只关心具有快速索引标题滚动解决方案仅适用。在这种情况下,你不会显示节标题。请参阅下面的示例代码。

否则,我同意refulgentis一个瞬态属性是最好的解决方案。此外,在创建NSFetchedResultsController时,该sectionNameKeyPath有此限制:

如果这个关键路径是不一样的,通过在fetchRequest第一个排序 描述符中指定 ,他们必须 产生相同的相对排序。例如,fetchRequest中的第一个排序描述符 可能为持久性属性指定密钥 ; sectionNameKeyPath可能会为从 派生的瞬态属性指定密钥 持久性属性。

样板UITableViewDataSource实现使用NSFetchedResultsController:

- (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' 瞬态特性,在模型中,并检查一个新字符串属性添加到相关实体“瞬态”框。

有几种实现getter的方法。如果您正在生成/创建子类,那么您可以将其添加到子类的实现(.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 

此外,在这个版本中,不要忘记通过 'uppercaseFirstLetterOfName' 作为sectionNameKeyPath:

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

而且,在UITableViewDataSource实施以取消tableView:titleForHeaderInSection:

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

我试着把名字放在'sectionNameKeyPath'中,但是每个返回的名字都有一段。我获得了与名称一样多的部分,每部分只有一个条目(名称)。 – 2009-11-16 10:19:23

+0

您可能没有正确安装。再读一个答案。它像广告一样工作。 – 2009-11-16 10:33:34

+0

我不相信答案本身就是“有效的”,我不知道为什么回应者将第一个名字作为关键路径的行为是一个奇迹。 – refulgentis 2009-11-16 10:36:28

11

有可能是一个更优雅的方式来做到这一点,但我最近有同样的问题,并提出了这个解决方案。

首先,我在被称为firstLetterOfName的索引对象上定义了一个transient属性,并将该getter写入该对象的.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 - 这不正是完全成熟但恕我直言,超越文档中列出的简单案件的任何情况下,你可能会更好做它的“旧时尚“的方式。

+0

+1瞬态属性是显示节标题时的最佳解决方案。 – gerry3 2009-11-16 20:07:35

+1

请注意'substringToIndex:'!如果字符串是一个UTF-16字符串,那只会抓取第一个字符的前半部分。正确的方法是'[aString subStringWithRange:[aString rangeOfComposedCharacterSequenceAtIndex:1]] – 2009-11-16 20:32:51

+0

感谢您的答案。我不明白你在哪里调用'uppercaseFirstLetterOfName'方法。你应该调用这个而不是'caseInsensitiveCompare',还是有另一种方式被调用? – 2009-11-17 03:33:38

4

我解决了这个使用UILocalizedIndexCollat​​ion作为NSFetchedResultsController v.s. UILocalizedIndexedCollation问题

+1

这真的是解决它的唯一方法。UILocalizedIndexCollat​​ion有一大堆内置行为,其他答案中建议的uppercaseFirstLetterOfName代码甚至没有接近。作为一个例子,UILocalizedIndexCollat​​ion可以正确折叠日文半片和全片片假名到正确的部分,部分名称用正确的平假名字符表示 - 它对世界上几乎所有其他语言都是正确的。 – JosephH 2012-08-27 16:59:58

1

优雅的办法是做使“firstLetter”短暂属性中提到的,在实践中是缓慢的但是。

这很慢,因为要计算瞬态属性,需要将整个对象故障存入内存。如果你有很多记录,它会非常非常慢。

快速但不雅的方法是创建一个非暂时性的“firstLetter”属性,每次设置“name”属性时都会更新它。有几种方法可以做到这一点:覆盖“setName:”评估者,覆盖“willSave”,KVO。

+0

你能确认吗?我无法发现我的项目中瞬态和永久性属性之间的差异。 – 2012-05-16 04:53:48

+0

我在评论@ gerry3答案时质疑“优雅”的方式。而“不雅”则显得更“不雅”。索引应该来自'UILocalizedIndexedCollat​​ion'。因此,您应该重置更改区域设置猜疑的每个条目,以便将当前整理与您的第一个字母匹配。 – user500 2013-12-25 20:25:23

1

查看我对类似问题的回答here,其中我介绍如何创建持久化的本地化sectionKey索引(因为您无法对NSFetchedResultsController中的瞬态属性进行排序)。

0

下面是obj-c,几年和一种语言的简单解决方案。它在我的一个项目中工作并且很快运行。

首先,我在NSString上创建了一个类别,命名文件NSString + Indexing。我写返回字符串的第一个字母

@implementation NSString (Indexing) 

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

然后我用在所取得的结果控制器的定义,方法与两个stringIndex如下

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

上面的代码的组合,方法方法将工作,不需要搞乱瞬态属性或存储一个单独的属性,只保留核心数据中的第一个字母

但是,当我试图做与Swift一样的它会抛出一个异常,因为它不喜欢有一个函数作为部分关键路径(或计算出的字符串属性 - 我也试过)如果有人知道如何在Swift中实现同样的事情,我会非常想知道如何。

相关问题