UICollectionView reloadData issue - cell's subview issue - ios

I have maked following view.
But when calling -reloadData, the view becomes...
I think it's because cells is reused.
Do you have any idea to keep view as first image even if calling reloadData?
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier forIndexPath:indexPath];
if (indexPath.item == 0)
{
cell.label.text = #"Hello";
}
else
{
UIImage *image = dataArray[indexPath.item];
cell.imageView.image = image;
}
return cell;
}
CustomCell.m
#property (nonatomic, strong) UIImageView* imageView;
#property (nonatomic, strong) UILabel* label;
//...
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.imageView = [[UIImageView alloc]initWithFrame:imgViewFrame];
[self.contentView addSubview:self.imageView];
self.label = [[UILabel alloc]initWithFrame:labelFrame];
[self.contentView addSubview:self.label];
}
return self;
}
EDIT:
I modified my code.
- setting all default values before setting them with correct data
- calling -prepareForReuse
When calling -reloadData, Although imageView.image stay nil, label.text in indexPath.item==0 becomes nil and label.text in indexPath.item==3 outputs #"Hello".

Yes, its because of reusability. You can avoid it by changing line of code to this:
if (indexPath.item == 0)
{
cell.label.text = #"Hello";
cell.imageView.image = nil;
}
else
{
UIImage *image = dataArray[indexPath.item];
cell.imageView.image = image;
cell.label.text = #"";
}

When you're retrieving the dequeued cells they are already setup. So the first time you retrieve 9 different cells and initialise them accordingly.
The second time tough the cells properties are already set so you need to restart them.
So your code should look as follows:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier forIndexPath:indexPath];
if (indexPath.row == 0)
{
cell.imageView.image = nil;
cell.label.text = #"Hello";
}
else
{
UIImage *image = dataArray[indexPath.item];
cell.imageView.image = image;
cell.label.text = nil;
}
return cell;
}

Try setting all default values of your cell before doing any check:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier forIndexPath:indexPath];
//Set all default values before setting them with correct data
cell.imageView.image = nil;
cell.label.text = #"";
if (indexPath.row == 0)
{
cell.label.text = #"Hello";
}
else
{
UIImage *image = dataArray[indexPath.item];
cell.imageView.image = image;
}
return cell;
}
EDIT:
You could add this method to your CustomCell.m
- (void)prepareForReuse
{
//Set default values here
}

REPORT:
I solved the issue on my own.so, I will report it.
I used the property hidden.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier forIndexPath:indexPath];
if (indexPath.item == 0)
{
cell.label.text = #"Hello";
cell.imageView.hidden = YES;
}
else
{
UIImage *image = dataArray[indexPath.item];
cell.imageView.image = image;
cell.label.hidden = YES;
}
return cell;
}

Related

How to check UICollectionView has image

I have 4 UICollectionViewCell in which I want to make first 2 cell to have image in it mandatory. Sample code is as below. Below is a usecase:
There are 4 UICollectionViewCell if I click on each cell will open camera and we can either take a photo or select image from image gallery. How to know first 2 cell must have image. Looking for condition to check this logic.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
Image *image = (Image *)self.defect.imageSet[indexPath.item];
ADRPhotoCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([ADRPhotoCollectionViewCell class]) forIndexPath:indexPath];
cell.image = image.thumbnailImage ? image.thumbnailImage : nil;
cell.photoType = [self.defect defectPhotoTypeForIndex:indexPath];
return cell;
}
#pragma mark - UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
Image *image = self.defect.imageSet[indexPath.item];
if (self.selectedImage != image) {
[self updateAnnotatedImage];
self.selectedImage = image;
[self.jotController clearAll];
[self updateUserInterface];
if (image.thumbnailImage) {
[self transitionImage:YES];
} else {
self.imageView.image = nil;
[self showCameraPicker];
}
} else if (!image.thumbnailImage) {
//clicked the + to add a new image
[self showCameraPicker];
}
}
You can check if there is an image in UIImageView or not by this way:
-(BOOL) checkImages {
for (int i=0, i<2 , i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
ADRPhotoCollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
if (cell.imageView.image == nil || CGSizeEqualToSize(cell.imageView.image.size, CGSizeZero)) {
//there isn't any image or the image is empty
return NO;
}
}
return YES;
}

IOS UICollectionView throws assertion when using two collection views

