Dear readers,
I had successfully used UICollectionView with dynamic heights of images which fetches data from NSURL string. iOS 6.0 and later versions can use "UICollectionView" to make a model like "Water Flow" or "Pinterest Layout".
Please see below steps to have it in your application.
A. Include these two files into your project source directory.
1.) " CHTCollectionViewWaterfallLayout.h"
2.) " CHTCollectionViewWaterfallLayout.m"
B. You can download these files from CHTCollectionViewWaterfallLayout .
C. Import "ImageIO.framework" into project.=======================================================================
*** STEP 2:
A) Generate two class files “CategoryCell.h” and “CategoryCell.m” for UICollectionViewCell.
#import "FXImageView.h"
@interface CategoryCell : UICollectionViewCell
@property (nonatomic, strong) UIButton *btnPhoto;
@property (nonatomic, strong) FXImageView *photoView;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIView *bottomView;
#import "CategoryCell.h"
const CGFloat kTMPhotoQuiltViewMargin = 5;
@implementation CategoryCell
@synthesize photoView = _photoView;
@synthesize titleLabel = _titleLabel;
@synthesize btnPhoto = _btnPhoto;
@synthesize bottomView = _bottomView;
#pragma mark - Accessors
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, self.contentView.bounds.size.width, 30.0)];
_titleLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.textColor = WHITE_COLOR;
if (IS_IPAD) {
_titleLabel.font = FONT_BOLD(14.0);
}
else
{
_titleLabel.font = FONT_BOLD(12.0);
}
_titleLabel.textAlignment = NSTextAlignmentCenter;
}
return _titleLabel;
}
- (UIView *)bottomView {
if (!_bottomView) {
_bottomView = [[UIView alloc] initWithFrame:CGRectMake(0.0, self.contentView.bounds.size.height - 30.0, self.contentView.bounds.size.width, 30.0)];
_bottomView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
_bottomView.backgroundColor = BLACK_COLOR;
_bottomView.alpha = 0.6;
}
return _bottomView;
}
- (FXImageView *)photoView {
if (!_photoView) {
_photoView = [[FXImageView alloc] initWithFrame:self.contentView.bounds];
_photoView.asynchronous = YES;
_photoView.contentMode = UIViewContentModeScaleAspectFit;
_photoView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_photoView.backgroundColor = CLEAR_COLOR;
_photoView.layer.shadowOpacity = 2.0;
_photoView.layer.shadowColor = LIGHT_GRAY_COLOR.CGColor;
_photoView.layer.shadowRadius = 2.0;
_photoView.layer.shadowOffset = CGSizeMake(-0.5, 0.5);
}
return _photoView;
}
- (UIButton *)btnPhoto{
if (!_btnPhoto) {
_btnPhoto = [UIButton buttonWithType:UIButtonTypeCustom];
_btnPhoto.frame = self.contentView.bounds;
_btnPhoto.contentMode = UIViewContentModeScaleAspectFill;
_btnPhoto.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_btnPhoto.backgroundColor = CLEAR_COLOR;
_btnPhoto.layer.shadowOpacity = 5.0;
_btnPhoto.layer.shadowColor = LIGHT_GRAY_COLOR.CGColor;
_btnPhoto.layer.shadowRadius = 5.0;
_btnPhoto.layer.shadowOffset = CGSizeMake(-1.0, 1.0);
}
return _btnPhoto;
}
#pragma mark - Life Cycle
#if !__has_feature(objc_arc)
- (void)dealloc {
[_photoView removeFromSuperview];
_photoView = nil;
[_titleLabel removeFromSuperview];
_titleLabel = nil;
[super dealloc];
}
#endif
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self.contentView addSubview:self.photoView];
[self.contentView addSubview:self.btnPhoto];
[self.bottomView addSubview:self.titleLabel];
[self.contentView addSubview:self.bottomView];
}
return self;
}
@end
========================================================================
*** STEP 3:
#import <ImageIO/ImageIO.h>
#import "CHTCollectionViewWaterfallLayout.h"
#define CELL_WIDTH 150.0
#define CELL_IDENTIFIER @"GCCategoryCell"
#import "CategoryCell.h"
@interface CategoryViewController () <UICollectionViewDataSource, CHTCollectionViewDelegateWaterfallLayout>
{
NSMutableArray *cellHeights;
NSMutableArray *arrCategory;
UICollectionView *collectionView;
CGFloat cellWidth;
}
@property (nonatomic, strong) NSMutableArray *cellHeights;
@property (nonatomic, strong) NSMutableArray * arrCategory;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic) CGFloat cellWidth;
@implementation CategoryViewController
@synthesize cellHeights;
@synthesize arrCategory;
@synthesize collectionView;
@synthesize cellWidth;
- (void)viewDidLoad
{
self.arrCategory = [[NSMutableArray alloc] init];
self.cellWidth = CELL_WIDTH; // Default if not setting runtime attribute
CHTCollectionViewWaterfallLayout *layout = [[CHTCollectionViewWaterfallLayout alloc] init];
layout.sectionInset = UIEdgeInsetsMake(0.0, 10, 10, 5.0);
layout.headerHeight = 0;
layout.footerHeight = 0;
if (IS_IPAD) {
layout.minimumColumnSpacing = 30;
layout.minimumInteritemSpacing = 30;
}
else
{
layout.minimumColumnSpacing = 5;
layout.minimumInteritemSpacing = 5;
}
CGRect frame;
if (IS_IPAD) {
frame = CGRectMake(0, lblTitle.frame.size.height, self.view.frame.size.width, self.view.frame.size.height-36.0);
}
else
{
frame = CGRectMake(0, lblTitle.frame.size.height, self.view.frame.size.width, self.view.frame.size.height-(Appdel.objTab.frame.size.height + 36.0));
}
self.collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout];
self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
self.collectionView.backgroundColor = [UIColor clearColor];
[self.collectionView registerClass:[GCCategoryCell class] forCellWithReuseIdentifier:CELL_IDENTIFIER];
[self.view addSubview:self.collectionView];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self updateLayoutForOrientation:[UIApplication sharedApplication].statusBarOrientation];
}
#pragma mark --------------------------
#pragma mark Accessors
#pragma mark --------------------------
- (NSMutableArray *)cellHeights
{
if (!cellHeights)
{
cellHeights = [[NSMutableArray alloc] init];
}
return cellHeights;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation
duration:duration];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {
[self updateLayoutForOrientation:toInterfaceOrientation];
}
}
- (void)updateLayoutForOrientation:(UIInterfaceOrientation)orientation
{
CHTCollectionViewWaterfallLayout *layout = (CHTCollectionViewWaterfallLayout *)self.collectionView.collectionViewLayout;
layout.columnCount = UIInterfaceOrientationIsPortrait(orientation) ? 2 : 3;
}
========================================================================
*** STEP 4:
// Call this method first to fill height array after you objects list
-(void)fillHeightArray
{
for (NSMutableDictionary *dictGCCatInfo in self.arrCategory)
{
NSNumber *width = [NSNumber numberWithFloat:200];
NSNumber *height = [NSNumber numberWithFloat:200];
NSString *urlString = [dictGCCatInfo valueForKey:@"Photo"];
NSURL *imageFileURL = [NSURL URLWithString:urlString];
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)imageFileURL, NULL);
if (imageSource) {
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:NO], (NSString *)kCGImageSourceShouldCache, nil];
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
if (imageProperties) {
width = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
height = (NSNumber *)CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
CFRelease(imageProperties);
}
}
CFRelease(imageSource); // Added
CGSize size = CGSizeMake([width floatValue], [height floatValue]);
[self.cellHeights addObject:[NSValue valueWithCGSize:size]];
}
[self.collectionView reloadData];
}
#pragma mark -----------------------------
#pragma mark UICollectionViewDataSource
#pragma mark -----------------------------
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [arrCategory count];
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView1 cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CategoryCell *cell = (CategoryCell *)[collectionView1 dequeueReusableCellWithReuseIdentifier:CELL_IDENTIFIER
forIndexPath:indexPath];
NSString *urlString = [[arrCategory objectAtIndex:indexPath.row] valueForKey:@"Photo"];
if (urlString)
{
[cell.photoView setProcessedImage:nil];
//set image
[cell.photoView setImageWithContentsOfURL:[NSURL URLWithString:urlString]];
}
else
{
[cell.photoView setImage:[UIImage imageNamed:@"no-image.png"]];
}
[cell.photoView setTag:indexPath.row];
[cell.btnPhoto setTag:indexPath.row];
cell.titleLabel.text = [[arrCategory objectAtIndex:indexPath.row] valueForKey:@"GiftCertificateCatName"];
[cell.btnPhoto addTarget:self action:@selector(btnCategoryClicked:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
#pragma mark ------------------------------------
#pragma mark CHTCollectionViewDelegateWaterfallLayout
#pragma mark ------------------------------------
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
CGSize size = [[self.cellHeights objectAtIndex:indexPath.row] CGSizeValue];
return size;
}
Hope, you will fully enjoy this post about UICollectionView.
Best Regards,
Nilesh M. Prajapati
- (NSMutableArray *)cellHeights
ReplyDelete{
if (!cellHeights)
{
cellHeights = [[NSMutableArray alloc] init];
}
return cellHeights;
}
crashing after this... cellHeights is nil ... outbound index error
Hi,
DeleteSorry for delay in reply as was busy with my current project. There's no longer need of this function. So, you can initialise once at the time of loading view.
#import "FXImageView.h" which file is this
ReplyDeleteHi,
DeleteSorry for that "FXImageView.h" is asynchronous image downloading & auto caching framework which was used by me. You can removed it from your code.
i use this example for Pinterest like layout. i have 10 objects with image size of 10*150 and 600*600. Five objects are 150 and five object with 600. but it take 150 for all the 10 objects. please help me for that.
ReplyDeletei have 10 objects with image url and used this tutorial for Pinterest like layout. five images in that objects are size 150*150 and others five have 600*600. but my all cell are display with 150 size. please help me for that.
ReplyDeleteAs, I had mentioned in my code that you had to fetch heights of your object in "cellHeight" array first and then utilize that array for height as well. So, now you have to use this array for width & height of an object and you have to write your own formula for object's width & height in mobile device using actual height and width of an object by considering the column count as well.
DeleteI have mentioned about the formula which you can use with non-customized collection view. For above example you just need to use the size parameter directly from the array in below method :
Delete- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath