Custom UICollectionViewCell properties are null after dequeueing - ios

So I have a UICollectionView and a custom cell and it all shows up and works great. I register the class in viewDidLoad:
myCollectionView = [[UICollectionView alloc] initWithFrame:collectionViewFrame collectionViewLayout:layout];
[myCollectionView setDataSource:self];
[myCollectionView setDelegate:self];
[myCollectionView setBackgroundColor:[UIColor myColor]];
[myCollectionView registerClass:[SBCustomCell class] forCellWithReuseIdentifier:#"Cell"];
In my cellForItemAtIndexPath method I dequeue the cell and set its properties and return it:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
SBCustomCell *cell= [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
[cell setName: "My Name"];
cell.image = [UIImage imageNamed:#"lol.png"];
cell.score = 100.0;
cell.backgroundColor=[UIColor whiteColor];
return cell;
}
This all works fine and it shows up in the UI. My issue is when I set a gesture recognizer on the collectionView, when I long press a certain cell, I want to be able to access its properties. I am trying to do so as such:
-(void)handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer {
CGPoint locationPoint = [longPressRecognizer locationInView:myCollectionView];
if (longPressRecognizer.state == UIGestureRecognizerStateBegan) {
NSIndexPath *indexPathOfMovingCell = [myCollectionView indexPathForItemAtPoint:locationPoint];
SBCustomCell *cell= [myCollectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPathOfMovingCell];
NSLog(#"%#",cell.name);
When I try to access any of my custom cell's properties, it is (null) in console. Why is that? What am I doing wrong?

You need to use:
SBCustomCell* cell = (SBCustomCell*)[myCollectionView cellForItemAtIndexPath:indexPathOfMovingCell];
instead of:
SBCustomCell* cell = [myCollectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPathOfMovingCell];
Also, you are using SBSingleCandidateUnitView as the cell type instead of your SBCustomCell.

It was because I wasn't setting my properties correctly. In my header file I need to be setting a property, aka #property ... UIImageView myImageView;
And in my CustomCell.m file I should be not overriding those setters and getters. Instead just alloc and initing them and adding them to the view.
And back in my ViewController.m I should have added the properties as such:
customcell.myImageView.image = [UIImage imageNamed:#"cartman.png"];

Related

collectionview just filling one cell

I'm trying to fill a collectionview with data from an array which I've checked it has the data, but at runtime it's filling just the cell at index 0.
The collectionview is a list of friends in pages of 9 items showing photo and name. The number of items at section is working properly, I mean, if the array has 3 objects, the collectionview displays three cells but just the first one with the photo and name of the object, concretly the last one in the array, not the first one. And the other cells show the prototype cell.
I guess I'm dealing wrong with the indexpath of the collection view, but I have another one in my storyboard and works properly. This other one has only one cell per page, could be something related to this?
I paste my collectionview methods:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [self.miListaAmigos count];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"friendCell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
UILabel *nameLabel = (UILabel *)[self.view viewWithTag:102];
nameLabel.font = [UIFont fontWithName:#"Ubuntu" size:12.0];
nameLabel.text = [[self.miListaAmigos objectAtIndex:indexPath.row] valueForKey:#"usr_username"];
return cell;
}
you not directly reuse UILabel from self.view, UILabel *nameLabel = (UILabel *)[self.view viewWithTag:102];
try this below code for solve your problem:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"friendCell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
if(cell == nil)
{
//Create UILabel here
UILabel *nameLabel=[[UILabel alloc]initWithFrame:CGRectMake(5, 5, 310, 20)];
nameLabel.tag=100;
[cell addSubview:nameLabel];
nameLabel.font = [UIFont fontWithName:#"Ubuntu" size:12.0];
}
// Access label and reuse
UILabel *nameLabel = (UILabel *)[cell viewWithTag:100];
nameLabel.text = [[self.miListaAmigos objectAtIndex:indexPath.row] valueForKey:#"usr_username"];
return cell;
}
Check your collectionView alloc and required deleagte:
UICollectionViewDelegate、UICollectionViewDataSource、 UICollectionViewDelegateFlowLayout.
e.g.
UICollectionViewFlowLayout *collectionLayout = [[UICollectionViewFlowLayout alloc] init];
[collectionLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:collectionLayout];
[collectionView registerClass:[CNCollectionViewCell class] forCellWithReuseIdentifier:cellIndetify];
collectionView.delegate = self;
collectionView.dataSource = self;
[self.view addSubview:collectionView];
Your label is in self.view not in the cell... You really should make a subclass of UICollectionViewCell
File -> New -> File -> Cocoa Touch Class
Class: MYFriendCollectionViewCell
Subclass of: UICollectionViewCell
Also Create XIB: Tick
Language: Objective C
Look at MYFriendCollectionViewCell.xib and make your label inside here... connect the labels to an IBOutlet... don't use tags...
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
https://www.youtube.com/watch?v=GusRijNLUGg <- connecting IBOutlets
Add the line below in your ViewController
[self.collectionView registerNib:[UINib nibWithNibName:#"MYFriendCollectionViewCell" bundle:nil] forCellWithReuseIdentifier: CellIdentifier];
Change your cellForItem to:
static NSString *CellIdentifier = #"friendCell";
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath{
MYFriendCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: CellIdentifier forIndexPath: indexPath];
cell.nameLabel.font = nameLabel.font = [UIFont fontWithName:#"Ubuntu" size:12.0]; // This ideally would be in MYFriendCollectionViewCell.m
cell.nameLabel.text = [[self.miListaAmigos objectAtIndex:indexPath.row] valueForKey:#"usr_username"];
return cell;
}

UICollectionView - Image is getting set randomly

I am using collectionView in my App. I am setting image for the cell backgroundView in didSelect delegate. But When i select one cell indexPath the image is getting set for 3 cell indexPath. When i scroll the collectionView the images are getting changed randomly? Please Help me. thanks in advance.
- (void)viewDidLoad
{
[super viewDidLoad];
[collection registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:uio];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection: (NSInteger)section
{
return 50;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collection dequeueReusableCellWithReuseIdentifier:uio
forIndexPath:indexPath];
cell.backgroundColor = [UIColor whiteColor];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"index %#",indexPath);
UICollectionViewCell *cell = [collection cellForItemAtIndexPath:indexPath];
cell.backgroundView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"download.jpg"]];
}
That's because you reuse your cell. An option would be to have an dictionary variable to say that your cell has been selected and reset the image if it has not been.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"index %#",indexPath);
UICollectionViewCell *cell = [collection cellForItemAtIndexPath:indexPath];
cell.backgroundView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"download.jpg"]];
[selectedDictionary setObject:[NSNumber numberWithBool:YES] forKey:[NSNumber numberWithInteger:indexPath.row]];
}
Then in your cellForItemAtIndexPath method you would check that value
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collection dequeueReusableCellWithReuseIdentifier:uio
forIndexPath:indexPath];
BOOL selected = [[selectedDictionary objectForKey:[NSNumber numberWithInteger:indexPath.row]] boolValue];
if(selected){
cell.backgroundView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"download.jpg"]];
}else{
cell.backgroundView = nil;
}
cell.backgroundColor = [UIColor whiteColor];
return cell;
}
Of course if you use some kind of object as model, it would appropriate to have a selected variable in here, you won't need a nsdictionary any more.
The Problem is dequeueReusableCellWithReuseIdentifier.
When you scroll UICollectionview then cell are reused that is problem
add Collectionview inside scrollview.
Try this Inside:
Scroll_View is Your Scroll View
collection is Your Collectionview
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
self.Scroll_View.contentSize = CGSizeMake(self.view.frame.size.width, collectionView.contentSize.height);
CGRect fram_For_Collection_View = self.collection_view.frame;
fram_For_Collection_View.size.height = collectionView.contentSize.height;
self.collection.view.frame = fram_For_Collection_View;
}
Your -collectionView:didSelectItemAtPath: is adding a new image view to the cell. Nothing is removing that image view when the cell is reused. So, when you say:
UICollectionViewCell *cell = [collection dequeueReusableCellWithReuseIdentifier:uio
forIndexPath:indexPath];
in your -collectionView:cellForItemAtIndexPath:, you're may get back some cell that already has one or more image views.
My suggestion would be to add the image view to the cell in the cell prototype, perhaps in your storyboard or in the cell's initializer. Have your -collectionView:cellForItemAtIndexPath: set the image for that image view to the correct image for the given path.
What's happening is that UICollectionView reuses cells. So in didSelectItemAtIndexPath: you set the cell background, but then the UICollectionView reuses that same cell as needed (and you're not resetting the cell.backgroundView in cellForItemAtIndexPath:).
The way to fix this is to maintain an NSIndexSet of selected cells. In didSelectItemAtIndexPath: you can add the index of the item that was selected, and then force a reload of that item by calling reloadItemsAtIndexPaths. Then, in your cellForItemAtIndexPath: check the index set to see if the selected index is included, and if so, set the backgroundView of the cell.
I had the same issue few days ago & I posted a question here. Here is the answer I got & it works for me.
Collection View Cell multiple item select Error
And also if you are using a custom cell you can add this code to the init method of that cell & it will work too.
CGFloat borderWidth = 6.0f;
UIView *bgView = [[UIView alloc] initWithFrame:frame];
bgView.layer.borderColor = [UIColor redColor].CGColor;
bgView.layer.borderWidth = borderWidth;
self.selectedBackgroundView = bgView;

