ios7.1.1 UICollectionView Not Reloading Correctly - ios

This is a very strange problem. I have a UICollectionView which seems to run perfectly on the iPad or the simulator as an iPad, but has a problem when running on the iPhone or iPhone simulator. The problem is that if an item is added to the datasource and I perform a reloadData, the freshly added item will not show up in the collection view. If I exit the view controller altogether and reopen it, the item is there. This problem is on the iPhone only, not the iPad, which works perfectly.
In all cases, the number of items returned is correct and the number of passes through the dequeue loop is correct (verified via debugger).
Relevant code below. I initialize the CV in viewDidAppear with the following:
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
flowLayout.minimumInteritemSpacing = 3.0f;
UICollectionViewCell *cvCell = [[UICollectionViewCell alloc] init];
cv = [[UICollectionView alloc] initWithFrame:CGRectMake(1.0f, 80.0f, [Utility screenWidth]-2.0f, [Utility screenHeight]-60.0f) collectionViewLayout:flowLayout];
[cv setDelegate:self];
[cv setDataSource:self];
[cv setAllowsMultipleSelection:NO];
[cv setAllowsSelection:NO];
[cv setBackgroundColor: [UIColor whiteColor]];
[cv registerClass:[cvCell class] forCellWithReuseIdentifier:#"Cell"];
[self.view addSubview:cv];
[self.view sendSubviewToBack:cv];
[self queryData]; // [self queryData] also does a [cv reloadData]
The CV delegate is as follows:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection: (NSInteger)section
{
return [cvData count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
[cell.contentView.subviews makeObjectsPerformSelector: #selector(removeFromSuperview)];
Item *item = (Item *) [cvData objectAtIndex:indexPath.row];
CGFloat cellSize = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) ? 100.0f : 160.0f;
ImageWithAttachedLabel *imageWithLabel = [[ImageWithAttachedLabel alloc] initWithFrame:CGRectMake(0,0,cellSize,cellSize)
withImage: [UIImage imageWithData: item.itemPhoto]
withLabel: item.itemName
roundBottoms: YES];
[cell.contentView addSubview:imageWithLabel];
return cell;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return 3.0f;
}
Running the debugger finds that numberOfItemsInSection is returns the CORRECT count of the items and the number of passes through the dequeue loop is correct, but the last item added (which should always be the last cell in the cv, as the items are unsorted) is not displayed.
I haven't been able to find any articles mentioning anything quite like this so I'm guessing I have a bug somewhere, but it is elusive.
Any suggestions appreciated.

Perhaps you can force the newly added item to be displayed by doing this after you add the new item to your data source:
int size = [cvData count];
NSIndexPath *lastCell = [NSIndexPath indexPathForRow:size inSection:0];
[self.collectionView insertItemsAtIndexPaths:#[lastCell]];
(assuming you point self.collectionView at your cv)

Related

Customizing UICollectionViewCell depending on indexPath

Hi i'm trying to customize my collectionviewcell depending on its indexPath but also if I set
if (indexPath.row == 0)
{
[cell addSubView: view];
}
the view appear random in some cells.
This is the code I'm using
- (NSInteger)collectionView:(UICollectionView *)collectionView 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];
NSInteger row = indexPath.row;
UIView *contentCell = [[UIView alloc] initWithFrame:cell.frame];
if (row == 0)
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(1.0f, 1.0f, 50.0f, 50.0f)];
label.text = #"Test";
[contentCell addSubview:label];
}
[cell addSubview:contentCell];
cell.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"container"]];
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%d", indexPath.row);
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(414, 228);
}
You have to read about reusable cells.
you can avoid the problem for now by doing this.
if (row == 0)
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(1.0f, 1.0f, 50.0f, 50.0f)];
label.text = #"Test";
label.tag = 200;
[contentCell addSubview:label];
}else{
UIView* lbl = [contentCell viewWithTag:200];
if(lbl)
[lbl removeFromSuperView];
}
but this will affect the scrolling and memory performance, you can put the label in the cell by default && show/hide it in the if/else blocks
You are adding view several times as a subview. Cells are reused due to "dequeueReusableCellWithReuseIdentifier", so after you scroll up and down, you get back existing view, which already has subview added.
You should read more about reusing cells.
One way to avoid that behavior is to create all views when you create a cell and just show / hide them.
Otherwise create cells with different identifier for different rows - in your case for row 0.

UICollectionView cell layout not correct

