Ответ 1
Damien
Вы должны просто использовать NSFetchRequest
форму массива упорядоченного набора. Он будет работать нормально. Контроллеру нужен атрибут для сортировки. Следовательно, вам также нужно указать это.
Эндрю
У меня возникла проблема (если честно), с NSFetchedResultsController и новыми отношениями NSOrderedSet, доступными в iOS 5.
У меня есть следующая модель данных (хорошо, мой реальный не ящик и носок!), но это служит в качестве простого примера:
Ящик и носок - это NSManagedObjects в модели/хранилище основных данных. В Drawer
отношение socks
- отношение упорядочено к-многим к Sock
. Идея состоит в том, что носки находятся в ящике в определенном порядке. В Sock
отношение Drawer
является обратным отношению socks
.
В UIViewController я рисую UITableView на основе этих объектов. Я кормлю таблицу, используя NSFetchedResultsController
.
- (NSFetchedResultsController *)fetchedResultsController1 {
if (_fetchedResultsController1 != nil) {
return _fetchedResultsController1;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Sock" inManagedObjectContext:[NSManagedObjectContext MR_defaultContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"drawer.socks" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
self.fetchedResultsController1 = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[NSManagedObjectContext MR_defaultContext] sectionNameKeyPath:nil cacheName:@"SocksCache"];
self.fetchedResultsController1.delegate = self;
return _fetchedResultsController1;
}
Когда я запускаю это, я получаю следующее сообщение об ошибке: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'to-many key not allowed here'
Это имеет смысл для меня, поскольку отношение < <29 > , а не одна сущность для сравнения для целей сортировки.
Я хочу достичь, чтобы socks
появился в UITableView
в порядке, указанном в отношении socks
. Я действительно не хочу иметь порядок сортировки, но NSFetchedResultsController
, который является отличным компонентом, настаивает на том, что он должен быть одним. Как я могу сказать, что он использует порядок носков на объекте Ящика. Я не хочу, чтобы в таблице отображались объекты Drawer.
Примечание. Я использую это только в приложении iOS5, поэтому доступны упорядоченные отношения.
Любой, кто может предложить мне какое-либо направление, будет очень благодарен. Спасибо за ваше время.
Изменить: Таким образом, представление таблицы, отображающее носки, делает это только для одного ящика. Я просто хочу, чтобы представление таблицы соблюдало порядок, в котором содержатся отношения с носками. Я не уверен, что установить критерии сортировки, чтобы убедиться, что это происходит.
Damien
Вы должны просто использовать NSFetchRequest
форму массива упорядоченного набора. Он будет работать нормально. Контроллеру нужен атрибут для сортировки. Следовательно, вам также нужно указать это.
Эндрю
Вы можете присвоить носителю индекс и присвоить им носки:
sock01.index = [sock01.drawer.socks indexOfObject:sock01];
Насколько я понимаю эту функциональность, она позволяет упорядочить Sock
в каждом Drawer
. Поскольку Apple пишет в документации:
Вы должны использовать их только в том случае, если отношение имеет внутренний порядок, который имеет решающее значение для собственного представления - например, шаги в рецепте.
Это означает, что вы не можете получить все Sock
с помощью отсортированного отношения. Сортировка Sock
будет доступна только для каждого объекта Drawer
.
Я нашел эту нить, ища ответ на точный вопрос, заданный OP.
Я никогда не встречал примеров представления таких данных в tableView
без добавления дополнительного поля сортировки. Конечно, добавление поля сортировки в значительной степени устраняет любую выгоду при использовании упорядоченных отношений. Поэтому, учитывая, что я получил эту работу, я подумал, что это может быть полезно другим людям с тем же вопросом, если я разместил здесь свой код. Это оказалось довольно простым (гораздо проще, чем использование дополнительного поля сортировки) и, по-видимому, с хорошей производительностью. То, что некоторые люди, возможно, не осознали (включая меня, изначально), состоит в том, что тип (NSOrderedSet
) атрибута отношения "упорядоченный ко многим" имеет метод получения objectAtIndex
и что NSMUtableOrderedSet
имеет методы для вставки и удаления objectAtIndex
.
Я избегал использования NSFetchedResultsController
, как предлагали некоторые плакаты. Я не использовал массив, никакого дополнительного атрибута для сортировки и никакого предиката. Мой код имеет дело с tableView
, в котором есть один объект маршрутизации и многопозиционные объекты, причем itinerary.places
является полем "упорядоченные ко многим". Я включил редактирование/переупорядочение, но не удаляет ячейки. Метод moveRowAtIndexPath
показывает, как я обновил базу данных с переупорядочением, хотя для хорошей инкапсуляции я, вероятно, должен переместить переупорядочение базы данных в файл категории для объекта, управляемого объектом. Здесь весь TableViewController.m
:
//
// ItineraryTVC.m
// Vacations
//
// Created by Peter Polash on 8/31/12.
// Copyright (c) 2012 Peter Polash. All rights reserved.
//
#import "ItineraryTVC.h"
#import "AppDelegate.h"
#import "Place+PlaceCat.h"
#import "PhotosInVacationPlaceTVC.h"
@interface ItineraryTVC ()
@end
@implementation ItineraryTVC
#define DBG_ITIN YES
@synthesize itinerary ;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem ;
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated] ;
UIManagedDocument *doc = UIAppDelegate.vacationDoc;
[doc.managedObjectContext performBlock:^
{ // do this in the context thread (should be the same as the main thread, but this made it work)
// get the single itinerary for this document
self.itinerary = [Itinerary setupItinerary: doc ] ;
[self.tableView reloadData] ;
}];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown );
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [self.itinerary.places count ];
}
- (UITableViewCell *) tableView: (UITableView *) tableView
cellForRowAtIndexPath: (NSIndexPath *) indexPath
{
static NSString *CellIdentifier = @"Itinerary Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier ];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: CellIdentifier];
}
Place *place = [self.itinerary.places objectAtIndex:indexPath.row];
cell.textLabel.text = place.name;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%d photos", [place.photos count]];
return cell;
}
#pragma mark - Table view delegate
- (BOOL) tableView: (UITableView *) tableView
canMoveRowAtIndexPath:( NSIndexPath *) indexPath
{
return YES;
}
-(BOOL) tableView: (UITableView *) tableView
canEditRowAtIndexPath: (NSIndexPath *) indexPath
{
return YES ;
}
-(void) tableView: (UITableView *) tableView
moveRowAtIndexPath: (NSIndexPath *) sourceIndexPath
toIndexPath: (NSIndexPath *) destinationIndexPath
{
UIManagedDocument * doc = UIAppDelegate.vacationDoc ;
[doc.managedObjectContext performBlock:^
{ // perform in the context thread
// itinerary.places is the "ordered, to-many" relationship attribitute pointing to all places in itinerary
NSMutableOrderedSet * places = [ self.itinerary.places mutableCopy ] ;
Place *place = [ places objectAtIndex: sourceIndexPath.row] ;
[places removeObjectAtIndex: sourceIndexPath.row ] ;
[places insertObject: place atIndex: destinationIndexPath.row ] ;
self.itinerary.places = places ;
[doc saveToURL: doc.fileURL forSaveOperation: UIDocumentSaveForOverwriting completionHandler: ^(BOOL success) {
if ( !success ) NSLog(@"Error saving file after reorder, startPos=%d, endPos=%d", sourceIndexPath.row, destinationIndexPath.row) ;
}];
}];
}
- (UITableViewCellEditingStyle) tableView: (UITableView *) tableView
editingStyleForRowAtIndexPath: (NSIndexPath *) indexPath
{
return ( UITableViewCellEditingStyleNone ) ;
}
- (void) prepareForSegue:(UIStoryboardSegue *) segue sender: (id) sender
{
NSIndexPath *indexPath = [self.tableView indexPathForCell: sender] ;
PhotosInVacationPlaceTVC * photosInVacationPlaceTVC = segue.destinationViewController ;
Place *place = [self.itinerary.places objectAtIndex:indexPath.row ];
photosInVacationPlaceTVC.vacationPlace = place ;
photosInVacationPlaceTVC.navigationItem.title = place.name ;
UIBarButtonItem *backButton =
[[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
}
@end
Как я только что ответил здесь, я предпочитаю просто добавлять новое свойство в свой NSManagedObject через категорию.
Просто добавьте метод:
- (NSUInteger)indexInDrawerSocks
{
NSUInteger index = [self.drawer.socks indexOfObject:self];
return index;
}
Затем в вашем NSFetchedResultsController используйте дескриптор сортировки:
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"indexInDrawerSocks" ascending:YES]];
Чтобы добавить некоторую ясность в ответ от adonoho (спасибо mate), который помог мне разобраться - вместо того, чтобы указывать отношения "многие" как любой сортировочный ключ, который я также не мог получить, укажите свойство -взаимодействие в предикате, чтобы выбрать, какие объекты вы хотите в выбранном контроллере результатов, и указать отношение "принадлежность" как ключ сортировки.
Это удовлетворяет основную цель получения результатов в NSFetchedResultsController со всей этой добротой и уважает порядок во многих отношениях.
В этом конкретном примере (выборка носков ящиком):
// socks belong-to a single drawer, sort by drawer specified sock order
fetchRequest.sortDescriptors = @[[[NSSortDescriptor alloc] initWithKey:@"drawer" ascending:YES]];
// drawer has-many socks, select the socks in the given drawer
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"drawer = %@", drawer];
По существу, это использует заданный ящик, чтобы указать, какие носки должны войти в NSFetchedResultsController, и порядок, определенный отношением to-many.
Глядя на сгенерированный SQL (аннотированный из моего примера с использованием разных имен сущностей) с помощью отладки SQL ядра SQL:
CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, ...fields..., t0.ZDRAWER, t0.Z_FOK_DRAWER FROM ZSOCKS t0 WHERE t0.ZDRAWER = ? ORDER BY t0.Z_FOK_DRAWER
вы можете увидеть упорядочение SQL, используя столбец Z_FOK_DRAWER, который использует Core Data для позиции этого носка.