Infinite scroll implementation as a category for UIScrollView.
Be aware that this category swizzles setContentOffset
and setContentSize
on UIScrollView
.
* The content used in demo app is publicly available and provided by hn.algolia.com and Flickr. Both can be inappropriate.
Just add the following line in your Podfile:
pod 'UIScrollView-InfiniteScroll'
Objective-C:
// Somewhere in your implementation file
#import <UIScrollView+InfiniteScroll.h>
// ...
- (void)viewDidLoad {
[super viewDidLoad];
// change indicator view style to white
self.tableView.infiniteScrollIndicatorStyle = UIActivityIndicatorViewStyleWhite;
// setup infinite scroll
[self.tableView addInfiniteScrollWithHandler:^(UITableView* tableView) {
//
// fetch your data here, can be async operation,
// just make sure to call finishInfiniteScroll in the end
//
// finish infinite scroll animation
[tableView finishInfiniteScroll];
}];
}
Swift (with bridging header):
override func viewDidLoad() {
super.viewDidLoad()
// change indicator view style to white
tableView.infiniteScrollIndicatorStyle = .White
// Add infinite scroll handler
tableView.addInfiniteScrollWithHandler { (scrollView) -> Void in
let tableView = scrollView as! UITableView
//
// fetch your data here, can be async operation,
// just make sure to call finishInfiniteScroll in the end
//
tableView.finishInfiniteScroll()
}
}
UICollectionView#reloadData
causes contentOffset to reset. Please use UICollectionView#performBatchUpdates
instead when possible.
Objective-C:
// Somewhere in your implementation file
#import <UIScrollView+InfiniteScroll.h>
// ...
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
[self.collectionView addInfiniteScrollWithHandler:^(UICollectionView* collectionView) {
//
// fetch your data here, can be async operation,
// just make sure to call finishInfiniteScroll in the end
//
// suppose this is an array with new data
NSArray *newStories;
NSMutableArray *indexPaths = [NSMutableArray new];
NSInteger index = weakSelf.allStories.count;
// create index paths for affected items
for(Story *story in newStories) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index++ inSection:0];
[weakSelf.allStories addObject:story];
[indexPaths addObject:indexPath];
}
// Update collection view
[collectionView performBatchUpdates:^{
// add new items into collection
[collectionView insertItemsAtIndexPaths:indexPaths];
} completion:^(BOOL finished) {
// finish infinite scroll animations
[collectionView finishInfiniteScroll];
}];
}];
}
Swift:
override func viewDidLoad() {
super.viewDidLoad()
// Add infinite scroll handler
collectionView?.addInfiniteScrollWithHandler { [weak self] (scrollView) -> Void in
let collectionView = scrollView as! UICollectionView
// suppose this is an array with new data
let newStories = [Story]()
var indexPaths = [NSIndexPath]()
let index = self?.allStories.count
// create index paths for affected items
for story in newStories {
let indexPath = NSIndexPath(forItem: index++, inSection: 0)
indexPaths.append(indexPath)
self?.allStories.append(story)
}
// Update collection view
collectionView.performBatchUpdates({ () -> Void in
// add new items into collection
collectionView.insertItemsAtIndexPaths(indexPaths)
}, completion: { (finished) -> Void in
// finish infinite scroll animations
collectionView.finishInfiniteScroll()
});
}
}
You can use custom indicator instead of default UIActivityIndicatorView
.
Custom indicator must be a subclass of UIView
and implement the following methods:
- (void)startAnimating;
- (void)stopAnimating;
Objective-C:
// optionally you can use custom indicator view
CustomInfiniteIndicator *infiniteIndicator = [[CustomInfiniteIndicator alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
self.tableView.infiniteScrollIndicatorView = indicator;
Swift:
// optionally you can use custom indicator view
tableView.infiniteScrollIndicatorView = CustomInfiniteIndicator(frame: CGRectMake(0, 0, 24, 24))
Please see example implementation of indicator view:
-
Objective-C: CustomInfiniteIndicator.m
At the moment InfiniteScroll uses indicator's frame directly so make sure you size custom indicator view beforehand. Such views as UIImageView
or UIActivityIndicatorView
will automatically resize themselves so no need to setup frame for them.