t0a0
t0a0

Reputation: 688

UITableView section footer view position after endUpdates

On ios8 I'm using core data table view controller, and after deleting rows my section footer view suddenly goes all the way down to the bottom of the UITableView. When I scroll the table view, footer view goes back to its place. How can fix this and why is this happening?

Here is the code just in case.

- (void)controller:(NSFetchedResultsController *)controller
   didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath
     forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath
{       
    if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
    {
        switch(type)
        {
            case NSFetchedResultsChangeInsert:
                [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeDelete:
                [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
                break;

            case NSFetchedResultsChangeUpdate:
                [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeMove:
                [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;
        }
    }
}

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

Upvotes: 18

Views: 5114

Answers (5)

Bonan
Bonan

Reputation: 719

I still need the section header and footer view to be sticky. So that instead of changing the tableview style to Grouped, I simply did a reloadSection after deleting the rows...

Upvotes: 0

KevinM
KevinM

Reputation: 1807

I've ran into the same problem. None of the answers so far gave me exactly what I was looking for. Setting the table view to 'grouped' wasn't the behavior I wanted and the other solutions still resulted in the footer briefly being at the bottom of the table view until reloadData was called, which them abruptly snapped it back into the correct position.

Below is my solution that smoothly animates it to where it should be.

NSIndexPath* iPath = [NSIndexPath indexPathForRow:_lineItemArray.count-1 inSection:0];

// Calculate the Y position of the bottom of the footer within the table view frame.
CGFloat footerBaseY = (_footer.y + _footer.height) - _sTableView.contentOffset.y;
// Calculate the Y position of the bottom taking into account the bottom content inset.
CGFloat tableViewContentBottomY = _sTableView.height - _sTableView.contentInset.bottom;

if (footerBaseY < tableViewContentBottomY) {
    [_sTableView insertRowsAtIndexPaths:@[iPath] withRowAnimation:UITableViewRowAnimationBottom];
    [UIView animateWithDuration:0.3
                     animations:^{
                         CGFloat y = _lineItemArray.count * [_sTableView rectForRowAtIndexPath:iPath].size.height;
                         CGFloat newBaseY = (y + _footer.height) - _sTableView.contentOffset.y;

                         if (newBaseY < tableViewContentBottomY) {
                             _footer.y = y;
                         } else {
                             // The footer is within a cell's height away from the bottom.  Subtract the diff from the new y.
                             _footer.y = y - (newBaseY - tableViewContentBottomY);
                         }
                     } completion:^(BOOL finished) {
                         // This ensures that the bottom row animates up if the footer was within a cell's height away from the bottom.
                         [_sTableView scrollToRowAtIndexPath:iPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
                     }];
} else {
    // The footer was already at the bottom of the vew so I'm animating the added row up.
    [_sTableView insertRowsAtIndexPaths:@[iPath] withRowAnimation:UITableViewRowAnimationBottom];
    [_sTableView scrollToRowAtIndexPath:iPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

Upvotes: 0

RoLife
RoLife

Reputation: 529

I ran into the same problem and spent a lot of effort to attempt to address it. And now, finally!

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {

        NATask *task = sections[indexPath.section][indexPath.row];
        [sections[indexPath.section] removeObjectAtIndex:indexPath.row];
        [appDelegate.managedObjectContext deleteObject:task];

        [CATransaction begin];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

        UIView *footerView = [tableView footerViewForSection:indexPath.section];
        CABasicAnimation *animation = [[footerView.layer animationForKey:@"position"] mutableCopy];
        CGPoint fromValue = [(NSValue *)animation.fromValue CGPointValue];
        animation.toValue = [NSValue valueWithCGPoint:CGPointMake(0, fromValue.y - 44)];
        [footerView.layer removeAnimationForKey:@"position"];
        [footerView.layer addAnimation:animation forKey:@"position"];
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, .4 * NSEC_PER_SEC);
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [tableView reloadData];
        });

        [CATransaction commit];
    }
}

Of course, this is not most ideal solution, but it works! I'm almost two weeks wrestled with this problem, I hope, at least, is useful to someone.

Upvotes: 1

Blake Carrier
Blake Carrier

Reputation: 111

This is an iOS 8 animation change. You must change your tableview style to 'grouped' in order to keep the section footer from moving to the bootom of the uitableview

Upvotes: 11

nihilvex
nihilvex

Reputation: 231

I just came across the same problem. When I use insertRowsAtIndexPaths.. the footer goes to the bottom. Also noticed that if you call reloadData on the table view it will fix the problem so I did this:

[CATransaction begin];
[CATransaction setCompletionBlock: ^{
    // Code to be executed upon completion
    [tableView reloadData];
}];

[tableView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationLeft];
[CATransaction commit]; 

All it does is reload the table when the insert animation finishes... It's not a real solution, more like avoiding the problem but it hides the issue until someone explains why does it happen and how to fix it....

Upvotes: 3

Related Questions