В настоящее время я создаю приложение для ранжирования команд на основе различных критериев, и я хотел бы дать пользователям возможность сортировать команды в табличном представлении на основе этих различных критериев. Я использую основные данные в своем приложении для хранения своих команд и использую шаблон на основе навигации, который начинается с показа пользователю табличного представления с возможностью щелкать по каждой ячейке для редактирования команды. Примером того, что я хочу сделать, является то, как приложение Errands позволяет сортировать ваши задачи по сроку выполнения, приоритету, алфавиту и вручную.
Я пытался создать кнопки, которые вызывали метод для изменения, ключ sortDescriptor, но мне приходилось каждый раз пересоздавать fetchedResultsController для его обновления (т. Е. Не проверять, если fetchedResultsController_! = Nil), и это похоже на копирование ячейка, которая меняет позиции на новую, а не перемещает их туда.
Как мне следует кодировать кнопку сортировки, которая позволяет пользователю изменять критерии сортировки таблицы и корректно обновлять таблицу? Я прочитал много постов, касающихся fetchResultsController и NSSortDescriptors, но решения кажутся разовыми.
Я мог бы действительно использовать некоторую помощь или ссылку на учебник, который показывает, как это сделать.
Вот мой RootViewController.m:
#import "RootViewController.h"
#import "TeamViewController.h"
@interface RootViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
@end
@implementation RootViewController
@synthesize fetchedResultsController=fetchedResultsController_;
@synthesize managedObjectContext=managedObjectContext_;
@synthesize sortParam;
//@synthesize sortDescriptors;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
NSLog(@"viewDidLoad called");
sortParam = @"totalRank";
[super viewDidLoad];
self.title = @"Title";
// Set up the edit and add buttons.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(showTeamView)];
self.navigationItem.rightBarButtonItem = addButton;
[addButton release];
UIBarButtonItem* button1 = [[[UIBarButtonItem alloc] initWithTitle:@"Rank" style:UIBarButtonItemStyleBordered target:self action:@selector(reSort1:)] autorelease];
UIBarButtonItem* button2 = [[[UIBarButtonItem alloc] initWithTitle:@"Name" style:UIBarButtonItemStyleBordered target:self action:@selector(reSort2:)] autorelease];
[self setToolbarItems:[NSArray arrayWithObjects:button1, button2, nil]];
}
-(void)showTeamView {
NSLog(@"showTeamView called");
TeamViewController *teamViewController = [[TeamViewController alloc] initWithRootController:self team:nil];
[self presentModalViewController:teamViewController animated:YES];
[teamViewController release];
}
// Implement viewWillAppear: to do additional setup before the view is presented.
- (void)viewWillAppear:(BOOL)animated {
NSLog(@"viewWillAppear called");
// sortParam = @"totalRank";
[super viewWillAppear:animated];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
NSLog(@"configureCell:atIndexPath called");
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *textLabelText = [NSString stringWithFormat:@"%@ - %@", [[managedObject valueForKey:@"teamName"]description], [[managedObject valueForKey:@"teamNumber"]description]];
// NSLog(textLabelText);
cell.textLabel.text = textLabelText;//[[managedObject valueForKey:@"teamName"] description];
cell.detailTextLabel.text = [[managedObject valueForKey:@"totalRank"] description];
// cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
- (void)reSort1:(id)sender {
NSLog(@"reSort1 called by: %@", sender);
sortParam = @"totalRank";
[self.tableView reloadData];
[self viewDidLoad];
}
- (void)reSort2:(id)sender {
NSLog(@"resort2 called by %@", sender);
sortParam = @"teamName";
[self.tableView reloadData];
[self viewDidLoad];
}
#pragma mark -
#pragma mark Add a new object
-(void)insertTeamWithName:(NSString *)teamName teamNumber:(NSString *)teamNumber noteField:(NSString *)noteField driveTrain:(NSString *)driveTrain autoRank:(NSNumber *)autoRank offenseRank:(NSNumber *)offenseRank defenseRank:(NSNumber *)defenseRank speedRank:(NSNumber *)speedRank shooterRank:(NSNumber *)shooterRank totalRank:(NSNumber *)totalRank{
// Create a new instance of the entity
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// Configure the team
[newManagedObject setValue:teamName forKey:@"teamName"];
[newManagedObject setValue:teamNumber forKey:@"teamNumber"]; //new
[newManagedObject setValue:noteField forKey:@"noteField"];
[newManagedObject setValue:autoRank forKey:@"autoRank"];
[newManagedObject setValue:driveTrain forKey:@"driveTrain"];
[newManagedObject setValue:offenseRank forKey:@"offenseRank"];
[newManagedObject setValue:defenseRank forKey:@"defenseRank"];
[newManagedObject setValue:speedRank forKey:@"speedRank"];
[newManagedObject setValue:shooterRank forKey:@"shooterRank"];
/* [newManagedObject setValue:armRank forKey:@"armRank"];
[newManagedObject setValue:speedRank forKey:@"speedRank"]; */
[newManagedObject setValue:totalRank forKey:@"totalRank"];
[self saveContext];
NSLog(@"insertTeamWithNameCalled");
}
- (void)saveContext {
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
// Save the context.
NSError *error = nil;
if (![context save:&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 in saveContext %@, %@", error, [error userInfo]);
// abort();
}
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
// Prevent new objects being added when in editing mode.
[super setEditing:(BOOL)editing animated:(BOOL)animated];
self.navigationItem.rightBarButtonItem.enabled = !editing;
}
#pragma mark -
#pragma mark Table view data source
/*
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
}
*/
- (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];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"tableView:cellForRowAtIndexPath called");
static NSString *CellIdentifier = @"TableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object for the given index path
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
// Save the context.
[self saveContext];
}
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// The table view should not be re-orderable.
return NO;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *team = [[self fetchedResultsController] objectAtIndexPath:indexPath];
TeamViewController *teamViewController = [[TeamViewController alloc] initWithRootController:self team:team];
[self presentModalViewController:teamViewController animated:YES];
[teamViewController release];
}
#pragma mark -
#pragma mark Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController {
NSLog(@"fetchResultsController called");
/*
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:@"Team" 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:sortParam ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSLog(@"sortParam == %@", sortParam);
// 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:@"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![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 in fetchedResultsController %@, %@", error, [error userInfo]);
// abort();
}
return fetchedResultsController_;
}
#pragma mark -
#pragma mark Fetched results controller delegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
NSLog(@"controllerWillChangeContent called");
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
NSLog(@"controller:didChangeObject called");
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
NSLog(@"controllerDidChangeContent called");
[self.tableView endUpdates];
}
/*
// Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed.
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// In the simplest, most efficient, case, reload the table view.
[self.tableView reloadData];
}
*/
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc {
[fetchedResultsController_ release];
[managedObjectContext_ release];
// [sortParam release];
[super dealloc];
}
@end