UITableView
provides the methods indexPathsForVisibleRows
and visibleCells
, but how can I get the visible sections?
12 Answers
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.

- 1,691
- 23
- 28
-
1What a beautifully simple answer. Thanks! – ArtSabintsev Nov 12 '14 at 17:42
-
1@MikkelSelsøe If there are no rows in the section then the section isn't visible either... so I'm not sure what you're saying doesn't work. – Christopher King Mar 04 '15 at 14:18
-
3@ChristopherKing you can have section headers with no section rows. There are use cases for that. This answer solves that case: http://stackoverflow.com/questions/15204328/how-to-retrieve-all-visible-table-section-header-views/23538021#23538021 – Mikkel Selsøe Mar 04 '15 at 14:21
-
4Very nice! I think a refinement that's a little easier on the eyes is to use the [Collection Operator](http://nshipster.com/kvc-collection-operators/) `@distinctUnionOfObjects`, which saves creating the `NSSet`. That gives: `[[self.tableView indexPathsForVisibleRows] valueForKeyPath: @"@distinctUnionOfObjects.section"]`. – Benjohn Sep 25 '15 at 10:33
-
What would be swift equivalent of it ? – itsji10dra Apr 03 '16 at 08:41
-
@itsji10dra, (tableView.indexPathsForVisibleRows! as NSArray).value(forKeyPath: "section") – OhadM Aug 04 '17 at 13:42
Swift version
if let visibleRows = tableView.indexPathsForVisibleRows {
let visibleSections = visibleRows.map({$0.section})
}
-
Same comment as above... If you have ten rows in section 0 and 10 rows in section 1 and they're all on the screen, doesn't this print each ten times instead of once each? – Mark A. Donohoe Feb 19 '19 at 17:49
-
@MarkA.Donohoe Add the following line: `let uniqueSections = Array(Set(visibleSections)).sorted(); //converting to set loses order, hence the sorting` – Kevin Jul 10 '20 at 06:12
-
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
}

- 976
- 9
- 20
-
3but if no cells in the sections then the method 'indexPathsForVisibleRows' will return null ..... – iXcoder Nov 23 '10 at 05:29
-
I want know whether the section is visible on screen then I can free or create it in dynamic or else it have to be retain that will eat lot of memory ..... – iXcoder Nov 23 '10 at 05:30
-
I added an answer that handles sections with no cells here http://stackoverflow.com/a/23538021/895099 – adamsiton May 08 '14 at 09:46
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 )}

- 2,659
- 1
- 22
- 41
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];
}

- 1,298
- 1
- 11
- 18
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.

- 1,655
- 22
- 31
-
If you read my answer as posted 3 months prior to yours I already use this technique and then use a set to get rid of the duplicates. – Christopher King Mar 04 '15 at 14:20
-
@christopherKing, my bad. Din't check at the time of posting the answer. – geekay Apr 07 '15 at 11:26
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.
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));
}
}
}
}

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

- 5,355
- 43
- 38
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
}
}

- 15,174
- 22
- 76
- 112

- 2,809
- 22
- 26
-
-
@VyachaslavGerchicov updated my answer to include sections that might be empty but have a visible header view – rolling_codes Mar 09 '21 at 21:39
Have you tried this in Swift 4?
let sections = tableView.indexPathsForVisibleRows?.map { $0.section } ?? []
for section in sections {
print(String(format: "%d", section))
}

- 15,174
- 22
- 76
- 112
-
If you have ten rows in section 0 and 10 rows in section 1 and they're all on the screen, doesn't this print each ten times instead of once? – Mark A. Donohoe Feb 19 '19 at 17:48
-
-
@VyachaslavGerchicov sections without any rows are not considered visible by the UI framework – rolling_codes Mar 09 '21 at 20:45
-
I don't know which framework do you mean but in my case section has section header view and zero rows. So this section is visible and your code doesn't work for it. – Vyachaslav Gerchicov Mar 10 '21 at 06:13