【问题标题】:Scroll immediately to row in table before view shows在视图显示之前立即滚动到表格中的行
【发布时间】:2024-05-21 07:30:01
【问题描述】:

带有表格的视图被推送到屏幕上,我希望它在屏幕实际显示之前滚动到表格中的某一行。我在最终视图控制器中使用此代码。

NSIndexPath *scrollToPath = [NSIndexPath indexPathForRow:5 inSection:0]; 
[theTable scrollToRowAtIndexPath:scrollToPath atScrollPosition:UITableViewScrollPositionTop animated:NO];

当我把它放在 viewDiDAppear 方法中时,它会从表格的初始位置(顶部)短暂闪烁到我想要的行。我不希望它显示初始位置。如果我将它放在 viewDidLoad 或 viewWillAppear 中,那么它会因 NSRangeException 而崩溃,可能是因为该表尚未设置。

如何让它滚动而不显示初始位置?

【问题讨论】:

  • Thisthis 可能会有所帮助。

标签: iphone objective-c cocoa-touch uitableview scroll


【解决方案1】:

感谢 Shaggy 和 Dying Cactus 为我指明了正确的方向。答案是加载表格并在 viewWillAppear 中滚动:

-(void)viewWillAppear:(BOOL)animated
{
    [theTable reloadData];
    NSIndexPath *scrollToPath = [NSIndexPath indexPathForRow:5 inSection:0]; 
    [theTable scrollToRowAtIndexPath:scrollToPath atScrollPosition:UITableViewScrollPositionTop animated:NO];   
}

【讨论】:

  • 请务必将您自己的答案标记为已接受,以便阅读此问题的其他人可以一目了然:)
  • 这非常适合我的需要。唯一不同的是我使用了 UITableViewScrollPositionNone 而不是 ScrollPositionTop,这样用户就可以尽可能多地看到所选行上方的表格。
  • 这对我不起作用,因为在较新版本的 iOS(5 和 6)上,直到 viewWillAppear 之后,我才看到操作系统设置帧。 viewWillLayoutSubviews/viewDidLayoutSubviews 在 vi​​ewDidAppear 之后才会被调用。
  • 不幸的是,此解决方案在 iOS 11 中不起作用。在表 reloadData 调用之前,我尝试在视图控制器视图上调用 layoutIfNeeded,但它仍然存在问题。滚动位置为 .middle 时,滚动不足。
【解决方案2】:

我遇到了完全相同的问题,在尝试了一切之后,这很有效,关键是如果您使用的是 autolayout ,您必须在 viewDidLayoutSubviews 中编写 scrollToBottom 代码

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

【讨论】:

    【解决方案3】:

    viewWillAppear: 中调用scrollToRowAtIndexPath: 对我不起作用,因为尚未加载tableView。但是我可以在延迟一段时间后调用scrollToRowAtIndexPath: 来解决这个问题。

    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
    
        [self performSelector:@selector(scrollToCell) withObject:nil afterDelay:0.1];
    }
    
    - (void) scrollToCell
    {
        [_tableView reloadData];
        NSIndexPath *scrollToPath = [NSIndexPath indexPathForRow:5 inSection:0];
        [_tableView scrollToRowAtIndexPath:scrollToPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }
    

    希望对某人有所帮助。

    【讨论】:

    • 此答案效果更好,但您也可以使用 dispatch_after。
    • 在 iOS 8 上尝试了很多东西,只有这个有效。但也适用于主线程上的dispatch_async
    • 延迟为 0 也可以正常工作,而且看起来比 100 毫秒要好,在推桌子后 100 毫秒你仍然会看到它跳跃
    • 0 延迟后调度对我有用。这不是一个干净的解决方案,但不幸的是它是唯一可靠的解决方案!
    【解决方案4】:

    如果我把它放在 viewDidLoad 或 viewWillAppear 然后它崩溃了 NSRangeException,大概是因为 该表尚未设置。

    为什么桌子还没有摆好?你是用什么方法设置的?

    【讨论】:

    • 它是在 cellforrowatindexpath 中创建的
    • 可以在viewWillAppear中加载表格
    【解决方案5】:

    您可以使用 GCD 将滚动分派到 viewDidLoad 中的主运行循环的下一次迭代中以实现此行为。滚动将在表格视图显示在屏幕上之前执行,因此不会闪烁。

    - (void)viewDidLoad {
       dispatch_async (dispatch_get_main_queue (), ^{
            NSIndexPath *indexPath = YOUR_DESIRED_INDEXPATH;
            [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
        }];
    }
    

    【讨论】:

      【解决方案6】:

      我刚刚完成了与这个的角力。我的在列表顶部添加了一个搜索栏,最初隐藏在顶部... ala 一些核心应用程序。我其实也想问同样的问题!

      我害怕提供这个,因为那些提供东西的人似乎会被击倒......但是......我很惊讶没有一个简单的解决方案......所以我最终这样做了:

      我使用 ABTableViewCell(你可以用谷歌搜索)自定义绘制的单元格(又好又快!),当我被调用绘制第二行时(你可以在没有 ABTableViewCell 的自定义绘制的单元格中执行此操作),我设置它在那里,只有一个火信号:

      if ( self.mOnlyOnce == NO && theRow > 1 ) {
          self.mOnlyOnce = YES;
          [self.mParentTable scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:NO];
      }
      

      (选择适合您的适当行/部分...如果它们在同一部分中,您可能会将行设置为 0 以外的值)

      如果您讨厌我的解决方案(因为我还不能发表评论),请帮我把它保持为零并让更好的解决方案浮出水面。

      哦,还有一个关于在顶部隐藏搜索的条目...但我的已经作为自定义单元格完成了...这里是 link

      享受

      【讨论】:

        【解决方案7】:

        需要在表格视图出现在屏幕上之前滚动到单元格,但在表格视图加载数据之后。在viewDidLoad 中使用它对我不起作用。

        这段代码对我很有效:

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            
            DispatchQueue.main.async { [weak self]
                let indexPath = IndexPath(row: rowIndex, section: sectionIndex)
                self?.tableView.scrollToRow(at: indexPath, at: .middle, animated: false)
            }
        }
        

        iOS 14.1 上测试。

        即使没有DispatchQueue.main.async,它也可以在viewDidAppear 中工作,但用户可以看到滚动。我认为这不是一个好的选择。

        【讨论】:

          【解决方案8】:

          在 iOS 8 及更高版本中,viewWillAppear 之前的 scrollToRowAtIndexPath 中的 [self.tableView reloadData] 不起作用,您必须在 viewDidAppear 中重新加载,如下所示:

          - (void)viewDidAppear:(BOOL)animated
          {
              [super viewDidAppear:YES];
          
              [self.tableView reloadData];
          
               [self.tableView scrollToRowAtIndexPath:[[NSIndexPath indexPathForRow:18 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
          
          }
          

          注意:您必须使用UITableViewScrollPositionTop,否则如果您将scrollToRow指定为最后一行,滚动将不会像预期的那样滚动到底部甚至最后一行之后的空白区域。

          我认为在 iOS 8 及更高版本中,第一次加载 tableView 时它不会加载用于构造表格的所有数据,这就是你必须使用 [tableView reload] 的原因,我认为这有点奇怪。

          【讨论】:

            最近更新 更多