Executing network request once cellForItemAtIndexPath is completed for CollectionView? - ios

I have a collection view that in which a cell is populated based on an array of int's.
After the cells are created I want to check for parameters for each cell via network request.
At the moment I carry out the network request for each cell in this method and it causes a cell to be created before its parameters are assigned due to inconsistent networks.
Is there a method to run a network function (sendGetPar:) on completion of laying out the cells? Obviously this can then be reused when the user scrolls etc.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
BOOL isFocusOn = [_userDefault boolForKey:#"mixFocusOn"];
if (isFocusOn == TRUE) {
CDCChannelStrip *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
NSNumber *setChan = [self.focusChannels objectAtIndex:indexPath.section];
NSInteger chanInt = [setChan intValue] +1;
cell.clipsToBounds = YES;
[cell initData:(chanInt)];
[self.mixMonitorView setChannelsStripToType:(cell)];
[self.mixMonitorView sendGetPar:chanInt];
return cell;
}

I believe the Apple's documentation would be helpful
probably the methods:
collectionView:didEndDisplayingCell:forItemAtIndexPath:
Tells the delegate that the specified cell was removed from the collection view.
collectionView:didUpdateFocusInContext:withAnimationCoordinator:
Tells the delegate that a focus update occurred.

Something like this:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
BOOL isFocusOn = [_userDefault boolForKey:#"mixFocusOn"];
if (isFocusOn == TRUE) {
CDCChannelStrip *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
NSInteger chanInt = indexPath.row +1;
cell.clipsToBounds = YES;
[cell initData:(chanInt)];
[self.mixMonitorView setChannelsStripToType:(cell)];
[self.mixMonitorView sendGetPar:chanInt];
return cell;
}
-(void) sendGetPar:(NSInteger)index // you could return the parameter via this method.
{
NSLog(#"Parameter:%ld", (long)index);
}

Related

collectionviewcell didSelectItemAtIndexPath/didDeselectItemAtIndexPath not responding for specific cells

I am trying to preselect some of the cell according to condition, which sets those cells selected and also change its background color while drawing those cells. Now the method
didSelectItemAtIndexPath / didDeselectItemAtIndexPath
is not getting called only for those preselected cells and hence I am not able to toggle selection and background color. The select/deselect delegate methods are being called for other cells
-(UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"cellForItemAtIndexPath: %#", indexPath);
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"collectionViewCell" forIndexPath:indexPath];
if(indexPath.row != 0 && indexPath.row != 8 && indexPath.section != 0 && indexPath.section != 25){
NSMutableDictionary *blockedHours = [blockedDaysArray objectAtIndex:indexPath.row-1];
NSString *blockedVal = [blockedHours valueForKey:#(indexPath.section-1).stringValue];
[cell setBlockedVal:(NSString*)blockedVal];
}
[cell addDayTimeLable:indexPath];
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"didSelectItemAtIndexPath: %# ", indexPath);
NSMutableDictionary *blockedHours = [blockedDaysArray objectAtIndex:indexPath.row-1];
[blockedHours setValue:#"1" forKey:#(indexPath.section-1).stringValue];
CollectionViewCell *cell = (CollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
cell.selected = YES;
}
-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"didDeselectItemAtIndexPath %#", indexPath);
NSMutableDictionary *blockedHours = [blockedDaysArray objectAtIndex:indexPath.row-1];
[blockedHours setValue:#"0" forKey:#(indexPath.section-1).stringValue];
CollectionViewCell *cell = (CollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
cell.selected = NO;
}
In CollectionViewCell.m:
Self.selected calls the setter method and hence chagnes the background color
-(void)setBlockedVal:(NSString*)blockedVal{
if([blockedVal isEqualToString:#"1"]){
self.selected = YES;
}
}
-(void)setSelected:(BOOL)selected{
NSLog(#"set selected: %d", selected);
[super setSelected:selected];
if(selected)
self.backgroundColor = [SFIColors lightGreenColor];
else
self.backgroundColor = [UIColor whiteColor];
}
Note:
(1) didHighlightItemAtIndexPath/didUnHighlightItemAtIndexPath are
getting called for preselected cells.
(2)I Just found out that setting selected via didselect/didunselect is
redundant and I just removed from my code.Noticed that setSeleted is
auto called on clicking the other cells. Still this setSelected is not
being for preselected cells
Any Inputs to fix this or another way that I can do my task would be of great help.
I found the answer in this link: UICollectionView - didDeselectItemAtIndexPath not called if cell is selected
I actually searched a lot actually, but only now I found this link.
I had let my collection view know about my selection and that did the trick:
[collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];

UICollectionViewCell Only Updates After Scrolling

I've got an issue with my UICollectionView where my cells are always initiated blank/in a default state with no data.
The data only appears in the cells after scrolling them in and out of view.
Code:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"EquipmentCell";
APInventoryCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if (!cell) cell = [[APInventoryCollectionViewCell alloc] init];
//set cell data...
cell.label.text = [arrayOfStrings objectAtIndex:indexPath.row];
return cell;
}
Your problem is that you probably set the arrayOfStrings somewhere in the viewDidLoad method of your view controller, the problem with that is that the collectionview datasource calls are done before that.
What you should do in your viewDidLoad method just call [collectionview reloadData]; an you will be fine

Reusing cell causes unwanted behaviour when updating cell view

I want ot set a border to a selected cell, i save a cell property that represents the selected one and manipulate it:
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
NSArray *eyeArray = [self eyesArrayConfigure][indexPath.row];
if (indexPath.row==5) {
int r = arc4random() % 6;
eyeArray = [self eyesArrayConfigure][r];
}
[borderedCell.contentView.layer setBorderWidth:0.0f] ;
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
borderedCell = cell;
[borderedCell.contentView.layer setBorderColor:self.view.tintColor.CGColor];
[borderedCell.contentView.layer setBorderWidth:3.0f];
}
and the cellForView: (Im usinng 2 types of cell identifiers because one cell contains a label - "Random cell":
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row ==5) {
UICollectionViewCell *randomCell =[collectionView
dequeueReusableCellWithReuseIdentifier:#"randomCell"
forIndexPath:indexPath];
randomCell.backgroundColor = [UIColor purpleColor];
borderedCell = randomCell;
[borderedCell.contentView.layer setBorderColor:self.view.tintColor.CGColor];
[borderedCell.contentView.layer setBorderWidth:3.0f];
return randomCell;
}
UICollectionViewCell *myCell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
NSArray *eyeArray = [self eyesArrayConfigure][indexPath.row];
myCell.backgroundView = [[UIImageView alloc] initWithImage:eyeArray[1]];
return myCell;
}
What i get is if a click one cell it will be fine till i scroll and then i get weird behaviour when couple of cells might be with the border.
Thanks for the help.
The solution is to use a view model. You are already doing this for your cells' background views. Apply the same concept for the border.
#interface MyCollectionView ()
#property (nonatomic) NSInteger selectedRow;
#end
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
//update self.selectedRow and either reload entire collection view or just the currently selected and previously selected.
self.selectedRow = indexPath.row;
[collectionView reloadData];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *myCell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
CGFloat borderWidth = (indexPath.row == self.selectedRow) ? 3.0 : 0.0;
[myCell.contentView.layer setBorderWidth:borderWidth];
return myCell;
}

UICollectionView allowsMultipleSelelections not working

I am trying to make some photoPicker with CollectionView.
Have
allowsMultipleSelection = YES
Using following method
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
selectedPictures = [NSMutableArray array];
[selectedPictures addObject:[imagesArray objectAtIndex:indexPath.item]];
NSLog(#"Selected list:\n %#", selectedPictures);
NSLog(#"Objects in Array %i", selectedPictures.count);
}
While I am selecting cells, it's always adding to MutableArray only one object according it's indexPath. What could be an issue?
Why don't u keep the selectedPictures as a member variable
in your code
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
selectedPictures = [NSMutableArray array]; //keep on creation the new array on each selection
[selectedPictures addObject:[imagesArray objectAtIndex:indexPath.item]]; //adding the selected images means single image
NSLog(#"Selected list:\n %#", selectedPictures);
NSLog(#"Objects in Array %i", selectedPictures.count);
}
try this
put his in viewDidLoad
- (void)viewDidLoad
{
selectedPictures = [[NSMutableArray alloc]init]; //initilise hear
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
// selectedPictures = [NSMutableArray array]; //keep on creation the new array on each selection
[selectedPictures addObject:[imagesArray objectAtIndex:indexPath.item]]; //adding the selected images means single image to already initialised array
NSLog(#"Selected list:\n %#", selectedPictures);
NSLog(#"Objects in Array %i", selectedPictures.count);
}
Hope this helps u .. :)
it may be caused by not calling super. While the documentation for UICollectionReusableView fails to mention this, the documentation for UITableViewCell, which has the same method, does.
- (void)prepareForReuse
{
[super prepareForReuse]
// Your code here.
}
Old Answer:
This may be a bug with the UICollectionView.
What's happening is cells that were previously selected are being reused and maintain the selected state. The collection view isn't setting selected to "NO".
The solution is to reset the the selected state in prepareForReuse of the cell:
- (void)prepareForReuse
{
self.selected = NO;
}
If the reused cell is selected, the collection view will set selected to "YES" after prepareForReuse is called.
This is something the UICollectionView should be doing on it's own. Thankfully the solution is simple. Unfortunately I spent a ton of time working around this bug by tracking my own select state. I didn't realize why it was happening until I was working on another project with smaller cells.
Also Try this
I'm not seeing why this would take place. I do not believe the issue is the use of row vs item, though you really should use item. I can imagine, though, if your collection view has more than one section, that only looking at row/item but ignoring section would be a problem (i.e. it would select the same item number in every section).
To cut the Gordian knot, I'd suggest saving the NSIndexPath of the selected item, and then using that for the basis of comparison. That also makes it easy to render an optimization in didSelectItemAtIndexPath. Anyway, first define your property:
#property (nonatomic, strong) NSIndexPath *selectedItemIndexPath;
And then implement cellForItemAtIndexPath and didSelectItemAtIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
CollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.imageView.image = ...
if (self.selectedItemIndexPath != nil && [indexPath compare:self.selectedItemIndexPath] == NSOrderedSame) {
cell.imageView.layer.borderColor = [[UIColor redColor] CGColor];
cell.imageView.layer.borderWidth = 4.0;
} else {
cell.imageView.layer.borderColor = nil;
cell.imageView.layer.borderWidth = 0.0;
}
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
// always reload the selected cell, so we will add the border to that cell
NSMutableArray *indexPaths = [NSMutableArray arrayWithObject:indexPath];
if (self.selectedItemIndexPath)
{
// if we had a previously selected cell
if ([indexPath compare:self.selectedItemIndexPath] == NSOrderedSame)
{
// if it's the same as the one we just tapped on, then we're unselecting it
self.selectedItemIndexPath = nil;
}
else
{
// if it's different, then add that old one to our list of cells to reload, and
// save the currently selected indexPath
[indexPaths addObject:self.selectedItemIndexPath];
self.selectedItemIndexPath = indexPath;
}
}
else
{
// else, we didn't have previously selected cell, so we only need to save this indexPath for future reference
self.selectedItemIndexPath = indexPath;
}
// and now only reload only the cells that need updating
[collectionView reloadItemsAtIndexPaths:indexPaths];
}
Check also this
Your observation is correct. This behavior is happening due to the reuse of cells. But you dont have to do any thing with the prepareForReuse. Instead do your check in cellForItem and set the properties accordingly. Some thing like..
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cvCell" forIndexPath:indexPath];
if (cell.selected) {
cell.backgroundColor = [UIColor blueColor]; // highlight selection
}
else
{
cell.backgroundColor = [UIColor redColor]; // Default color
}
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
datasetCell.backgroundColor = [UIColor blueColor]; // highlight selection
}
-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
datasetCell.backgroundColor = [UIColor redColor]; // Default color
}
I solved my issue;
The problem was very simple, I should have initialise MutableArray not in the Method didSelectItemAtIndexPath, but in the ViewDidLoad. Now it adding pictures one by one

How to make two collection views appear on the same view controller?

I want to have two UICollectionView in same page. Two UICollectionView that would show different data as required. How can I do this?
Thanks in advance.
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifierHall2 = #"hall2";
/* Uncomment this block to use subclass-based cells */
timeCell *cell = (timeCell *)[myCollectionViewHall2 dequeueReusableCellWithReuseIdentifier:cellIdentifierHall2 forIndexPath:indexPath];
[cell.timeButton setTitle:[[allSimilarMutableArray valueForKey:#"showTime"] objectAtIndex:indexPath.item] forState:UIControlStateNormal];
return cell;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifierHall3 = #"hall3";
/* Uncomment this block to use subclass-based cells */
timeCell *cell = (timeCell *)[myCollectionViewHall3 dequeueReusableCellWithReuseIdentifier:cellIdentifierHall3 forIndexPath:indexPath];
[cell.timeButton setTitle:[[allSimilarMutableArray valueForKey:#"showTime"] objectAtIndex:indexPath.item] forState:UIControlStateNormal];
return cell;
}
You can do the same by differentiating in 'cellForItemAtIndexPath'
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.collectiveview1) {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionCell" forIndexPath:indexPath];
UILabel *titleLabel = (UILabel *)[cell viewWithTag:100];
[titleLabel setText:celldata];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:200];
[imageView setImage:[UIImage imageNamed:connimage]];
return cell;
} else {
static NSString *cellIdentifier = #"FollowCell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
UILabel *titleLabel = (UILabel *)[cell viewWithTag:100];
[titleLabel setText:celldata];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:200];
[imageView setImage:[UIImage imageNamed:cellImage]];
return cell;
}
}
Hope it helps.
Because both UICollectionView's respond to the same protocol: UICollectionViewDataSource, you must differentiate between them in the method:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
You can do this multiple ways, depending on how your UICollectionView's are created.
1. If you created them in Storyboard or in Interface Builder, link them to your class using IBOutlet's. Then you can distinguish between them with the following code:
if (collectionView == self.firstCollectionView) { ... }
else if (collectionView == self.secondCollectionView) { ... }
Both firstCollectionView and secondCollectionView must be properties in your View Controller class.
2. Another way is to set tags to them either on Storyboard/Interface Builder or in code with tag property.
So you can for example do this:
self.firstCollectionView.tag = 100;
self.secondCollectionView.tag = 200;
And then again in the cellForItemAtIndexPath method you can distinguish them like:
if (collectionView.tag == 100) { ... }
else if (collectionView.tag == 200) { ... }
Even if you created the collection views in code, you can use the same techniques. Either create properties that point strongly to the collections and compare the pointers or compare them by tags.
The delegate method for cellForItemAtIndexPath also passes in the instance of the collection view it's requesting a cell for. This code snippet is correcting the code example you put in the original post. To take your example, here's how you do this:
- (void)viewDidLoad {
// Initialization of collectionView done previously, set delegates to yourself.
[self.collectionView1 setDelegate:self];
[self.collectionView2 setDelegate:self];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// Setting the cellIdentifier to "hall2" by default, no need to check twice.
NSString *cellIdentifier = #"hall2";
// The collectionView requesting a cell is collectionView2, change the cellIdentifier
if ([collectionView isEqual:self.collectionView2]) {
cellIdentifier = #"hall3";
}
// Since the collectionView we need is already passed, just dequeue and reuse from it.
timeCell *cell = (timeCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[cell.timeButton setTitle:[[allSimilarMutableArray valueForKey:#"showTime"] objectAtIndex:indexPath.item] forState:UIControlStateNormal];
return cell;
}
I hope this helps you understand how to use multiple collection views with the same delegate.
You can tag the two collection view with different value.
In order to add two UICollectionView in the same page, i user collectionView.tag to identify each of them and decide witch data to pass to each one.
Also, i needed to define different cell sizes for each collection view, but in this method collectionView.tag wasn't working for me. So i had to define a different UICollectionViewLayout to each UICollectionView and then:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionViewLayout == layout1) {
return CGSizeMake(100,100);
} else {
return CGSizeMake(50, 50);
}
}

Resources