检测 UITableView 部分标题何时捕捉到屏幕顶部

Posted

技术标签:

【中文标题】检测 UITableView 部分标题何时捕捉到屏幕顶部【英文标题】:Detect when UITableView section header snaps to the top of the screen 【发布时间】:2017-01-16 05:47:48 【问题描述】:

我想检测UITableView 部分标题何时捕捉到屏幕顶部,然后修改标题的高度,但我不知道该怎么做。有人可以帮忙吗?

【问题讨论】:

我从未实际尝试过,但也许你可以试试frame.x == 0 有一个rectForHeader 方法可用于获取该部分的标题框架,然后有一个contentOffset 属性UItableView。我相信您可以计算出标题是否位于屏幕顶部。还有UIScrollViewdelegate 方法scrollViewDidScroll 可以用来检查UITableView 何时滚动 非常感谢 改进标题,简化解释 @BenOng 我建议您发表您的评论作为答案 【参考方案1】:

通过使用didEndDisplayingHeaderViewwillDisplayHeaderView 委托方法,我能够完成检测哪个标题卡在表格视图的顶部。这里的想法是,当我们滚动浏览表格视图的各个部分时,标题周围的行变得可见,我们可以使用tableView.indexPathsForVisibleRows 获取所有可见行的索引路径。根据我们是向上还是向下滚动,我们将屏幕上的第一个或最后一个 indexPath 与刚刚出现/消失的视图的索引路径进行比较。

//pushing up
func tableView(_ tableView: UITableView,
               didEndDisplayingHeaderView view: UIView,
               forSection section: Int) 

    //lets ensure there are visible rows.  Safety first!
    guard let pathsForVisibleRows = tableView.indexPathsForVisibleRows,
        let lastPath = pathsForVisibleRows.last else  return 

    //compare the section for the header that just disappeared to the section
    //for the bottom-most cell in the table view
    if lastPath.section >= section 
        print("the next header is stuck to the top")
    



//pulling down
func tableView(_ tableView: UITableView,
               willDisplayHeaderView view: UIView,
               forSection section: Int) 

    //lets ensure there are visible rows.  Safety first!
    guard let pathsForVisibleRows = tableView.indexPathsForVisibleRows,
        let firstPath = pathsForVisibleRows.first else  return 

    //compare the section for the header that just appeared to the section
    //for the top-most cell in the table view
    if firstPath.section == section 
        print("the previous header is stuck to the top")
    

【讨论】:

强大而强大的解决方案! 这是唯一对我有用的方法。在滚动视图中测量东西确实滚动并将矩形转换为超级视图坐标空间在快速滚动时永远不会起作用。【参考方案2】:

我把我的评论变成了代码,它的工作原理是:

override func scrollViewDidScroll(_ scrollView: UIScrollView) 

    if scrollView == yourTableView 
        if yourTableView.rectForHeader(inSection: <#sectionIWant#>).origin.y <= tableView.contentOffset.y-defaultOffSet.y &&
            yourTableView.rectForHeader(inSection: <#sectionIWant#>+1).origin.y > tableView.contentOffset.y-defaultOffSet.y 
            print("Section Header I want is at top")
        
     else 
        //Handle other scrollViews here
    

因此,您将 sectionIWant 替换为 Int,当该部分的标题位于顶部时,print 将运行。请注意,yourTableView 不是 scrollViewDidScroll 的参数,因此您必须保留引用到您的UITableView 其他地方并在此处使用它。

或者,如果您想不断更新您可以使用哪个部分标题位于顶部:

override func scrollViewDidScroll(_ scrollView: UIScrollView) 

    if scrollView == yourTableView 
        var currentSection = 0
        while !(tableView.rectForHeader(inSection: currentSection).origin.y <= (tableView.contentOffset.y-defaultOffSet.y) &&
            tableView.rectForHeader(inSection: currentSection+1).origin.y > (tableView.contentOffset.y)-defaultOffSet.y) 
                if tableView.contentOffset.y <= tableView.rectForHeader(inSection: 0).origin.y 
                    break   //Handle tableView header - if any
                
                currentSection+=1
                if currentSection > tableView.numberOfSections-2 
                    break   //Handle last section
                
        
        print(currentSection)
    


请注意,在这两个代码中都有一个defaultOffSet,它是加载时tableViewcontentOffSet,这是为了考虑到contentOffSet 在某些情况下不会从0 开始 - 我是不知道是什么时候,但我确实遇到过这种情况。

【讨论】:

其实UITableView是UIScrollView的一个子类,所以传入的scrollView可以投到你的table view类guard let tableView = scrollView as? UITableView else return 同意,但在大多数情况下,表格视图已经是一个 IBOutlet,因此您可以使用它而不是添加另一行来投射滚动视图,但我想添加一个检查以确保表格视图是实际上应该完成一个滚动而不是另一个滚动视图......我稍后会更新我的答案【参考方案3】:

这个解决方案对我有用。

func scrollViewDidScroll(_ scrollView: UIScrollView) 
  self.visibleHeaders.forEach  header in
    let headerOriginalFrame = self.myTableView.rectForHeader(inSection: header.tag)
    let headerCurrentFrame = header.frame
    let fixed = headerOriginalFrame != headerCurrentFrame
    // Do things you want
  

这里是完整的源代码。 https://github.com/jongwonwoo/CodeSamples/blob/master/TableView/TableviewFixedHeader/Tableview/ViewController.swift

【讨论】:

【参考方案4】:

事实证明,我对这个问题的解决方案非常有效且用途广泛。如果这对某人有帮助,我会很高兴

func scrollViewDidScroll(_ scrollView: UIScrollView) 
let sectionPosition = tableView.rect(forSection: sectionIndex)
let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)

if let dataSource = dataSource,
   scrollView.contentOffset.y >= sectionPosition.minY 

//Next section

    guard dataSource.count - 1 > sectionIndex else  return 
    sectionIndex += 1
 else if translation.y > 0,
          scrollView.contentOffset.y <= sectionPosition.minY 

//Last section

    guard sectionIndex > 0 else  return 
    sectionIndex -= 1

【讨论】:

【参考方案5】:

聚会迟到了,但查看所有回复并没有解决我的问题。

下面的做法,各取一点,同时加入,比较通用,不固定的做法。

    override func scrollViewDidScroll(_ scrollView: UIScrollView) 
        guard let visibleIndexs = self.tableView.indexPathsForVisibleRows,
              !visibleIndexs.isEmpty else  return 
        
        var visibleHeaders = [Int]()
        visibleIndexs.forEach  anIndexPath in
            if !visibleHeaders.contains(anIndexPath.section) 
                visibleHeaders.append(anIndexPath.section)
            
        
        
        visibleHeaders.forEach  header in
            let headerView = tableView.headerView(forSection: header)
            let headerOriginalFrame = self.tableView.rectForHeader(inSection: header)
            if headerOriginalFrame != headerView?.frame 
    //do something with top headerView
             else 
    //do something else with all the others headerView(s)
            
        
    

【讨论】:

以上是关于检测 UITableView 部分标题何时捕捉到屏幕顶部的主要内容,如果未能解决你的问题,请参考以下文章

检测 UITableView 何时滚动到底部

UITableView 检测section header的停靠和脱离

对于这种边缘情况,如何检测何时从 iOS 的 uitableview 中删除单元格?

在 UITableView 中点击时找出部分标题的部分

如何判断何时点击了 UITableView 部分索引字母?

Flutter:可以检测抽屉何时打开?