Showing Cells in Demands in Uicollectionview with Vertical Infinite Scroll

Showing cells in demands in UICollectionView with vertical infinite scroll

Using the scrollViewDidScroll function like crazy_phage did above you can observe when finally reach the final of the CollectionView then you can update the numberOfRowInSections and call the reloadData in the following way :

class CollectionSampleViewController: UICollectionViewController {

private let reuseIdentifier1 = "Cell"
private var numberOfItemsPerSection = 9

override func viewDidLoad() {
super.viewDidLoad()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}

override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numberOfItemsPerSection
}

override func scrollViewDidScroll(scrollView: UIScrollView) {
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height

if offsetY > contentHeight - scrollView.frame.size.height {
numberOfItemsPerSection += 6
self.collectionView.reloadData()
}
}
}

When the reloadData function its called the scroll remains in the same place and the new data it's loaded.

In the above code you can use an Activity Indicator View or anything else you want to add to your code.

View with continuous scroll; both horizontal and vertical

You can get infinite scrolling, by using the technique of re-centering the UIScrollView after you get a certain distance away from the center. First, you need to make the contentSize big enough that you can scroll a bit, so I return 4 times the number of items in my sections and 4 times the number of sections, and use the mod operator in the cellForItemAtIndexPath method to get the right index into my array. You then have to override layoutSubviews in a subclass of UICollectionView to do the re-centering (this is demonstrated in the WWDC 2011 video, "Advanced Scroll View Techniques"). Here is the controller class that has the collection view (set up in IB) as a subview:

#import "ViewController.h"
#import "MultpleLineLayout.h"
#import "DataCell.h"

@interface ViewController ()
@property (weak,nonatomic) IBOutlet UICollectionView *collectionView;
@property (strong,nonatomic) NSArray *theData;
@end

@implementation ViewController

- (void)viewDidLoad {
self.theData = @[@[@"1",@"2",@"3",@"4",@"5"], @[@"6",@"7",@"8",@"9",@"10"],@[@"11",@"12",@"13",@"14",@"15"],@[@"16",@"17",@"18",@"19",@"20"]];
MultpleLineLayout *layout = [[MultpleLineLayout alloc] init];
self.collectionView.collectionViewLayout = layout;
self.collectionView.showsHorizontalScrollIndicator = NO;
self.collectionView.showsVerticalScrollIndicator = NO;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.view.backgroundColor = [UIColor blackColor];
[self.collectionView registerClass:[DataCell class] forCellWithReuseIdentifier:@"DataCell"];
[self.collectionView reloadData];
}


- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
return 20;
}

- (NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView {
return 16;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

DataCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"DataCell" forIndexPath:indexPath];
cell.label.text = self.theData[indexPath.section %4][indexPath.row %5];
return cell;
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
// UICollectionViewCell *item = [collectionView cellForItemAtIndexPath:indexPath];
NSLog(@"%@",indexPath);

}

Here is the UICollectionViewFlowLayout subclass:

#define space 5
#import "MultpleLineLayout.h"

@implementation MultpleLineLayout { // a subclass of UICollectionViewFlowLayout
NSInteger itemWidth;
NSInteger itemHeight;
}

-(id)init {
if (self = [super init]) {
itemWidth = 60;
itemHeight = 60;
}
return self;
}

-(CGSize)collectionViewContentSize {
NSInteger xSize = [self.collectionView numberOfItemsInSection:0] * (itemWidth + space); // "space" is for spacing between cells.
NSInteger ySize = [self.collectionView numberOfSections] * (itemHeight + space);
return CGSizeMake(xSize, ySize);
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path {
UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
attributes.size = CGSizeMake(itemWidth,itemHeight);
int xValue = itemWidth/2 + path.row * (itemWidth + space);
int yValue = itemHeight + path.section * (itemHeight + space);
attributes.center = CGPointMake(xValue, yValue);
return attributes;
}


-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSInteger minRow = (rect.origin.x > 0)? rect.origin.x/(itemWidth + space) : 0; // need to check because bounce gives negative values for x.
NSInteger maxRow = rect.size.width/(itemWidth + space) + minRow;
NSMutableArray* attributes = [NSMutableArray array];
for(NSInteger i=0 ; i < self.collectionView.numberOfSections; i++) {
for (NSInteger j=minRow ; j < maxRow; j++) {
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:j inSection:i];
[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
}
return attributes;
}

And finally, here is the subclass of UICollectionView:

-(void)layoutSubviews {
[super layoutSubviews];
CGPoint currentOffset = self.contentOffset;
CGFloat contentWidth = self.contentSize.width;
CGFloat contentHeight = self.contentSize.height;
CGFloat centerOffsetX = (contentWidth - self.bounds.size.width)/ 2.0;
CGFloat centerOffsetY = (contentHeight - self.bounds.size.height)/ 2.0;
CGFloat distanceFromCenterX = fabsf(currentOffset.x - centerOffsetX);
CGFloat distanceFromCenterY = fabsf(currentOffset.y - centerOffsetY);

if (distanceFromCenterX > contentWidth/4.0) { // this number of 4.0 is arbitrary
self.contentOffset = CGPointMake(centerOffsetX, currentOffset.y);
}
if (distanceFromCenterY > contentHeight/4.0) {
self.contentOffset = CGPointMake(currentOffset.x, centerOffsetY);
}
}

Snap to center of a cell when scrolling UICollectionView horizontally

While originally I was using Objective-C, I since switched so Swift and the original accepted answer did not suffice.

I ended up creating a UICollectionViewLayout subclass which provides the best (imo) experience as opposed to the other functions which alter content offset or something similar when the user has stopped scrolling.

class SnappingCollectionViewLayout: UICollectionViewFlowLayout {

override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) }

var offsetAdjustment = CGFloat.greatestFiniteMagnitude
let horizontalOffset = proposedContentOffset.x + collectionView.contentInset.left

let targetRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionView.bounds.size.width, height: collectionView.bounds.size.height)

let layoutAttributesArray = super.layoutAttributesForElements(in: targetRect)

layoutAttributesArray?.forEach({ (layoutAttributes) in
let itemOffset = layoutAttributes.frame.origin.x
if fabsf(Float(itemOffset - horizontalOffset)) < fabsf(Float(offsetAdjustment)) {
offsetAdjustment = itemOffset - horizontalOffset
}
})

return CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y)
}
}

For the most native feeling deceleration with the current layout subclass, make sure to set the following:

collectionView?.decelerationRate = UIScrollViewDecelerationRateFast



Related Topics



Leave a reply



Submit