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.

  • Are there rate limits on silent push notifications in iOS 7?
  • Capture groups not working in NSRegularExpression
  • iOS/Swift UIImageView (.jpg) not recognizing my tap gesture?
  • Specify images for iPad in Xcode like @2x for iPhone 4
  • How do I save a gif to my photo album?
  • Drawing in a background thread on iOS
  • - (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];
        }
    }
    

    5 Solutions Collect From Internet About “UITableView section footer view position after endUpdates”

    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

    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….

    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.

    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];
    }
    

    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…