I've got a table view and this method to find the indexPath of a subview of one of its cells...
- (NSIndexPath *)indexPathContainingView:(UIView *)view {
while (view && ![view isKindOfClass:[UITableViewCell self]]) {
view = view.superview;
}
UITableViewCell *cell = (UITableViewCell *)view;
return (cell)? [self.tableView indexPathForCell:cell] : nil;
}
I call with a subview of the topmost cell (which is visible, the table view has not been scrolled), and, with a breakpoint on the return line, I get this perplexing result in lldb...
cell looks good. The cell's table view matches my tableView (there's some intermediate UITableViewWrapperView as the direct superview). But see how indexPathForCell: returns nil? I am certain that the cell is visible.
I don't think it matters, but the subview of the cell I begin with is a UICollectionView, and I'm calling this on the collection view's datasource methods.
Any ideas?
EDIT more context...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// do stuff to setup the cell
// now reload the collection view that it contains
UICollectionView *collectionView = (UICollectionView *)[cell viewWithTag:33];
[collectionView reloadData];
return cell;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
NSIndexPath *indexPath = [self indexPathContainingView:collectionView];
return // get my model from the index path and return the count of an array it contains
// but here's the problem. index path is nil here
}
Assuming you are using storyboards and if you are willing to change the approach you are using a possible solution would be (And a cleaner solution in my opinion):
Drag an outlet of your UICollectionView inside your cell into the UITableViewCell. In your storyboard again set the delegate and dataSource of the UICollectionView to your class by dragging the outlet. Of course you will have to use a custom UITableViewCell
In your cellForRowAtIndexPath for the UITableView set the tag of the the UICollectionView to be your indexPath.row. Now in your numberOfItemsInSection you can access the UICollectionView tag property and it will contain the indexPath.row of the UITableViewCell you are trying to access and hence you can access your model.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// do stuff to setup the cell
// now reload the collection view that it contains
cell.collectionView.tag = indexPath.row;
[cell.collectionView reloadData];
return cell;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
// get my model from the index path and return the count of an array it contains
Model *model = dataArray[collectionView.tag];
return model.count;
}
Related
I am new to Objective-C, I have a function which accepts NSArray as a parameter now I want to store that array in a local var in my function and then populate collection view through that array, ps I'll also have to update cellForItemAtIndexPath delegate method in the same class.
This is the method in the tableView's Cell class. Here I've embedded a collectionView in the cell.
-(void)initCellWithSetsArray:(NSArray *)setsArray{
NSMutableArray *setArray = [NSMutableArray array];
}
This is where i am calling this method which accept the array in cellForRowAt tableView's delegate method.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
[cell initCellWithSetsArray :self.setsHistoryArray];
}
So i need to update the collectionView based on the array parsed in the func when called in cellForRowAtIndexPath. I hope you guys get the picture now.
You need to add a property to your cell class to hold the array:
#property (strong, nonatomic) NSArray *setsArray;
-(void) setSetsArray(NSArray *)sets {
_setsArray = sets;
[self.collectionView reloadData];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section {
return [self.setsArray count];
}
You can use this array in your cellForItemAt method in your cell class.
Also, in your cell classes prepareForReuse you should clear the existing array and reload the collection view:
-(void)prepareForReuse {
self.setsArray = nil;
[self.collectionView reloadData];
}
Then you can simply assign the property in your cellForRowAt::
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
MyCellClass *cell = (MyCellClass *)[tableView dequeueReusableCellWithIdentifier:"MyCellIdentifier" forIndexPath:indexPath];
cell.setsArray = self.setsHistoryArray; // Note: This looks wrong; - you should normally use indexPath.row to get the right data for this row
return cell;
}
Try this ;
pragma mark CollectionView
-(NSInteger) numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return arrNames.count;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"areacell" forIndexPath:indexPath];
UILabel *lblName = (UILabel *)[cell viewWithTag:101]; // set tag in storyboard for this label
lblName.text = arrNames[indexPath.item];
return cell;
}
I am working with the UICollectionView inside UITableViewCell. I am done with adding the contents and I am able to see the UICollectionView inside UITableViewCell.
But now I don't know how to check which cell of UICollectionView is tapped inside the which row of UITableView.
So if anybody knows how to recognize it pls help.
Thanks in advance.
#pragma mark
#pragma mark - UITableViewDelegate and UITableViewDatasource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 15;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
CellForCollectionView *cell = [tableView dequeueReusableCellWithIdentifier:#"CellForCollectionView"];
if (cell == nil) {
cell = [[CellForCollectionView alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CellForCollectionView"];
}
cell.categoryCollectionView.delegate = self;
cell.categoryCollectionView.dataSource = self;
[cell.categoryCollectionView registerNib:[UINib nibWithNibName:#"CollectionCellForCategory" bundle:nil] forCellWithReuseIdentifier:#"CollectionCellForCategory"];
cell.lblCategoryName.text = [NSString stringWithFormat:#" Category %d",indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
#pragma mark
#pragma mark - UIcollectionViewDelegate and UIcollectionViewDatasource
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return 15;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CollectionCellForCategory *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionCellForCategory" forIndexPath:indexPath];
cell.imagCategory.layer.borderColor = [[UIColor blackColor]CGColor];
cell.imagCategory.layer.borderWidth = 1.0f;
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"%ld",(long)indexPath.row);
NSLog(#"%ld",(long)indexPath.section);
}
You can subclass UICollectionView and add a variable to track the indexPath of the tableViewCell
In your tableViewCell change the collectionView type to your custom collectionView
Set the variable in "cellForRowAtIndexPath" method of UITableViewDatasource
Custom collectionView:
#interface IndexedCollectionView : UICollectionView
#property (nonatomic, strong) NSIndexPath* parentIndexpath;
#end
Your custom tableViewCell (approx.)
#interface CellForCollectionView : UITableViewCell
#property (nonatomic, weak) IBOutlet IndexedCollectionView* categoryCollectionView;
#end
UITableViewDataSource method implementation:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
CellForCollectionView *cell = [tableView dequeueReusableCellWithIdentifier:#"CellForCollectionView"];
if (cell == nil) {
cell = [[CellForCollectionView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CellForCollectionView"];
}
cell.categoryCollectionView.delegate = self;
cell.categoryCollectionView.dataSource = self;
[cell.categoryCollectionView registerNib:[UINib nibWithNibName:#"CollectionCellForCategory" bundle:nil] forCellWithReuseIdentifier:#"CollectionCellForCategory"];
cell.lblCategoryName.text = [NSString stringWithFormat:#" Category %d",indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.categoryCollectionView.parentIndexpath = indexPath;
return cell;
}
A bit off-topic, but in case someone has the same problem as myself having collection view cells touch inside tableview work and reached this question looking for an answer :
It does work with all the default settings. So, make sure you don't have another touch gesture in your view controller (such as a tap gesture like me) blocking the touch from reaching the collection view cell.
If you have such a gesture recognizer, and you want to keep it, you need to set its "cancelTouchesInView" property to false. And that's all. You don't need to implement the IUGestureRecognizerDelegate functions to have it work (although that may be different if you have multiple gesture recognizer and not just one).
It's simple. Add tag to each cell of UITableView in "cellForRowAtIndexPath:" method i.e
cell.tag = indexPath.row
same goes for the CollectionViewCell inside the UICollectionView
Let say you have a tableView with 2 rows and which is having 2 collectionViews respectively.
row1 of UITableView will have tag 0 and other one will have tag 1.
Now when the user clicks on the collectionViewCell inside the collectionView which is inside a UITableViewCell
Now, if you click the first collectionViewCell of the collectionView inside the first UITableViewCell
You will get the cell tag as 0 and as you already know that self.tag i.e is the tableViewCell is 0
Now, if you click the first collectionViewCell of the collectionView inside the 2nd UITableViewCell
You will get the cell tag as 0 and as you already know that self.tag i.e is the tableViewCell is 1
I think this was what you needed.Do let me know if it's clear to you.. Thanks :)
I have a uicollectionView inside a tableview cell. I am trying to figure out to have unique data source for each collectionview based on the index of the tableViewCell. Here is what I have so far... How do you get each cell to have different collectionview data source?
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"collectionViewCell" forIndexPath:indexPath];
UILabel *title = (UILabel* )[cell viewWithTag:100];
title.text = [collection1 objectAtIndex:indexPath.row];
// Configure the cell
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"tableViewCell" forIndexPath:indexPath];
// Configure the cell...
return cell;
}
This should set you on the right track:
Your data source for your UITableView should be a collection of data sources for each cell and hence each UICollectionView. So for example, say you want to show some images in each of your UICollectionViewCells. In this scenario, the data source for your UITableView could be an Array containing Arrays of images (one Array of images for each cell in your UITableView).
You will need a custom UITableViewCell that exposes a method like "SetDataSourceForCollectionView".
Then in the cellForRowAtIndexPath method of the UITableView you can get the data source for the cell by indexing your collection using the NSIndexPath parameter. Just pass this value to the SetDataSourceForCollectionViewmethod of your UITableViewCell.
Your custom UITableViewCell now has a datasource it can assign to it's UICollectionView.
I have a UITableView that display a program name and the DJ name. For some programmes, there are no any DJs. So I created a custom UITableViewCell by putting 2 UILabels. Middle of the UITableViewCell displays the programme name and bottom UILabel displays the DJ name. To display both programme and the DJ name I have to set the cell size and UITableViewCell size as 147. But when DJ is not available I want to hide that bottom label and resize the cell and row height to 70. so how should I do this ?
I am checking if this DJ is null or not inside my cellForRowAtIndexPath method.
How I can resize the cell, and row height ?
Thanks for the help.
You need to use the following delegate:
-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*) indexPath {
if(...){
return 147.0f; //or whatever
}
else{
return 70.f;
}
}
Add NSIndexPath below #implementation
NSIndexPath *selectedCellIndexPath;
Add Both of these methods.
// And in the implementation file:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
selectedCellIndexPath = indexPath;
// Forces the table view to call heightForRowAtIndexPath
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// Note: Some operations like calling [tableView cellForRowAtIndexPath:indexPath]
// will call heightForRow and thus create a stack overflow
if(selectedCellIndexPath != nil
&& [selectedCellIndexPath compare:indexPath] == NSOrderedSame)
return 338;
return 64;
}
You have 2 types of cell. With height of 147 and 70 pixels.
1) As Anoop Vaidya said you should set cell height by checking "DJ is null or not".
2) To avoid reusing wrong cell template, you should use 2 types of cellIdentifier:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = nil;
if (check DJ is nil or not) {
CellIdentifier = #"CellWithHeight_147";
} else {
CellIdentifier = #"CellWithHeight_70";
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
...
return cell;
}
I have a UICollectionView whose all cells contains a UITableView. My UICollectionView delegate / datasource methods are handled by a view controller (let's say TTCollectionViewController), and tableViews are handled directly by collection view cells (TTCollectionViewCell).
In order to change some label colors, I need to keep track of each selected rows on my tableViews so i did something like that :
//
// TTCollectionViewCell.m
//
[...]
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// keep track of selected drugs
if (![self.selectedDrugIndexPaths containsObject:indexPath])
{
[self.selectedDrugIndexPaths addObject:indexPath];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TTTableViewCell *contentCell = [tableView dequeueReusableCellWithIdentifier:#"test"];
if (contentCell == nil)
contentCell = [[TTTableViewCell alloc] init];
[...]
// test
if ([self.selectedDrugIndexPaths containsObject:indexPath]) {
contentCell.drugLabel.textColor = [UIColor grayColor];
} else {
contentCell.drugLabel.textColor = [UIColor blackColor];
}
}
[...]
But here's the thing : with the dequeue reusable thing, my collectionView is messing up all my tracks (on scrolling, selected rows on table 1 become selected on table 3 for example).
I know it's a bad idea to don't use dequeuereusable (i don't even know if it's possible to not use it), but is there something similar that i can do ?
Do anyone have already implement this kind of case ?
Thx in advance.
EDIT :
Here's my cellForRow... methods of my collectionView as you asked for :
///
/// TTCollectionController.m
///
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
TTCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
cell.data = [self.data objectAtIndex:indexPath.row];
return cell;
}