I have two UICollectionViews in a single UIViewController. I am separating them by tag number so that I can use the data source and delegate methods for both. However, when I run the code it crashes with the Exception:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView received layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0xc000000000200016> {length = 2, path = 0 - 1}'.
I looked this up in the forum and most people say you need to invalidate then reload the UIControllerView but in my case this is not working.
Anyone have idea how to fix this issue ?
Here is my code:
-(void)viewDidLoad {
self.socialMediaGrayIcons = [[NSMutableArray alloc] initWithObjects:[UIImage imageNamed:#"fb-gray.png"],
[UIImage imageNamed:#"twitter-gray.png"],
[UIImage imageNamed:#"insta-gray.png"],
[UIImage imageNamed:#"sms-gray.png"],
[UIImage imageNamed:#"email-gray.png"], nil];
// setup collection view
self.avatarCollectionView.tag = 200;
self.socialMediaCollectionView.tag = 201;
UINib *cellNib = [UINib nibWithNibName:#"NibCell" bundle:nil];
[self.avatarCollectionView registerNib:cellNib forCellWithReuseIdentifier:#"cvCell"];
[self.socialMediaCollectionView registerNib:cellNib forCellWithReuseIdentifier:#"smCell"];
// setup collection view layout
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:CGSizeMake(40, 40)];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[self.avatarCollectionView setCollectionViewLayout:flowLayout];
[self.socialMediaCollectionView setCollectionViewLayout:flowLayout];
[self.avatarCollectionView reloadData];
[self.avatarCollectionView.collectionViewLayout invalidateLayout];
[self.socialMediaCollectionView reloadData];
[self.socialMediaCollectionView.collectionViewLayout invalidateLayout];
}
....
#pragma mark UICollectionView DataSource and Delegate mathods
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
if (collectionView.tag == 200)
{
return self.children.count;
} else if (collectionView.tag == 201){
return self.socialMediaGrayIcons.count;
}
return 1;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell;
if (collectionView.tag == 200)
{
static NSString *cellIdentifier = #"cvCell";
cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
Child *currentChild = [self.children objectAtIndex:indexPath.row];
UIImage *curImage = [UIImage imageWithData:currentChild.thumbnail];
UIImageView *thumbView = (UIImageView *)[cell viewWithTag:100];
if (curImage != nil)
{
[thumbView setImage:curImage];
}
} else if (collectionView.tag == 201){
static NSString *cellIdentifier = #"smCell";
cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
UIImage *curImage = (UIImage*) [self.socialMediaGrayIcons objectAtIndex:indexPath.row];
UIImageView *thumbView = (UIImageView *)[cell viewWithTag:101];
if (curImage != nil)
{
[thumbView setImage:curImage];
}
}
return cell;
}
Taking #Paulw's good advice looks like this:
#property(weak,nonatomic) IBOutlet UICollectionView *collectionViewA;
#property(weak,nonatomic) IBOutlet UICollectionView *collectionViewB;
Your datasource methods have to be religious about always dividing in two branches of a conditional based on the collection view they were passed, and always using one datasource array in one and the other in the other.
You can enforce this religion by always getting your datasource via a convenience method, like this...
- (NSArray *)datasourceForCollectionView:(UICollectionView *)collectionView {
if (collectionView == self.collectionViewA) {
return self.children;
} else { // NOTICE - no else-if, there's no other valid condition
return self.socialMediaGrayIcons;
}
}
Use it everywhere, as in...
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [self datasourceForCollectionView:collectionView].count;
}

IOS Collection View load Image from custom cell class

I am trying to implement collectionView with custom collectionViewCell class. Below code works fine, where the image is loading from Collection view class itself.
customCollectionView.m
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
MyCell *cell = (MyCell *)[collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImage *Img = [UIImage imageNamed:[Images objectAtIndex:indexPath.row]];
cell.myImageView.image = Img;
cell.layer.borderWidth=2.0f;
cell.layer.borderColor=[UIColor lightGrayColor].CGColor;
return cell;
}
MyCell.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
Whereas when I moved the image loading part to custom CollectionViewCell class MyCell the image is not displaying.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
UIImage *Img = [UIImage imageNamed:#"AppIcon.png"];
_myImageView.image = Img;
}
return self;
}
My purpose is to loading different image from MyCell class in specific time interval and display on each cell.
Try to implement prepareForReuse method of UICollectionViewCell inside your customCell MyCell
-(void)prepareForReuse {
_myImageView.image = [UIImage imageNamed:#"AppIcon.png"];
}
Try awakeFromNib and remove prepareForReuse
- (void)awakeFromNib {
_myImageView.image = [UIImage imageNamed:#"AppIcon.png"];
}

UICollectionview changes selection/disabling selection while scrolling - iOS

I'm populating data on uicollectionview cell and selecting and deselecting, everything works perfect but when I start scrolling sometime selection is not there sometimes selection changing with the cell. Below is the code, help much appreciated.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
cell = (BYOCollectionCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"CVCCell" forIndexPath:indexPath];
cell.vSelectionView.hidden = YES;
cell.vSelectionView.backgroundColor = customLightGreenColor;
[self makeRoundElement:cell.vSelectionView forLabel:nil withCorner:8.0f withBorder:0];
pizzaInfo *pizzainfo= [[pizzaInfo alloc]init];
pizzainfo = [_lstDishCollection objectAtIndex: indexPath.row];
if (pizzainfo._bIsSelected)
{
cell.vSelectionView.hidden = NO;
}
else
{
cell.vSelectionView.hidden = YES;
}
//label customization
return cell;
}
DidselectItem
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
cell = (BYOCollectionCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"CVCCell" forIndexPath:indexPath];
pizzaInfo *pizzaInfoCellData = [_lstDishCollection objectAtIndex: indexPath.row];
byoPizzaInfo = [_lstDishCollection objectAtIndex:indexPath.row];
if ( pizzaInfoCellData._bIsSelected)
{
cell.vSelectionView.hidden = NO;
pizzaInfoCellData._bIsSelected = NO;
[self._byodelegate deltaDeSelection:pizzaInfoCellData];
}
else
{
cell.vSelectionView.hidden = YES;
pizzaInfoCellData._bIsSelected = YES;
// deltaSelection:(pizzaInfo *)selectedItem
[self._byodelegate deltaSelection:pizzaInfoCellData];
if (self._IsNotifiable) {
[self showView];
}
}
[_vCVC reloadData];
}
More over collectionViewCell is inside tableViewCell.
In your cellForItemAtIndexPath you have already added condition for hide and show the selected view so you need to change your didSelectItemAtIndexPath like this
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
pizzaInfo *pizzaInfoCellData = [_lstDishCollection objectAtIndex: indexPath.row];
if (pizzaInfoCellData._bIsSelected)
{
[self._byodelegate deltaDeSelection:pizzaInfoCellData];
}
else
{
[self._byodelegate deltaSelection:pizzaInfoCellData];
}
pizzaInfoCellData._bIsSelected = !pizzaInfoCellData._bIsSelected
[_vCVC reloadData];
}
Note:- Class name always start with uppercase latter so it is batter if you change your class name pizzaInfo with PizzaInfo, its suggestion for good coding guidelines.

iOS UICollectionViewPerformance issue

I am trying to create a simple collection view with custom cells that have an UIImageView as a background. It's going to look something like :
This is my code :
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self.collectionView registerClass:[MyCell class] forCellWithReuseIdentifier:#"MY_CELL"];
leftInset = rightInset = 0;
numberOfDays = 31;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 200;
}
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section
{
return numberOfDays;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
MyCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"MY_CELL" forIndexPath:indexPath];
if(indexPath.row < 10)
cell.imageView.image = [UIImage imageNamed:#"no"];
else if(indexPath.section%3==1)
{
cell.imageView.image = [UIImage imageNamed:#"ok"];
}
else if(indexPath.section%3==2)
{
cell.imageView.image = [UIImage imageNamed:#"sa"];
}
else{
cell.imageView.image = [UIImage imageNamed:#"happy_vote"];
}
return cell;
}
#pragma mark – UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(CELL_DIM, CELL_DIM);
}
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
// top left bottom right
return UIEdgeInsetsMake(5, leftInset, 0, 0);
}
MyCell.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSLog(#"Init with frame!");
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, frame.size.width, frame.size.height)];
[self.contentView addSubview:self.imageView];
[self.imageView setBackgroundColor:[UIColor redColor]];
}
return self;
}
It works fine for around 10 sections in the collection view. The problem is when I want to add more sections, the vertical scrolling starts to move really slow. I noticed that all the cells are allocated all at once. I am searching for a way to make this more resource friendly, and have the scrolling move continuously rather than slow and laggish. Could you tell me how to take care of the resources allocation to avoid this problem ? Thank you.
PS : I've tried removing the image view from the cell background and in the method cellForItemAtIndexPath: set a background color for the cell but again, after 10 or more sections it moves really slow.
What you might want to do to help performance time is to check if an object is already initiated. You could also separately instantiate your images so that it doesnt have to be read from memory every time:
Make image1 a property of the view controller:
- (void)viewDidLoad
{
image1 = [UIImage imageNamed:#"image1"];
image2 = [UIImage imageNamed:#"image2"];
image3 = [UIImage imageNamed:#"image3"];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
MyCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"MY_CELL" forIndexPath:indexPath];
if(indexPath.row < 10)
cell.imageView.image = [UIImage imageNamed:#"no"];
else if(indexPath.section%3==1)
{
cell.imageView.image = image1;
}
else if(indexPath.section%3==2)
{
cell.imageView.image = image2;
}
else{
cell.imageView.image = image3;
}
return cell;
}

Resources