I have an UICollectionView with three different prototype cells, each of which with different heights set via Storyboard. During runtime, the Collection View uses its own cell size, ignoring my Storyboard ones.
I am currently using collectionView:layout:sizeForItemAtIndexPath: with a couple conditionals to set each CGSize straight.
Is there a better way to set the cell sizes? I don't seem to be able to retrieve the Storyboard size each cell has, and CGSizeMake seems too hardcoded and not really flexible.
It seems that there's currently no easy way to:
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 your UICollectionView
prototype cells in all Storyboards.
Add a Run script
Build Phase to your project with the script that pulls out UICollectionViewCell
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 your UICollectionView
:
- (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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With