【问题标题】:How to get a UITableView's visible sections?如何获得 UITableView 可见部分?
【发布时间】:2010-11-16 04:49:59
【问题描述】:

UITableView 提供了indexPathsForVisibleRowsvisibleCells 方法,但是我怎样才能得到可见部分呢?

【问题讨论】:

    标签: iphone ios objective-c uitableview


    【解决方案1】:

    或者真正简单的方法是利用 valueForKeyPath 和 NSSet 类:

    NSSet *visibleSections = [NSSet setWithArray:[[self.tableView indexPathsForVisibleRows] valueForKey:@"section"]];
    

    基本上,您会在可见行中获得一个节值数组,然后用它填充一个集合以删除重复项。

    【讨论】:

    • 多么简单的答案啊。谢谢!
    • @MikkelSelsøe 如果该部分中没有行,则该部分也不可见...所以我不确定您所说的是否行不通。
    • @ChristopherKing 你可以有没有节行的节标题。有一些用例。这个答案解决了这种情况:stackoverflow.com/questions/15204328/…
    • 非常好!我认为对眼睛更容易一点的改进是使用Collection Operator@distinctUnionOfObjects,这样可以节省创建NSSet。这给出了:[[self.tableView indexPathsForVisibleRows] valueForKeyPath: @"@distinctUnionOfObjects.section"].
    • 什么是 swift 的等价物?
    【解决方案2】:

    斯威夫特版本

    if let visibleRows = tableView.indexPathsForVisibleRows {
        let visibleSections = visibleRows.map({$0.section})
    }
    

    【讨论】:

    • 与上述相同的评论...如果您在第 0 部分有 10 行,在第 1 部分有 10 行并且它们都在屏幕上,这不是每十次打印一次而不是每次打印一次?
    • @MarkA.Donohoe 添加以下行:let uniqueSections = Array(Set(visibleSections)).sorted(); //converting to set loses order, hence the sorting
    • 你忘了零行的部分!
    【解决方案3】:

    UITableViews 使用 NSIndexPath 存储它们的单元格。因此,没有针对部分的对象。使用下面的代码,我们可以遍历表并使用可见部分的索引执行操作(我不确定你为什么想要可见部分,因为可见只意味着它们当前在屏幕上,但无论如何)。

    for (NSIndexPath* i in [yourTableViewName indexPathsForVisibleRows])
    {
      NSUInteger sectionPath = [i indexAtPosition:0];
      //custom code here, will run multiple times per section for each visible row in the group
    }
    

    【讨论】:

    • 但如果部分中没有单元格,则方法“indexPathsForVisibleRows”将返回 null .....
    • 我想知道该部分是否在屏幕上可见,然后我可以释放或动态创建它,否则必须保留它会占用大量内存.....
    • 我在这里添加了一个处理没有单元格的部分的答案stackoverflow.com/a/23538021/895099
    【解决方案4】:

    从可见行列表中提取部分:

    NSArray *indexPathsForVisibleRows = [tableView indexPathsForVisibleRows];
    NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
    for ( NSIndexPath *indexPath in indexPathsForVisibleRows ) {
         [indexSet addIndex:indexPath.section];
    }
    NSLog(@"indexSet %@",indexSet);
    // indexSet <NSMutableIndexSet: 0x11a5c190>[number of indexes: 5 (in 1 ranges), indexes: (9-13)]
    

    或者:

    NSArray *indexPathsForVisibleRows = [detailTableView indexPathsForVisibleRows];
    NSMutableSet *sectionSet = [NSMutableSet set];
    for ( NSIndexPath *indexPath in indexPathsForVisibleRows ) {
        [sectionSet addObject:[NSNumber numberWithInt:indexPath.section]];
    }
    NSLog(@"sectionSet %@",sectionSet);
    // sectionSet {(13, 11, 9, 10, 12 )}
    

    【讨论】:

      【解决方案5】:

      我已经找到了解决方案。

      第一步,每个部分都会显示一个由- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section创建的UIView,并将其存储到数组中。

      当 TableView 滚动时,我想释放不可见的部分视图,所以我需要知道哪个部分是可见的,下面的函数代码将为此检测,如果视图可见则释放它。

      -(BOOL)isVisibleRect:(CGRect)rect containerView:(UIScrollView*)containerView
      {
          CGPoint point = containerView.contentOffset;
          CGFloat zy = point.y ;
      
          CGFloat  py =  rect.origin.y + rect.size.height;
          if (py - zy <0) {
                  return FALSE;
          }
          CGRect  screenRect = containerView.frame;
      
          CGFloat by = screenRect.size.height + zy ;
          if (rect.origin.y > by) {
                  return FALSE;
          }
          return TRUE;
      }
      

      rectUIView部分的框架;containerViewUITableView

      这样,我可以得到UITableView的可见部分,但我希望SDK可以直接为此提供API。

      【讨论】:

        【解决方案6】:

        在 UITableView 中获取可见部分的 2 步解决方案:

        1) 将标题视图添加到 viewForHeaderInSection 中的可变数组中
        2) 当tableview在scrollViewDidScroll滚动时更新数组

        注意使用标签属性来保存节号

        @property (nonatomic, strong, readwrite) NSMutableArray *headerArray;
        
        - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
            UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 40)];
            headerView.backgroundColor = [UIColor greenColor];
            headerView.tag = section;
            [_headerArray addObject:headerView];
            return headerView;
        }
        
        - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
            [self updateHeaderArray];
            NSLog(@"------------");
            for (UIView *view in _headerArray) {
                NSLog(@"visible section:%d", view.tag);
            }
        }
        
        - (void)updateHeaderArray {
            // remove invisible section headers
            NSMutableArray *removeArray = [NSMutableArray array];
            CGRect containerRect = CGRectMake(_tableView.contentOffset.x, _tableView.contentOffset.y,
                                              _tableView.frame.size.width, _tableView.frame.size.height);
            for (UIView *header in _headerArray) {
                if (!CGRectIntersectsRect(header.frame, containerRect)) {
                    [removeArray addObject:header];
                }
            }
            [_headerArray removeObjectsInArray:removeArray];
        }
        

        【讨论】:

          【解决方案7】:

          使用 kvc 回答更简单整洁

          NSArray *visibleSections = [self.tableView.indexPathsForVisibleRows valueForKey:@"section"];
          

          这可能会给您一个包含重复值的数组,但您可以从那里进行管理。

          【讨论】:

          • 如果您阅读我在您之前 3 个月发布的答案,我已经使用了这种技术,然后使用一组来消除重复项。
          • @christopherKing,我的错。不要在发布答案时检查。
          【解决方案8】:
          for (NSUInteger section = 0; section < self.tableView.numberOfSections; ++section) {
              UIView *headerView = [self.tableView headerViewForSection:section];
              if (headerView.window) {
                  NSLog(@"its visible");
              }
          }
          

          【讨论】:

            【解决方案9】:

            另一种解决方案,在节标题视图的标签中使用 1 位,就像那样

            #define _TBL_TAG_SECTION(_TAG) ((_TAG)|(1<<30))
            #define _TBL_TAG_CLEAR(_TAG) ((_TAG)&((1<<30)-1))
            #define _TBL_TAG_IS_SECTION(_TAG) ((_TAG)>>30)
            
            - (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
            {
                // alloc header view
                UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
                header.tag = _TBL_TAG_SECTION(section);
                return header;
            }
            
            - (void)scrollViewDidScroll:(UIScrollView *)scrollView
            {
                CGRect r = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y,
                                  CGRectGetWidth(scrollView.frame),
                                  CGRectGetHeight(scrollView.frame));
                for (UIView *v in [_tableView subviews]) {
                    if ( CGRectIntersectsRect(r, v.frame) ) {
                        if ( _TBL_TAG_IS_SECTION(v.tag) ) {
                            NSLog(@"visible section tag %d", _TBL_TAG_CLEAR(v.tag));
                        }
                    }
                }
            }
            

            【讨论】:

              【解决方案10】:

              斯威夫特 4.1 ?

              self.sections.indices.forEach{ (i:Int) in
                  let section:UIView? = self.tableView(self, viewForHeaderInSection: i)
              }
              

              【讨论】:

                【解决方案11】:

                Swift 4 和 Swift 5

                我们可以执行以下操作,因为空白部分仍然可以有可见的标题视图:

                extension UITableView {
                
                    var indexesOfVisibleSections: [Int] {
                        var indexes = [Int]()
                        (0 ..< numberOfSections).forEach {
                            let headerRect = (style == .plain ? 
                                              rect(forSection: $0) : 
                                              rectForHeader(inSection: $0))
                            // The "visible part" of the tableView is based on the
                            // content offset and the tableView's size.
                            let visiblePartOfTableView =
                                CGRect(x: contentOffset.x, y: contentOffset.y,
                                       width: bounds.size.width, height: bounds.size.height)
                            if (visiblePartOfTableView.intersects(headerRect)) {
                                indexes.append($0)
                            }
                        }
                        return indexes
                    }
                
                }
                

                【讨论】:

                • 你忘了零行的部分!
                • @VyachaslavGerchicov 更新了我的答案以包含可能为空但具有可见标题视图的部分
                【解决方案12】:

                你在 Swift 4 中试过这个吗?

                let sections = tableView.indexPathsForVisibleRows?.map { $0.section } ?? []
                for section in sections { 
                    print(String(format: "%d", section)) 
                }
                

                【讨论】:

                • 如果您在第 0 部分有 10 行,在第 1 部分有 10 行并且它们都在屏幕上,这不是每十次打印一次而不是一次吗?
                • 你忘了零行的部分!
                • @VyachaslavGerchicov 没有任何行的部分被 UI 框架视为不可见
                • 我不知道你指的是哪个框架,但在我的情况下,部分有部分标题视图和零行。所以这个部分是可见的,你的代码不适合它。
                猜你喜欢
                • 2011-10-10
                • 2012-08-05
                • 2023-03-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多