Using 2 UICollectionView instances but cells aren't showing in one of them

Goal:
Set up two collectionViews with different styles. One grid style and other single file style. These collectionViews will be toggleable giving users shopping within the ability to view the items for sale in which ever style (grid/single file) they prefer.
I have an existing collectionView with a custom cell set up in interface builder and it's working fine. I've tried to find info on how to add a second one via interface builder but have had no luck in finding any.
What I've done:
I've created the second collectionView programatically in my viewDidLoad method. I have an instance variable named _collectionView2.
- (void)viewDidLoad
{
[super viewDidLoad];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
_collectionView2 = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
[_collectionView2 setDataSource:self];
[_collectionView2 setDelegate:self];
[_collectionView2 registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"Cell"];
[_collectionView2 setBackgroundColor:[UIColor redColor]];
[self.view addSubview:_collectionView2];
[_collectionView2 setHidden:YES];
I've modified delegate and datasource methods to make them aware about the new collectionView:
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
if (collectionView == _collectionView) {
NSArray *people = [_thisController objects];
return [people count];
} else {
return 20;
}
}
.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
if (collectionView == _collectionView) {
static NSString *CellIdentifier = #"Cell";
VAGGarmentCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: CellIdentifier forIndexPath:indexPath];
[[cell activityIndicator] startAnimating];
PFFile *userImageFile = [object valueForKey:#"image"];
[[cell imageView] setFile: userImageFile];
[[cell imageView] loadInBackground];
[[cell activityIndicator] stopAnimating];
[[cell title] setText:[object valueForKey:#"title"]];
[[cell price] setText:[NSString stringWithFormat: #"£%# GBP", [object valueForKey:#"price"]]];
return cell;
} else {
static NSString *CellIdentifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor greenColor];
return cell;
}
//_addToFavouritesButton = [cell addFavouriteButton];
[_addToFavouritesButton addTarget:_thisController action:#selector(addToFavouritesButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
if (collectionView == _collectionView2) {
return CGSizeMake(50, 50);
} else {
return CGSizeMake(140, 272);
}
}
Then I have a method that responds to the UISegmentControl being switched from grid style display to single file display:
- (void)displayTypeSegmentSelected
{
_selectedDisplayTypeIndex = [_displayTypeControl selectedSegmentIndex];
if (_selectedDisplayTypeIndex == 0) {
NSLog(#"Single file item view selected");
[_collectionView setHidden:YES];
[_collectionView2 setHidden:NO];
} else {
NSLog(#"Grid style view selected");
[_collectionView setHidden:NO];
[_collectionView2 setHidden:YES];
}
}
CollectionView2 is hidden initially then unhidden when the segment control is used. It looks like it's working because when I toggle a red background shows and I did set a red background when I created the collectionView but green cells aren't showing at all.
I can't seem to see where I've gone wrong. I'd like to get this working so I can replace the cell with my custom cell. The only difference between grid display and single file display is that single file display will be an enlarge cell that takes up the width of the view. All properties etc will be the exact same meaning I can use my current custom cell.
Why aren't my cells showing? I've followed a clear tutorial on creating a collectionView programmatically but still no cells are showing.
Update:
I put a log message in the if statement that checks what collectionView is present and it is only ever trigger for my _collectionView and not _collectionView2.
Help is appreciated.
Regards
Not sure this will fix your issue, but when I have 2 collection views in one controller, I use their tag's to determine which one I am dealing with in a given function, like cellForItemAtIndexPath.
I also have always used different cell identifiers for the two cells and usually different subclasses of uicollectionviewcell.
Where do you create the first collection view and set its delegate?
Reloading the table data in the method my UISegmentedControl triggers got the cells to show up.

UIImageView in custom UICollectionViewCell returning nil all the time

OK in my story board I have made a UICollectionView with 3 cells. One of the cells I made a custom class for that obviously extends the UICollectionViewCell:
And I registered the class in my ViewDiDApear:
[self.collectionView registerClass:[DeviceImageCell class] forCellWithReuseIdentifier:#"Cell1"];
Also I have added an imageView to that specific cell and an outlet for that imageView in my custom class. Problem occurs when I make my custom cell it forgets everything that was set in my storyboard, aka the background color, where my image view is and so on. Because of this my imageView returns nil all the time.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row == 0)
{
static NSString *identifier = #"Cell1";
DeviceImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImage *image = [UIImage imageNamed:#"dysart-woods-full.jpg"];
cell.imageview.image = image;
cell.backgroundColor = [UIColor blueColor];
//cell.imageview.image = image;
return cell;
}
else if(indexPath.row == 1)
{
static NSString *identifier = #"Cell2";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
return cell;
}
else
{
static NSString *identifier = #"Cell3";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:1];
imageView.image = [UIImage imageNamed:#"dysart-woods-full.jpg"];
return cell;
}
}
You have probably long solved this issue.
However for those finding this and having the same issue
it is the registerClass call in your viewDidAppear
[self.collectionView registerClass:[DeviceImageCell class] forCellWithReuseIdentifier:#"Cell1"];
You are already registering the class in Interface Builder, so the call to registerClass:forCellWithReuseIdentifier: is replacing the entry created by IB.
Removing this line will fix the issue.
You should register the custom cell's nib if you are using one like this-
So it will be:
[self.collectionView registerNib:[UINib nibWithNibName:#"CustomCell" bundle:nil] forCellWithReuseIdentifier:#"CustomCellIdentifier"];
instead of:
[self.collectionView registerClass:[CustomCell class] forCellWithReuseIdentifier:#"CustomCellIdentifier"];
Well, this is odd. I know a solution, but I don't quite understand why it makes a difference. I had the same exact setup and the same problem. The IBOutlet was getting set to nil for the UIImageView, but not the UILabels.
Change your declaration for the UIImageView to be a strong property instead of an instance variable.
#property (strong) IBOutlet UIImageView* imageView;
This solved the issue for me. Clearly there is something with the way that UIImageView is connected / instantiated from the storyboard. As an added 'weird' factor, I use the same setup of ivars with a UITableViewCell and have no issue, only in the UICollectionViewCell
I had the similar problem and it's been solved by adding the following code in the DeviceImageCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_imageview = [[UIImageView alloc] initWithFrame:self.contentView.bounds];
[self.contentView addSubview:_imageview];
}
return self;
}
Unfortunately I haven't managed to get _imageview initialised without this code addition.
Also check this one,
So now when you declare Custom CollectionView Cell in its Storyboard you need to specify the Attribute named "Identifier" under Collection Reusable View to some String.
Now this may sound weired but ,
Check if name of Identifier is not same as Collection View Cell's class name.
Class/File name of Cell and Cell Identifier must be Different.
That really worked for me , hence posting.

Change attributes of a UICollectionViewCell in didSelectItemAtIndexPath

I am configuring a UICollectionViewCell in a subclass, it adds 2 subviews to the contentView property, both are UIImageView and both have the hidden property set to YES. These subviews are "checked" and "unchecked" images that overlay the primary UIImageView in the cell to indicate whether or not the current cell is selected using UICollectionView's "multiple select" feature.
When the cell is tapped, collectionView:didSelectItemAtIndexPath: is called on the delegate, and I'd like to setHidden:NO on the "checked" UIImageView. Calling this on the cell does nothing at all -- the cell is seemingly locked in its originally drawn state.
Is it possible to make changes to a cell outside collectionView:cellForItemAtIndexPath:? I have tried manually adding subviews within collectionView:didSelectItemAtIndexPath:, but it just makes absolutely no change to the UI. I have verified that the delegate method is getting called, it's just not making my cell changes.
- (void) collectionView(UICollectionView *)cv didSelectItemAtIndexPath(NSIndexPath *)indexPath {
ShotCell *cell = [self collectionView:cv cellForItemAtIndexPath:indexPath];
UILabel *testLabel = UILabel.alloc.init;
testLabel.text = #"FooBar";
testLabel.sizeToFit;
[cell.contentView.addSubview testLabel];
}
The way you're trying to do this is incorrect. You need to keep a reference to the selected cell or cells in a property. In this example, I use an array to hold index paths of the selected cells, then check whether the index path passed in to cellForItemAtIndexPath is contained in that array. I unselect the cell if you click on one that's already selected:
#interface ViewController ()
#property (strong,nonatomic) NSArray *theData;
#property (strong,nonatomic) NSMutableArray *paths;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.paths = [NSMutableArray new];
self.theData = #[#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight"];
[self.collectionView registerNib:[UINib nibWithNibName:#"CVCell" bundle:nil] forCellWithReuseIdentifier:#"cvCell"];
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[self.collectionView setCollectionViewLayout:flowLayout];
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.theData.count;
}
-(CVCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cvCell";
CVCell *cell = (CVCell *) [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor whiteColor];
cell.label.text = self.theData[indexPath.row];
if ([self.paths containsObject:indexPath]) {
[cell.iv setHidden:NO]; // iv is an IBOutlet to an image view in the custom cell
}else{
[cell.iv setHidden:YES];
}
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if ([self.paths containsObject:indexPath]) {
[self.paths removeObject:indexPath];
}else{
[self.paths addObject:indexPath];
}
[self.collectionView reloadItemsAtIndexPaths:#[indexPath]];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(150, 150);
}

Resources