Background
I am implementing a UICollectionView
(for the first time) in an effort to achieve a paged horizontal scroll view of tiles. I'd like each tile to show in the center of the frame with it's sister tiles partially visible to the left and right (something like the page selector in the Safari app). I'm interested in using the UICollectionView
to take advantage of built-in cell dequeueing and would rather not use a rotated UITableView
.
Issue
The issue I'm finding is that when using pagingEnabled = YES
and clipsToBounds = NO
, the UICollectionView
removes cells outside the collectionView
frame (they're not in the visibleCells
array) as soon as paging is complete. Can anyone provide advice on how to achieve the effect of displaying previews of the sister tiles while maintaining this basic setup? Or am I approaching this incorrectly?
Screenshots
start scrolling end
The scrolling screen is exactly correct. But in the start and end shots I want there to be green visible in the blue margins.
Here's what's in my AppDelegate.m (credit to tutsplus.com for the basic setup here):
#import "AppDelegate.h"
@interface ViewController : UICollectionViewController
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"ID"];
[self.view setBackgroundColor:[UIColor blueColor]];
// pad the collection view by 20 px
UIEdgeInsets padding = UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0);
[self.collectionView setFrame:UIEdgeInsetsInsetRect(self.view.frame, padding)];
// set pagingEnabled and clipsToBounds off
[self.collectionView setPagingEnabled:YES];
[self.collectionView setClipsToBounds:NO];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 5;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ID" forIndexPath:indexPath];
UILabel *label = [[UILabel alloc] initWithFrame:cell.bounds];
label.textAlignment = NSTextAlignmentCenter;
label.text = [NSString stringWithFormat:@"%d", indexPath.row];
[label setBackgroundColor:[UIColor greenColor]];
[cell.contentView addSubview:label];
return cell;
}
@end
@implementation AppDelegate
{
ViewController *vc;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// setup the UICollectionViewFlowLayout
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(280, 280);
layout.minimumInteritemSpacing = 0;
layout.minimumLineSpacing = 0;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
// add a custom UICollectionViewController to the window
vc = [[ViewController alloc] initWithCollectionViewLayout:layout];
self.window.rootViewController = vc;
self.window.backgroundColor = [UIColor yellowColor];
[self.window makeKeyAndVisible];
return YES;
}
@end
Turns out the solution to this was actually quite simple. I just needed to overlap the
UICollectionViewCell
cells by enough pixels to have them still show within the collectionView's frame after the paged scrolling finishes. The relevent code wasAnd I subclassed the
UICollectionViewFlowLayout
and overrode the(CGSize)collectionViewContentSize
method to return the non-overlapped size of the cells.I'm not an expert in collectionView, but it could be possibly do with this line in cellForItemAtIndexPath:
Everytime it's called, another label subview is added to cell. Either check for an existing label or subclass UICollectionViewCell?
Many thanks for the tip about using a negative minimumLineSpacing. I created a tester application which uses a collection view cell loaded from a xib file. The cell has a transparent background and an “inner” view for the cell's content.
In this way, a custom flow layout is not necessary.
https://github.com/j4johnfox/CollectionViewTester
You'll want to also override
-pointInside:withEvent:
to allow scroll gestures to start outside the frame of the collection view. I do this using aUIEdgeInsets
property in my collection view subclass:If you don't need App Store safety, you can override
_visibleBounds
to avoid negative spacing hacks:If you're not too pressed on code size and need App Store safety you could also subclass PSTCollectionView and possibly override
visibleBoundRects
for the same effect.