UICollectionView with infinite scrolling? - ios

I'm attempting to implement a collection view with an infinite scrolling behaviour. The scrolling should be circular - like in a UIPickerView. Can it be done with Apple's UICollectionView or there is no choice but to create a horizontal PickerView ?
What do you think?

You should watch the WWDC 2011 session video "Advanced ScrollView Techniques", they talk about infinite scrolling, and I'm pretty sure it's possible to use that with UICollectionViews. It recenters while you scroll, and places the new items after the current visible items (or before, depending on the scroll direction).
Like they say in the session, you should not use an approach that has an ending. Like stated in the session: People will definitely find the edge at one point.

Yes, you can do this. Return a very large number for collectionView:numberOfItemsInSection:, and in collectionView:cellForItemAtIndexPath:, you use the mod operator to always return a number for indexPath.Row that's between 0 and the last index in your data array. So if you had 20 items in your array, you would do something like this:
item.whatever.text = [self.theData objectAtIndex:indexPath.row %20];

Thanks to rdelmar for a tricky solution. That idea of "large number for collectionView:numberOfItemsInSection" worked like a charm. I have tried to implement the same with UIScrollView delegate methods. But the output was jerky.
Here's some part of my code.
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 100;}
// CellforRow
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// Setup cell identifier
static NSString *cellIdentifier = #"cvCell";
CVCell *cell;
cell = (CVCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[cell.titleLabel setText:[self.dataArray objectAtIndex:(indexPath.row)%self.dataArray.count]];
return cell;
}
// DidSelect
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"Index path is %d", indexPath.row%self.dataArray.count);
}
This will give infinite scrolling effect.
To enable infinite scrolling both sides, use ScrolltoIndexpath:(middle of Itemscount) in ViewWillAppear !!
Hope this will help someone who are about to build an infinite scrolling UICollectionView.

Override this method to enable infinite scrolling both ways. It center content whenever it is too far from the center and user never hits the end.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint currentOffset = self.collectionView.contentOffset;
CGFloat contentWidth = self.collectionView.contentSize.width;
CGFloat centerOffsetX = (contentWidth - self.collectionView.bounds.size.width) / 2.0;
CGFloat distanceFromCenter = fabs(currentOffset.x - centerOffsetX);
if (distanceFromCenter > (contentWidth / 4.0)) {
self.collectionView.contentOffset = CGPointMake(centerOffsetX, currentOffset.y);
}
}

Related

UICollectionView reuse cell issue

I have a problem with collection view cell. When my collection view first loaded, it display items like that:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
CalendarCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"CalendarCell" forIndexPath:indexPath];
[cell bindDate:_datesMgr.currentMonthItems[indexPath.row] andNowDate:_datesMgr.nowDate];
// bind events
if (_eventsMgr.eventsArray.count > 0){
for (int i = 0; i < _eventsMgr.eventsArray.count ; i ++) {
[cell bindConference:_eventsMgr.eventsArray[i]];
}
}
return cell;
}
Inside those methods are logic for adding subviews to custom cell class, which depend on certain circumstances.
Its all work, but, when collection view reloaded (i did force reload after 1 second) some of cells are reused and placed on others, therefore, it show "old" images and subviews.
I could see possible solution in forcing uicollection view to stop reusing cells (it is, load new cells every time). Is there any way to do this?
Try to implement prepareForReuse method for reset your old content in your custom UICollectionViewCell
-(void)prepareForReuse {
[super prepareForReuse];
self.yourimageview.image = nil; //and etc.
}

In collection view One item should be shown at a time in screen?

In my project i have collection ,in which horizontal pagination is enabled, Every thing working fine but, when i scroll,particular cell item not fit entire cell,It show half image of previous cell item and half current Items, i need One item to fit the entire screen ??? Any suggestion are welcome
Here is the code:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath; {
SingleItemCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"SingleItemCollectionViewCell" forIndexPath:indexPath];
[cell ConfigaureCollectionViewItem:self.totalCellItems[indexPath.row]]; return cell;
}
-(CGSize)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize size = CGSizeMake(self.bounds.size.width - 20, self.bounds.size.height - 40);
return size;
}
Perhaps you should be using UIPageViewController in stead of UICollectionView. From your question it seems that you are trying to mimic the behaviour of the former.

UITextView auto resize with autolayout

I'm building a status item kind of thing for a UICollectionView. My problem is when I want to add some text to the status area I can't get the thing to auto resize to the new text. I have auto layout on and I've tried all kinds of things found on stacky.
The one which I think is the closest to being correct is this:
-(UICollectionViewCell *) collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
StatusItemModel *statusModel = [self.items objectAtIndex[indexPath indexPosition:0]];
StatusItemEventCell *statusCell = [collectionView dequeueResusableCellwithReuseIdentifier: #"EventStatusItem" forIndexPath:indexPath];
statusCell.statusTitleLabel.text = [statusModel.statusDetails valueForKey:#"title"];
statusCell.statusContentTextView.text = [statuaModel.statusDetails valueForKey:#"content"];
[statusCell layoutIfNeeded];
return statusCell;
}
// After which I believe we have to do some magic in this but what?
- (CGSize) collectionView:(UiCollectionView *) collectionView layout:(UICollectionViewLayout *) collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
// How do I get the size of the textview statusContentTextView.text?
// With that I'll be able to figure out what needs to be returned.
return CGSizeMake(299.f, 200.f);
}
The autolayout is setup with constraints for all elements in the cell. I've even played around with the intrinsic size and placeholders, however still now luck. Please can someone point me in the right direction.
So after going around in circles thinking there was a better way, no we need to know the size before we can set the size of the cell for the collection view. Pretty counter productive, because sometimes we don't know the size of it at run time. The way I solved this was to create a mock UITextView object and then called sizeThatFits.
So here is what I did with my code:
- (CGSize) collectionView:(UICollectionView *) collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
StatusItemModel *statusModel = [self.items objectAtIndex:[indexPath indexAtPosition:0]];
UITextView *temporaryTextView = [[UITextView alloc] init];
temporaryTextView.text = [statusModel.statusDetails valueForKey:#"content"];
CGSize textBoxSize = [temporaryTextView sizeThatFits:CGSizeMake(299.0f, MAXFLOAT)];
// Can now use the height of the text box to work out the size of the cell and
// the other components that make up the cell
return textBoxSize;
}

iOS UICollectionViewCells Layout with intersection

I try to make UICollectionView with cells, that intersect and partially overlay each other as it is done at screenshot:
This layout was reached by setting
self.minimumLineSpacing = -100;
at my UICollectionViewFlowLayout subclass.
When I scroll down, everything is OK. I see what I want. But when I scroll up, I see another behaviour, not like I expected:
So my question is: how can I make my layout look as at the first screen regardless scroll view direction.
Note: I have to support both iOS 6 and 7.
Thanks very much for any advices and any help.
Hmm, interesting. Since the collection view recycles cells, they are continuously added to and removed from the view hierarchy as they move on and off the screen. That being said, it stands to reason and when they are re-added to the view, they are simply added as subviews meaning that when a cell gets recycled, it now has the highest z-index of all of the cells.
One fairly pain-free way to rectify this would be to manually adjust the z position of each cell to be incrementally higher with the index path. That way, lower (y) cells will always appear above (z) the cells above (y) them.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"CELLID";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
if (cell.layer.zPosition != indexPath.row) {
[cell.layer setZPosition:indexPath.row];
}
return cell;
}
Found another sollution to solve this problem. We need to use UICollectionViewFlowLayout subclass.
#interface MyFlowLayout : UICollectionViewFlowLayout
#end
#implementation MyFlowLayout
- (void)prepareLayout {
[super prepareLayout];
// This allows us to make intersection and overlapping
self.minimumLineSpacing = -100;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *layoutAttributes = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes *currentLayoutAttributes in layoutAttributes) {
// Change zIndex allows us to change not only visible position, but logic too
currentLayoutAttributes.zIndex = currentLayoutAttributes.indexPath.row;
}
return layoutAttributes;
}
#end
Hope that helps someone else.

Scrollable GridView for iPad

