I've constructed an UICollectionView in a storyboard and implemented all of its required data source and delegate methods in the view controller. In the storyboard, I checked the Section Header property on the collection view and set the header view's class to a subclass of UICollectionResusableView (in the storyboard).
From here, I dragged in two UI elements onto the header view via the storyboard--a label and a segmented control:
When the program is executed, the label appears in the header view of the collection view (with no actual code required), but the segmented control does not. However, when a segmented control is dragged onto a typical UIView, it displays and is manipulatable with no code required. Even when instantiated through code in an IBOutlet, the segmented control does not appear.
Why is the segmented control not visible on the collection view's header while it is in a typical UIView, and why does the label display without issue?
UPDATE
Here is the init method for the custom header view, in which I attempted adding the segmented control programmatically (as opposed to in the storyboard):
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_segmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"One", #"Two", nil]];
[_segmentedControl setFrame:CGRectMake(0, 0, 100, 50)];
[_segmentedControl addTarget:self action:#selector(segmentedControlChanged:) forControlEvents:UIControlEventValueChanged];
[self addSubview:_segmentedControl];
}
return self;
}
As requested, here is the -[UICollectionReusableView viewForSupplementaryElementOfKind:] method in the main view controller:
- (UICollectionReusableView *)collectionView:(UICollectionView *)cv viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
GalleryHeader *headerView = [cv dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
return headerView;
}
I'm not able to reproduce your storyboard problem, it works just fine for me when i add the segmented control by dragging it directly in the storyboard (with no code required). As for your alternative way of adding it programmatically, the problem here is that when a view is initialized from the storyboard (as it is in this case), the initWithCoder initializer method is used (not the initWithFrame initializer method). Hence, if you override that method, inserting the code in there, it should work:
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if(self){
_segmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"One", #"Two", nil]];
_segmentedControl.bounds = CGRectMake(0, 0, 100, 50);
[_segmentedControl addTarget:self action:#selector(segmentedControlChanged:) forControlEvents:UIControlEventValueChanged];
[self addSubview:_segmentedControl];
}
return self;
}
P.S. It doesnt affect this specific case, but you should just do:
GalleryHeader *headerView = [cv dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
instead of:
GalleryHeader *headerView = [cv dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
As it is the collection view that will ask for the right kind of view, you should worry about specifying it!
EDIT: The steps i followed to create the header from storyboard are:
Select the collection view and tick the box labelled Section Header
Select the newly created header and select the right class in the identity inspector
Give the header section a unique identifier
Drag the UI elements in the header in the storyboard (i also changed its background color)
Finally implement the collectionView:viewForSupplementaryElementOfKind:atIndexPath: method in your collection view's data source class
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath
{
return [self.collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"collectionViewHeader" forIndexPath:indexPath];
}
Let me know if you can spot any difference between what you did and what I did!
Related
I have a UICollectionView that uses a couple of different layouts depending on user preferences. I want certain cells to show up for some layouts but not for others. How do I accomplish this? Do I actually need to reload the collection view data?
There are two steps to accomplishing this (which also works with UITableView).
Update the data source. If you're using an NSDictionary or NSArray, then you'll need to add or remove the items you want to show/hide.
Call reloadData on the UICollectionView or UITableView. That's pretty much it.
If you want to remove or add with an animation, that's different. There are a couple more methods in the middle that you have to call and make sure that your update sequence is correct. But that is a different question altogether.
EDIT:
As an example of how to use an array
- (void)methodCalledWhenLayoutChanges:(BOOL)includeOptionalString {
if (includeOptionalString) {
[_collectionViewDataSourceArray addObject:_optionalString];
} else {
[_collectionViewDataSourceArray removeObject:_optionalString];
}
[self.collectionView reloadData];
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return _collectionViewDataSourceArray.count;
}
// Never actually setup a collection view like this. This is just an example of how to reference a data source for creating a collection view cell.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [[UICollectionViewCell alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 50.0f, 50.0f)];
UILabel *textLabel = [[UILabel alloc] initWithFrame:cell.bounds];
textLabel.text = [_collectionViewDataSourceArray objectAtIndex:indexPath.row];
[cell addSubview:textLabel];
return cell;
}
I use the following code to create a custom view and add it to the contentView of a UICollectionViewCell
-(UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell* newcell;// = [[UICollectionViewCell alloc] init];
newcell = [collectionView dequeueReusableCellWithReuseIdentifier:#"hi" forIndexPath:indexPath];
TT2TimeViewController* controller = [[TT2TimeViewController alloc] init];
NSString* city = [cities objectAtIndex:indexPath.row];
controller.city = city;
UIView* view = [[[NSBundle mainBundle] loadNibNamed:#"TT2TimeViewController"
owner:controller
options:nil] objectAtIndex:0];
[newcell.contentView addSubview:view];
return newcell;
}
In the xib of the TT2TimeViewController, I have placed a UILabel in the center and want the controller to update the label text to the value of the city variable.
However for some reason the label is not updated as expected. I have double-checked and ensured the IBOutlet is created and valid.
This is the code of the controller class TT2TimeViewController:
#import "TT2TimeViewController.h"
#implementation TT2TimeViewController
-(void) viewDidLoad {
[self.lblCity setText:[self city]];
}
-(void)viewDidAppear:(BOOL)animated {
[self.lblCity setText:[self city]];
}
Neither functions is called.
One more screen shot to show how the file owner is set:
So my question is: Have I done anything wrong in the implementation of the function collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath?
I found out the solution: I need to link the 'view' variable of the controller to the view object in the xib file, like so:
Then it starts to work.
(The odd thing is: only viewDidAppear get called)
I have a UICollectionView controller embedded inside a navigation controller. The collectionView lists projects and each cell is supposed to segue to a ProjectDetail screen.
I simply cannot get the segue to trigger. If I simply drop a button on the nav bar and hook up a segue to the detail, it works. But triggering from my CollectionView cell doesn't.
Here is what the storyboard looks like: http://cl.ly/RfcM I do have a segue hooked up from the CollectionViewCell to the ProjectDetailViewController
Here's the relevant code inside my ProjectDetailViewController:
#interface ProjectCollectionViewController () {
NSArray *feedPhotos;
Projects *projects;
}
#end
#implementation ProjectCollectionViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[FeedViewCell class] forCellWithReuseIdentifier:#"cell"];
[self loadData];
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"selected %d", indexPath.row);
Project *project = [projects getProject:indexPath.row];
NSLog(#"project = %#", project);
}
- (void)loadData {
[self.projectLoader loadFeed:self.username
onSuccess:^(Projects *loadedProjects) {
NSLog(#"view did load on success : projects %#", loadedProjects);
projects = loadedProjects;
[self.collectionView reloadData];
}
onFailure:^(NSError *error) {
[self handleConnectionError:error];
}];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return projects.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"cell";
FeedViewCell *cell = (FeedViewCell *) [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:1.0];
UIImageView *cellImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
Project *project = [projects getProject:indexPath.row];
NSString *imageUrl = [project coverPhotoUrl:200 forHeight:200];
NSLog(#"imageurl =>%#", imageUrl);
if (imageUrl) {
[cellImageView setImageWithURL:[NSURL URLWithString:imageUrl]];
}
[cell addSubview:cellImageView];
cell.imageView = cellImageView;
return cell;
}
I'm guessing the problem is somewhere in how I'm hooking up the Cells to the CollectionView.
Any help would be greatly appreciated!
You cannot create segues directly from cells in a storyboard because the collectionview is populated dynamically through the data source. You should use the collectionView:didSelectItemAtIndexPath: and perform the segue programatically using performSegueWithIdentifier:sender:. Something like this:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"MySegueIdentifier" sender:self];
}
where MySegueIdentifier is the identifier of the segue defined in storyboard.
TLDR: FOR A STORYBOARD, do not call registerClass:forCellWithReuseIdentifier:. It overrides what the storyboard sets up for the cell (including how segues are handled):
How to set a UILabel in UICollectionViewCell
Brief setup
Used a storyboard
Created a new collection view controller using the Xcode template,
setting it as a subclass of UICollectionViewController.
Initially used the default UICollectionViewCell, adding a UILabel
programmatically.
The generated UICollectionViewController code registered the cell in viewDidLoad:
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
First Issue:
The prepareForSegue:sender: event was not firing, which brought me to this answer .
I implemented the UICollectionViewDelegate and collectionView:didSelectItemAtIndexPath: event, then called the segue programmatically.
This fixed my first issue.
Second Issue: I switched to a custom cell containing one label. After hooking everything up, the cell label was not displaying.
After some digging, I found a solution contained in the link at the top of my answer.
Third Issue and Solution: I removed the registerClass:forCellWithReuseIdentifier: line. When I ran my app, the label appeared correctly, but when I tapped a cell, it called the prepareForSegue:sender event twice. By removing the registerClass:forCellWithReuseIdentifier line, the cell was processing cell touches directly, without the need of the delegate method. This is how I expected the storyboard to work. I deleted the collectionView:didSelectItemAtIndexPath: event, which resolved the double-firing of prepareForSegue:sender:. If you are using a storyboard, do not register the cell class. It overwrites what storyboard sets up.
Have you made your CollectionView Cell's connection in Triggered Segues on selection?
You can also trigger a segue programatically using
[self performSegueWithIdentifier:#"segueIdentifier" sender:nil]
in
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
Equivalent Swift code for similar question.
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier(#"TargetSegway", sender: self)
}
Make sure, in case if your cell has other overlapping views, "User Interaction Enabled" is unchecked (you can find this option, under attribute inspector View/Interaction). Otherwise, your Tap Gesture is consumed by the overlapping view, didSelectItemAtIndexPath may not be called.
I have a UITableView where I have section headers that can be tapped to expand or collapse the section. In my particular example each section only has one row, which is either visible (section expanded) or hidden (section collapsed).
As section header i'm using custom UITableViewHeaderFooterView - HeaderAccountView. I created *.xib file at Interface Builder , and set it custom class to my HeaderAccountView (still at IB field).
There are no any changes to init method or smth like this in my HeaderAccountView.h and HeaderAccountView.m files - only some functions to highlight self (selected section) etc.
in my main ViewController .m file
- (void)viewDidLoad
{
[super viewDidLoad];
.........
.........
UITableView *tableView = (id)[self.view viewWithTag:1];
UINib *nib= [UINib nibWithNibName:#"HeaderAccountView" bundle:nil];
[tableView registerNib:nib forHeaderFooterViewReuseIdentifier:#"HeaderCell"];
}
and then
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
HeaderAccountView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:#"HeaderCell"];
if (headerView==nil)
{ headerView = [[HeaderAccountView alloc]
initWithReuseIdentifier:#"HeaderCell"];
}
return headerView;
}
when i'm running project everything going OK - sections load with needed data in it, when section receive tap - it highlights (like standard cell).
But when i'm scrolling away tableview to bottom for example from selected highlighted section, and this highlighted section already is not visible at view - that section that just appeared from bottom - already highlighted!
I understand that its because it creates new instance of my HeaderAccountView with property BOOL selected set to YES.
But I'm new to objective-c (and coding) and don't understand how to correct resolve this.
I tried to use prepareForReuse method of my custom UITableViewHeaderFooterView like this
HeaderAccountView.m:
-(void) prepareForReuse
{
self.selectedBackground.alpha = 0;
}
It works better - but now i have another issue - when i returning to my first (truly) selected and highlighted section - it obviously don't highlight.
Thanks for any help and sorry if it elementary question.
You have to manually keep a list of your selected headers indexes.
Next, implement the method tableView:willDisplayHeaderView: in your view controller to refresh your header when it will be displayed.
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
view.selectedBackground.alpha = ([_highlightedHeadersList containsObject:#(section)] ? 0.0f : 1.0f);
}
And you have to add / remove indexes in _highlightedHeadersList.
I've done this using the following. In the table view controller I created a property (NSInteger), and called it sectionForSelectedHeader. Set it to -1 initially so no section will be initially selected.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
RDHeader *header = [tableView dequeueReusableHeaderFooterViewWithIdentifier:#"Header"];
header.tag = section;
if (header.gestureRecognizers.count == 0) {
UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(headerSelected:)];
[header addGestureRecognizer:tapper];
}
header.selected = (self.sectionForSelectedHeader == section)? 1 : 0;
return header;
}
-(void)headerSelected:(UITapGestureRecognizer *) tapper {
if ([(RDHeader *)tapper.view selected] != 1) {
self.sectionForSelectedHeader = tapper.view.tag;
}else{
self.sectionForSelectedHeader = -1;
}
[self.tableView reloadData];
}
Then in my custom header subclass, I have a method setSelected: (selected is an NSInteger property in the RDHeader class) like this:
-(void)setSelected:(NSInteger)selected {
_selected = selected;
if (selected) {
self.contentView.backgroundColor = [UIColor orangeColor];
}else{
self.contentView.backgroundColor = [UIColor yellowColor];
}
}
I have a UICollectionView, which receives data from a webservice. Based on the data it receives, it draws cells on my UICollectionView. Each cell is a custom Class which extends UICollectionViewCell. Each UICollectionViewCell is loaded via a .nib. My init method looks like this :
#implementation GridCell
- (id)initWithFrame:(CGRect)frame
{
if (self)
{
// Initialization code
NSArray *arrayOfViews = [[NSBundle mainBundle] loadNibNamed:#"GridCell" owner:self options:nil];
if ([arrayOfViews count] < 1) {
return nil;
}
if (![[arrayOfViews objectAtIndex:0] isKindOfClass:[UICollectionViewCell class]]) {
return nil;
}
self = [arrayOfViews objectAtIndex:0];
// It is important that you set the properties of the view after the above assignment
// because self is assigned to the nib after that call.
self.layer.masksToBounds = NO;
self.layer.shadowColor = [[UIColor blackColor] CGColor];
self.layer.shadowOffset = CGSizeMake(0.0, 1.0);
self.layer.shadowOpacity = 0.17;
self.layer.shadowRadius = 0.35f;
self.layer.shouldRasterize = YES;
}
return self;
}
I have a GridViewController which is basically a UIViewController :
#implementation GridViewController
- (id)initWithSize:(CGFloat)frameWidth :(CGFloat)frameHeight {
self = [super init];
if(self) {
// Have the getGridView returning the initialized view
// then assign it to the GridViewControllers view property
self.view = [self getGridView:frameWidth:frameHeight];
}
return self;
}
-(UIView *)getGridView:(CGFloat)width :(CGFloat)height {
UIView *gridHolder = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
UICollectionViewFlowLayout *flow = [[UICollectionViewFlowLayout alloc]init];
[flow setScrollDirection:UICollectionViewScrollDirectionHorizontal];
CGRect screen = [[UIScreen mainScreen] bounds];
CGFloat screenheight = screen.size.height;
// It is an iphone 5, setup the cellsize and the spacing between cells.
if(screenheight > 480) {
[flow setItemSize:CGSizeMake( (gridHolder.frame.size.width / 1.85) , (gridHolder.frame.size.height / 3.25) )];
[flow setSectionInset:UIEdgeInsetsMake(0, 0, 0, 0)];
[flow setMinimumInteritemSpacing:0];
[flow setMinimumLineSpacing:10];
// It is an iphone 4, setup the cellsize and the spacing between cells.
} else {
[flow setItemSize:CGSizeMake( (gridHolder.frame.size.width / 2.5) , (gridHolder.frame.size.height / 3.1) - 10)];
[flow setSectionInset:UIEdgeInsetsMake(0, 0, 0, 0)];
[flow setMinimumInteritemSpacing:0];
[flow setMinimumLineSpacing:10];
}
self.grid = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, gridHolder.frame.size.width, gridHolder.frame.size.height) collectionViewLayout:flow];
[_grid setBackgroundColor:[UIColor colorWithRed:0.961 green:0.961 blue:0.961 alpha:1]];
[_grid setDataSource:self];
[_grid setDelegate:self];
[_grid setBounces:NO];
[_grid registerClass:[GridCell class] forCellWithReuseIdentifier:Cell];
[gridHolder addSubview:self.grid];
return gridHolder;
}
This UIViewController is also my UICollectionViewDataSource. So i have the following methods for it :
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [releases count];
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(10, 10, 10, 10);
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
GridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:Cell forIndexPath:indexPath];
return cell; // <-- finally return the cell
}
I added an UIButton over the cell. And i connected an IBAction to the button. But each time i click on this button, i get a nullpointer exception because the GridIcon is already thrown away by ARC. Must i retain the GridIcon somewhere? Or what is the best practice to overcome this (simple?) problem?
The method which resides in my GridIcon.m is :
- (IBAction)cellClicked {
NSLog(#"test");
}
And my GridIcon.h file has an IBAction described :
#interface GridCell : UICollectionViewCell
- (IBAction)cellClicked;
#end
Edit :
jackslash, thank you for your time and effort to make me realize this really mad structure. Let me explain how i setup this project.
I have a MainViewController which i set as the RootViewController in my AppDelegate.m. Then in my MainViewController i init 2 UIVIewController's. One of them is my GridViewController which sets the UICollectionView.
So the datasource for this UICollectionView is the UIViewController i init'ed. I needed to draw a region of the screen for this UIViewController so i give the height of the other UIViewController to the GridViewController. And the getGridView method gets the height of the screen minus the height of the view of the other UIViewConroller's view.
The structure is like this :
#interface MainViewController: UIViewController
// These next two classes both extend UIViewController
#property(nonatomic, strong) GridViewController *gridViewcontroller;
#property(nonatomic, strong) BottomBarController *bottomBarcontroller;
#end
In my MainViewController.m [viewDidLoad] i both init those UIViewcontrollers and add their views as a subView to the MainViewController.
What would be the best way to accomplish this then? Should i make a UICollectionViewController separate from my GridViewController? But how does this UICollectionView knows which region of the screen to draw on?
And for the init method within my GridCell, i actually use this to draw a shadow underneath my GridCell. How would i achieve the drawing underneath my cells without this init method?
Edit 2:
In addition to my above edit:
I have an UIImageView and a UILabel on each GridCell. How would i target those in my code?
So whats happening here is messed up in quite a few ways.
Your GridCell init method is mental
You have left some objective-c method parameters unnamed (bad form)
You have a method - (id)initWithSize:(CGFloat)frameWidth :(CGFloat)frameHeight which one would expect to take a CGSize from the name yet it takes two floats
For some reason you are using the -(id)initWithSize:: method to init your view controller
Your GridCell class files have a different name from the class they contain (bad form)
You seem to fundamentally have misunderstood how all this is supposed to work. I'm going to help you here, but bear in mind that you should take the time to understand everything here and to also go and read a lot more about iOS. Perhaps look at the iOS course on iTunes U (CS193p) which is free and will help you write better code.
1.
This is madness. If you want to load your UICollectionViewCell subclass from a xib you dont do this, you register the nib with the collection view in a UICollectionViewController subclass like this:
UINib * cellNib = [UINib nibWithNibName:#"GridCell" bundle:[NSBundle mainBundle]];
[self.collectionView registerNib:cellNib forCellWithReuseIdentifier:cellReuseIdentifier];
Then the cell is loaded from the xib file for you when you get the cell in
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
Make sure you have the class of the cell in your GridCell.xib file set to your GridCell subclass. Never write an init method like this again.
2.
-(UIView *)getGridView:(CGFloat)width :(CGFloat)height
Always name all the parameters in an Objective-c method signature.
-(UIView *)getGridViewWithWidth:(CGFloat)width andHeight:(CGFloat)height
Is miles and miles better. But you would probably better using a CGSize...
3
- (id)initWithSize:(CGFloat)frameWidth :(CGFloat)frameHeight
In Objective-c we would expect this to take a CGSize parameter because your method is named "initWithSize". A CGSize is a C struct with a width and height. You can make one with CGSizeMake(<width>,<height>). You would make your init method look like this:
- (id)initWithSize:(CGSize)size
But you don't really want to be initing a UIViewController with a CGSize...
4
There is a reason UIViewController does not have ANY kind of init method that takes any kind of size or dimension. The idea of a view controller is that it has the size of its view set by its parent (or window) and then is responsible for drawing its contents within its view, whatever size that may be. As a UIViewController subclass In UIKit there is a place for loading other views and view controllers that are needed to display whatever it is you are supposed to display. Check out the method -(void)viewDidLoad and override it. But even so you would be much better off if you simply subclassed UICollectionViewController as you wouldn't need any sort of container and your view controller's view property would already be a collectionView along with many other benefits.
5
Rename your GridIcon.h and GridIcon.m files to be the name of the class they contain. Always follow this rule for the sanity of future you, if not everyone else who works on your project.
Finally the reason its crashing isn't because of ARC or other technology failure its because the implementation here has real fundamental issues.