RGScrollLayoutCache
Pre-load layout of UITableView or UICollectionView in background thread
- RGScrollLayoutCache is a category of UIScrollview
- RGScrollLayoutCache could help UITableView or UICollectionView pre-load layout and save result in cache
- The auto cache loading mechanism refer to Example app using Photos framework
Installation
Add via CocoaPods by adding this to your Podfile:
pod 'RGScrollLayoutCache'
Usage
- Set cache delegate
[self.tableView rg_setLayoutCacheDelegate:self];
- Enable auto cache
self.tableView.rg_autoCache = YES;
- Else use custom cache
RGScrollLayoutCache could cooperate with UITableViewDataSourcePrefetching. However, the performance of this way is not satisfactory after testing.
- (void)tableView:(UITableView *)tableView prefetchRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths {
[tableView rg_startCachingLayoutForIndexPaths:indexPaths];
}
- (void)tableView:(UITableView *)tableView cancelPrefetchingForRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths {
[tableView rg_stopCachingLayoutForIndexPaths:indexPaths];
}
- RGLayoutCacheDelegate
// Do layout at this delegate.
- (CGSize)scrollView:(UIScrollView *)scrollView sizeForRowAtIndexPath:(NSIndexPath *)indexPath isMainThread:(BOOL)isMainThread {
// safe-get size whether in the main thread or in the background thread
CGSize size = scrollView.rg_nowFrame.size;
// get data source. ⚠️realm database need get a new instance in other thread.
RLMRealm *realm = nil
if (!isMainThread) {
realm = self.caCheRealm;
[realm refresh];
} else {
realm = self.realm;
}
// do layout with size and data source
return size;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [tableView rg_layoutCacheSizeAtIndexPath:indexPath].height;
}
- Clear cache when data source changed or scrollView size changed
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
CGRect last = self.tableView.rg_lastFrame;
// width is related to layout in this example.
if (self.tableView.frame.size.width != last.size.width) {
[self.tableView rg_updateLastFrame];
[self.tableView rg_clearlayoutCache];
[self.tableView reloadData];
}
}
[self.tableView rg_clearlayoutCacheAtIndexPaths:indexPaths];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
- Enable log
[UIScrollView rg_setCacheLogEnable:YES];
Test Report
This demo generates 1000 rows of text randomly and sleep 0.01s in layout method
-
Demo GIF
-
Layout code
#pragma mark - RGLayoutCacheDelegate
- (CGSize)scrollView:(UIScrollView *)scrollView sizeForRowAtIndexPath:(NSIndexPath *)indexPath isMainThread:(BOOL)isMainThread {
[NSThread sleepForTimeInterval:0.01];
CGSize size = scrollView.rg_nowFrame.size;
size.width -= (52 + 20 + 20 + 20);
size.height = CGFLOAT_MAX;
NSString *string = self.fakeData[indexPath.row];
size = [string
boundingRectWithSize:size
options:NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13.f]}
context:nil].size;
size.height = MAX(60, size.height);
size.height += 40;
return size;
}
- 25 times layout in main thread
- 33 times layout wait for background thread done
- 551 times layout in background thread
- 0 times cancel layout