简体   繁体   中英

How to Start UITableView on the Last Cell?

In Apple's Messages app, when you click a correspondent's name and switch to the table view of the conversation (with balloons for each message), the table appears scrolled all the way to the end. No animation or anything, it's just there.

Similarly, in Tweetie 2, when you load the tweets view, it appears right where you last looked at it. No animation to get there, it's just there, as if none of the cells above were loaded.

How do these apps do this? Are they calling scrollToRowAtIndexPath:atScrollPosition:animated: somewhere in the table controller? If so, how do they know what to pass to atScrollPosition: ? And in what method is it called?

scrollToRowAtIndexPath should work.

In viewWillAppear: , try this:

[theTableView reloadData];    
NSIndexPath* ip = [NSIndexPath indexPathForRow:rowNumberHere inSection:sectionNumberHere];
[theTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO];

rowNumberHere is the row number in the data source you want to scroll to.

atScrollPosition is just one of the values in the UITableViewScrollPosition enum which can determine where on the screen the row number you want will show up. However, depending on the number of rows and which row you are scrolling to, it may not make a difference.

Putting reloadData: avoids an exception if the data is not loaded yet in viewWillAppear: . If you put the scrollToRowAtIndexPath in viewDidAppear: , you would not need the reloadData: but you will see the table jump a little which you say you don't want.

Edit: @Theory, try changing your code as follows...

[tableView reloadData];
int lastRowNumber = [tableView numberOfRowsInSection:0] - 1;
NSIndexPath* ip = [NSIndexPath indexPathForRow:lastRowNumber inSection:0];
[tableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO];

Please note numberOfRowsInSection returns row count, not the last row number (which is row count - 1).

You can call -scrollToRowAtIndexPath:atScrollPosition:animated within the -viewWillAppear: method of your TableViewController.

atScrollPosition: allows you to set where you want your cell for rowAtIndexPath to appear. There are four options:

UITableViewScrollPositionTop - puts your cell right at the top of the view

UITableViewScrollPositionMiddle - centers your cell in the view

UITableViewScrollPositionBottom - puts your cell at the bottom

UITableViewScrollPositionNone - Using this setting will position in the cell in user view with minimum scrolling/movement.

The behavior is different in three scenarios :-

If the cell is already in view, it does nothing.

If the cell is above the current view, it scrolls the cell to the top of the view.

If the cell is beneath the current view, it scrolls the cell to the bottom of the view.

Answer of @DyingCactus in Swift 3 & Swift 4 :

    let lastRow: Int = self.tableView.numberOfRows(inSection: 0) - 1
    let indexPath = IndexPath(row: lastRow, section: 0);
    self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)

I'm using autolayout and none of the answers worked for me. Here is my solution that finally worked:

@property (nonatomic, assign) BOOL shouldScrollToLastRow;


- (void)viewDidLoad
{
    [super viewDidLoad];

    _shouldScrollToLastRow = YES;
}


- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    // Scroll table view to the last row
    if (_shouldScrollToLastRow)
    {
        _shouldScrollToLastRow = NO;
        [self.tableView setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
    }
}

Following DyingCactus's reply above, I added this method to my controller:

-(void)viewWillAppear:(BOOL)animated {
      [self.tableView reloadData];    
      NSIndexPath* ip = [NSIndexPath indexPathForRow:[self.tableView numberOfRowsInSection:0] - 1 inSection:0];
      [self.tableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO];
}

And now it works, exactly what I wanted. Thanks!

The issue with scrollToRowAtIndexPath method is its slow and the tableView takes time to scroll to the bottom.

i had the exact same problem, after trying everything(same as you), this worked, the key is if you're using autolayout initialize scrollToBottom to true and then do this

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    // Scroll table view to the last row
    [self scrollToBottom];
}

-(void)scrollToBottom {
    if (shouldScrollToLastRow)
    {
        CGPoint bottomOffset = CGPointMake(0, self.tableView.contentSize.height - self.tableView.bounds.size.height);
        [self.tableView setContentOffset:bottomOffset animated:NO];
    } }

doing this will ensure you're almost at the bottom of you're tableView but might not be at the very bottom as its impossible to know the exact bottom offset when you're at the top of the tableView, so after that we can implement scrollViewDidScroll

-(void)scrollViewDidScroll: (UIScrollView*)scrollView
{
    float scrollViewHeight = scrollView.frame.size.height;
    float scrollContentSizeHeight = scrollView.contentSize.height;
    float scrollOffset = scrollView.contentOffset.y;

    // if you're not at bottom then scroll to bottom
    if (!(scrollOffset + scrollViewHeight == scrollContentSizeHeight))
    {
        [self scrollToBottom];
    } else {
    // bottom reached now stop scrolling
        shouldScrollToLastRow = false;
    }
}
#import "ViewController.h"


@interface ViewController ()
@end

@implementation ViewController
CGFloat labelWidth = 260.0f;
CGFloat labelRequiredHeight = 180.0f;
@synthesize tblView;
@synthesize txtField;
@synthesize chatData;

- (void)viewDidLoad
{
    [super viewDidLoad];
    tblView.delegate = self;

    [self.tblView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
    chatData  = [[NSMutableArray alloc] init];
    [self registerForKeyboardNotifications];

}

-(IBAction) textFieldDoneEditing : (id) sender
{
    NSLog(@"the text content%@",txtField.text);
    [sender resignFirstResponder];
    [txtField resignFirstResponder];
}

- (IBAction)sendButton:(id)sender
{
    if (txtField.text.length>0) {
        // updating the table immediately
        NSArray *data = [NSArray arrayWithObject:@"text"];
        NSArray *objects = [NSArray arrayWithObject:txtField.text];
        NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects forKeys:data];
        [chatData addObject:dictionary];

        NSMutableArray *insertIndexPaths = [[NSMutableArray alloc] init];
        NSIndexPath *newPath = [NSIndexPath indexPathForRow:0 inSection:0];
        [insertIndexPaths addObject:newPath];
        [tblView beginUpdates];
        [tblView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationTop];
        [tblView endUpdates];
        [tblView reloadData];

        txtField.text = @"";
        [self.view endEditing:YES];
    }
}

