{"id":24510,"date":"2015-07-30T12:21:49","date_gmt":"2015-07-30T06:51:49","guid":{"rendered":"http:\/\/www.tothenew.com\/blog\/?p=24510"},"modified":"2015-07-30T12:55:55","modified_gmt":"2015-07-30T07:25:55","slug":"ios-working-with-collection-view-layouts","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/ios-working-with-collection-view-layouts\/","title":{"rendered":"iOS : Working with collection view layouts"},"content":{"rendered":"<p>In this blog we are going to discuss about \u201cCollection View Custom Layouts\u201d and \u201cSwitching between two collection view layouts\u201d in <a title=\"Native iOS Application Development Services\" href=\"http:\/\/www.tothenew.com\/mobile-ios-application-development-services\">iOS development<\/a>.<\/p>\n<p>First of all we will know about Collection View Layouts.<\/p>\n<p style=\"text-align: left;\"><strong>What Is a Layout?<\/strong><\/p>\n<p style=\"text-align: left;\">UICollectionViewLayout is an abstract class that should not be created itself; its only purpose is to be subclassed. Each collection view has a layout associated with it whose job it is to lay content out. Layouts are not concerned with the data contained in the views they lay out. they are only interested in their layout to the user. We can use UICollectionViewFlowLayout and UICollectionViewLayout to create our own layouts.\u00a0To use layout we need to create subclass of layout class.<\/p>\n<p style=\"text-align: left;\"><strong>How it works?<\/strong><\/p>\n<p style=\"text-align: left;\">First, the collection view interrogates its data source for information about the contents to be displayed to the user. This includes the number of sections and the number of items and supplementary views in each individual section.<br \/>\nNext, the collection view gathers information from its layout object about how to display the cells, supplementary views, and decoration views. This information is stored in instances of a class called UICollectionViewLayoutAttributes.<br \/>\nFinally, the collection view forwards information about the layout to the cells, supplementary views, and decoration views. Each of these classes is responsible for using the information it has been given to apply those layout attributes to itself. Deferring to the superclass\u2019s implementation, or omitting an implementation entirely, will ensure that the layout attributes already handled by the collection view, like frame, are applied. Your implementations should concentrate on any custom attributes that you\u2019ve added.<br \/>\nThese steps occur whenever the existing layout is invalidated, which you can force by calling invalidateLayout on the layout object.<br \/>\nNow we are aware of the different classes used in laying content out:<\/p>\n<p><strong>UICollectionView:<\/strong> which is the view that presents content to the user.<br \/>\n<strong>UICollectionViewCell:<\/strong> which is responsible for displaying one unit of content to a user at a time.<br \/>\n<strong>UICollectionViewLayout:<\/strong> which determines the attributes of items and returns that information to the collection view.<br \/>\n<strong>UICollectionViewLayoutAttributes:<\/strong> which is a class in which the layout stores information to be marshalled to the cells, supplementary views, and decoration views. The class contains the following properties, which are applied to items at runtime:<br \/>\n1. Frame (convenience property for centre and size)<br \/>\n2. Center<br \/>\n3. Size<br \/>\n4. 3D Transform<br \/>\n5. Alpha (opacity)<br \/>\n6. Z-index<br \/>\n7. Hidden<br \/>\n8. Element category (cell, supplementary view, or decoration view)<br \/>\n9. Element kind (nil for cells)<\/p>\n<p>Now we will create two layouts of collection view and switching between them.<\/p>\n<p>Steps :<br \/>\n<strong>1. Create a project<\/strong><br \/>\n\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014<\/p>\n<p><strong>2. Create Controller which will extends UICollectionViewController<\/strong><br \/>\nViewController code includes following code<\/p>\n<p>[code language=&#8221;objc&#8221;]<br \/>\n#import &quot;ViewController.h&quot;<\/p>\n<p>#import &quot;CollectionViewFlowLayout.h&quot;<\/p>\n<p>#import &quot;CollectionViewCircleLayout.h&quot;<\/p>\n<p>#import &quot;CollectionViewCell.h&quot;<\/p>\n<p>static NSString *CellIdentifier = @&quot;CellIdentifier&quot;;<\/p>\n<p>@interface ViewController ()<\/p>\n<p>@property (nonatomic,assign) NSInteger cellCount;<br \/>\n@property (nonatomic, strong) CollectionViewCircleLayout *circleLayout;<br \/>\n@property (nonatomic, strong) CollectionViewFlowLayout *flowLayout;<br \/>\n@property (nonatomic, strong) UISegmentedControl *layoutChangeSegmentedControl;<\/p>\n<p>@end<\/p>\n<p>@implementation ViewController<\/p>\n<p>&#8211; (void)viewDidLoad {<br \/>\n[super viewDidLoad];<\/p>\n<p>self.layoutChangeSegmentedControl = [[UISegmentedControl alloc] initWithItems:@[@&quot;Circle Layout&quot;, @&quot;Flow Layout&quot;]];<br \/>\nself.layoutChangeSegmentedControl.selectedSegmentIndex = 0;<br \/>\n[self.layoutChangeSegmentedControl addTarget:self action:@selector(layoutChangeSegmentedControlDidChangeValue:) forControlEvents:UIControlEventValueChanged]; self.navigationItem.titleView = self.layoutChangeSegmentedControl;<\/p>\n<p>self.circleLayout = [[CollectionViewCircleLayout alloc] init];<\/p>\n<p>self.flowLayout = [[CollectionViewFlowLayout alloc] init];<\/p>\n<p>self.collectionView.collectionViewLayout = self.circleLayout;<\/p>\n<p>\/\/ Register our classes so we can use our custom subclassed \/\/ cell and header<br \/>\n[self.collectionView registerClass:[CollectionViewCell class]<br \/>\nforCellWithReuseIdentifier:CellIdentifier];<\/p>\n<p>self.cellCount = 20;<br \/>\n}<br \/>\n\/\/ Do any additional setup after loading the view, typically from a nib.<\/p>\n<p>&#8211; (void)didReceiveMemoryWarning {<br \/>\n[super didReceiveMemoryWarning];<br \/>\n\/\/ Dispose of any resources that can be recreated.<br \/>\n}<\/p>\n<p>-(void)layoutChangeSegmentedControlDidChangeValue:(id)sender<br \/>\n{<br \/>\n\/\/ We need to explicitly tell the collection view layout<br \/>\n\/\/ that we want the change animated.<br \/>\nif (self.collectionView.collectionViewLayout == self.circleLayout)<br \/>\n{<br \/>\n[self.flowLayout invalidateLayout];<br \/>\n[self.collectionView setCollectionViewLayout:self.flowLayout animated:YES];<br \/>\n}else<br \/>\n{<br \/>\n[self.circleLayout invalidateLayout];<br \/>\n[self.collectionView setCollectionViewLayout:self.circleLayout<br \/>\nanimated:YES];<br \/>\n}<br \/>\n}<\/p>\n<p>&#8211; (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section;<br \/>\n{<br \/>\nreturn self.cellCount;<br \/>\n}<\/p>\n<p>-(UICollectionViewCell *)collectionView: (UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;<br \/>\n{<br \/>\nCollectionViewCell *cell = (CollectionViewCell *)<br \/>\n[collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];<br \/>\n[cell setLabelString:[NSString stringWithFormat:@&quot;%ld&quot;, (long)indexPath.row]];<br \/>\nreturn cell;<br \/>\n}<\/p>\n<p>@end<\/p>\n<p>[\/code]<\/p>\n<p>\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014<\/p>\n<p><strong>3. Create Simple Flow Layout : Will extend \u201cUICollectionViewFlowLayout\u201d<\/strong><\/p>\n<p>[code language=&#8221;objc&#8221;]<br \/>\n#import &quot;CollectionViewFlowLayout.h&quot;<\/p>\n<p>@implementation CollectionViewFlowLayout<\/p>\n<p>-(id)init<br \/>\n{<br \/>\nself = [super init];<br \/>\nif(self)<br \/>\n{<br \/>\nself.itemSize = CGSizeMake(60, 60);<br \/>\nself.sectionInset = UIEdgeInsetsMake(13.0f, 13.0f, 13.0f, 13.0f); self.minimumInteritemSpacing = 13.0f;<br \/>\nself.minimumLineSpacing = 13.0f;<br \/>\n}<br \/>\nreturn self;<\/p>\n<p>}<br \/>\n@end<\/p>\n<p>[\/code]<\/p>\n<p>\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014<\/p>\n<p><strong>4. Create Circle Layout : Will extend \u201cUICollectionViewLayout\u201d<\/strong><\/p>\n<p>[code language=&#8221;objc&#8221;]<br \/>\n#import &quot;CollectionViewCircleLayout.h&quot;<\/p>\n<p>#define kItemDimension 60<\/p>\n<p>@interface CollectionViewCircleLayout ()<\/p>\n<p>@property(nonatomic,assign)NSInteger cellCount;<br \/>\n@property(nonatomic,assign)CGPoint center;<br \/>\n@property(nonatomic,assign)NSInteger radius;<\/p>\n<p>@end<\/p>\n<p>@implementation CollectionViewCircleLayout<\/p>\n<p>\/\/ optional method called every time collection view reload data or collection view invalidate layout.<br \/>\n\/\/ Here we will do some useful tasks which are needed before loading collection view cells.<br \/>\n\/\/ 1st method which is called before loading of collection view.<\/p>\n<p>-(void)prepareLayout<br \/>\n{<br \/>\n[super prepareLayout];<br \/>\nCGSize size = self.collectionView.bounds.size;<br \/>\nself.cellCount = [[self collectionView] numberOfItemsInSection:0];<br \/>\nself.center = CGPointMake(size.width \/ 2.0, size.height \/ 2.0);<br \/>\nself.radius = MIN(size.width, size.height) \/ 2.5;<br \/>\n}<\/p>\n<p>\/\/ 2nd method which is called after prepareLayout<\/p>\n<p>-(CGSize)collectionViewContentSize<br \/>\n{<br \/>\n\/\/ calculate here for collection view content size. Try to avoid it.<br \/>\nCGRect bounds = [[self collectionView] bounds];<br \/>\nreturn bounds.size;<br \/>\n}<\/p>\n<p>\/\/ If the layout supports any supplementary or decoration view types, it should also implement the respective atIndexPath: methods for those types.<\/p>\n<p>\/\/ 3rd method which is called after collectionViewContentSize<br \/>\n\/\/- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect<br \/>\n\/\/{<br \/>\n\/\/<br \/>\n\/\/}<\/p>\n<p>&#8211; (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path<br \/>\n{<\/p>\n<p>UICollectionViewLayoutAttributes* attributes =<br \/>\n[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];<br \/>\nattributes.size = CGSizeMake(kItemDimension, kItemDimension);<br \/>\nattributes.center = CGPointMake(self.center.x + self.radius * cosf(2 * path.item * M_PI \/ self.cellCount &#8211; M_PI_2), self.center.y + self.radius * sinf(2 * path.item * M_PI \/ self.cellCount &#8211; M_PI_2));<br \/>\nattributes.transform3D = CATransform3DMakeRotation((2 * M_PI * path.item \/ self.cellCount), 0, 0, 1);<\/p>\n<p>return attributes;<\/p>\n<p>}<\/p>\n<p>-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect<br \/>\n{<br \/>\nNSMutableArray* attributes = [NSMutableArray array];<br \/>\nfor (NSInteger i = 0 ; i &amp;lt; self.cellCount; i++)<br \/>\n{<br \/>\nNSIndexPath* indexPath = [NSIndexPath<br \/>\nindexPathForItem:i inSection:0];<br \/>\n[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];<br \/>\n}<br \/>\nreturn attributes;<br \/>\n}<\/p>\n<p>@end<\/p>\n<p>[\/code]<\/p>\n<p><strong>Below is the flow of layout :<\/strong><\/p>\n<p>1. <strong>prepareLayout<\/strong> is called on the layout so it has an opportunity to perform any up- front computations.<br \/>\n2. <strong>collectionViewContentSize<\/strong> is called on the layout to determine the collection view\u2019s content size.<br \/>\n3. <strong>layoutAttributesForElementsInRect<\/strong>: is called.<br \/>\nThen, the layout becomes live and continues to call <strong>layoutAttributesForElementsInRect<\/strong>: and <strong>layoutAttributesForItemAtIndexPath<\/strong>: until the layout becomes invalidated. Then, the process is repeated again.<br \/>\n\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014<br \/>\n<strong>5. Create collection view cell class<\/strong><\/p>\n<p>[code language=&#8221;objc&#8221;]<br \/>\n#import &quot;CollectionViewCell.h&quot;<\/p>\n<p>@interface CollectionViewCell ()<\/p>\n<p>@property (nonatomic, strong) UILabel *label;<\/p>\n<p>@end<\/p>\n<p>@implementation CollectionViewCell<\/p>\n<p>&#8211; (id)initWithFrame:(CGRect)frame<br \/>\n{<br \/>\nself = [super initWithFrame:frame];<\/p>\n<p>if(self)<br \/>\n{<br \/>\nself.backgroundColor = [UIColor orangeColor];<br \/>\nself.label = [[UILabel alloc] initWithFrame:self.contentView.bounds];<br \/>\nself.label.backgroundColor = [UIColor clearColor]; self.label.textAlignment = NSTextAlignmentCenter; self.label.textColor = [UIColor whiteColor]; self.label.font = [UIFont boldSystemFontOfSize:24];<br \/>\n[self.contentView addSubview:self.label];<br \/>\n}<br \/>\nreturn self;<br \/>\n}<\/p>\n<p>-(void)prepareForReuse<br \/>\n{<br \/>\n[super prepareForReuse];<br \/>\n[self setLabelString:@&quot;&quot;];<br \/>\n}<\/p>\n<p>-(void)setLabelString:(NSString *)labelString<br \/>\n{<br \/>\nself.label.text = labelString;<br \/>\n}<\/p>\n<p>\/\/***********************************************************************<br \/>\n#pragma mark &#8211;<br \/>\n#pragma mark &#8211; Cell layout related methods<br \/>\n#pragma mark &#8211;<br \/>\n\/\/***********************************************************************<\/p>\n<p>\/**<br \/>\n* Called when layout changed of a cell. This can be used to change cell layout<br \/>\n* according to current layout.<br \/>\n* @author Harish<br \/>\n*<br \/>\n* @param layoutAttributes : attributes of cell in case using custom layouts<br \/>\n* @return nil<br \/>\n*\/<\/p>\n<p>-(void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes<br \/>\n{<br \/>\n[super applyLayoutAttributes:layoutAttributes];<\/p>\n<p>self.label.center = CGPointMake( CGRectGetWidth(self.contentView.bounds) \/ 2.0f, CGRectGetHeight(self.contentView.bounds) \/ 2.0f);<br \/>\n}<\/p>\n<p>@end<\/p>\n<p>[\/code]<\/p>\n<p>Now run project and You will get something like :<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-24513\" src=\"\/blog\/wp-ttn-blog\/uploads\/2015\/07\/iOS-Simulator-Screen-Shot-30-Jul-2015-12.09.59-pm.png\" alt=\"iOS Simulator Screen Shot 30-Jul-2015 12.09.59 pm\" width=\"1536\" height=\"2048\" \/> <img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-24514\" src=\"\/blog\/wp-ttn-blog\/uploads\/2015\/07\/iOS-Simulator-Screen-Shot-30-Jul-2015-12.10.02-pm.png\" alt=\"iOS Simulator Screen Shot 30-Jul-2015 12.10.02 pm\" width=\"1536\" height=\"2048\" \/> <img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-24515\" src=\"\/blog\/wp-ttn-blog\/uploads\/2015\/07\/iOS-Simulator-Screen-Shot-30-Jul-2015-12.10.05-pm.png\" alt=\"iOS Simulator Screen Shot 30-Jul-2015 12.10.05 pm\" width=\"1536\" height=\"2048\" \/><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this blog we are going to discuss about \u201cCollection View Custom Layouts\u201d and \u201cSwitching between two collection view layouts\u201d in iOS development. First of all we will know about Collection View Layouts. What Is a Layout? UICollectionViewLayout is an abstract class that should not be created itself; its only purpose is to be subclassed. [&hellip;]<\/p>\n","protected":false},"author":155,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":26},"categories":[1],"tags":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/24510"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/155"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=24510"}],"version-history":[{"count":0,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/24510\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=24510"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=24510"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=24510"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}