An index-title-scrubber-bar, for use with a
UICollectionView
(or even aPSTCollectionView
). Gives a collection view the index title bar for-sectionIndexTitles
that aUITableView
gets for (almost) free. A huge thank you to @Yang from this Stack Overflow post, which saved my bacon here.
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).
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!
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.
Just use the awesome CocoaPods library for integrating this into your project.
In your Podfile
...
pod 'BDKCollectionIndexView'
Then run pod install
and it'll be in your project workspace. Then implement as above!
If you use this in your project, drop me a line and let me know! I'd love to hear about it. You can hit me up via email, on Twitter, or carrier pigeon.