我正在尝试使用带有混合语言数据的FRC,并希望有一个部分索引。
似乎从文档中你应该能够覆盖FRC的
- (NSString *)sectionIndexTitleForSectionName:(NSString *)sectionName
- (NSArray *)sectionIndexTitles
然后使用UILocalizedIndexedCollation来获得本地化的索引和部分。但遗憾的是,这不起作用,也不是意图使用的:(
有没有人能够使用带有UILocalizedIndexedCollation的FRC,或者我们被迫使用示例UITableView + UILocalizedIndexedCollation示例中提到的手动排序方法(示例代码包含在我工作的地方)。
使用以下属性
@property (nonatomic, assign) UILocalizedIndexedCollation *collation;
@property (nonatomic, assign) NSMutableArray *collatedSections;
和代码:
- (UILocalizedIndexedCollation *)collation
{
if(collation == nil)
{
collation = [UILocalizedIndexedCollation currentCollation];
}
return collation;
}
- (NSArray *)collatedSections
{
if(_collatedSections == nil)
{
int sectionTitlesCount = [[self.collation sectionTitles] count];
NSMutableArray *newSectionsArray = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount];
collatedSections = newSectionsArray;
NSMutableArray *sectionsCArray[sectionTitlesCount];
// Set up the sections array: elements are mutable arrays that will contain the time zones for that section.
for(int index = 0; index < sectionTitlesCount; index++)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[newSectionsArray addObject:array];
sectionsCArray[index] = array;
[array release];
}
for(NSManagedObject *call in self.fetchedResultsController.fetchedObjects)
{
int section = [collation sectionForObject:call collationStringSelector:NSSelectorFromString(name)];
[sectionsCArray[section] addObject:call];
}
NSArray *sortDescriptors = self.fetchedResultsController.fetchRequest.sortDescriptors;
for(int index = 0; index < sectionTitlesCount; index++)
{
[newSectionsArray replaceObjectAtIndex:index withObject:[sectionsCArray[index] sortedArrayUsingDescriptors:sortDescriptors]];
}
}
return [[collatedSections retain] autorelease];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// The number of sections is the same as the number of titles in the collation.
return [[self.collation sectionTitles] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// The number of time zones in the section is the count of the array associated with the section in the sections array.
return [[self.collatedSections objectAtIndex:section] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if([[self.collatedSections objectAtIndex:section] count])
return [[self.collation sectionTitles] objectAtIndex:section];
return nil;
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [self.collation sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [self.collation sectionForSectionIndexTitleAtIndex:index];
}
我希望仍能使用FRCDelegate协议获得更新通知。似乎没有什么好方法可以让这两个对象很好地协同工作。
答案 0 :(得分:6)
由于您无法对瞬态属性进行排序,因此我实施的解决方案是......
为Core Data模型中每个实体中的每个可排序属性创建一个名为“sectionKey”的字符串属性。 sectionKey属性将是从基本属性(例如,名称或标题属性)导出的计算值。它必须是持久化的,因为(当前)瞬态属性不能用于获取请求的排序描述符。在每个sectionKey和base属性上启用索引,以便为其提供排序。要将此更新应用于现有应用程序,您需要执行轻量级迁移,并且还包括更新预先存在的数据库的例程。
如果您正在播种数据(例如,使用标准数据集填充新安装,或者为每种目标语言创建本地化SQLite数据库,其中一个将在初始启动时被复制),代码,计算和更新每个实体的sectionKey属性。关于播种数据的“最佳”方法的意见不一,但是值得注意的是,每种语言的少数plist文件(通常范围从几个字节到20k,即使是由几百个值组成的列表)也会离开与每种语言的单个SQLite数据库相比,总体占用空间要小得多(每个语言的起始速度约为20k)。另外,Microsoft Excel for Mac可以配置为通过启用语言功能(3)来提供列表的本地化排序。
在获取的结果控制器构造函数中,对sectionKey和base属性进行排序,并为section name键路径传递sectionKey。
添加计算逻辑以更新所有添加或编辑用户输入中的sectionKey属性,例如,在textFieldDidEndEditing中:。
就是这样!没有手动将获取的对象分区为数组数组。 NSFetchedResultsController将为您执行本地化排序规则。例如,在中文(简体)的情况下,获取的对象将通过语音发音(4)进行索引。
(1)来自Apple IOS开发人员库&gt;国际化编程主题&gt; Internationalization and Localization。 (2)TableViewSuite的3_SimpleIndexedTableView。 (3)How to enable Chinese language features in Microsoft Office for Mac。 (4)汉语通常按笔画数或语音发音排序。
答案 1 :(得分:3)
Brent,我的解决方案基于FRC,我从fetch中获取分区,在我的模型对象上指定一个transient属性,返回对象的section名称。我只在getter属性的实现中使用UIlocalizedIndexedCollation然后我依赖于表视图控制器上的FRC实现。当然,我使用localizedCaseInsensitiveCompare作为fetch的排序选择器。
- (NSString *)sectionInitial {
NSInteger idx = [[UILocalizedIndexedCollation currentCollation] sectionForObject:self collationStringSelector:@selector(localeName)];
NSString *collRet = [[[UILocalizedIndexedCollation currentCollation] sectionTitles] objectAtIndex:idx];
return collRet;
}
我唯一的缺点是我最后不能拥有#部分,因为我没有改变数据库中的排序。其他一切都运作良好。
答案 2 :(得分:2)
最近面临同样的问题让我搜索web(首先是stackoverflow)以获得适当的解决方案,以使NSFetchedResultsController(FRC)和UILocalizedIndexedCollation(LIC)协同工作。大多数人发现解决方案不足以满足所有要求。值得一提的是,我们不能使用LIC按照需要的方式对获取的对象进行排序,因此我们将失去巨大的性能,而FRC不会充分利用它。
所以,这是一般的问题:
1)我们有一些DB,我们想要在带有索引的列表(UITableView)中使用FRC获取和显示某些数据(类似于Contacts.app)。我们需要传递对象值键,以便FRC可以做出排序决定。
2)即使我们将为我们的CoreData模型添加特殊字段以进行部分排序并使用FRC的部分索引标题,我们也无法获得所需的结果,当然FRC仅提供找到的索引,但不是完整的字母表。除此之外我们还会遇到错误索引显示的问题(不确定为什么会这样,也许是FRC中的一些错误)。例如,在俄语字母的情况下,将会有完全空白或“奇怪”的符号($,?,',...)。
3)如果我们尝试使用LIC来显示好的本地化索引,我们将面临在FRC中映射基于数据的部分以在LIC中完成本地化字母“部分”的问题。
4)在我们决定使用LIC并以某种方式解决问题之后3)我们会注意到LIC会将“#”部分放在底部(即最高部分索引),但FRC会将“#” - 类似对象放到顶部(即最低部分指数 - 0)。因此将有完整的部分位移。
考虑到所有这些,我决定“欺骗”FRC而没有任何大的“黑客攻击”但是让它按照我需要的方式对数据进行排序(移动所有来自“#”的对象 - 如部分到列表底部)
以下是我的解决方案:
我将扩展方法添加到我的NSManagedObject实例,以准备我们将在排序描述符和FRC设置的部分键路径中使用的排序名称。除了将在下面描述的那些之外,不需要特殊的移动。
问题4)由于FRC的排序算法(低级SQL)可以稍微修改而发生:仅通过应用更依赖于数据的排序描述符,谓词并使用无法解决的固定预定义比较器问题我注意到FRC决定“#”符号低于任何与LIC相对的字母符号,其中“#”最高。
FRC的逻辑非常简单,因为UTF-8中的“#”符号是U + 0023。拉丁语大写“A”是U + 0041,所以23&lt; 41.为了使FRC将“#” - 像对象放到最高索引部分,我们需要传递最高的UTF-8符号。为了这个源http://www.utf8-chartable.de/unicode-utf8-table.pl,UTF-8符号是U + 1000FF()。当然,这个符号几乎没有办法在现实生活中出现。让我们使用U + 100000清晰。
排序名称更新方法如下所示:
#define UT8_MAX @"\U00100000"
- (void)updateSortName
{
NSMutableString *prSortName = [NSMutableString stringWithString:[self dataDependantSortName]]; // for sort descriptors
NSString *prSectionIdentifier = [[prSortName substringToIndex:1] uppercaseString]; // section keypath
UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
NSUInteger sectionIndex = [collation sectionForObject:prSectionIdentifier collationStringSelector:@selector(stringValue)]; // stringValue is NSString category method that returns [NSString stringWithString:self]
if(sectionIndex == [[collation sectionTitles] count] - 1) // last section tile '#'
{
prSectionIdentifier = UT8_MAX;
}
else
{
prSectionIdentifier = [collation sectionTitles][sectionIndex];
}
[prSortName replaceCharactersInRange:NSMakeRange(0, 1) withString:prSectionIdentifier];
// sortName, sectionIdentifier - non-transient string attributes in CoreData model
[self willChangeValueForKey:@"sortName"];
[self setPrimitiveValue:prSortName forKey:@"sortName"];
[self didChangeValueForKey:@"sortName"];
[self willChangeValueForKey:@"sectionIdentifier"];
[self setPrimitiveValue:prSectionIdentifier forKey:@"sectionIdentifier"];
[self didChangeValueForKey:@"sectionIdentifier"];
}
FRC设置:
- (void)setupFRC
{
NSEntityDescription *entityDescription =
[NSEntityDescription entityForName:@"entity"
inManagedObjectContext:self.moc];
NSSortDescriptor *sortNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sortName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]; // or any selector you need
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortNameDescriptor, nil];
NSFetchRequest *fetchRequest = [NSFetchRequest new];
[fetchRequest setEntity:entityDescription];
[fetchRequest setFetchBatchSize:BATCH_SIZE];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *fetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.moc
sectionNameKeyPath:@"sectionIdentifier"
cacheName:nil];
self.fetchedResultsController = fetchedResultsController;
}
FRC委托方法是默认的。电视代表和数据源方法:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [[self localizedIndexedCollation] sectionTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
NSString *indexTitle = [title isEqualToString:@"#"] ? UT8_MAX : title;
NSInteger fetchTitleIndex = NSNotFound;
NSArray *sections = [self.fetchedResultsController sections];
for (id <NSFetchedResultsSectionInfo> sectionInfo in sections)
{
if([[sectionInfo name] isEqualToString:indexTitle])
{
fetchTitleIndex = [sections indexOfObject:sectionInfo];
break;
}
}
return fetchTitleIndex;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
NSString *fetchTitle = [sectionInfo name];
NSInteger collationTitleIndex = [[self localizedIndexedCollation] sectionForObject:fetchTitle
collationStringSelector:@selector(stringValue)];
return [[[self localizedIndexedCollation] sectionTitles] objectAtIndex:collationTitleIndex];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
多数民众赞成。到目前为止效果很好。也许它会对你有用。
答案 3 :(得分:2)
我找到了解决这个问题的简单方法!
只需更换&#34;#&#34;到&#34; ^&#34;在您的核心数据中,以便您的tableview的部分将是&#34; A-Z ^&#34;。虽然&#39;#&#39;的unicode小于&#39; A&#39; ^&#39;&#39;&#39;&nbsp;&#39;&#39;&#39;&#39;&#39;&#39;所以你预测&#39; ^&#39;并不困难。将在你的章节中跟随Z.
然后,您应该替换获取的结果控制器部分。只需这几行代码:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[self.frc sectionIndexTitles]];
// If "^" is in the section, replace it to "#"
if ( [[array lastObject] isEqualToString:@"^"])
{
[array setObject:@"#" atIndexedSubscript:[array count]-1];
return array;
}
// If "#" is not in the section
return [self.frc sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title
atIndex:(NSInteger)index
{
if ([title isEqualToString:@"#"]) {
return [self.frc sectionForSectionIndexTitle:@"^" atIndex:index];
}
return [self.frc sectionForSectionIndexTitle:title atIndex:index];
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if ([[[self.frc sectionIndexTitles] objectAtIndex:section] isEqualToString:@"^"]) {
return @"#";
}
return [[self.frc sectionIndexTitles] objectAtIndex:section];
}