3

I want to add cells at the bottom of a UITableView, and scroll to make it fully visible (just like in Whatsapp when you send or receive a message).

I've tried doing this:

[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:[self.tableView numberOfRowsInSection:0]-1 inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];

But this makes a graphical glitch and the table flashes and make a bad scrolling instead of a smooth one.

Any help with this?

facumedica
  • 596
  • 5
  • 20
  • 2
    I'm not in a position to say with any certainty, but it's possible that WhatsApp actually scrolls to the top of the tableView (0, 0). It's a trick used by some messaging apps to rotate the tableView and then rotate each cell by 180 degrees. This gives the visual of scrolling down, but the programmatic view of scrolling up. – Avi Nov 03 '15 at 19:42
  • 1
    Change animated:YES to animated:NO – Subash Nov 03 '15 at 21:27
  • Subash's suggestion works. You can't have both calls animated. – Dave Feb 16 '17 at 00:40

4 Answers4

2

here what I have done is, inserted a row and then changed the content inset of the tableView

-(IBOutlet)sendButton:(id)sender
{
    [array addObject:textView.text];
    [self addAnotherRow];
}
- (void)addAnotherRow {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:(array.count - 1) inSection:0];
        [self.messagesTableView beginUpdates];
        [self.messagesTableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        [self.messagesTableView endUpdates];
        [self updateContentInsetForTableView:self.messagesTableView animated:YES];

        // TODO: if the scroll offset was at the bottom it can be scrolled down (allow user to scroll up and not override them)

        [self.messagesTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

- (void)updateContentInsetForTableView:(UITableView *)tableView animated:(BOOL)animated {
    NSUInteger lastRow = [self tableView:tableView numberOfRowsInSection:0];
    NSLog(@"last row: %lu", (unsigned long)lastRow);
    NSLog(@"items count: %lu", (unsigned long)array.count);

    NSUInteger lastIndex = lastRow > 0 ? lastRow - 1 : 0;

    NSIndexPath *lastIndexPath = [NSIndexPath indexPathForItem:lastIndex inSection:0];
    CGRect lastCellFrame = [self.messagesTableView rectForRowAtIndexPath:lastIndexPath];

    // top inset = table view height - top position of last cell - last cell height
    CGFloat topInset = MAX(CGRectGetHeight(self.messagesTableView.frame) - lastCellFrame.origin.y - CGRectGetHeight(lastCellFrame), 0);

    // What about this way? (Did not work when tested)
    // CGFloat topInset = MAX(CGRectGetHeight(self.tableView.frame) - self.tableView.contentSize.height, 0);

    NSLog(@"top inset: %f", topInset);

    UIEdgeInsets contentInset = tableView.contentInset;
    contentInset.top = topInset;
    NSLog(@"inset: %f, %f : %f, %f", contentInset.top, contentInset.bottom, contentInset.left, contentInset.right);

    NSLog(@"table height: %f", CGRectGetHeight(self.messagesTableView.frame));

    UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState;
    [UIView animateWithDuration:animated ? 0.25 : 0.0 delay:0.0 options:options animations:^{
        tableView.contentInset = contentInset;

    } completion:^(BOOL finished) {
    }];
}

Note: to view existing messages from array, you have to include this also.

- (void)viewDidLayoutSubviews {
    [self updateContentInsetForTableView:self.messagesTableView animated:NO];
}
Smit Shah
  • 209
  • 2
  • 9
0

example function to add an item:

- (IBAction)addItem:(UIBarButtonItem *)sender {
  [self.items addObject:[NSString stringWithFormat:@"Item %lu", self.items.count + 1]];

  NSIndexPath *indexPathToInsert = [NSIndexPath indexPathForRow:self.items.count - 1 inSection:0];
  [self.tableView insertRowsAtIndexPaths:@[indexPathToInsert] withRowAnimation:UITableViewRowAnimationAutomatic];
  [self.tableView scrollToRowAtIndexPath:indexPathToInsert atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

for me the animation looks perfectly fine this way!

André Slotta
  • 12,851
  • 1
  • 18
  • 30
0

Well, what I did is use [self.tableView scrollToRowAtIndexPath... but calling it from a 0.1s NSTimer.

facumedica
  • 596
  • 5
  • 20
0

Make insertions animated and after endUpdates scroll bottom not animating. Worked for me

Swift

self.tableView.beginUpdates()
self.tableView.insertRowsAtIndexPaths(newIndexPaths, withRowAnimation: UITableViewRowAnimation.Top)
self.tableView.endUpdates()
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: self.tableDataCount-1, inSection: 0), atScrollPosition: .Bottom, animated: false)
ergunkocak
  • 2,938
  • 1
  • 26
  • 27