Skip to content

Instantly share code, notes, and snippets.

@kreeger
Last active December 12, 2015 09:59
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kreeger/4756030 to your computer and use it in GitHub Desktop.
Save kreeger/4756030 to your computer and use it in GitHub Desktop.

BDKCollectionIndexView

An index-title-scrubber-bar, for use with a UICollectionView (or even a PSTCollectionView). Gives a collection view the index title bar for -sectionIndexTitles that a UITableView gets for (almost) free. A huge thank you to @Yang from this Stack Overflow post, which saved my bacon here.

The problem

When you're using a UITableView and you define the UITableViewDataSource method -sectionIndexTitlesForTableView:, you get a sweet right-hand-side view for scrubbing through a long table view of fields, separated by sections. The titles are the names of the sections, by default (or at least letters based on the section names).

UITableView with section index titles

Unfortunately, you get jack when you use a UICollectionView (or in my case, a PSTCollectionView). There's no similar method defined on the UICollectionViewDataSource protocol. Stack Overflow to the rescue!

The solution

This solution was presented by Yang on Stack Overflow. Just roll your own! By subclassing UIControl, laying out a series of UILabel views in a vertical (or horizontal) fashion, and watching over them with a UITapGestureRecognizer and UIPanGestureRecognizer, you can get your own sectionIndexTitles bar thing. I've written a gist that covers the header and implementation in full, based on Yang's proposal. I use it in a controller like so.

@property (strong, nonatomic) BDKCollectionIndexView *indexView;

- (BDKCollectionIndexView *)indexView {
    if (_indexView) return _indexView;
    CGFloat indexWidth = 28;
    CGRect frame = CGRectMake(CGRectGetWidth(self.collectionView.frame) - indexWidth,
                              CGRectGetMinY(self.collectionView.frame),
                              indexWidth,
                              CGRectGetHeight(self.collectionView.frame));
    _indexView = [BDKCollectionIndexView indexViewWithFrame:frame indexTitles:@[]];
    _indexView.autoresizingMask = (UIViewAutoresizingFlexibleHeight |
                                   UIViewAutoresizingFlexibleLeftMargin);
    [_indexView addTarget:self
                   action:@selector(indexViewValueChanged:)
         forControlEvents:UIControlEventValueChanged];
    return _indexView;
}

When my collection view has loaded data in it, I set the indexTitles property of my self.indexView (I'm using a NSFetchedResultsController in a parent class that also serves a sub-class that manages a UITableView; go-go-gadget code reuse!).

self.indexView.indexTitles = self.resultsController.sectionIndexTitles;

Then I merely watch for changes using this method (which was assigned to watch for UIControlEventValueChanged).

- (void)indexViewValueChanged:(BDKCollectionIndexView *)sender {
    NSIndexPath *path = [NSIndexPath indexPathForItem:0 inSection:sender.currentIndex];

    // If you're using UICollectionView, substitute "PST" for "UI" and you're all set.
    [self.collectionView scrollToItemAtIndexPath:path
                                atScrollPosition:PSTCollectionViewScrollPositionTop
                                        animated:NO];

    // I bump the y-offset up by 45 points here to account for aligning the top of
    // the section header view with the top of the collectionView frame. It's
    // hardcoded, but you get the idea.
    self.collectionView.contentOffset = CGPointMake(self.collectionView.contentOffset.x,
                                                    self.collectionView.contentOffset.y - 45);
}

Again, big thanks to @Yang for the solution on which this is based.

@leeprobert
Copy link

Thanks for this. Just what I need. And the best bit is that it uses the fetch result controllers index titles, so it will work with Japanese and Chinese characters. Thanks!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment