It seems that there's currently no easy way to:
- Fetch UICollectionViewCell prototype cell sizes runtime from Storyboard(s).
- Manage sizes of prototype cells just in one place (rather than having to enter them in the Storyboard cell prototype and implement
sizeForItemAtIndexPath
).
A method proposed here (for UITableViews) does not work, because using dequeueReusableCellWithReuseIdentifier
in sizeForItemAtIndexPath
will cause an indefinite loop.
However, I've managed to do this the following way:
Add a unique (across all
UICollectionViewCell
s in every storyboard) reuse identifier into each of yourUICollectionView
prototype cells in all Storyboards.Add a
Run script
Build Phase to your project with the script that pulls outUICollectionViewCell
frame sizes from all Storyboards.output=${PROJECT_DIR}/StoryboardPrototypeCellSizes.h printf "@{" > $output for storyboard in $(find ${PROJECT_DIR} -name "*.storyboard") do echo "Scanning storyboard $storyboard..." delimiter= for line in $(xpath $storyboard "//collectionViewCell/@reuseIdentifier[string-length()>0] | //collectionViewCell/rect" 2>&-) do case $line in reuseIdentifier*) reuseIdentifier=$(sed 's/[^"]*"\([^"]*\)".*/\1/' <<< $line) ;; width*) if [ -n "$reuseIdentifier" ]; then width=$(sed 's/[^"]*"\([^"]*\)".*/\1/' <<< $line) fi ;; height*) if [ -n "$reuseIdentifier" ]; then height=$(sed 's/[^"]*"\([^"]*\)".*/\1/' <<< $line) fi ;; esac if [ -n "$reuseIdentifier" ] && [ -n "$width" ] && [ -n "$height" ]; then printf "$delimiter@\"$reuseIdentifier\" : [NSValue valueWithCGSize:CGSizeMake($width, $height)]" >> $output unset reuseIdentifier unset width unset height delimiter=,\\n fi done done printf "};\n" >> $output
This creates a header file called
StoryboardPrototypeCellSizes.h
with a following example content:@{@"TodayCell" : [NSValue valueWithCGSize:CGSizeMake(320, 80)], @"SpecialDayCell" : [NSValue valueWithCGSize:CGSizeMake(320, 42)], @"NameDayCell" : [NSValue valueWithCGSize:CGSizeMake(320, 30)]};
Add a helper method to return the
UICollectionViewCell
reuse identifier in the view controller controlling yourUICollectionView
:- (NSString *)cellReuseIdentifierAtIndexPath:(NSIndexPath *)indexPath { switch (indexPath.item) { case 0: return @"TodayCell"; case 1: return @"SpecialDayCell"; case 2: return @"NameDayCell"; } return nil; }
Be sure to use the same reuse identifier in
cellForItemAtIndexPath
:- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: [self cellReuseIdentifierAtIndexPath:indexPath] forIndexPath:indexPath]; ...
Finally implement
sizeForItemAtIndexPath
:- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { NSDictionary *storyboardPrototypeCellSizes = #import "StoryboardPrototypeCellSizes.h" return [(NSValue *)storyboardPrototypeCellSizes[ [self cellReuseIdentifierAtIndexPath:indexPath] ] CGSizeValue]; }
This solution allows you to define UICollectionViewCell
prototype cell sizes only once in the Storyboard(s) and also doesn't do any non-App-Store-compliant at runtime.
****Edit:**** You can also fetch UICollectionReusableView sizes by adding another script with the same content and replacing "collectionViewCell" with "collectionReusableView", and renaming the header file to, for example, StoryboardReusableViewSizes.h