Collection view is crashing for some reason - ios

//collection view
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
self.collectionView=[[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
[self.collectionView setDataSource:self];
[self.collectionView setDelegate:self];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cellIdentifier"];
[self.collectionView setBackgroundColor:[UIColor redColor]];
[self.view addSubview:self.collectionView];
}
- (NSInteger)collectionView:(UICollectionView *)tcollectionView numberOfItemsInSection:(NSInteger)section
{
return 15;
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
cell.backgroundColor=[UIColor greenColor];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(50, 50);
}
crash on :
-[NSISUnrestrictedVariable collectionView:numberOfItemsInSection:]: unrecognized selector sent to instance 0x7fce804111b0
What is that ?
(the collection view is inside a view controller,tand he is inside a scrollview)

It looks like your UICollectionView has been deallocated from memory but you are still trying to access it.
Enable NSZombies and you'll see where you make the call to the deallocated memory and fix the code accordingly.

Found the solution . As mentioned here, the collection view is deallocated from memory.
Reason is because that view controller that holds it, is inside a scrollview, and i didn't create it a property as strong to retain it , so :
//the view controller that holds the collection
#property(nonatomic,strong) BlisterView *blister;
solves the problem.

Related

Dynamically loading cells for UICollectionView

Hi I am very new for Ios and in my project I am using UICollectionView ok that's fine.
But here my main requirement is that I want to load the UICollectionView cells dynamically when we are scrolling the collectionView.
I mean when we launch the app then first "5" records need to load and when we are scrolling for the first time next "5" records need to load and when we scrolling for the second time the next "5" records need to be displayed and in that way all records need to load.
Please help me.
How can I do this?
my code:-
#import "ViewController.h"
#interface ViewController ()
{
UICollectionView * _collectionView;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
_collectionView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, 0, 320, 480) collectionViewLayout:layout];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cellIdentifier"];
[_collectionView setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:_collectionView];
UIView *refreshView = [[UIView alloc] initWithFrame:CGRectMake(0, _collectionView.frame.size.height - 50, 0, 0)];
[_collectionView addSubview:refreshView];
UIRefreshControl *refreshControl = [UIRefreshControl new];
refreshControl.tintColor = [UIColor redColor];
[refreshControl addTarget:self action:#selector(viewDidBeginRefreshing:) forControlEvents:UIControlEventValueChanged];
[refreshView addSubview:refreshControl];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return 5;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
for (id subview in cell.contentView.subviews) {
if ([subview isKindOfClass:[UIImageView class]]) {
[subview removeFromSuperview];
} else if ([subview isKindOfClass:[UILabel class]]) {
[subview removeFromSuperview];
}
}
cell.backgroundColor = [UIColor lightGrayColor];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake(70, 70);
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
UIEdgeInsets insets=UIEdgeInsetsMake(10, 10, 10, 10);
return insets;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 30.0;
}
Solution: You can add infinite scrolling to your existing UICollectionView use the following open source library to archive your requirement.
UIScrollView-InfiniteScroll
Solution 1: If you want to do infinite scrolling in a manner more like a desktop web app, you should implement the UIScrollViewDelegate protocol (or UICollectionViewDelegate, which is a superset) and listen for the scrollViewDidScroll: callback.
Within that callback, iterate through the UICollectionView's indexPathsForVisibleItems and check to see if any of the index paths map to the 'last' item in your collection, which indicates the user has scrolled to the end.
At that point, call your logic to load more stuff.
Solution 2:
In table view, you should load more data in willDisplayCell() method. With collections, there is no such a counterpart. But, you can do like this. Hope, it helps.
(void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
// Other code here ...
if (indexPath.item == [self.data count] - 1) {
[self loadMoreData];
}
}
May be it will help you.

UICollectionView Header xib not showing

