CollectionView with multiple cell types - ios

I have a collectionView, where each 5th cell must be different than others.
i have write the following code:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
if ((indexPath.row + 1) % 5 == 0) {
return CGSizeMake(screenRect.size.width, screenRect.size.height/5);
}
return CGSizeMake(screenRect.size.width/2, screenRect.size.height/2);
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if ((indexPath.row + 1) % 5 == 0 ) {
NSLog(#"iAdCellWithIndex:%ld", (long)indexPath.row);
CustomCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CustomCell" forIndexPath:indexPath];
if (!cell)
cell = [[CustomCollectionViewCell alloc] init];
return cell;
}
ImageThumbCell * cell1 = [collectionView dequeueReusableCellWithReuseIdentifier:#"ImageCell" forIndexPath:indexPath];
if (!cell1)
cell1 = [[ImageThumbCell alloc] init];
[cell1 setResultElement:intermediateResults_[indexPath.row]];
return cell1;
}
1-st cycle (first 5 elements) appear well, but then is loading 6-th element I get an exception :
MyApp[1503:888411] -[CustomCollectionViewCell resultElement]: unrecognized selector sent to instance 0x1a9882a0
I dont understand - Why ? the 6-th cell (cell at index 5) must be kind of ImageThumbCell class, not CustomCollectionView class.
can anyone explain this mistake ?
// sorry for bad english. i'm learning, honestly :)
// thanks

The code you posted is calling the setResultElement method, not resultElement. Thus that code shouldn't generate the crash you're seeing.
Your code for creating cells seems reasonable. On every 5th cell you try to dequeue a different type of cell, and if none are available, you alloc/init that other cell type. That makes sense.
My guess is that your bug is somewhere else in your code. Are you trying to read the value of your cell's resultElement property somewhere else?

Related

Custom CollectionViewCell With Different Subviews?

I'm creating a custom collectionViewCell called MessageCell. This message cell has three components, a headerLabel, messageContainerView, and footerLabel. The problem is that depending on the type of message (video, transaction, delivery confirmation, photo, text, etc) I want to display a specific type of view with specific actions etc.
What's the best way to accomplish this? I've tried setting up my container view as a UIView in my cell subclass, and depending on the type of message, set it equal to a specific subview but that isn't working:
- (void)setMessage:(EMKMessage *)message {
//Set Message
_message = message;
//Check Message Type
switch (message.type) {
case MessageTypeText:
default: {
//Create Message Content View
TextContentView *textContentView = [[TextContentView alloc] initForAutoLayout];
textContentView.frame = CGRectMake(0, 0, 300, 200);
[textContentView setText:message.text];
self.messageContainerView = textContentView;
break;
}
}
}
Any help would be greatly appreciated.
You can create separately all the cells you need. In
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
you can renturn different cells depending on your needs. Just check the type of object you want to represent at the indexPath and return the corresponding cell. Those cell can have delegates if you need to interract with them or you can use block properties. Something like:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
VideoMessageCell *cell = [tableView.dequeueReusableCellWithIdentifier:#"VideoMessageCell"];
//set the cell properties
return cell;
} else if (indexPath.row == 1) {
AudioMessageCell *cell = [tableView.dequeueReusableCellWithIdentifier:#"AudioMessageCell"];
//set the cell properties
return cell;
}
}
Now, I don't know how you decide which type of cell do you need for a given index but you can replace indexPath.row to suit your needs. Also do not forget to set reusable identifiers accordingly.

Customize a simple UITableViewCell efficiently

I would like to customize a simple UITableViewCell so that I run the customization only once and add values (e.g., cell title) later. My app's cell is more complex - it has subviews and uses auto layout; however, a simple example, I believe, will help in focusing on the objective.
I am using iOS 8, Xcode 6.X, Objective-C and Nibs (no storyboard) to keep it simple. I have not created a custom class for UITableViewCell. Instead, I have the following code:
- (void)viewDidLoad {
[super viewDidLoad];
//[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1; //FIXED VALUE FOR EXAMPLE'S SAKE
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 3; //FIXED VALUE FOR EXAMPLE'S SAKE
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"tableView:cellForRowAtIndexPath:");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
NSLog(#"cell == nil");
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
//CUSTOMIZING CELL THAT I WANT TO RUN ONLY ONCE
cell.backgroundColor = [UIColor redColor];
}
NSArray *numbersArray = #[#1,#2,#3];
cell.textLabel.text = [NSString stringWithFormat:#"%#", numbersArray[indexPath.row]];
return cell;
}
Which outputs:
tableView:cellForRowAtIndexPath:
cell == nil
tableView:cellForRowAtIndexPath:
cell == nil
tableView:cellForRowAtIndexPath:
cell == nil
FIRST QUESTION: Why is cell == nil run 3 times? It seems wasteful to run the customization code cell.backgroundColor = [UIColor redColor]; 3 times.
Now, when I enable:
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
And use:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Instead of:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
I get the output:
tableView:cellForRowAtIndexPath:
tableView:cellForRowAtIndexPath:
tableView:cellForRowAtIndexPath:
SECOND QUESTION: Why isn't cell == nil run at all?
FINAL QUESTIONS: How can I make cell == nil run only once so that I format the UITableViewCell only once? Is there a better way to customize a simple cell, running the customization code only once?
Why is cell == nil run 3 times? It seems wasteful to run the customization code cell.backgroundColor = [UIColor redColor]; 3 times.
The table view most likely displays three cells at once, hence requiring three distinct cell objects.
Why isn't cell == nil run at all?
The documentation states that -dequeueReusableCellWithIdentifier:forIndexPath: always returns a valid cell if you registered the identifier previously. It basically takes care of checking if a new cell is required for you.
How can I make cell == nil run only once so that I format the UITableViewCell only once?
You don't. You will have to customize every single instance. I would recommend to use a custom subclass though, rather then messing with UITableViewCell from the outside.
The best way to do this, is to create a custom class for your cell, and do any customization that isn't dependent on the indexPath there. Usually, I do this in initWithCoder or awakeFromNib. You should register the nib in viewDidLoad; I don't see anything wrong with the code you mention in your comment to Christian's answer, unless the name of the file is wrong. It really isn't the view controller's business to be adding subviews or customizing your cell; that code belongs in the cell's class.
BTW, this doesn't keep the customization code from running multiple times. It needs to run once for each cell instance that you create, just like it does in your original code. The number of cells created will be equal to the number that fit on the screen at one time (plus one maybe).

Two different UICollectionViewCells, switch between them in a UISegmentedController

I wonder which's the easiest way to have two different UICollectionViewCells-identifiers in the same UICollectionView? I've got an UISegmentedController that I want to switch between to different styles of UICollectionViewCells.. Where to implement which code. I've got an PatternViewCell for the first Cell but how do i do with the other? Please advice!
You can have two collection view cell prototypes registered for a single cell class for a single collection view with a single data source.
First, in your storyboard, set Cell1 as a reuse identifier for the first cell prototype and Cell2 for the second one. Both of them should have PatternViewCell class.
Then, on changing value of your segmented control you reload your collection view:
- (IBAction)segmentedControlValueChanged:(id)sender {
[self.collectionView reloadData];
}
Bind this action to your segmented control for the Value Changed event.
Then in - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath method you can choose a reuse identifier depending on selected index.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = nil;
if (self.segmentedControl.selectedSegmentIndex == 0) {
identifier = #"Cell1";
} else {
identifier = #"Cell2";
}
PatternViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
//configure cell
return cell;
}

