Как запустить UITableView на последней ячейке? - PullRequest
25 голосов
/ 28 января 2010

В приложении «Сообщения Apple», когда вы щелкаете по имени корреспондента и переключаетесь на табличное представление беседы (с воздушными шарами для каждого сообщения), таблица прокручивается до конца. Никакой анимации или чего-то еще, это просто там.

Аналогично, в Tweetie 2, когда вы загружаете вид твитов, он появляется там, где вы в последний раз смотрели его. Нет анимации, чтобы попасть туда, она просто там, как будто ни одна из ячеек выше не была загружена.

Как эти приложения делают это? Они звонят scrollToRowAtIndexPath:atScrollPosition:animated: где-нибудь в контроллере таблицы? Если так, то как они узнают, что передать на atScrollPosition:? И в каком методе это называется?

Ответы [ 8 ]

57 голосов
/ 28 января 2010

scrollToRowAtIndexPath должно работать.

В viewWillAppear: попробуйте это:

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

rowNumberHere - это номер строки в источнике данных, к которому вы хотите перейти.

atScrollPosition - это только одно из значений в перечислении UITableViewScrollPosition, которое может определить, где на экране появится номер нужной вам строки. Однако, в зависимости от количества строк и того, к какой строке вы прокручиваете, это может не иметь значения.

Установка reloadData: позволяет избежать исключения, если данные еще не загружены в viewWillAppear:. Если вы введете scrollToRowAtIndexPath в viewDidAppear:, вам не понадобится reloadData:, но вы увидите, что стол немного подпрыгнет, что, как вы говорите, не нужно.

Edit: @ Теория, попробуйте изменить код следующим образом ...

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

Обратите внимание, numberOfRowsInSection возвращает количество строк, а не номер последней строки (то есть количество строк - 1).

9 голосов
/ 28 января 2010

Вы можете вызвать -scrollToRowAtIndexPath: atScrollPosition: animated в -viewWillAppear: метод вашего TableViewController.

atScrollPosition: позволяет указать, где вы хотите разместить ячейку для rowAtIndexPath. Есть четыре варианта:

UITableViewScrollPositionTop - помещает вашу ячейку прямо вверху экрана

UITableViewScrollPositionMiddle - центрирует вашу клетку в поле зрения

UITableViewScrollPositionBottom - ставит свою клетку внизу

UITableViewScrollPositionNone - Использование этого параметра позволит позиционировать ячейку в поле зрения пользователя с минимальной прокруткой / движением.

Поведение отличается в трех сценариях: -

Если ячейка уже видна, она ничего не делает.

Если ячейка находится выше текущего представления, она прокручивает ячейку до верхней части представления.

Если ячейка находится ниже текущего представления, она прокручивает ячейку до нижней части представления.

7 голосов
/ 17 сентября 2016

Ответ @DyingCactus в 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)
7 голосов
/ 31 января 2014

Я использую autolayout, и ни один из ответов не работал для меня. Вот мое решение, которое, наконец, сработало:

@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)];
    }
}
7 голосов
/ 29 января 2010

После ответа DyingCactus выше, я добавил этот метод в свой контроллер:

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

И теперь это работает, именно то, что я хотел. Спасибо!

4 голосов
/ 05 сентября 2016

Проблема с методом scrollToRowAtIndexPath заключается в его медленной работе, и tableView требуется время для прокрутки к нижней части.

У меня была точно такая же проблема, после попытки всего (так же, как вы), это сработало, ключ, если вы используете autolayout инициализировать scrollToBottom в true, а затем сделать это

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

выполнение этого гарантирует, что вы почти в нижней части таблицы, но не в самой нижней части, так как невозможно точно определить смещение дна, когда вы находитесь в верхней части таблицы, поэтому мы можем реализовать 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;
    }
}
0 голосов
/ 02 августа 2015
#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
0 голосов
/ 09 февраля 2012

Примечание для прокрутки до нижнего ряда, раздел должен быть последним разделом, а не 0 (первый раздел):

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];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...