I'm very new to UICollectionViews. I'm making a universal app. What I want is that on the iPad there are 2 cells on each row. But on the iPhone there is only one cell on each row. I was able to get the iPad version set correctly. But I can't get my head around the iPhone settings.
Here are the settings so far. And has you can see on my storyboard it looks oké. But when I run it I see that there are still two cells on each row.
Can someone help me with this ?
Kind regards
- (void)viewDidLoad
{
[super viewDidLoad];
[self.collectionView registerClass:[CVCell class] forCellWithReuseIdentifier:#"cvCell"];
// Configure layout
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:CGSizeMake(100, 100)];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[self.collectionView setCollectionViewLayout:flowLayout];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
deviceModel = (NSString*)[UIDevice currentDevice].model;
NSLog(#"device is = %#",deviceModel);
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
if ([deviceModel isEqualToString:#"iPhone Simulator"]) {
return 1;
}else
{
return 2;
}
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 3;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// Setup cell identifier
static NSString *cellIdentifier = #"cvCell";
/* Uncomment this block to use subclass-based cells */
CVCell *cell = (CVCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if (indexPath.section == 0) {
cell.backgroundColor = [UIColor redColor];
}
if (indexPath.section == 1) {
cell.backgroundColor = [UIColor blueColor];
}
cell.titleLabel.text = [NSString stringWithFormat:#"item %ld",(long)indexPath.section];
// Return the cell
return cell;
}
Check this code which will give you one item in iPhone cell and two item in iPad..

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.

UICollectionView not redrawing cells when inserted at position where cells were deleted before

I am making a UICollectionView control which would look like (fig-1) :
I have added the ability to delete cell by swiping the cells to right.
My problem case - If I delete the last cell by swiping (fig-2) , which will call the following code.
- (void)removeTheCell:(AnyObject *)obj {
// remove the object
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:[self.allObjects indexOfObject:obj] inSection:0];
[self.allObjects removeObjectAtIndex:indexPath.row];
[self.collectionView deleteItemsAtIndexPaths:#[indexPath]];
}
And then add a new cell with different color using following method (fig-4):
- (void)addNewObject:(NSNotification *)notification {
NSDictionary *dict = notification.userInfo;
NSArray *newObjects_a = [dict objectForKey:ALL_OBJECTS];
NSMutableArray *indexArrays = [[NSMutableArray alloc] init];
for (AnyObject *obj in newObjects_a) {
[self.allObjects addObject:obj];
[indexArrays addObject:[NSIndexPath indexPathForItem:[self.allObjects indexOfObject:obj] inSection:0]];
}
[self.collectionView performBatchUpdates:^{
[self.collectionView insertItemsAtIndexPaths:indexArrays];
} completion:nil];
}
The cell that is displayed still looks like the old deleted cell with its last state (fig-4). But i checked the data source it doesn't contain the deleted object. It contains the latest data.
(fig-5)If i change to list layout by selecting the segment control which call the following method:
- (IBAction)SwitchCellFrames:(id)sender {
int selection = ((UISegmentedControl *)sender).selectedSegmentIndex;
isGridView = selection == 0 ? YES : NO;
if (isGridView) {
[self.collectionView setCollectionViewLayout:gridFlowLayout animated:YES];
}else {
[self.collectionView setCollectionViewLayout:listFlowLayout animated:YES];
}
}
layout variables are defined as :
gridFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[gridFlowLayout setItemSize:CGSizeMake(160, 155)];
[gridFlowLayout setMinimumInteritemSpacing:0.0f];
[gridFlowLayout setMinimumLineSpacing:0.0f];
[gridFlowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
listFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[listFlowLayout setItemSize:CGSizeMake(320, 80)];
[listFlowLayout setMinimumInteritemSpacing:0.0f];
[listFlowLayout setMinimumLineSpacing:0.0f];
[listFlowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
The collectionView now updates the new cell with the right color (fig-5/fig-6).
I tried [self.collectionView setNeedsDisplay] / [self.collectionView setNeedsLayout] / [self.collectionView reloadData]. These are not causing the UI to redraw itself.
I don't know what is causing the UICollectionView to retain the deleted view. Please Help.
Found a work arround that is working for me in this situation :
I was creating and updating the ui under - (void)awakeFromNib() of my customCell class. So, when the the new cell is added at the location from where a cell was earlier deleted, - (void)awakeFromNib() was not getting called again and a previous copy of cell was being returned.
Therefore, i made the ui update method public and removed it from - (void)awakeFromNib(). Calling UI update method explicitly from cellForItemAtIndexPath solved the problem for me.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CustomViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionCell" forIndexPath:indexPath];
cell.obj = [self.allObjects objectAtIndex:indexPath.row];
[cell handleChangesForLayoutAndPosition];
[cell updateUI];
[cell resetScrollView];
cell.delegate = self;
return cell;
}

Layout of Images in UICollectionView

I am trying to layout a collection of Magazine covers in a UICollectionView in which the background is a bookshelf. I want it to have 2 magazines per shelf, and the rest to be seen when scrolling down. Here is my code I have:
-(void)viewDidLoad {
UINib *cellNib = [UINib nibWithNibName:#"NibCell" bundle:nil];
[self.collectionView registerNib:cellNib forCellWithReuseIdentifier:#"cvCell"];
self.collectionView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"shelves.png"]];
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
NSLog(#"1");
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [_allEntries count];
NSLog(#"2");
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
RSSEntry *entry = [_allEntries objectAtIndex:indexPath.row];
static NSString *cellIdentifier = #"cvCell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
UIImageView *titleLabel = (UIImageView *)[cell viewWithTag:100];
UILabel *titleLabel2 = (UILabel *)[cell viewWithTag:200];
NSString *thearticleImage = entry.articleImage;
[titleLabel setImageWithURL:[NSURL URLWithString:entry.articleImage] placeholderImage:[UIImage imageNamed:#"icon#2x.png"]];
// [titleLabel2 setText:entry.articleTitle];
return cell;
}
I am using an XIB to create the collectionView cell.
When there are only 2 issues, it looks fine, but as more and more issues get added and you begin to have to scroll, they get quite off:
Your calculation of your UICollectionViewCell with paddings between cells could be kind a wrong. You can either play with size of images or set the size of cell programmatically:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(192.f, 192.f);
}
You can also play with paddings in Interface Builder.
You can also do this programmatically:
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:CGSizeMake(100, 200)];
[flowLayout setMinimumInteritemSpacing:10];
[flowLayout setMinimumLineSpacing:10];

Resources