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

14

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

具体而言,我的表格中有一些行是后面行的"标题",我希望该标题行保持在屏幕顶部,以便用户向上滚动时一直可见。当用户滚动到足够接近下一个标题行时,该标题行将移出屏幕。

类似于Any.DO 应用程序的情况。"今天"、"明天"和"稍后"的表格行始终可见。

请问是否有任何关于如何实现此功能的建议?

目前,我的想法是跟随TableDidScroll代理,在合适的位置放置自己的单元格。但问题在于,其他时候我真的想让这些单元格成为真正的表单元格,以便用户可以对其进行重新排序等操作。

谢谢,

Tim


1
为什么不使用UITableView的表头?您可以在那里放置文本或制作自定义视图。有UITableViewDelegate的方法可供使用。 - Novarg
3个回答

13

我已经尝试过了,并想出了一个简单的解决方案。

首先,我们在控制器中添加一个单独的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];
}

最后,我们需要拦截该单元格上的操作并执行正确的操作...


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

3

这是在普通UITableView实例中,分区标题的默认行为。如果要创建自定义标题,请在表格视图代理中实现tableView:viewForHeaderInSection:方法,并返回您的标题视图。

尽管您将需要管理分区和行而不仅仅是行。


问题在于我想能够记录部分标题。据我所知,没有UI可以让用户以与他们当前拖动部分行的方式进行操作。 - tarmes
我明白了,你将不得不自己处理这个问题。看起来有点复杂,但我相信可以做到。似乎没有什么现成的解决方案。也许我可以帮你,并且我们可以把它放在Github上或其他地方。 - Fran Sevillano
有趣的是,在iOS5中现在有一个moveSection:toSection:调用,但我不认为有一种方法可以从UI启动此移动... - tarmes
您可以在标题视图中添加一个按钮或手势识别器。这样,您就可以知道哪个部分被选中了。 - Fran Sevillano

0

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接