objective-c 如何在最后一个单元格上启动 UITableView?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2156614/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 22:50:58  来源:igfitidea点击:

How to Start UITableView on the Last Cell?

iphoneobjective-cuitableview

提问by theory

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.

在 Apple 的 Messages 应用程序中,当您单击通讯员的姓名并切换到对话的表格视图(每条消息都有气球)时,表格会一直滚动到最后。没有动画或任何东西,它就在那里。

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.

同样,在 Tweetie 2 中,当您加载推文视图时,它就会出现在您上次查看它的位置。没有动画到达那里,它就在那里,好像上面的单元格都没有被加载。

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:atScrollPosition:animated:在表控制器的某个地方打电话吗?如果是这样,他们怎么知道要传递给atScrollPosition:什么?它以什么方法调用?

回答by

scrollToRowAtIndexPathshould work.

scrollToRowAtIndexPath应该管用。

In viewWillAppear:, try this:

viewWillAppear:,试试这个:

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

rowNumberHereis the row number in the data source you want to scroll to.

rowNumberHere是要滚动到的数据源中的行号。

atScrollPositionis just one of the values in the UITableViewScrollPositionenum 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.

atScrollPosition只是UITableViewScrollPosition枚举中的值之一,它可以确定您想要的行号将显示在屏幕上的哪个位置。但是,根据行数和滚动到的行,它可能没有区别。

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

reloadData:避免了异常,如果数据没有在尚未加载viewWillAppear:。如果你把scrollToRowAtIndexPathviewDidAppear:,你不会需要的reloadData:,但你会看到表跳,你说你不想一点点。

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

编辑:@Theory,尝试按如下方式更改您的代码...

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

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

请注意numberOfRowsInSection返回行数,而不是最后一行数(即行数 - 1)。

回答by Gent Berani

Answer of @DyingCactus in Swift 3& Swift 4:

@DyingCactus 在Swift 3Swift 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)

回答by Deepak Danduprolu

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

您可以在 TableViewController 的 -viewWillAppear: 方法中调用 -scrollToRowAtIndexPath:atScrollPosition:animated。

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

atScrollPosition:允许您设置您希望单元格出现的位置。有四个选项:

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

UITableViewScrollPositionTop- 将您的单元格放在视图的顶部

UITableViewScrollPositionMiddle- centers your cell in the view

UITableViewScrollPositionMiddle- 在视图中居中你的单元格

UITableViewScrollPositionBottom- puts your cell at the bottom

UITableViewScrollPositionBottom- 将您的单元格放在底部

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

UITableViewScrollPositionNone- 使用此设置将在用户视图中以最小滚动/移动定位在单元格中。

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.

如果单元格在当前视图下方,它会将单元格滚动到视图底部。

回答by theory

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

按照上面 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];
}

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

现在它起作用了,正是我想要的。谢谢!

回答by RaffAl

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

回答by Ankur Teotia

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

scrollToRowAtIndexPath 方法的问题是它很慢,并且 tableView 需要时间滚动到底部。

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

我遇到了完全相同的问题,在尝试了所有内容(与您相同)之后,这奏效了,关键是如果您使用自动布局将 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];
    } }

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

这样做将确保您几乎处于 tableView 的底部,但可能不在最底部,因为当您位于 tableView 顶部时,不可能知道确切的底部偏移量,因此在那之后我们可以实现滚动视图DidScroll

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

回答by jeetendra

#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

回答by Gamma-Point

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

注意滚动到底行,该部分需要是最后一部分而不是 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];