带有一个可见单元格的 UITableView:确定哪个最可见

Posted

技术标签:

【中文标题】带有一个可见单元格的 UITableView:确定哪个最可见【英文标题】:UITableView with one visible cell: determine which is most visible 【发布时间】:2014-12-11 03:43:58 【问题描述】:

在任何给定时间给定一个带有单个可见单元格的 UITableView,我如何确定滚动表格视图时哪个单元格在视图中?

我知道我可以通过这样做获得一组可见单元格:

NSArray *paths = [tableView indexPathsForVisibleRows];

然后通过执行以下操作获取最后一个单元格(或第一个单元格,或其他):

UITableViewCell* cell = (UITableViewCell*)[tableView cellForRowAtIndexPath:[paths lastObject]];

但是我如何比较所有可见的单元格并确定它们中的哪一个最显眼?

【问题讨论】:

【参考方案1】:

以下逻辑将使您在滚动结束时获得最明显的单元格:

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 

    CGRect visibleRect = (CGRect).origin = self.tableView.contentOffset, .size = self.tableView.bounds.size;
    CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
    NSIndexPath *visibleIndexPath = [self.tableView indexPathForRowAtPoint:visiblePoint];


【讨论】:

【参考方案2】:

算法因返回的路径数而异:

如果只有一条路径,那就是那里最明显的单元格 如果存在三个或更多路径,则中间的任何单元格(即除第一个和最后一个单元格之外的所有单元格)都同样可见 如果恰好有两个单元格,则在其父视图中找到分隔这两个单元格的位置*,并计算两个距离 - 上到中和中到下。如果顶部到中间较大,则顶部单元格最明显。如果中间到底部更大,则第二个单元格更明显。否则,这两个单元格同样可见。

* 中点位置是第二个单元格的底部。顶部和底部位置是表格视图的顶部和底部。

【讨论】:

这实际上可能是最有意义的,因为如果释放触摸时只能看到一个单元格,那么滚动时最多应该看到两个。【参考方案3】:

基于@Sebyddd 答案的 Swift 解决方案:

func scrollViewDidEndDecelerating(scrollView: UIScrollView) 
  scrollToMostVisibleCell()


func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) 
  if !decelerate
    scrollToMostVisibleCell()
  


func scrollToMostVisibleCell()
  let visibleRect = CGRect(origin: tableView.contentOffset, size: tableView.bounds.size)
  let visiblePoint = CGPoint(x: CGRectGetMidX(visibleRect), y: CGRectGetMidY(visibleRect))
  let visibleIndexPath: NSIndexPath = tableView.indexPathForRowAtPoint(visiblePoint)!

  tableView.scrollToRowAtIndexPath(visibleIndexPath, atScrollPosition: .Top, animated: true)

【讨论】:

【参考方案4】:

您可以使用表格视图的rectForRowAtIndexPath: 获取每个可见单元格的框架,然后将它们(使用CGRectOffset)偏移-contentOffset.y 以考虑滚动,然后将它们与表格视图的bounds 相交以找出每个单元格在表格视图中可见的程度。

【讨论】:

【参考方案5】:

每当用户停止滚动时,以下逻辑将为您提供在 UITableView 中最明显或最靠近中心的 UITableViewCell。希望这个逻辑对某人有所帮助。

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

    if (!decelerate)
    
        if (isScrollingStart)
        
            isScrollingStart=NO;
            isScrollingEnd=YES;
            [self scrollingStopped];
        
    

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView


    if (isScrollingStart)
    
        isScrollingStart=NO;
        isScrollingEnd=YES;
        [self scrollingStopped];
    

- (void)scrollViewDidScroll:(UIScrollView *)scrollView

    isScrollingStart=YES;

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView

    isScrollingStart=YES;

-(void)scrollingStopped

    NSMutableArray* arrVideoCells=[NSMutableArray array];
    NSLog(@"Scrolling stopped");
    NSArray* arrVisibleCells=[self.tableTimeline visibleCells];
    for (TimeLineCell* cell in arrVisibleCells)
    
        if ([cell isKindOfClass:[TimeLineCellMediaVideo class]])
        
            [arrVideoCells addObject:cell];
        
    

    TimeLineCellMediaVideo* videoCell=[self getCellNearCenterOfScreen:arrVideoCells];


-(TimeLineCellMediaVideo*)getCellNearCenterOfScreen:(NSMutableArray*)arrCells

    TimeLineCellMediaVideo* closetCellToCenter;
    CGRect filterCGRect;
    for (TimeLineCellMediaVideo* videoCell in arrCells)
    
        if (arrCells.count==1)
            closetCellToCenter= videoCell;


        NSIndexPath* cellIndexPath=[self.tableTimeline indexPathForCell:videoCell];
        CGRect rect = [self.tableTimeline convertRect:[self.tableTimeline rectForRowAtIndexPath:cellIndexPath] toView:[self.tableTimeline superview]];
        if (closetCellToCenter)
        

            CGRect intersect = CGRectIntersection(self.tableTimeline.frame, filterCGRect);
            float visibleHeightFilterCell = CGRectGetHeight(intersect);

            intersect = CGRectIntersection(self.tableTimeline.frame, rect);
            float visibleHeightCurrentCell = CGRectGetHeight(intersect);
            if (visibleHeightCurrentCell>visibleHeightFilterCell)
            
                filterCGRect=rect;
                closetCellToCenter= videoCell;
            

        
        else
        
            closetCellToCenter=videoCell;
            filterCGRect=rect;

        
    
    return closetCellToCenter;

【讨论】:

【参考方案6】:

我执行以下操作来找到大多数可见单元格的 indexPath 并且它工作正常。

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) 
            guard let tableView = scrollView as? UITableView else 
                return
            
            let visibleHeights = tableView.visibleCells.compactMap  cell -> (indexPath: IndexPath, visibleHeight: CGFloat)? in
                guard let indexPath = tableView.indexPath(for: cell) else 
                    return nil
                
                let cellRect = tableView.rectForRow(at: indexPath)
                let superView = tableView.superview
                let convertedRect = tableView.convert(cellRect, to: superView)
                let intersection = tableView.frame.intersection(convertedRect)
                let visibleHeight = intersection.height
                return (indexPath, visibleHeight)
            

            guard let maxVisibleIndexPath = visibleHeights.max(by:  $0.visibleHeight < $1.visibleHeight )?.indexPath else 
                return
            
            print("maxVisibleIndexPath: \(maxVisibleIndexPath)")

        

【讨论】:

以上是关于带有一个可见单元格的 UITableView:确定哪个最可见的主要内容,如果未能解决你的问题,请参考以下文章

带有自定义单元格的 UITableView

带有包含自定义单元格的 UITableView 的 UISearchBar => 空白单元格

UITableView 仅加载在设备上可见的自定义单元格,并在添加更多单元格时崩溃

带有 WKWebView 单元格的滚动 UICollectionView 的 UITableView

带有单个自定义单元格的 UITableView? (苹果手机)

iOS - 带有自定义单元格的可点击 UITableView