I have a collectionview and I want to use a xib as the header. However, the xib will not appear. First I tried adding a label to the xib, which didn't appear. Then I set the whole background color to red. It it doesn't appear. The collectionview items leave a gap for the header, but it's completely blank. Similar SO threads are this and this, but as I'm not using storyboards and my header height is greater than zero, they don't solve my issue. Here is all my relevant code:
#import "ScoreboardCollectionViewController.h"
#import "ScoreboardCollectionViewCell.h"
#import "ScoreboardReusableView.h"
#import "ScoreboardModel.h"
#import "Scoreboard.h"
...
- (void)viewDidLoad {
[super viewDidLoad];
// Register cell classes
UINib *cellNib = [UINib nibWithNibName:#"ScoreboardCollectionViewCell" bundle:nil];
[self.collectionView registerNib:cellNib forCellWithReuseIdentifier:#"cell"];
[self.collectionView registerClass:[ScoreboardReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"scoreboardHeader"];
self.collectionView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"dark_fish_skin_"]];
}
...
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
return CGSizeMake(self.view.frame.size.width, 34);
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath {
ScoreboardReusableView *view = [[ScoreboardReusableView alloc] init];
view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:#"scoreboardHeader"
forIndexPath:indexPath];
return view;
}
Here is a screenshot of my xib:
OK, I solved it. The problem was that in my viewDidLoad method I had
[self.collectionView registerClass:[ScoreboardReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"scoreboardHeader"];
when since I was using a nib, it should have been:
[self.collectionView registerNib:[UINib nibWithNibName:#"ScoreboardReusableView" bundle:nil]
forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:#"scoreboardHeader"];
I was missing "Section Header" option of Collection View to enable:-

UICollectionView insertItemAtIndexPath not working

I am using a UICollectionView to produce a grid of cells say total of 10 i.e. 0-9.
Now, I want to insert a new cell in the grid on click of one of the cells.
so I have added the following line of code [_collectionView insertItemsAtIndexPaths:#[[NSIndexPath indexPathForItem:10 inSection:0]]]; inside the function didSelectItemAtIndexPath.
So now, if I set indexPathForItem: as 10 (i.e. insert at last) then I get 'Assertion failure' error on this line. If I set `indexPathForItem:' anything between 0-9 then I get 'EXC_BAD_ACCESS...' error on this line.
This is my complete code implementing UICollectionView:
- (void)loadView
{
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
_collectionView=[[UICollectionView alloc] initWithFrame:CGRectMake(0, 97.5, self.view.frame.size.width, self.view.frame.size.height-67.5) collectionViewLayout:layout];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cellIdentifier"];
[_collectionView setBackgroundColor:[UIColor whiteColor]];
[self.view addSubview:_collectionView];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection: (NSInteger)section
{
return 35;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
cell.layer.borderWidth=.5f;
cell.layer.borderColor=[UIColor blackColor].CGColor;
if(indexPath.item<31)
{
_dayNumber = [[UILabel alloc] initWithFrame:CGRectMake(30, 30, 15, 15)];
_dayNumber.font = [UIFont systemFontOfSize:12];
_dayNumber.text = [NSString stringWithFormat:#"%ld",(indexPath.item + 1)];
[cell addSubview:_dayNumber];
}
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout: (UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(self.view.frame.size.width/7, self.view.frame.size.width/7);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout: (UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex: (NSInteger)section
{
return 0.0;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return 0.0;
}
// Layout: Set Edges
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout: (UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
return UIEdgeInsetsMake(0,0,0,0); // top, left, bottom, right
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath: (NSIndexPath *)indexPath
{
[_collectionView insertItemsAtIndexPaths:#[[NSIndexPath indexPathForItem:0 inSection:0]]];
}
Any help?
Well,
first let's consider this method,
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection: (NSInteger)section
{
return 35; // returning constant value means that you can't add or remove cells later
}
so let me change this to
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection: (NSInteger)section
{
return self.itemsCount;
}
declare itemsCount property in your class interface, like this
#interface YourClass ()
#property (nonatomic) NSInteger itemsCount;
#end
initialize it in loadView or init method,
_itemsCount = 35; // or whatever you want, initial count
now we can insert/delete items, right ? when we call insertItemAtIndexPaths all we have to do is updating actual data before that call, (for example self.itemsCount++, [self.myItems addObject:newItem] )
here is changes in your code
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
self.itemsCount++; // updating data
[_collectionView insertItemsAtIndexPaths:#[[NSIndexPath indexPathForItem:0 inSection:0]]];
}
One last important thing, in cellForItemAtIndexPath don't alloc init any kind of view and add as subview on cell, this code every time creates UILabels on cell, if you want custom view on cell (like an imageview, button, etc ..) you should subclass UICollectionViewCell and create this stuff in it's init method, here is how it will look like
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
YourCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
cell.layer.borderWidth = 0.5;
cell.layer.borderColor = [UIColor blackColor].CGColor;
cell.dayNumber.font = [UIFont systemFontOfSize:12];
cell.dayNumber.text = [NSString stringWithFormat:#"%d",(indexPath.row + 1)];
return cell;
}
assuming you also changed this line,
[_collectionView registerClass:[YourCell class] forCellWithReuseIdentifier:#"cellIdentifier"];
note that YourCell is a subclass of UICollectionViewCell and has property dayNumber
Apple has a great guide about collection views. I recommend to read it.
Good luck.

Creating shapes inside collectionView cellForItemAtIndexPath - Objective C

I am working on a project of mine that populates 5 items based on data returned from a MySQL database. My issue is I need to make each item a circle or square based on the data returned.
When running the following:
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
_collectionView=[[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cellIdentifier"];
[_collectionView setBackgroundColor:[UIColor whiteColor]];
CGRect frame = [_collectionView frame];
[_collectionView setFrame:CGRectMake(10,
5,
300,
frame.size.height - 100)];
[self.view addSubview:_collectionView];
..A custom layout that creates a square is called. My question is how can I make it a square or a circle inside of the cell creation function?
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
.... do my creation of circle or square in here
}
instead of in here:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(50, 50);
}
You would subclass UICollectionViewCell and put the drawing code inside the subclass, in drawRect:
Once you create the actual class you use it by doing something like
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier";
// set custom properties here
return cell;
}
No need to get all crazy and subclass anything.
Design your cells as a square (the default).
In sizeForRowAtIndex (i'm typing in my phone, so i dont remember the exact name), do your logic to know when to make a circle.
When you need to make a circle just do cell.layer.cornerRadius = cell.frame.size.width/2; (this will make it a circle).
Else, reset the square (do the same thing as above, but set the radius to 0).
This approach will keep your code modular

UICollectionView not calling didSelectItemAtIndexPath

I have a Collection View and has custom cells with images and labels in there. I have set my collection view as follows -
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
flowLayout.minimumLineSpacing = 150.0f;
flowLayout.minimumInteritemSpacing = 104.0f;
flowLayout.sectionInset = UIEdgeInsetsMake(20, 20, 100, 120);
_archiveCollectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:flowLayout];
_archiveCollectionView.frame = CGRectMake(30, 218, _archiveCollectionView.frame.size.width - 60, _archiveCollectionView.frame.size.height - 350);
_archiveCollectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_archiveCollectionView.backgroundColor = [UIColor clearColor];
_archiveCollectionView.delegate = self;
_archiveCollectionView.dataSource = self;
[self.archiveCollectionView registerNib:[UINib nibWithNibName:#"FullArchiveEditionCell" bundle:nil] forCellWithReuseIdentifier:#"MyCell"];
[_archiveCollectionView reloadData];
[self.view addSubview:_archiveCollectionView];
I have also set the following methods:
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return _chosenCategoryArray.count;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self addEditionsChildView];
}
-(BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
However, my didSelectItemAtIndexPath never gets called when I select a cell. Any help please?
I had a similar problem and it turned out I was using the same cell reuse identifier in two different collection views
In your header file have you implemented UICollectionViewDelegate as like below,
#interface HAViewController : UIViewController <UICollectionViewDataSource, UICollectionViewDelegate>
I have the same problem. And I solved by using storyboard to locate the collection cell in the collection view controller. Then tick User InterAction Enabled. I think using code to set in the UICollectionViewCell would also work. Hope it would help.
Try with changing sequence as given below
[_archiveCollectionView reloadData];
[self.view addSubview:_archiveCollectionView];
to
[self.view addSubview:_archiveCollectionView];
[_archiveCollectionView reloadData];
Implement the below delegate method.
- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
And implement your
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
//your selection management code
}
and
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath{
//deselection handling code
}

Resources