Last active
May 30, 2018 08:15
-
-
Save simonpang/7147727e7b2a38e2c737837018daf758 to your computer and use it in GitHub Desktop.
Dynamic collection view cell size using auto-layout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2018-05-30 13:08:05.132277+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:07.329048+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:09.120296+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:10.334120+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:10.860428+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:13.211666+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:15.438966+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:17.102895+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:17.539026+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:18.844853+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:20.902874+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:22.332761+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:22.627645+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:24.122012+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:24.475678+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:25.512129+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:25.885873+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:28.048902+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:30.458764+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:31.459993+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:31.586576+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:31.877040+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:32.800872+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:32.878281+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:33.182918+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:33.425012+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:34.492225+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:34.492506+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:34.866927+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:35.867912+0800 DemoScrolling[30506:1014563] set cell width to 200 | |
2018-05-30 13:08:36.111121+0800 DemoScrolling[30506:1014563] startLoading | |
2018-05-30 13:08:36.976462+0800 DemoScrolling[30506:1014563] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array' | |
*** First throw call stack: | |
( | |
0 CoreFoundation 0x000000010ace912b __exceptionPreprocess + 171 | |
1 libobjc.A.dylib 0x000000010a37df41 objc_exception_throw + 48 | |
2 CoreFoundation 0x000000010ad290cc _CFThrowFormattedException + 194 | |
3 CoreFoundation 0x000000010ac18e94 -[__NSArrayM objectAtIndex:] + 148 | |
4 UIKit 0x000000010c604f01 -[_UIFlowLayoutInfo setSize:forItemAtIndexPath:] + 363 | |
5 UIKit 0x000000010c578f86 -[UICollectionViewFlowLayout invalidationContextForPreferredLayoutAttributes:withOriginalAttributes:] + 304 | |
6 UIKit 0x000000010c52e05a -[UICollectionView _checkForPreferredAttributesInView:originalAttributes:] + 591 | |
7 UIKit 0x000000010c52eb62 -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:isFocused:notify:] + 974 | |
8 UIKit 0x000000010c52e78e -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:] + 35 | |
9 UIKit 0x000000010c533d00 -[UICollectionView _updateVisibleCellsNow:] + 4860 | |
10 UIKit 0x000000010c539c21 -[UICollectionView layoutSubviews] + 364 | |
11 UIKit 0x000000010bb2da6d -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1439 | |
12 QuartzCore 0x0000000110bf661c -[CALayer layoutSublayers] + 159 | |
13 QuartzCore 0x0000000110bfa7ad _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 401 | |
14 QuartzCore 0x0000000110b8186c _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 364 | |
15 QuartzCore 0x0000000110bae946 _ZN2CA11Transaction6commitEv + 500 | |
16 QuartzCore 0x0000000110baf694 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 76 | |
17 CoreFoundation 0x000000010ac8bc07 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23 | |
18 CoreFoundation 0x000000010ac8bb5e __CFRunLoopDoObservers + 430 | |
19 CoreFoundation 0x000000010ac70124 __CFRunLoopRun + 1572 | |
20 CoreFoundation 0x000000010ac6f889 CFRunLoopRunSpecific + 409 | |
21 GraphicsServices 0x000000010fd9e9c6 GSEventRunModal + 62 | |
22 UIKit 0x000000010ba5c5d6 UIApplicationMain + 159 | |
23 DemoScrolling 0x0000000109a6ef6f main + 111 | |
24 libdyld.dylib 0x000000010e7b0d81 start + 1 | |
) | |
libc++abi.dylib: terminating with uncaught exception of type NSException |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ViewController.m | |
// DemoScrolling | |
// | |
// Created by Simon Pang on 30/5/2018. | |
// Copyright © 2018 Simon Pang. All rights reserved. | |
// | |
#import "ViewController.h" | |
@interface ViewController () | |
@end | |
NSMutableDictionary<NSNumber *, NSNumber *> *cachedWidth; | |
UICollectionViewFlowLayout *globalLayout; | |
@interface AwesomeCell : UICollectionViewCell | |
- (void)setCellWidth:(CGFloat) width; | |
@property (nonatomic, strong) NSLayoutConstraint *cellWidthConstraint; | |
@end | |
@implementation AwesomeCell | |
- (instancetype)initWithFrame:(CGRect)frame | |
{ | |
self = [super initWithFrame:frame]; | |
if (self) { | |
self.contentView.translatesAutoresizingMaskIntoConstraints = NO; | |
self.cellWidthConstraint = [self.contentView.widthAnchor constraintEqualToConstant:0.f]; | |
} | |
return self; | |
} | |
- (void)setCellWidth:(CGFloat) width { | |
self.cellWidthConstraint.constant = width; | |
self.cellWidthConstraint.active = YES; | |
} | |
- (void)startLoading:(NSIndexPath *)indexPath { | |
NSLog(@"startLoading"); | |
[NSTimer scheduledTimerWithTimeInterval:1 repeats:NO block:^(NSTimer * _Nonnull timer) { | |
NSLog(@"set cell width to 200"); | |
cachedWidth[@(indexPath.row)] = @(200); | |
[self setCellWidth:200]; | |
//[globalLayout invalidateLayout]; | |
UICollectionViewFlowLayoutInvalidationContext *ctx = [UICollectionViewFlowLayoutInvalidationContext new]; | |
[ctx invalidateItemsAtIndexPaths:@[indexPath]]; | |
[globalLayout invalidateLayoutWithContext:ctx]; | |
}]; | |
} | |
@end | |
@implementation ViewController | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
// quick | |
cachedWidth = [[NSMutableDictionary<NSNumber *, NSNumber *> alloc] init]; | |
UICollectionViewFlowLayout *layout = (id) self.collectionView.collectionViewLayout; | |
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; | |
layout.estimatedItemSize = CGSizeMake(100, 120); | |
globalLayout = layout; | |
[self.collectionView registerClass:[AwesomeCell class] forCellWithReuseIdentifier:@"cell"]; | |
} | |
- (void)didReceiveMemoryWarning { | |
[super didReceiveMemoryWarning]; | |
// Dispose of any resources that can be recreated. | |
} | |
#pragma mark UICollectionViewDelegate | |
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { | |
AwesomeCell *cell = (id) [collectionView cellForItemAtIndexPath:indexPath]; | |
[cell startLoading: indexPath]; | |
} | |
#pragma mark UICollectionViewDataSource | |
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { | |
AwesomeCell *cell = (id) [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath]; | |
cell.contentView.backgroundColor = [UIColor lightGrayColor]; | |
NSNumber *width = cachedWidth[@(indexPath.row)]; | |
if (width) { | |
[cell setCellWidth:[width floatValue]]; | |
} else { | |
[cell setCellWidth:100]; | |
} | |
return cell; | |
} | |
- (NSInteger)collectionView:(nonnull UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { | |
return 1000; | |
} | |
- (NSInteger)numberOfSections { | |
return 1; | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ViewController.m | |
// DemoScrolling | |
// | |
// Created by Simon Pang on 30/5/2018. | |
// Copyright © 2018 Simon Pang. All rights reserved. | |
// | |
#import "ViewController.h" | |
@interface ViewController () | |
@end | |
NSMutableDictionary<NSNumber *, NSNumber *> *cachedWidth; | |
UICollectionViewFlowLayout *globalLayout; | |
@interface AwesomeCell : UICollectionViewCell | |
@end | |
@implementation AwesomeCell | |
- (instancetype)initWithFrame:(CGRect)frame | |
{ | |
self = [super initWithFrame:frame]; | |
if (self) { | |
self.contentView.translatesAutoresizingMaskIntoConstraints = NO; | |
self.contentView.layer.borderWidth = 5; | |
self.contentView.layer.borderColor = UIColor.redColor.CGColor; | |
} | |
return self; | |
} | |
@end | |
@implementation ViewController | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
// quick | |
cachedWidth = [[NSMutableDictionary<NSNumber *, NSNumber *> alloc] init]; | |
UICollectionViewFlowLayout *layout = (id) self.collectionView.collectionViewLayout; | |
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; | |
//layout.estimatedItemSize = CGSizeMake(100, 120); // use delegate to return cell size | |
globalLayout = layout; | |
[self.collectionView registerClass:[AwesomeCell class] forCellWithReuseIdentifier:@"cell"]; | |
self.collectionView.prefetchDataSource = self; | |
} | |
#pragma mark UICollectionViewDelegate | |
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { | |
AwesomeCell *cell = (id) [collectionView cellForItemAtIndexPath:indexPath]; | |
//[cell startLoading: indexPath]; | |
} | |
#pragma mark UICollectionViewDataSource | |
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { | |
AwesomeCell *cell = (id) [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath]; | |
cell.contentView.backgroundColor = [UIColor lightGrayColor]; | |
// Trigger loading | |
NSNumber *width = cachedWidth[@(indexPath.row)]; | |
if (!width) { | |
[self startLoading: indexPath]; | |
} | |
return cell; | |
} | |
- (NSInteger)collectionView:(nonnull UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { | |
return 1000; | |
} | |
- (NSInteger)numberOfSections { | |
return 1; | |
} | |
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { | |
NSNumber *width = cachedWidth[@(indexPath.row)]; | |
if (width) { | |
return CGSizeMake([width floatValue], 120); | |
} | |
return CGSizeMake(100, 120); | |
} | |
#pragma mark UICollectionViewDataSourcePrefetching | |
- (void)collectionView:(UICollectionView *)collectionView prefetchItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths { | |
for (NSIndexPath *indexPath in indexPaths) { | |
// We must check if the item is cached | |
NSNumber *width = cachedWidth[@(indexPath.row)]; | |
if (!width) { | |
NSLog(@"Prefetching %@", indexPath); | |
[self startLoading:indexPath]; | |
} | |
} | |
} | |
- (void)startLoading:(NSIndexPath *)indexPath { | |
NSLog(@"startLoading"); | |
[NSTimer scheduledTimerWithTimeInterval:1 repeats:NO block:^(NSTimer * _Nonnull timer) { | |
NSLog(@"set cell width to 200"); | |
cachedWidth[@(indexPath.row)] = @(200); | |
[globalLayout invalidateLayout]; | |
// UICollectionViewFlowLayoutInvalidationContext *ctx = [UICollectionViewFlowLayoutInvalidationContext new]; | |
// [ctx invalidateItemsAtIndexPaths:@[indexPath]]; | |
// [globalLayout invalidateLayoutWithContext:ctx]; | |
}]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment