The diagram should show you that the tricky part here is going to be getting the UICollectionView to see the view controller as the delegate. That's fine, we'll just subclass UICollectionView to get it to have an index property. Subclassing UICollectionView isn't common, but it's perfectly acceptable here.
Adding the collection view to the cell is very strait forward. We'll create an instance of AFCollectionView using a standard UICollectionViewFlowLayout in our cell's designated initializer.
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (!(self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) return nil;
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
layout.itemSize = CGSizeMake(44, 44);
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.collectionView = [[AFIndexedCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:CollectionViewCellIdentifier];
self.collectionView.backgroundColor = [UIColor whiteColor];
self.collectionView.showsHorizontalScrollIndicator = NO;
[self.contentView addSubview:self.collectionView];
return self;
}
We'll adjust the side of the collection view to fill the cell in layoutSubviews.
-(void)layoutSubviews
{
[super layoutSubviews];
self.collectionView.frame = self.contentView.bounds;
}
Next, we'll set up our model in the view controller. We'll use UIColors because they're easy. Each cell will display a different, random colour.
This model is going to represent the table view and each collection view. We'll use an array; each object represents a table cell. These objects are themselves arrays, with each of their objects representing a collection view cell.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.colorArray.count;
}
Next we'll implement our UICollectionViewDataSource methods.
-(NSInteger)collectionView:(AFIndexedCollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
NSArray *collectionViewArray = self.colorArray[collectionView.index];
return collectionViewArray.count;
}
-(UICollectionViewCell *)collectionView:(AFIndexedCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CollectionViewCellIdentifier forIndexPath:indexPath];
NSArray *collectionViewArray = self.colorArray[collectionView.index];
cell.backgroundColor = collectionViewArray[indexPath.item];
return cell;
}
You'll see we're using the index property of the collection view to determine the appropriate model to use. Notice that the view controller doesn't retain references to any of the collection views – they belong to the UITableViewCells only. This is great, since they'll be re-used and save memory.
But where is the index getting set? We'll need to do that, too.
-(void)tableView:(UITableView *)tableView willDisplayCell:(AFTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[cell setCollectionViewDataSourceDelegate:self index:indexPath.row];
}
The only thing left to do is "remember" the content offset of each cell as we end displaying it to reset it when we begin displaying it again. I we don't do this, newly displayed collection views will have non-zero content offsets and returning collection views will be in different positions. We'll use an NSMutableDictionary to remember the content offsets.
-(void)tableView:(UITableView *)tableView willDisplayCell:(AFTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[cell setCollectionViewDataSourceDelegate:self index:indexPath.row];
NSInteger index = cell.collectionView.index;
CGFloat horizontalOffset = [self.contentOffsetDictionary[[@(index) stringValue]] floatValue];
[cell.collectionView setContentOffset:CGPointMake(horizontalOffset, 0)];
}
-(void)tableView:(UITableView *)tableView didEndDisplayingCell:(AFTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat horizontalOffset = cell.collectionView.contentOffset.x;
NSInteger index = cell.collectionView.index;
self.contentOffsetDictionary[[@(index) stringValue]] =
@(horizontalOffset);
}