NSFetchedResultsController与UILocalizedIndexedCollation
我正在尝试使用具有混合语言数据的FRC,并希望有一个段索引。
从文档看来,您应该能够覆盖FRC
- (NSString *)sectionIndexTitleForSectionName:(NSString *)sectionName - (NSArray *)sectionIndexTitles
然后使用UILocalizedIndexedCollation来获得本地化的索引和部分。 但可悲的是,这是行不通的,不是什么打算使用:(
有没有人能够使用UILocalizedIndexedCollation FRC或我们被迫使用示例UITableView + UILocalizedIndexedCollation示例中提到的手动sorting方法(示例代码包括我得到这个工作)。
使用以下属性
@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协议来通知更新。 似乎没有什么好办法使这两个对象很好地协同工作。
既然你不能sorting瞬态属性,我实现的解决scheme是…
-
为Core Data模型中的每个实体内的每个可sorting属性创build一个名为“sectionKey”的string属性。 sectionKey属性将是从基本属性(例如名称或标题属性)派生的计算值。 它必须被持久化,因为(当前)暂存属性不能在sorting描述符中用于获取请求。 对每个将要提供sorting的sectionKey和base属性启用索引。 为了将此更新应用到现有的应用程序,您将需要执行轻量级的迁移,并且还包括一个例程以更新预先存在的数据库。
-
如果您正在种子数据(例如,用一组标准数据填充新的安装,或者为每种目标语言创build本地化的SQLite数据库,其中一个将在初始启动时被复制),在该代码中计算并更新实体的sectionKey属性。 关于播种数据的“最佳”方法的观点各不相同,但是值得注意的是,每种语言的less量plist文件(通常范围从几个字节到20k,甚至包括几百个值的列表)将会离开每种语言的个体SQLite数据库的总占用空间要小得多(每个语言大约20K)。 在附注中,Microsoft Excel for Mac可以configuration为通过启用语言function来提供对列表的本地化sorting(3)。
-
在获取的结果控制器构造函数中,对sectionKey和base属性进行sorting,并为段名称关键path传递sectionKey。
-
添加计算逻辑以更新所有添加或编辑用户input中的sectionKey属性,例如,在textFieldDidEndEditing:中。
而已! 没有将获取的对象手动分区到数组数组中。 NSFetchedResultsController将为您执行本地化的sorting规则。 例如,在汉语(简体)的情况下,所提取的对象将通过语音发音(4)进行索引。
(1)来自Apple IOS开发者库>国际化编程主题> 国际化和本地化 。 (2) TableViewSuite的 3_SimpleIndexedTableView。 (3) 如何在Microsoft Office for Mac中启用中文语言function 。 (4)汉语通常按笔画数或拼音发音sorting。
布伦特,我的解决scheme是基于FRC,我从获取部分指定一个瞬态属性在我的模型对象返回对象的部分名称。 我只在属性getter的实现中使用UIlocalizedIndexedCollation,然后我依靠表视图控制器上的FRC实现。 当然,我使用localizedCaseInsensitiveCompare作为sortingselect器的fetch。
- (NSString *)sectionInitial { NSInteger idx = [[UILocalizedIndexedCollation currentCollation] sectionForObject:self collationStringSelector:@selector(localeName)]; NSString *collRet = [[[UILocalizedIndexedCollation currentCollation] sectionTitles] objectAtIndex:idx]; return collRet; }
我唯一的缺点是我不能在最后的#部分,因为我不改变数据库的sorting。 其他一切运作良好。
面对同样的问题最近使我searchnetworking(stackoverflow第一)为适当的解决scheme,使NSFetchedResultsController(FRC)和UILocalizedIndexedCollation(LIC)一起工作。 大多数发现解决scheme都不足以满足所有要求。 重要的是要提到,我们不能使用LIC来按照需要的方式对获取的对象进行sorting,我们将会有巨大的性能损失,FRC也不会充分利用。
所以,这是一般的问题:
1)我们有一些数据库,我们想要使用列表(UITableView)中的索引(类似于Contacts.app)来获取和显示某种数据。 我们需要传递对象值键,FRC可以做出sorting决定。
2)即使我们将Core Data模型中的特殊字段添加到FRC的段索引标题中,我们也不会达到预期的结果,但FRC只给出find的索引,而不是完整的字母表。 除此之外,我们将面临不正确的索引显示问题(不太确定为什么这样,也许在FRC中的一些错误)。 例如,在俄文字母的情况下,将会有完全空白或“奇怪”的符号($,?,',…)。
3)如果我们尝试使用LIC来显示漂亮的本地化索引,我们将面临在FRC中映射基于数据的部分以在LIC中完成本地化的字母“部分”的问题。
4)在我们决定使用LIC并以某种方式解决问题之后3)我们会注意到LIC会将“#”部分放置在底部(即最高部分索引),而FRC会把“#” – 放置在顶部(即最低部分索引 – 0)。 所以会有完整的部分位移。
把所有这一切都计算在内,我决定在没有任何大的“黑客行为”的情况下“欺骗”FRC,而是按照我需要的方式对数据进行sorting(将所有对象从“#”移到像列表底部一样)。
这是我来到的解决scheme:
我将扩展方法添加到我的NSManagedObject实例,以准备sorting名称,我们将在sorting描述符和部分关键path中使用FRC设置。 除了那些将在下面描述的之外,不需要特别的移动。
问题4)是由FRC的sortingalgorithm(低级SQL)引起的,可以稍微修改:只应用更多依赖于数据的sorting描述符,谓词并使用不能解决问题的固定预定义比较器。
我注意到FRC决定“#”符号低于任何与“#”最高的LIC相对的字母符号。
FRC的逻辑非常简单,因为UTF-8中的“#”符号是U + 0023。 拉丁大写字母“A”是U + 0041,所以23 <41.为了使FRC将“#”类对象放在最高索引段,我们需要传递最高的UTF-8符号。 为了这个来源http://www.utf8-chartable.de/unicode-utf8-table.pl UTF-8符号是U + 1000FF(</s>)。 当然,这个标志在现实生活中几乎是不可能发生的。 让我们使用U + 100000清晰。
sorting名称更新方法看起来像这样:
#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]; }
而已。 到目前为止效果很好。 也许它会为你工作。
我发现一个简单的方法来解决这个问题!
只需将核心数据中的“#”replace为“^”,以便tableview的部分将为“AZ ^”。 虽然“#”的unicode小于“A”,但“^”却恰恰相反。 所以你预测'^'在你的部分后面跟着Z并不难。
然后,你应该replace你的结果控制器的部分。 只是通过这几行代码:
- (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]; }