-(IBAction) backgroundTap:(id) sender
{
    [self.txtField resignFirstResponder];
}

-(BOOL)SendbtnShouldReturn:(UITextField *)textfield
{
    [textfield resignFirstResponder];
    return YES;
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    NSLog(@"the text content%@",txtField.text);
    [textField resignFirstResponder];
    if (txtField.text.length>0)
    {
        // updating the table immediately
        NSArray *keys = [NSArray arrayWithObject:@"text"];
        NSArray *objects = [NSArray arrayWithObject:txtField.text];
        NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
        [chatData addObject:dictionary];

        NSMutableArray *insertIndexPaths = [[NSMutableArray alloc] init];
        NSIndexPath *newPath = [NSIndexPath indexPathForRow:0 inSection:0];
        [insertIndexPaths addObject:newPath];
        [tblView beginUpdates];
        [tblView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationTop];
        [tblView endUpdates];
        [tblView reloadData];
        txtField.text = @"";
    }
    return NO;
}


// Keyboard Functionality

-(void) registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

-(void) freeKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

-(void) keyboardWasShown:(NSNotification*)aNotification
{
    NSLog(@"Keyboard was shown");
    NSDictionary* info = [aNotification userInfo];
    // Get animation info from userInfo
    NSTimeInterval animationDuration;
    UIViewAnimationCurve animationCurve;
    CGRect keyboardFrame;
    [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
    [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
    [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardFrame];
    // Move
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:animationDuration];
    [UIView setAnimationCurve:animationCurve];
    NSLog(@"frame..%f..%f..%f..%f",self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height);
    NSLog(@"keyboard..%f..%f..%f..%f",keyboardFrame.origin.x, keyboardFrame.origin.y, keyboardFrame.size.width, keyboardFrame.size.height);
    [self.view setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y- keyboardFrame.size.height, self.view.frame.size.width, self.view.frame.size.height)];
    [tblView setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y+ keyboardFrame.size.height, self.view.frame.size.width, self.view.frame.size.height-keyboardFrame.size.height)];
    [tblView scrollsToTop];
    [UIView commitAnimations];

}

-(void) keyboardWillHide:(NSNotification*)aNotification
{
    NSLog(@"Keyboard will hide");
    NSDictionary* info = [aNotification userInfo];
    // Get animation info from userInfo
    NSTimeInterval animationDuration;
    UIViewAnimationCurve animationCurve;
    CGRect keyboardFrame;
    [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
    [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
    [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardFrame];
    // Move
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:animationDuration];
    [UIView setAnimationCurve:animationCurve];
    [self.view setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y + keyboardFrame.size.height, self.view.frame.size.width, self.view.frame.size.height)];
    [tblView setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height)];
    [UIView commitAnimations];
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    self.tblView.contentInset = contentInsets;
    self.tblView.scrollIndicatorInsets = contentInsets;
    self.tblView.scrollEnabled=chatData;


}

#pragma mark UITableViewDataSource protocol methods
- (void)scrollTableToBottom
{
    int rowNumber = [self.tblView numberOfRowsInSection:1];
    if (rowNumber > 0) [self.tblView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rowNumber-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [chatData count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier=@"chatCell";
    chatCell *cell = (chatCell *)[tableView dequeueReusableCellWithIdentifier: @"chatCellIdentifier"];
    if(!cell)
        cell =[[chatCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
//    NSUInteger row = [chatData count]-[indexPath row]-1;
    NSUInteger row=[indexPath row];
    NSUInteger count = [chatData count];
    if (row <chatData.count)
    {
        NSString *chatText = [[chatData objectAtIndex:row] objectForKey:@"text"];
        cell.txtMsg.text = chatText;
    }
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellText = [[chatData objectAtIndex:chatData.count-indexPath.row-1] objectForKey:@"text"];
    UIFont *cellFont = [UIFont fontWithName:@"Helvetica" size:20.0];
    CGSize constraintSize = CGSizeMake(225.0f, MAXFLOAT);
    CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
        return labelSize.height + 40;
}


//-(void)scrollToBottomTableView
//{
//    if (self.tblView.contentOffset.y > self.tblView.frame.size.height)
//    {
//        [self.tblView scrollToRowAtIndexPath:[self. indexPathForLastMessage]
//                              atScrollPosition:UITableViewScrollPositionBottom animated:YES];
//    }
//}


-(void)viewWillAppear:(BOOL)animated
{

//        [tblView reloadData];
//   
//    int lastRowNumber = [tblView numberOfRowsInSection:0] - 1;
//    NSIndexPath* ip = [NSIndexPath indexPathForRow:lastRowNumber inSection:0];
//    [tblView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
-(void)viewDidAppear:(BOOL)animated
{
//[tblView reloadData];

}
- (void)reloadTableViewDataSource
{
    [tblView reloadData];

}

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

Note for scrolling to the bottom row, the section needs to be last section not 0 (first section):

int lastSection = [self.myTableView numberOfSections] -1;
if (lastSection < 0) return;

int lastRow = [self.myTableView numberOfRowsInSection:lastSection] - 1;
if (lastRow < 0) return;  
NSIndexPath* ip = [NSIndexPath indexPathForRow:lastRow inSection:lastSection];

 [self.myTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:YES];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM