SectionIndexTitles for a UICollectionView

2019-01-16 15:27发布

问题:

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!

回答1:

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


回答2:

Have you found any solution?

I'm trying to implement the same features for UICollectionView.

So far for the sectionIndexTitles I'm using custom UITableView with the same sections as the original UICollectionView, but with empty cells. As a result I have a IndexTableView class

indexTableView = [[IndexTableView alloc] initWithFrame:CGRectZero];
[self.view addSubview:indexTableView];

...

indexTableView.frame = CGRectMake(CGRectGetWidth(self.view.frame) - 40, 0, 40, CGRectGetHeight(self.view.frame));
indexTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
indexTableView.sectionHeaderHeight = 0;
indexTableView.backgroundColor = [UIColor clearColor];

I add IndexTableView on top of the UICollectionView, and got the same visual effect as an original UITableView. Now I'm trying to link index selection and UICollectionView and make it work.

EDITED: I've just added a UISearchBar to the UICollectionController

add following code to the your subclass of the UICollectionController

- (void)viewDidLoad
{
    [super viewDidLoad];
...
    self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.collectionView.frame.size.width, 44)];
    self.searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
    self.searchBar.delegate = self;
    [self.collectionView addSubview:self.searchBar];
    [self.collectionView setContentOffset:CGPointMake(44, 0)];
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    if (section == 0) {
        return UIEdgeInsetsMake(CGRectGetHeight(self.searchBar.frame), 6, 0, 6);
    }
    return UIEdgeInsetsMake(0, 6, 0, 6);
}


回答3:

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.



回答4:

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: