从 NSFetchedResultsController 索引结果
我在使用 NSFetchedResults 控制器的混合、索引、可搜索结果集时遇到一些问题。我将其设置为存储实体的索引 AZ 首字母缩写,然后希望它显示数字首字母缩写(即 # 正如 UILocalizedIndexCollation 所做的那样)。
我已经编写了代码,如果全名以数字开头,则将 Artist 对象的“firstInitial”属性保存为 NSString @"#",并且我似乎已经使用自定义的排序描述符在 UITableViewController 中使用了一半的代码。问题是它只能在我退出/重新启动应用程序之前有效。
此时,获取结果中的 # 部分将显示在顶部。它将一直保留在那里,直到我强制进行数据更改(添加/删除托管对象),然后搜索条目并清除搜索(使用 searchDisplayController)。此时,部分重新排序将启动,# 部分将移至底部...
我显然错过了一些东西/已经盯着相同的代码太久了。另外,还有一种更简单的方法,我不知道/在谷歌上找不到!
任何帮助将不胜感激!
谢谢
Sean
我的 UITableViewController 的相关代码如下。
- (void)viewDidLoad
{
// ----------------------------------
// Various other view set up things in here....
// ...
// ...
// ----------------------------------
NSError *error;
if (![[self artistResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(@"Failed to fetch artists: %@, %@", error, [error userInfo]);
exit(-1); // Fail
}
}
- (NSFetchedResultsController *)artistResultsController {
if (_artistResultsController != nil) {
return _artistResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"Artist" inManagedObjectContext:_context];
[fetchRequest setEntity:entity];
NSSortDescriptor *initialSort = [[NSSortDescriptor alloc]
initWithKey:@"firstInitial"
ascending:YES
comparator:^(id obj1, id obj2) {
// Various number conditions for comparison - if it's a # initial, then it's a number
if (![obj1 isEqualToString:@"#"] && [obj2 isEqualToString:@"#"]) return NSOrderedAscending;
else if ([obj1 isEqualToString:@"#"] && ![obj2 isEqualToString:@"#"]) return NSOrderedDescending;
if ([obj1 isEqualToString:@"#"] && [obj2 isEqualToString:@"#"]) return NSOrderedSame;
// Else it's a string - compare it by localized region
return [obj1 localizedCaseInsensitiveCompare:obj2];
}];
NSSortDescriptor *nameSort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:initialSort, nameSort, nil]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_context
sectionNameKeyPath:@"firstInitial"
cacheName:nil];
self.artistResultsController = theFetchedResultsController;
_artistResultsController.delegate = self;
[nameSort release];
[initialSort release];
[fetchRequest release];
[_artistResultsController release];
return _artistResultsController;}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return nil;
} else {
return [[[_artistResultsController sections] objectAtIndex:section] name];
}
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return nil;
} else {
return [[NSArray arrayWithObject:UITableViewIndexSearch] arrayByAddingObjectsFromArray:
[[UILocalizedIndexedCollation currentCollation] sectionIndexTitles]];
}
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return 0;
} else {
if (title == UITableViewIndexSearch) {
[tableView scrollRectToVisible:self.searchDisplayController.searchBar.frame animated:NO];
return -1;
}
else {
for (int i = [[_artistResultsController sections] count] -1; i >=0; i--) {
NSComparisonResult cr =
[title localizedCaseInsensitiveCompare:
[[[_artistResultsController sections] objectAtIndex:i] indexTitle]];
if (cr == NSOrderedSame || cr == NSOrderedDescending) {
return i;
}
}
return 0;
}
}
}
编辑:忘记提及 - 我的搜索过滤器在 fetchedResults 控制器上使用谓词,因此这会导致新的获取请求,如下所示
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
NSFetchRequest *aRequest = [_artistResultsController fetchRequest];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", searchText];
// set predicate to the request
[aRequest setPredicate:predicate];
// save changes
NSError *error = nil;
if (![_artistResultsController performFetch:&error]) {
NSLog(@"Failed to filter artists: %@, %@", error, [error userInfo]);
abort();
}
}
I'm having some issues with a mixed, indexed, searchable results set from an NSFetchedResults controller. I have it set up to store an indexed A-Z first initial for an entity, and then want it to display numeric first initials (i.e. # as the UILocalizedIndexCollation would do).
I have already written the code that saves a "firstInitial" attribute of an Artist object as NSString @"#" if the full name started with a number, and I seem to have gotten the code half working in my UITableViewController with a customised sort descriptor. The problem is that it only works until I quit/relaunch the app.
At this point, the # section from the fetched results appears at the top. It will stay there until I force a data change (add/remove a managed object) and then search for an entry, and clear the search (using a searchDisplayController). At this point the section re-ordering will kick in and the # section will be moved to the bottom...
I'm obviously missing something/have been staring at the same code for too long. Alternatively, there's a much easier way of doing it which I'm not aware of/can't find on Google!
Any help would be appreciated!
Thanks
Sean
The relevant code from my UITableViewController is below.
- (void)viewDidLoad
{
// ----------------------------------
// Various other view set up things in here....
// ...
// ...
// ----------------------------------
NSError *error;
if (![[self artistResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(@"Failed to fetch artists: %@, %@", error, [error userInfo]);
exit(-1); // Fail
}
}
- (NSFetchedResultsController *)artistResultsController {
if (_artistResultsController != nil) {
return _artistResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"Artist" inManagedObjectContext:_context];
[fetchRequest setEntity:entity];
NSSortDescriptor *initialSort = [[NSSortDescriptor alloc]
initWithKey:@"firstInitial"
ascending:YES
comparator:^(id obj1, id obj2) {
// Various number conditions for comparison - if it's a # initial, then it's a number
if (![obj1 isEqualToString:@"#"] && [obj2 isEqualToString:@"#"]) return NSOrderedAscending;
else if ([obj1 isEqualToString:@"#"] && ![obj2 isEqualToString:@"#"]) return NSOrderedDescending;
if ([obj1 isEqualToString:@"#"] && [obj2 isEqualToString:@"#"]) return NSOrderedSame;
// Else it's a string - compare it by localized region
return [obj1 localizedCaseInsensitiveCompare:obj2];
}];
NSSortDescriptor *nameSort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:initialSort, nameSort, nil]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_context
sectionNameKeyPath:@"firstInitial"
cacheName:nil];
self.artistResultsController = theFetchedResultsController;
_artistResultsController.delegate = self;
[nameSort release];
[initialSort release];
[fetchRequest release];
[_artistResultsController release];
return _artistResultsController;}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return nil;
} else {
return [[[_artistResultsController sections] objectAtIndex:section] name];
}
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return nil;
} else {
return [[NSArray arrayWithObject:UITableViewIndexSearch] arrayByAddingObjectsFromArray:
[[UILocalizedIndexedCollation currentCollation] sectionIndexTitles]];
}
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return 0;
} else {
if (title == UITableViewIndexSearch) {
[tableView scrollRectToVisible:self.searchDisplayController.searchBar.frame animated:NO];
return -1;
}
else {
for (int i = [[_artistResultsController sections] count] -1; i >=0; i--) {
NSComparisonResult cr =
[title localizedCaseInsensitiveCompare:
[[[_artistResultsController sections] objectAtIndex:i] indexTitle]];
if (cr == NSOrderedSame || cr == NSOrderedDescending) {
return i;
}
}
return 0;
}
}
}
EDIT: Forgot to mention - my search filter is using a predicate on the fetchedResults controller, so this causes a new fetch request, like so
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
NSFetchRequest *aRequest = [_artistResultsController fetchRequest];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", searchText];
// set predicate to the request
[aRequest setPredicate:predicate];
// save changes
NSError *error = nil;
if (![_artistResultsController performFetch:&error]) {
NSLog(@"Failed to filter artists: %@, %@", error, [error userInfo]);
abort();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我最终以不同的方式解决了这个问题。
当您还使用 CoreData 和 SQLite 作为后端存储时,SortDescriptors 似乎在运行自定义排序时存在问题。我尝试了一些事情; NSString 类别使用新的比较方法、上面列出的比较块,并多次刷新表以尝试强制使用排序标准进行更新。
最后,我无法强制排序描述符进行初始排序,因此我更改了实现。我将名字以数字开头的艺术家的firstInitial 属性设置为“zzzz”。这意味着 CoreData 将立即正确排序(数字最后)。
完成此操作后,我对 titleForHeaderInSection 方法进行硬编码,以在适当的情况下返回标题 #,如下所示:
本质上,这意味着它将数字排序到“zzzz”分组中,该分组应该是最后一个,我只是忽略该标题并说标题改为#。
不确定是否有更好的方法来做到这一点,但它将所有排序保留在 CoreData 中,从长远来看,这可能更高效/可扩展。
I ended up going about fixing this a different way.
SortDescriptors seem to have issues with running a custom sort when you are also using CoreData with SQLite for your backend storage. I tried a few things; NSString categories with a new comparison method, the compare block as listed above, and refreshing the table multiple times to try and force an update with the sort criterion.
In the end, I couldn't force the sort descriptor to do an initial sort, so I changed the implementation. I set the firstInitial attribute for artists whose names began with numerics to 'zzzz'. This means that CoreData will sort this correctly (numerics last) off the bat.
After doing this, I then hardcoded my titleForHeaderInSection method to return # for the title if appropriate, as below:
Essentially this means it's sorting numbers into a 'zzzz' grouping, which should be last, and I'm just ignoring that title and saying the title is # instead.
Not sure if there's a better way to do this, but it keeps all of the sorting inside CoreData, which is probably more efficient/scalable in the long run.