Use uicollectionview inside uitableviewcell

I can't solve next problem.
I want to display 20 table cells, each contains a unique UICollectionView.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *package=[_packageList objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"package";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UICollectionView *cellImageCollection=(UICollectionView *)[cell viewWithTag:9];
cellImageCollection.restorationIdentifier=[NSString stringWithFormat:#"%li", (long)indexPath.row, nil];
cellTracking.text=[NSString stringWithFormat:#"Row #%li",(long)indexPath.row];
return cell;
}
-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
int row=[collectionView.restorationIdentifier intValue];
return [[[_packages objectAtIndex:row] valueForKey:#"imageGallery"] count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
int row=[collectionView.restorationIdentifier intValue];
NSString *imgStrLink=[[[_packages objectAtIndex:row] valueForKey:#"imageGallery"] objectAtIndex:indexPath.row];
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"ImageID" forIndexPath:indexPath];
UIImageView *imageView=(UIImageView *)[cell viewWithTag:2];
imageView.image=....;
return cell;
}
Function tableView:cellForRowAtIndexPath: is called 20 times, but collectionView:numberOfItemsInSection: and collectionView:cellForItemAtIndexPath: only 4 times.
As seen in the screenshots UICollectionView is repeated every fourth line.
What is my fault?
I too faced this problem, the reason I got this problem was because both datasource and delegates for the tableview and collectionView were to the same parent viewcontroller.
It started working for me when I changed the datasource and delegate of the collectionView to another View say ParentView, and I added the collectionView to this ParentView and then finally added ParentView as conentView of the cell.
My previous answer which points to two nice tutorials was deleted by someone saying that I should include the essential parts of the content here(because the link may become invalid in future)...but I cannot do that as those are codes written by the tutorial owners and also the whole codes are essential too :), so now I am giving the link to the source code...
HorizontalCollectionViews
AFTabledCollectionView
-anoop
since Xcode 7 and iOS9 you can use UIStackView - best solution for design if collectionview or tableview are containing each other.

how to not return a UITableviewCell

I have a NSArray of NSDictionaries, in this array there are several values which I do not want to show in the UITableView, I would like to know how to avoid returning these cells in the tableView:cellForRowAtIndexPath:
I have tried to return nil; but this has caused me errors.
This is what my code looks like
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CustomInstallCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomInstallCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.selectionStyle = UITableViewCellSelectionStyleGray;
currentInstallDictionary = [sortedItemsArray objectAtIndex:indexPath.row];
NSNumber *tempDP = [currentInstallDictionary objectForKey:#"dp"];
NSInteger myInteger = [tempDP integerValue];
if (myInteger == 0) {
return cell;
}
return nil; // gives error
}
any help would be appreciated.
This method must return a cell. It cannot return nil. The best thing to do is filter your list before you load your table and use the filtered array when dequeueing cells.
The UITableView is only asking for a cell because you told it to ask. Your implementation of the UITableViewDataSource protocol implements the two methods:
numberOfSectionsInTableView:
tableView:numberOfRowsInSection:
In those methods you determine how many cells should appear on screen. As Brian Shamblen answered, if you don't want that data to appear in the table view, some how ignore (filter, delete, whatever) that data when you calculate the number of sections and rows. If you do so, no "extra" cells will be requested.

Resources