Detecting Tap on UICollectionView inside an UITableViewCell - ios

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 :)

Related

Using NSArray to populate collectionView

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;
}

indexPathForCell: returning nil

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;
}

UILabel appears multiple times in UITableViewCell

I'm currently creating my UITableViewCells programmatically like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Home-Cell" forIndexPath:indexPath];
UILabel *newLabel = [[UILabel alloc] initwithframe:cell.frame];
[newLabel setText:self.data[indexPath.row]];
[cell addSubview:newLabel];
return cell;
}
This seems to create a new UILabel each time the cell is reused though, which I definitely don't want. I tried doing the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Home-Cell" forIndexPath:indexPath];
if (cell == nil) {
UILabel *newLabel = [[UILabel alloc] initwithframe:cell.frame];
[cell addSubview:newLabel];
}
[newLabel setText:self.data[indexPath.row]];
return cell;
}
but then the UILabel seems to never be created. Perhaps this is because I'm using prototype cells with Storyboard and thus the cells are never nil?
You have two solutions.
Create a custom table view cell that already has the label.
If you want to add the label in code, don't register a class for the cell. Then the dequeued cell can be nil and you can add the label at that time (like in your 2nd set of code). This also requires using the dequeueReusableCellWithIdentifier method that doesn't also take an indexPath.
You should create a UITableViewCell subclass and add "newLabel" as a property.
The cell is never nil because the method you use to dequeue the table view cell always returns a cell, creating one if it doesn't already exist in the reuse queue.
A better solution would be to create the label in the cell prototype in the storyboard.
This implementation is against MVC architecture where controller managers stuff and do not deal with view. Here, you are trying to add stuff in view from controller. It is suggested to subclass UITableViewCell as below and add your custom UI controls in there
MyTableViewCell.h
#interface MyTableViewCell : UITableViewCell {
}
#property (nonatomic, strong) UILabel *myLabel;
#end
Then you can implement layoutSubviews in your MyTableViewCell.m file to define the look and feel of your cell.
MyTableViewCell.m
- (id)initWithStyle:(UITableViewCellStyle)iStyle reuseIdentifier:(NSString *)iReuseIdentifier {
if ((self = [super initWithStyle:iStyle reuseIdentifier:iReuseIdentifier])) {
self.myLabel = [[UILabel alloc] initWithFrame:<Your_Frame>];
// Set more Label Properties
[self.contentView addSubview:self.myLabel];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
// Override run time properties
}
Finally use your custom cell like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyTableViewCell *cell = (MyTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"Home-Cell" forIndexPath:indexPath];
if (cell == nil) {
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Home-Cell"];
}
cell.myLabel.text = self.data[indexPath.row];
return cell;
}
As a side note, I hope you know that you get textLabel and detailTextLabel free from default UITableViewCell implementation.

Custom Cells are created, but not displayed

So I've used this tutorial to populate a UITableView with custom cells that represent balances. When stepping through the code, I witness the correct amount of cells get created (only 4 with the current test data) and their labels' text set correspondingly.
My problem is when the table is displayed on the screen, only the first row/cell is displayed.
Any insight as to why this could be occurring would be greatly appreciated!
Removed old code.
BalanceCell.h:
#import <UIKit/UIKit.h>
#interface BalanceCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
#property (weak, nonatomic) IBOutlet UILabel *amountLabel;
#property (weak, nonatomic) IBOutlet UILabel *modifiedLabel;
#end
EDIT:
My TableView delegate methods are now as follows:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [_balances count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
BalanceCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[BalanceCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.backgroundColor = [_hex colorWithHexString:_themeColourString];
return cell;
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(BalanceCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
Balance *item = [_balances objectAtIndex:indexPath.row];
cell.nameLabel.textColor = _themeColour;
cell.nameLabel.text = item.name;
cell.amountLabel.textColor = _themeColour;
cell.amountLabel.text = [NSString stringWithFormat:#"%#%#", item.symbol, item.value];
cell.modifiedLabel.textColor = _themeColour;
cell.modifiedLabel.text = [NSString stringWithFormat:#"%#", item.modified];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 94;
}
As #Sebyddd suggested, I now register the NIB in the viewDidLoad method.
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerNib:[UINib nibWithNibName:#"BalanceCell" bundle:nil] forCellReuseIdentifier:#"Cell"];
}
These changes may make my code more correct but still only the first cell is displayed.
If cells are getting created and returned properly I guess height is not being set propery. By default I beleive all cells have a height of 44. If your cell exceeds this height it might not get displayed.
You can tell the tableview to adjust height for every cell using (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath delegate
In that delegate just return your cells height.
EDIT:
You are using dequeueReusableCellWithIdentifier: which will return A UITableViewCell object with the associated identifier or nil if no such object exists in the reusable-cell queue.
Instead use dequeueReusableCellWithIdentifier:forIndexPath: which will return A UITableViewCell object with the associated reuse identifier. This method always returns a valid cell.
You need to register the nib/class for that custom cell in viewDidLoad
Try this:
if (cell == nil) {
[tableView registerNib:[UINib nibWithNibName:#"BalanceCell" bundle:nil] forCellReuseIdentifier:#"Cell"];
cell = [[BalanceCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
Use this tuto : http://www.appcoda.com/uitableview-tutorial-storyboard-xcode5/ , your tuto is a bit outdated, and hard to follow !

Custom UITableView - iphone

I used a UITableView and I built a customCell, the UITableView is connected to NavigationController.
I want that cell 1-3 use the navigationBar (e.g. click on cell pass to the next view)
and cell 4-5 just remain as clickable cells (e.g. I click on this cell and the cell background changes).
In the storyBoard the identifier is equal to Cell.
Right now any action pass me to the other view, can some one help?
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
return cell;
}
When declaring your cellForRowAtIndexPath, be sure to to add your custom cell as:
YourCustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
If you don't need the custom cell, then just leave it as UITableViewCell.
Use the didSelectCellAtIndexPath method for the action:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *segueIdentifier = [[NSString alloc]init];
switch (indexPath.row) {
case 0:
segueIdentifier = #"segue1";
break;
case 1:
segueIdentifier = #"segue2";
break;
case 2:
segueIdentifier = #"segue3";
break;
if (indexPath.row != 3 && indexPath.row !=4) {
[self performSegueWithIdentifier:segueIdentifier sender:self];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
You can use (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method for this.
Implement this method in your tableviewcontroller and in this method do following stuff.
If you have added tableview in viewcontroller then do following -
1) In .h file add delegates #interface TestTableViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
2) In .m file in (void)viewDidLoad() method add
self.tableView.delegate=self;
self.tableView.dataSource=self;
If user click on 1-3 cells call [self performSegueWithIdentifier:SEGUE_SHOWDETAILS sender:self];in didSelectRowAtIndexPath method to redirect to new page
and for 4-5 cells change cell background color in didSelectRowAtIndexPath method.

Resources