iXcoder
iXcoder

Reputation: 1564

How to get a UITableView's visible sections?

UITableView provides the methods indexPathsForVisibleRows and visibleCells, but how can I get the visible sections?

Upvotes: 18

Views: 21784

Answers (12)

Rashid Latif
Rashid Latif

Reputation: 2901

Swift 4 and Swift 5

We can do the following since an empty section could still have a visible header view:

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
    }

}

Upvotes: 0

rollingcodes
rollingcodes

Reputation: 15978

Have you tried this in Swift 4?

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

Upvotes: -1

Sentry.co
Sentry.co

Reputation: 5569

Swift 4.1 🔸

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

Upvotes: 0

AAV
AAV

Reputation: 3803

Swift version

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

Upvotes: 13

alex1704
alex1704

Reputation: 529

for (NSUInteger section = 0; section < self.tableView.numberOfSections; ++section) {
    UIView *headerView = [self.tableView headerViewForSection:section];
    if (headerView.window) {
        NSLog(@"its visible");
    }
}

Upvotes: 1

geekay
geekay

Reputation: 1673

Answer is a lot simpler and neater with kvc

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

this might give you an array with duplicate values but you can manage from there.

Upvotes: 1

cbartel
cbartel

Reputation: 1298

2 step solution to get the visible sections in a UITableView:

1) Add the header views to a mutable array in viewForHeaderInSection
2) Update the array when the tableview scrolls in scrollViewDidScroll

note the use of the tag property to hold the section number

@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];
}

Upvotes: 1

Christopher King
Christopher King

Reputation: 1715

Or the really easy way would be to take advantage of valueForKeyPath and the NSSet class:

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

Basically you get an array of the section values in the visible rows and then populate a set with this to remove duplicates.

Upvotes: 23

iXcoder
iXcoder

Reputation: 1564

I have got the solution.

First step, each section will show a UIView that created by - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section, that will be stored into array.

When the TableView is scrolled , I want free the invisible section view, so I need know which section is visible or not, follow function code will detect for this purpose, if the view is visible then free it.

-(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;
}

(rect is the frame of the section UIView; containerView is the UITableView)

In this way, I can get visible sections of the UITableView, but I hope the SDK can provide API for this purpose directly.

Upvotes: 1

LALAWUE
LALAWUE

Reputation: 1

another solution, use 1 bit in your section header view's tag, like that

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

Upvotes: 0

Jeff
Jeff

Reputation: 2699

Extract the sections from the list of visible rows:

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

Or:

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

Upvotes: 2

Tozar
Tozar

Reputation: 986

UITableViews store their cells using an NSIndexPath. As a result there is no object for sections. Using the following code we can traverse the table and perform operations using the indexes of visible sections (I'm not sure why you want visible sections since visible only means they are currently on the screen, but whatever).

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
}

Upvotes: 8

Related Questions