UITableView - 如何在用户滚动时保持表格行固定

Posted

技术标签:

【中文标题】UITableView - 如何在用户滚动时保持表格行固定【英文标题】:UITableView - How to keep table rows fixed as user scrolls 【发布时间】:2012-06-05 08:50:28 【问题描述】:

我希望能够在用户滚动时修复 UITableView 中某些行的位置。

具体来说,我有一个表格,其中某些行是后面行的“标题”,我希望标题在用户向上滚动时保持在屏幕顶部。然后,当用户滚动到下一个标题行将取代它的位置时,它会移开。

Any.DO 应用就是一个类似的例子。 “今天”、“明天”和“以后”表格行在屏幕上始终可见。

有人对如何实施有任何建议吗?

我目前正在考虑跟随 TableDidScroll 委托并将我自己的单元格放置在表格视图前面的适当位置。问题是,在其他时候,我真的希望这些单元格成为真正的表格单元格,以便它们可以被用户重新排序。

谢谢,

提姆

【问题讨论】:

为什么不使用 UITableView 标头?您可以将文本放在那里或制作自定义视图。有 UITableViewDelegate 的方法 【参考方案1】:

我一直在玩这个,我想出了一个简单的解决方案。

首先,我们向控制器添加一个 UITableViewCell 属性。这应该被初始化,使其看起来与我们将用于创建虚假节标题的行单元格完全相同。

接下来,我们截取表格视图的滚动

- (void)scrollViewDidScroll:(UIScrollView *)scrollView

    // Add some logic here to determine the section header. For example, use 
    // indexPathsForVisibleRows to get the visible index paths, from which you 
    // should be able to get the table view row that corresponds to the current 
    // section header. How this works will be implementation dependent.
    //
    // If the current section header has changed since the pervious scroll request 
    // (because a new one should now be at the top of the screen) then you should
    // update the contents.

    IndexPath *indexPathOfCurrentHeaderCell = ... // Depends on implementation
    UITableViewCell *headerCell = [self.tableView cellForRowAtIndexPath:indexPathOfCurrentHeaderCell];

    // If it exists then it's on screen. Hide our false header

    if (headerCell)
        self.cellHeader.hidden = true;

    // If it doesn't exist (not on screen) or if it's partially scrolled off the top,
    // position our false header at the top of the screen

    if (!headerCell || headerCell.frame.origin.y < self.tableView.contentOffset.y )
    
        self.cellHeader.hidden = NO;
        self.cellHeader.frame = CGRectMake(0, self.tableView.contentOffset.y, self.cellHeader.frame.size.width, self.cellHeader.frame.size.height);
    

    // Make sure it's on top of all other cells

    [self.tableView bringSubviewToFront:self.cellHeader];

最后,我们需要拦截对该单元格的操作并做正确的事情...

【讨论】:

非常感谢,塔姆斯。我正在寻找类似的东西,并且还需要在我的应用程序中实现您的逻辑。但是,我不明白你的意思是什么 headerCell ?当你的意思是 headerCell 在屏幕上? headerCell 是表格视图中您希望在滚动时保持在表格顶部的单元格。 问题:您说您使用它来重新排序标题。您是否折叠内容单元格以使标题重新排序更容易?我正在考虑,但不确定是否可行,因为reloadData 或其他类型的表格视图更新可能会中断/恢复浮动标题单元格。 要清楚 - 以防万一 - 我的意思是当用户点击处理程序开始拖动标题时,此时我想“关闭”这些部分,因此只有标题可见,并且将标题放在新位置更容易。我需要触发reloadData 或更新,以便在用户开始拖动时折叠这些部分,我认为这会取消拖动。 如果不可能的话,也可以有一个单独的按钮,用户可以手动折叠所有标题然后重新排序...但是如果可以在用户开始拖动时自动完成,那就太好了。 【参考方案2】:

这是纯 UITableView 实例中节标题的默认行为。 如果要创建自定义标题,请在表视图委托中实现 tableView:viewForHeaderInSection: 方法并返回标题的视图。

虽然您将不得不管理节和行,而不仅仅是行。

【讨论】:

问题是我希望能够记录节标题。据我所知,没有任何 UI 允许用户以与当前可以拖动部分行相同的方式执行此操作... 我明白了,您必须自己处理。看起来有点复杂,但我相信它可以完成。似乎没有任何实现。也许我可以帮助你,我们可以把它放在 Github 上。 有趣的是,现在在 ios5 中有一个 moveSection:toSection: 调用,但我认为没有办法从 UI 启动这个移动... 您可以将按钮或手势识别器添加到标题视图。这样您就可以在选择该部分时获得。【参考方案3】:

Swift 5 解决方案

var header: UIView?

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell(indexPath: indexPath) as UITableViewCell
    header = cell.contentView
    return cell


func scrollViewDidScroll(_ scrollView: UIScrollView) 
    let headerCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0))
    guard headerCell == nil || (headerCell!.frame.origin.y < self.tableView.contentOffset.y + headerCell!.frame.height/2) else 
        header?.isHidden = true
        return
    
    guard let hdr = header else  return 
    hdr.isHidden = false
    hdr.frame = CGRect(x: 0, y: tableView.contentOffset.y, width: hdr.frame.size.width, height: hdr.frame.size.height)
    if !tableView.subviews.contains(hdr) 
        tableView.addSubview(hdr)
    
    tableView.bringSubviewToFront(hdr)

【讨论】:

以上是关于UITableView - 如何在用户滚动时保持表格行固定的主要内容,如果未能解决你的问题,请参考以下文章

IOS - 滚动时将 UIView 保持在 UITableView 之上

UITableView 选定的单元格在滚动时不会保持选中状态

UITableView 在更新 NSFetchedResultsController 时保持滚动在正确的位置

为 UITableview 的高度设置动画时,滚动手势识别器保持相同大小

iOS:在 reloadData 时保持 UITableView 在同一位置

如何滚动头与UITableView的相处?