21

So I implemented a proper TableView with a search functionality and sectionIndexTitles. Now, I am trying to implement a UICollectionView and it is working so far, except that I can't easily have sectionIndexTitles (the right scroll bar).

If I look at the Facebook Application, it looks like a UICollectionView, but with indeed sectionIndexTitles and a searchbar. I can't seem to find such functions for the UICollectionView model.

Any ideas?!

Thanks!

enter image description here

tkanzakic
  • 5,499
  • 16
  • 34
  • 41
abisson
  • 4,365
  • 9
  • 46
  • 68
  • I'd like a good answer on this, too; I'd also like to avoid having another table view hidden, if possible (memory issues). – Ben Kreeger Jan 18 '13 at 18:09

5 Answers5

16

I had a similar requirement (for a horizontal collection view) and ended up building an index view subclass myself.

I plan to open-source it but that will likely have to wait until next month, so here's a stub to get you started:

YMCollectionIndexView.h

@interface YMCollectionIndexView : UIControl

- (id) initWithFrame:(CGRect)frame indexTitles:(NSArray *)indexTitles;

// Model
@property (strong, nonatomic) NSArray *indexTitles; // NSString
@property (readonly, nonatomic) NSUInteger currentIndex;
- (NSString *)currentIndexTitle;

@end

YMCollectionIndexView.m

#import "YMCollectionIndexView.h"

@interface YMCollectionIndexView ()
@property (readwrite, nonatomic) NSUInteger currentIndex;
@property (strong, nonatomic) NSArray *indexLabels;
@end

@implementation YMCollectionIndexView

- (id) initWithFrame:(CGRect)frame indexTitles:(NSArray *)indexTitles {
    self = [super initWithFrame:frame];
    if (self) {
        self.indexTitles = indexTitles;
        self.currentIndex = 0;
        // add pan recognizer
    }
    return self;
}

- (void)setIndexTitles:(NSArray *)indexTitles {
    if (_indexTitles == indexTitles) return;
    _indexTitles = indexTitles;
    [self.indexLabels makeObjectsPerformSelector:@selector(removeFromSuperview)];
    [self buildIndexLabels];
}

- (NSString *)currentIndexTitle {
    return self.indexTitles[self.currentIndex];
}

#pragma mark - Subviews

- (void) buildIndexLabels {
    CGFloat cumulativeItemWidth = 0.0; // or height in your (vertical) case
    for (NSString *indexTitle in self.indexTitles) {
            // build and add label
        // add tap recognizer
    }
    self.indexLabels = indexLabels;
}

#pragma mark - Gestures

- (void) handleTap:(UITapGestureRecognizer*)recognizer {
    NSString *indexTitle = ((UILabel *)recognizer.view).text;
    self.currentIndex = [self.indexTitles indexOfObject:indexTitle];
    [self sendActionsForControlEvents:UIControlEventTouchUpInside];
}

// similarly for pan recognizer

@end

In your view controller:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.collectionIndexView addTarget:self action:@selector(indexWasTapped:) forControlEvents:UIControlEventTouchUpInside];
    // similarly for pan recognizer
}

- (void)indexWasTapped:(id)sender {
    [self.collectionView scrollToIndexPath:...];
}

// similarly for pan recognizer
Yang Meyer
  • 5,409
  • 5
  • 39
  • 51
  • This looks awesome! If you open-source this, the iOS community at large will love you for it, I'm sure. Bounty awarded! – Ben Kreeger Jan 21 '13 at 18:12
  • 3
    I've whipped up a full implementation (with writeup) in [a gist right here](https://gist.github.com/kreeger/4756030) — let me know what you think. – Ben Kreeger Feb 11 '13 at 17:41
  • Looks like @BenKreeger created a GitHub repo/cocoapod for his solution: https://github.com/kreeger/BDKCollectionIndexView – Liron Yahdav Nov 25 '15 at 05:48
2

As of iOS 14 (which has severely enhanced UICollectionView, in particular for making UITableView obsolete), this works as expected by implementing the delegate method indexTitles.

DrMickeyLauer
  • 4,455
  • 3
  • 31
  • 67
1

The index titles do indeed work on iOS but it seems to only work on iOS 14+ when I tested.

func indexTitles(for collectionView: UICollectionView) -> [String]? {
    return Array(Set(objects.map{ String($0.name.prefix(1)) })).sorted(by: { $0 < $1 })
}

func collectionView(_ collectionView: UICollectionView, indexPathForIndexTitle title: String, at index: Int) -> IndexPath {
    guard let index = objects.firstIndex(where: { $0.name.prefix(1) == title }) else {
        return IndexPath(item: 0, section: 0)
    }
    return IndexPath(item: index, section: 0)
}
Leon
  • 3,614
  • 1
  • 33
  • 46
0

Edit: This is verified to be fixed and works as intended now in iOS 14

You can now simply use (as of iOS 10.3)

optional func indexTitles(for collectionView: UICollectionView) -> [String]?

Which expects an array like: ['A', 'B' ,'C'] as its return value.

Rob Caraway
  • 3,856
  • 3
  • 30
  • 37
  • 2
    this work for anyone? even in a simple example it I can't get the indexTitles func to get called. – Jake Almer Oct 05 '17 at 01:55
  • Also keen on understanding how to use this new data source method. I have tried both on iOS 10.3 and tvOS 10.2 (as mentioned in the docs) but never got index titles showing up. – Aloha Silver Oct 20 '17 at 04:28
  • 2
    I couldn't get it to work on iOS either (didn't try tvOS yet). The online documentation states it's available on iOS 10.3+ and tvOS 10.2+, but looking at the header files, it only says API_AVAILABLE(tvos(10.2)). – cargath Oct 23 '17 at 15:28
  • I poked around this for quite a while as well, with zero success. I believe it is just not implemented. I also searched for source code anywhere in the world :) that uses this feature and found none. – Ammo Goettsch Dec 27 '17 at 20:47
  • 2
    I posted a question on Apple Developer Forums https://forums.developer.apple.com/thread/95644 – nambatee Jan 23 '18 at 19:52
  • 2
    Unfortunately this is only implemented in tvOS, despite the online documentation stating otherwise. I've filed a radar for this. – svarrall Sep 24 '18 at 05:36
  • 1
    I just got this working simply by adding it to my data source. – Leon Dec 21 '20 at 14:39
-1

You can provide this header by implementing and returning a value for the following function for UICollectionViewDataSource

collectionView:viewForSupplementaryElementOfKind:atIndexPath:

Note that the size of the header will be from the height value of the CGRect returned from UICollectionViewFlowLayoutDelegate method collectionView:layout:referenceSizeForHeaderInSection:

Samuel
  • 401
  • 2
  • 10
  • 2
    The question refers to the `sectionIndexTitles` on the right side of the screen, and not the supplementary section headers. – Ben Kreeger Jan 18 '13 at 18:02