CocoaPods trunk is moving to be read-only. Read more on the blog, there are 18 months to go.

RGScrollLayoutCache 1.1.0

RGScrollLayoutCache 1.1.0

Maintained by Renge.



 
Depends on:
RGRunTime>= 0
RGObserver>= 0
 

  • By
  • RengeRenge

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

#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

load_in_main_sync

  • 33 times layout wait for background thread done

hit for async

  • 551 times layout in background thread

load in cahce queue

  • 0 times cancel layout

cancel in cache queue