質問

I've got a strange animation error that I think is based on my "moveUp" and "moveDown" methods. My editing code is based on THIS GITHUB PROJECT, so that's where the methods originated.

The editing is successful, however, after a very little bit of playing around, it begins to act very strangely. Cells are duplicated, some cells persist in edit mode after the Done button is press even though the others change, then very strangely if you try to move the cells that are still in edit they just hover over the non editable cells. It is all very confusing. I don't know if this is an issue in my moveRowAtIndexPath method or my setup of the actually movement in the custom methods. I've included an example of it's behavior below. (Sorry the background of the app is white, so it looks like the code is pushed down.)

enter image description here

UITableViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    NSError *error = nil;

    if (![[self fetchedResultsController]performFetch:&error]) {
        NSLog(@"Error! %@", error);
        abort();
    }
}

- (IBAction)unwindToMainViewController:(UIStoryboardSegue *)unwindSegue {

}

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (NSUInteger)countEvents
{
    NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
    NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
    NSFetchRequest *request = [[NSFetchRequest alloc] init] ;
    [request setEntity: entity];
    NSError *error = nil;
    NSUInteger count = [context countForFetchRequest:request error:&error];
    if (count == NSNotFound) {
        count = 0;
    }
    return count;
}

- (void)insertNewObject:(id)sender
{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Top" inManagedObjectContext:context];

    [fetchRequest setEntity:entity];

    NSUInteger orderIdx = 1 + [self countEvents];

    NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:@"Top" inManagedObjectContext:context];

    // If appropriate, configure the new managed object.
    // Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
    [newManagedObject setValue:[NSNumber numberWithInteger:orderIdx] forKey:@"orderIdx"];

    // 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.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
}

-(NSManagedObjectContext*)managedObjectContext {

    return [(TTAppDelegate*)[[UIApplication sharedApplication]delegate]managedObjectContext];

}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(UITableViewCell*)sender {

    if ([[segue identifier]isEqualToString:@"addTop"]) {

        UINavigationController *navigationController = segue.destinationViewController;

        AddTopViewController *addTopViewController = (AddTopViewController*) navigationController.topViewController;

        Top *addTop = [NSEntityDescription insertNewObjectForEntityForName:@"Top" inManagedObjectContext:self.managedObjectContext];

        addTopViewController.addTop = addTop;

    }

    if ([[segue identifier]isEqualToString:@"showList"]) {
        TopDetailTableViewController *topDetailTableViewController = [segue destinationViewController];

        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];

        Top *selectedTop = (Top *)[self.fetchedResultsController objectAtIndexPath:indexPath];

        topDetailTableViewController.selectedTop = selectedTop;

        UITableViewCell *selectedCell = (UITableViewCell *)sender;
        topDetailTableViewController.title = selectedCell.textLabel.text;
    }

}


-(void) viewWillAppear:(BOOL)animated {

    [self.tableView reloadData];

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return [[self.fetchedResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
    return [sectionInfo numberOfObjects];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    // Configure the cell...

    Top *top = [self.fetchedResultsController objectAtIndexPath:indexPath];

    if (!cell) {
        cell = [[UITableViewCell alloc] init];
        cell.showsReorderControl = YES;
    }

    cell.textLabel.text = top.topName;
    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
        [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];

        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.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}

- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

- (void)adjustOrderIdxForRow:(NSInteger)row inSection:(NSInteger)section by:(NSInteger)adjustment
{
    NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:section];
    Top *top = [[self fetchedResultsController] objectAtIndexPath:path];
    top.orderIdx = [NSNumber numberWithInteger:[top.orderIdx integerValue] + adjustment];
}

- (void)moveUp:(NSIndexPath*)fromIndexPath to:(NSIndexPath*)toIndexPath
{
    NSInteger section = fromIndexPath.section;

    Top *objectMoved = [[self fetchedResultsController] objectAtIndexPath:fromIndexPath];
    Top *firstObject = [[self fetchedResultsController] objectAtIndexPath:toIndexPath];
    NSNumber *firstOrderIdx = firstObject.orderIdx;
    for (NSInteger i = toIndexPath.row; i <= fromIndexPath.row - 1; i++) {
        [self adjustOrderIdxForRow:i inSection:section by:-1];
    }
    objectMoved.orderIdx = firstOrderIdx;
}

- (void)moveDown:(NSIndexPath*)fromIndexPath to:(NSIndexPath*)toIndexPath
{
    NSInteger section = fromIndexPath.section;

    Top *objectMoved = [[self fetchedResultsController] objectAtIndexPath:fromIndexPath];
    Top *lastObject = [[self fetchedResultsController] objectAtIndexPath:toIndexPath];
    NSNumber *lastOrderIdx = lastObject.orderIdx;
    for (NSInteger i = fromIndexPath.row + 1; i <= toIndexPath.row; i++) {
        [self adjustOrderIdxForRow:i inSection:section by:1];
    }
    objectMoved.orderIdx = lastOrderIdx;
}

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
    BOOL moveDown = toIndexPath.row > fromIndexPath.row;
    moveDown ? [self moveDown:fromIndexPath to:toIndexPath] : [self moveUp:fromIndexPath to:toIndexPath];
    NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
    NSError *error = nil;
    if (![context save:&error]) {
        NSLog(@"Unresolved error when swapping objects order: %@, %@", error, [error userInfo]);
        abort();
    }
}

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    Top *event = [self.fetchedResultsController objectAtIndexPath:indexPath];
    NSLog(@"Path: %@, orderIdx: %@", indexPath, event.orderIdx);
}

#pragma mark - Fetched results controller

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Top" 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:@"orderIdx" ascending:NO];
    NSArray *sortDescriptors = @[sortDescriptor];

    [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.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return _fetchedResultsController;
}

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [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
{
    UITableView *tableView = self.tableView;

    switch(type) {
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView endUpdates];
}
役に立ちましたか?

解決 2

I managed to get my desired result by simply adding

[self.tableView reloadData];

to the end of my custom adjustOrderIdxForRow method, as well as, again in the

-(void)controllerDidChangeContent

method. To be completely honest I do not know if this is appropriate, or what type repercussions I might face down the rode because of it. But, the app is behaving appropriately now. Below are both methods in full. Any criticism is welcome, again, I don't know that this is appropriate, it just gave me my desired result.

- (void)adjustOrderIdxForRow:(NSInteger)row inSection:(NSInteger)section by:(NSInteger)adjustment
{
    NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:section];

    Top *top = [[self fetchedResultsController] objectAtIndexPath:path];

    top.orderIdx = [NSNumber numberWithInteger:[top.orderIdx integerValue] + adjustment];

    [self.tableView reloadData];
}

and

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView endUpdates];

    [self.tableView reloadData];
}

他のヒント

in tableView:cellForRowAtIndexPath: add the following line of code

cell.clipsToBounds = YES;

Also when you start dragging the cell do this

[self.tableView bringSubViewToFront:cell];

To avoid any changes to the table view while dragging the cell

[self.tableView beginUpdated];

// Drag and Drop Cell

// Update data set as needed

[self.tableView endUpdates];
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top