【问题标题】:How to Start UITableView on the Last Cell?如何在最后一个单元格上启动 UITableView?
【发布时间】:2011-01-10 12:38:59
【问题描述】:

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

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

这些应用程序如何做到这一点?他们是否在表控制器的某个地方调用scrollToRowAtIndexPath:atScrollPosition:animated:?如果是这样,他们怎么知道将什么传递给atScrollPosition:?又是用什么方法调用的?

【问题讨论】:

    标签: iphone objective-c uitableview


    【解决方案1】:

    scrollToRowAtIndexPath 应该可以工作。

    viewWillAppear:,试试这个:

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

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

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

    如果数据尚未加载到 viewWillAppear: 中,则放入 reloadData: 可避免异常。如果您将scrollToRowAtIndexPath 放入viewDidAppear:,则不需要reloadData:,但您会看到表格稍微跳动,这是您说不想要的。

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

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

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

    【讨论】:

    • 谢谢,这似乎正是我需要知道的,除了我的实现使应用程序崩溃。 :-( 请参阅下面的“答案”。
    • 天啊!当然,我错过了我想要单元格索引号而不是行号。谢谢!我将更新我自己的答案以显示我的具体解决方案。
    • 您关于取决于行数和行位置的注释有时可能没有区别。请帮助了解在这种情况下可以做什么。
    • 将此代码放入viewWillAppear 时出现滚动问题,后来尝试调用insertRowsAtIndexPaths。即使有单元格被切断,表格视图也会拒绝滚动。我可以通过将代码放在 viewDidAppear 中来解决此问题。
    • 在 iOS 7 中的viewWillAppear 中,如果self.parentViewControllerUINavigationControllerself.view 仍然没有被调整大小。所以,我想出了这个solution: UITableView scroll to bottom after load
    【解决方案2】:

    Swift 3Swift 4 中@DyingCactus 的答案:

        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)
    

    【讨论】:

    • 这会导致跳跃。当 VC 最终出现在屏幕上时,表格视图才会滚动。我从viewWillAppear调用它
    【解决方案3】:

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

    atScrollPosition:允许您设置希望单元格出现在 rowAtIndexPath 的位置。有四种选择:

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

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

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

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

    三种情况下的行为不同:-

    如果单元格已经在视图中,它什么也不做。

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

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

    【讨论】:

    • 我们如何滚动到不可见的单元格?
    【解决方案4】:

    我正在使用自动布局,但没有一个答案对我有用。这是我最终奏效的解决方案:

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

    【讨论】:

    • 这导致我的tableview滚动到很远,所以我使用了[self.tableView setContentOffset:CGPointMake(0, self.tableView.contentSize.height - self.tableView.frame.size.height)];,并且要在视图加载后使用它,你需要确保所有单元格都被绘制,比如[self performSelector: withObject: afterDelay: 跨度>
    • 通过用计算出的高度替换 CGFLOAT_MAX 为我工作。也摆脱了烦人的视觉跳跃,没有我尝试过的所有其他解决方案可见的动画。
    【解决方案5】:

    根据上面 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];
    }
    

    现在它可以工作了,正是我想要的。谢谢!

    【讨论】:

    • 设置 NSIndexPath 的行看起来不正确。我会用我认为应该的方式编辑我的答案。
    • 已编辑以反映修复。愚蠢的我没有意识到我传递的是mumberOfRowsInSection 而不是索引。再次感谢!
    • 太棒了。您可能还想添加对零行的检查,并且在这种情况下不要调用滚动,除非您的 tableview 将始终至少有一行。
    • 今天早上洗澡的时候我也是这么想的。会的。
    【解决方案6】:

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

    我遇到了完全相同的问题,在尝试了一切(和你一样)之后,这很有效,关键是你是否使用了自动布局 将 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];
        } }
    

    这样做将确保您几乎处于 tableView 的底部,但可能不会处于最底部,因为当您处于 tableView 顶部时,不可能知道确切的底部偏移量,所以在那之后我们可以实现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;
        }
    }
    

    【讨论】:

      【解决方案7】:
      #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
      

      【讨论】:

      • 我的代码没有从表格的最后一个单元格中添加文本....就像聊天应用程序...请帮助我
      • 您还应该描述您的代码在做什么或为什么。
      • 我的代码从表格顶部添加文本,而不是在表格的最后一行滚动。
      • 当我使用 view willAppear 方法时,我的应用程序崩溃了。
      【解决方案8】:

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

      【讨论】:

      • func scrollToBot() { let lastSection = self.numberOfSections - 1 guard lastSection >= 0 else { return } let lastRow = self.numberOfRowsInSection(lastSection) - 1 guard lastRow >= 0 else { return }让 indexPath = NSIndexPath(forRow:lastRow, inSection: lastSection) self.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Top, animated:false) }
      猜你喜欢
      • 2012-02-22
      • 1970-01-01
      • 2012-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多