I am implementing a Table for iPad and facing some major problems.
For the GridView I implemented my own subclass of UITableViewCell which works fine.
The data is shown correctly, but I have a problem when I want to access a single cell to go to some new detail view. Since one row only contains one cell, the didSelectRowAtIndexPath only gives me access to the complete cell, but I don't know which column the single cell is in.
Then I implemented a TapGestureRecognizer. This shows me the row and column and works, but only until I start scrolling... the column part still works, but the row is shown incorrect since the TapRecognizer overlaps the didSelectRowAtIndexPath (bad but not so important side effect.. there is no blue highlighting of the selected row).
Is there a way to find out how many pixels I scrolled? Or is there an even better solution?
I highly recommend using UICollectionView over those 3rd party classes. There are quite a few advantages to having access to all of the delegate protocols (like showing the cut copy paste UIMenuController on a long press without a UIGestureRecognizer, for example) I use one myself as a grid.
To acheive a grid layout, I did the following...
1) I set the following Delegates in my .h file:
#interface YourViewControllerWithCollectionView : UIViewController <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> {
}
#property (nonatomic, retain) IBOutlet UICollectionView *myCollectionView;
#end
Notice, that I did not set the UICollectionViewDelegate because UICollectionViewDelegateFlowLayout is actually a sub-protocol of UICollectionViewDelegate, so there is no need to set both.
2) In the .m file, synthesize the collection view, and in viewDidLoad declare the datasource and delegates: (don't forget to connect your outlets, and you might want to put a background color on the cell so you can see it)
#synthesize myCollectionView;
viewdidLoad:
- (void)viewDidLoad
{
self.myCollectionView.delegate = self;
self.myCollectionView.dataSource = self;
//...
}
3) Implement the datasource
#pragma mark - UICollectionView Datasource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
//the number of cells you want per row
return 4;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
//load sublassed UICollectionViewCell called MyCollectionViewCell
static NSString *cellIdentifier = #"cell";
MyCustomCollectionViewCell *cell = (MyCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.title.text = #"Title"
// customize the cell...
return cell;
}
5) Implement the UICollectionViewDelegateFlowLayout
#pragma mark – UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
//this is what forces the collectionview to only display 4 cells for both orientations. Changing the "-80" will adjust the horizontal space between the cells.
CGSize retval = CGSizeMake((myCollectionView.frame.size.width - 80) / 4, 78);
return retval;
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
// for the entire section, which we have set to 1, adjust the space at
// (top, left, bottom, right)
// keep in mind if you change this, you will need to adjust the retVal
// in the method above
return UIEdgeInsetsMake(5, 20, 10, 20);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
CGFloat interimSpacing = 0.0f;
return interimSpacing;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
CGFloat lineSpacing = 0.0f;
return lineSpacing;
}
6) Last, but certainly not least, invalidate the layout on orientation change to redraw the cells:
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration{
[self.myCollectionView.collectionViewLayout invalidateLayout];
}
And because you implemented UICollectionViewDelegateFlowLayout, you already have access to UICollectionViewDelegate to handle selection, etc. like:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCollectionViewCell *cell = (MyCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
//do something when a cell is tapped...
}
More information can be found here: http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12
Look at AQGridView or some of the other controls at CocoaControls.com.
I recommend AGAINST UICollectionView. UICollectionView is easy to use, but not stable enough at this moment. I am using GMGridView for my apps. After a few months operations, I can say that it is stable enough for the production release. Another alternative is PSTCollectionView, which is a 100% API compatible replacement for UICollectionView. However, it is unfinished and contains even more bugs than UICollectionView.
The disturbing issues I have with PSTCollectionView are:
poor performance if you want to display > 80 cells on screen
reloading sections is not implemented
decoration views are not implemented
The disturbing issues I have with UICollectionView are:
the items in the first column may disappear
inserting the first cell will crash
reloading sections with header view will crash
blurry text in cells
Check open radar
https://openradar.appspot.com/search?query=UICollectionView
for all current issues with UICollectionView.
I believe UICollectionView and PSTCollectionView will be good choices when they are stable. But at this moment, GMGridView is a better choice.

Resources