В Cocoa Touch NSFetchedResultsController отвечает не только за извлечение, но и за сортировку данных.
- (NSFetchedResultsController *)fetchedResultsController { if (__fetchedResultsController != nil) { return __fetchedResultsController; } /* Set up the fetched results controller. */ // Create the fetch request for the entity. NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; // Edit the entity name as appropriate. NSEntityDescription *entity = [NSEntityDescription entityForName:@”Customer” inManagedObjectContext:self.managedObjectContext]; [fetchRequest setEntity:entity]; // Set the batch size to a suitable number. [fetchRequest setFetchBatchSize:20]; // Edit the sort key as appropriate. NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”name” ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; [fetchRequest setSortDescriptors:sortDescriptors]; // Edit the section name key path and cache name if appropriate. // nil for section name key path means “no sections”. NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@”Master”]; aFetchedResultsController.delegate = self; self.fetchedResultsController = aFetchedResultsController; NSError *error = nil; if (![self.fetchedResultsController performFetch:&error]) { /* Replace this implementation with code to handle the error appropriately. abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button. */ NSLog(@”Unresolved error %@, %@”, error, [error userInfo]); abort(); } return __fetchedResultsController; }
Один из вариантов сортировать данные, это добавить атрибут displayOrder и сортировать по нему.
В Core Data отношения обычно представляются множествами (NSSet), которые являются не упорядоченными структурами данных. Есть упорядоченные множества NSOrderedSet, но этот класс не является наследником NSSet.
Чтобы сделать строку перемещаемой надо сделать две вещи:
Тут есть 4 аспекта:
Когда вы создаете наследника NSManagedObject, вы получаете класс, который автоматически загружает данные из вашего persistent store. Вы можете получать доступ к свойствам класса напрямую используя KVC.
Директива @dynamic заменяет @synthesize для Core Data свойств. @synthesize автоматически создает акцессоры (геттеры и сеттеры). @dynamic автоматически создает такие методы как addJobs и removeJobs, которые объявлены в заголовочном файле.
Стандартный путь для управления перемещаемыми строками это создать NSMutableArray массив элементы которого тоже можно перемещать. Во view-контроллере надо создать такое свойство:
Надо добавить @synthesize директиву для этого свойства. Во viewDidLoad надо переместить объекты полученные из Core Data persistent store в muttable-массив. Пример перемещения top-level объектов в mutable-массив:
Перемещение связанных объектов в mutable-массив:
Нужно поддерживать mutable-массив обновленным, когда происходит добавление (insertNewObject) или удаление элементов (tableView:commitEditingStyle). Далее вместо того чтобы получать выбранный объект из результатов извлечения контроллером в tableView:didSelectRowAtIndexPath: или configureCell:atIndexPath:, берите его из mutable-массива:
Управление перемещениями:
Метод setEditing вызывается когда пользователь нажимает кнопку edit-done в UIViewController. Тут можно сделать сохранение перемещенных данных:
В Core Data отношения обычно представляются множествами (NSSet), которые являются не упорядоченными структурами данных. Есть упорядоченные множества NSOrderedSet, но этот класс не является наследником NSSet.
Чтобы сделать строку перемещаемой надо сделать две вещи:
- явно сделать строку перемещаемой;
- создать метод для управления перемещениями.
Можно устанавливать свойства отдельно для каждой строки, а можно сделать все строки перемещаемыми. Также можно решить будут ли строки перемещаться между секциями.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath: (NSIndexPath *)indexPath { // The table view should not be re-orderable. return NO; } - (void)tableView:(UITableView *)tableView moveRowAtIndexPath: (NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { }
С этими изменениями при нажатии на Edit можно будет переместить строку:
Тут есть 4 аспекта:
- Создайте наследника NSManagedObject для сущности из которых состоит список перемещаемых элементов.
- Получить множество объектов из persistent store и положите их в muttable-массив.
- При перемещении объектов, обновляйте значения атрибута displayOrder.
- Сохраните объекты muttable-массива в persistent store.
Когда вы создаете наследника NSManagedObject, вы получаете класс, который автоматически загружает данные из вашего persistent store. Вы можете получать доступ к свойствам класса напрямую используя KVC.
#import <Foundation/Foundation.h> #import <CoreData/CoreData.h> @interface Customer : NSManagedObject @property (nonatomic, retain) NSString * address; @property (nonatomic, retain) NSString * city; @property (nonatomic, retain) NSNumber * customerSince; @property (nonatomic, retain) NSNumber * displayOrder; @property (nonatomic, retain) NSString * email; @property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSString * state; @property (nonatomic, retain) NSString * zip; @property (nonatomic, retain) NSSet *jobs; @end @interface Customer (CoreDataGeneratedAccessors) - (void)addJobsObject:(NSManagedObject *)value; - (void)removeJobsObject:(NSManagedObject *)value; - (void)addJobs:(NSSet *)values; - (void)removeJobs:(NSSet *)values; @end
@implementation Customer @dynamic address; @dynamic city; @dynamic customerSince; @dynamic displayOrder; @dynamic email; @dynamic name; @dynamic state; @dynamic zip; @dynamic jobs; @end
Директива @dynamic заменяет @synthesize для Core Data свойств. @synthesize автоматически создает акцессоры (геттеры и сеттеры). @dynamic автоматически создает такие методы как addJobs и removeJobs, которые объявлены в заголовочном файле.
Стандартный путь для управления перемещаемыми строками это создать NSMutableArray массив элементы которого тоже можно перемещать. Во view-контроллере надо создать такое свойство:
@property (nonatomic, retain) NSMutableArray *customers;
Надо добавить @synthesize директиву для этого свойства. Во viewDidLoad надо переместить объекты полученные из Core Data persistent store в muttable-массив. Пример перемещения top-level объектов в mutable-массив:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // Set up the edit and add buttons. self.navigationItem.leftBarButtonItem = self.editButtonItem; UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject)]; self.navigationItem.rightBarButtonItem = addButton; // THIS IS THE CODE TO LOAD THE MUTABLE ARRAY NSMutableArray *sortedElements = [[NSMutableArray alloc] initWithArray:self.fetchedResultsController.fetchedObjects]; NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”displayOrder” ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; [sortedElements sortUsingDescriptors:sortDescriptors]; self.customers = sortedElements; [self.tableView reloadData]; }
Перемещение связанных объектов в mutable-массив:
- ( void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”displayOrder” ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:&sort Descriptor count:1]; Customer* myCustomer = (Customer*)self.detailItem; NSMutableArray *sortedElements = [[NSMutableArray alloc] initWithArray:[myCustomer.jobs allObjects]]; [sortedElements sortUsingDescriptors:sortDescriptors]; self.jobs = sortedJobs; [self configureView]; }
Нужно поддерживать mutable-массив обновленным, когда происходит добавление (insertNewObject) или удаление элементов (tableView:commitEditingStyle). Далее вместо того чтобы получать выбранный объект из результатов извлечения контроллером в tableView:didSelectRowAtIndexPath: или configureCell:atIndexPath:, берите его из mutable-массива:
NSManagedObject *object = [self.customers objectAtIndex:indexPath.row];
Управление перемещениями:
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *) fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { Customer *customer = [self.customers objectAtIndex:fromIndexPath.row]; [customers removeObjectAtIndex:fromIndexPath.row]; [customers insertObject:customer atIndex:toIndexPath.row]; NSInteger start = fromIndexPath.row; if (toIndexPath.row < start) { start = toIndexPath.row; } NSInteger end = toIndexPath.row; if (fromIndexPath.row > end) { end = fromIndexPath.row; } for (NSInteger i = start; i <= end; i++) { customer = [self.customers objectAtIndex:i]; customer.displayOrder = [NSNumber numberWithInteger: i]; } }
Метод setEditing вызывается когда пользователь нажимает кнопку edit-done в UIViewController. Тут можно сделать сохранение перемещенных данных:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated { [super setEditing:editing animated:animated]; [self.tableView beginUpdates]; [self.tableView endUpdates]; /* If editing is finished, save the managed object context. */ if (!editing) { NSManagedObjectContext *context = self.managedObjectContext; NSError *error = nil; if (![context save:&error]) { /* Replace this implementation with code to handle the error appropriately. */ NSLog(@”Unresolved error %@, %@”, error, [error userInfo]); abort(); } } }