为什么多个NSSortDescriptors不能与NSFetchedResultsController一起使用

时间:2012-08-07 19:03:04

标签: objective-c ios nsfetchedresultscontroller nssortdescriptor

我有一个带UITableView的控制器,它显示一个“Item”对象列表,这些项目就像支票簿条目。除了排序之外,一切似乎都在正常工作。我将在下面描述我正在寻找的排序,但目前记录按创建顺序显示。 Item对象(NSManagedObjects)具有包括date(NSDate),amount(NSDecimalNumber)和entry(Entry是与Item的关系的NSManaged对象)的属性。

在应用程序中,您创建一个类型为(NSNumber 0 == credit,1 = debit),title,description等的条目。然后添加一个或多个具有金额,日期等的Item对象。

我希望在表格视图部分中按日期对项目进行分组,以便共享相同日期的所有项目都在同一部分中。在每个部分中,他们应该首先按类型(信用优先然后借记),然后按金额递减。在某个部分中有这样的东西:

2012年7月3日

  • 信用:900
  • 借记:(25)
  • 借记:(15)

2012年8月7日

  • 信用:1000
  • 信用:900
  • 信用:100
  • 信用证:25
  • 借记:(700)
  • 借记:(450)
  • 借记:(25)

ViewController本身是一个UITableViewDelegate,UITableViewDataSource和NSFetchedResultsControllerDelegate。

这是NSFetchedResultsController getter方法:

- (NSFetchedResultsController *)aFetchedResultsController {

    if (_aFetchedResultsController != nil) {
        return _aFetchedResultsController;
    }

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(date >= %@) AND (date <= %@)", startDate, endDate];
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:self.context];
    [fetchRequest setEntity:entity];
    [fetchRequest setPredicate:predicate];

    NSSortDescriptor *sortDate = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];
    NSSortDescriptor *sortType = [[NSSortDescriptor alloc] initWithKey:@"entry.type" ascending:YES];
    NSSortDescriptor *sortAmount = [[NSSortDescriptor alloc] initWithKey:@"amount" ascending:NO];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDate, sortType, sortAmount, nil]];


    [fetchRequest setFetchBatchSize:20];

    NSFetchedResultsController *theFetchedResultsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                        managedObjectContext:self.context sectionNameKeyPath:@"day"
                                                   cacheName:@"Root"];
    self.aFetchedResultsController = theFetchedResultsController;
    _aFetchedResultsController.delegate = self;

    return _aFetchedResultsController;

}//end 

查看已加载:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.

    NSDate *today = [NSDate new];
    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];

    [offsetComponents setMonth:-1]; //1 month ago
    startDate = [gregorian dateByAddingComponents:offsetComponents toDate:today options:0];

    [offsetComponents setMonth:1]; //1 month ahead
    endDate = [gregorian dateByAddingComponents:offsetComponents toDate:today options:0];



    //set the MOC
    self.context = [((AppDelegate *)[[UIApplication sharedApplication] delegate]) managedObjectContext];

    [self fetchRecords];

}//end viewDidLoad

实际获取记录的方法:

-(void)fetchRecords{

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(date >= %@) AND (date <= %@)", startDate, endDate];
    [[self.aFetchedResultsController fetchRequest] setPredicate:predicate];
    [NSFetchedResultsController deleteCacheWithName:@"Root"];

    NSError *error;
    if (![[self aFetchedResultsController] performFetch:&error]) {

        // Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail

    }//end if


    [_tableView reloadData];

}//end fetchRecords

“day”是一个Item实例方法,我用它来让UITableView节标题显示格式化的日期字符串:

- (NSString *)day {
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    dateFormatter.dateStyle = NSDateFormatterFullStyle;

    return [dateFormatter stringFromDate:self.date];

}//end day

2 个答案:

答案 0 :(得分:0)

来自initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:

的文档
  

sectionNameKeyPath

     

...如果此键路径与第一个排序指定的路径不同   在fetchRequest中的描述符,它们必须生成相同的相对   排序

您的代码中不是这种情况。将日期转换为字符串不会保留顺序。

Apple Developer Library中有示例代码DateSectionTitles,演示如何正确执行此操作。

答案 1 :(得分:0)

因此事实证明上面的代码完美无缺,并且排序工作原理确实没有错误。我的“day”函数模糊了这个问题,该函数根据项目的“date”属性的NSDateFormatterFullStyle表示形成UITableView部分。然而,现实情况是,时间部分的日期都不同,这解释了为什么按照创建顺序对它们进行排序。

我在尝试了Martin R关于使用sectionNameKeyPath:@"date"的评论并在其自己的部分中看到每一行后发现了这一点。

因此我的问题的解决方案是只设置“date”属性设置的月,日和年,这对我的应用程序来说可能是唯一的,因为这些是我关心的日期的唯一部分关于,而不是时间。

要做到这一点,我使用以下代码覆盖了我的NSManagedObject“Item”中的“date”的setter:

- (void)setDate:(NSDate *)date
{

    NSDateComponents *components = [[NSCalendar currentCalendar] 
                                    components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit 
                                    fromDate:date];
    NSInteger day = [components day];
    NSInteger month = [components month];
    NSInteger year = [components year];

    NSDateComponents *comps = [[NSDateComponents alloc] init];

    [comps setDay:day];
    [comps setMonth:month];
    [comps setYear:year];

    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

    NSDate *newDate = [gregorian dateFromComponents:comps];

    [self willChangeValueForKey:@"date"];
    [self setPrimitiveValue:newDate forKey:@"date"];
    [self didChangeValueForKey:@"date"];

}//end setDate