Access the UICollectionViewCell Components in custom method - ios

I have some components in UICollectionViewCell and i can access all inside the cellForItemAtIndexPath. I need to access the component in a custom method,
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
// UIButton
cell.btn1.frame =CGRectMake(380,100, 150, 40);
[cell.btn1 addTarget:self action:#selector(btnClicked) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
In custom method,
-(void)btnClicked{
MycollectionclassView *cell =[[MycollectionclassView alloc]init];
cell.btn1.backgroundColor =[UIColor greenColor];
}
Here I cannot change the color of the button.

You must create a protocol and create a IBAction to a method. When your action method is called, you can notify the protocol and its screen that has the UICollectionView protocol will receive this event.
On your cell header file.h define the protocol:
#protocol YourCellNamelDelegate <NSObject>
#required
/**
* Notifies that current ITEM was clicked on icon.
*
* #param cell The current cell that was clicked.
*/
-(void)bookmarkClicked:(YourCellName *)cell; // OR without argument
#interface YourCellName : UITableViewCell
/**
* Reference to 'YourCellNameDelegate' delegate.
*/
#property (assign,nonatomic) id<YourCellNamelDelegate>delegate;
...
On your cell file.m
...
- (IBAction)bookmarkClickAction:(id)sender {
// change or update screen element here ...
if (self.delegate) {
[self.delegate bookmarkClicked:self];
}
}
On your ViewController implement the your protocol
#interface SMGListViewController () <YourCellNameDelegate>
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// get your cell here, for example in my case:
SMGCityGuideCollectionViewCell *cell;
cell = (SMGCityGuideCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
SMGTouristInfo *touristInfo = [self.listTouristInfo objectAtIndex:indexPath.row];
[cell setTouristInfoCell:touristInfo];
// IMPORTANT HERE *************************
cell.delegate = self;
// IMPORTANT HERE *************************
return cell;
}
...
#pragma mark <SMGPlaceCellDelegate>
-(void)bookmarkClicked:(SMGPlaceCell *)cell {
// notify events here ...
NSIndexPath *index = [self.tableViewPlaces indexPathForCell:cell];
[self.tableViewPlaces reloadRowsAtIndexPaths:#[index] withRowAnimation:UITableViewRowAnimationAutomatic];
}
I hope it helps! :)

To make things clear , when cellForItemAtIndexPath gets called it is the time that your cells get laid in the view , in your custom method you are just creating a cell object but this object does not reference the objects that is laid out in your collection view , so your just changing the color of a newly created object that does not reference anything .

In the Button Click function pass arguments as Sender , by which in the function call you can access the collectionView cell as Sender.SuperView .
You can also find the indexpath from the collectionviewcell now.
Now if you want to update a UI of the Collection view cell , you will have to do it in the cellforItemAtIndexPath function. which can be called by [collectionview reloadData].
So I would suggest to keep some flag variable in the data source to check if you have to update the UI or not.

Related

Unable to update the Subview in a Custom UITableViewCell

I have a custom UITableViewCell in which I have two UIViews . I want to change the BackgroundColor radius and some more properties of UIViews.
But I am unable to do so.
Here is my setUp.
Step 1:
Create A Custom Cell with XIB.
Step 2: Entered the Cell Identifer name in XIB for CustomCell.
Step 3: Instantiated NIB in viewDidLoad
UINib *nib = [UINib nibWithNibName:#"CustomCell" bundle:nil];
[[self mTableView] registerNib:nib forCellReuseIdentifier:#"CustomCell"];
Step 4: Cell For Row At Index Method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create an instance of ItemCell
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
[cell updateCellView];
return cell;
}
Step 5: Checked Twice the Outlet Connections all are Fine.
Step 6: Custom Cell Class:
#import "TransportCell.h"
#implementation TransportCell
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)updateCellView
{
self.backgroundView.backgroundColor = [UIColor redColor];
}
#end
This code has no effect on my Cell View.
I debug the code. When I logged the backgroundView I get nil when updateCellView method get called:
Here is my CustomCell's Xib:
I have to change the Properties of Inner UIView.(Blue in color)
Instead of calling the method in the cellForRowAtIndexPath try this:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create an instance of ItemCell
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
return cell;
}
and then in your custom method
- (void)updateCellView
{
self.backgroundView.backgroundColor = [UIColor redColor];
// reload the table view
[self.tableView reloadData];
}
you need to add [ tableview reloadData ]
in method update cell view
Ideally all the info needed to render the cell should come from either the data source or other events. For example, if you are changing the border-color it should mean that either some entity (say user) has performed an event or something has changed in the data-source like a value passed its threshold.
The change in data-source or those events should either trigger refreshing the whole table:
-(void) reloadData
or some cells:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
Only then the cells will get updated.
you are changing the self.backgroundview.backgroundColor
Default it is nil for cells in UITableViewStylePlain, and non-nil for UITableViewStyleGrouped.
The 'backgroundView' will be added as a subview behind all other views.
#property (nonatomic, strong, nullable) UIView *backgroundView;
try this method in TransportCell, it's swift code make same for objective c
override func layoutSubviews()
{
self.backgroundView.backgroundColor = [UIColor redColor];
}
Use this:
self.contentView.backgroundColor = [UIColor redColor];
Hope this helps!

Collapse a table row when a button is tapped

I have a table view that has 3 rows in it. When one of the rows is tapped, the row height expands to show a UICollectionView with 3 cells in it.
I want the row to reduce height again when one of the collection view cells is tapped. This is what I've attempted:
//customCell.m
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
ProductViewController *productView = [[ProductViewController alloc]init];
[productView collapseRow];
}
//ProductViewController.m
#property NSIndexPath *selectedRow;
- (void)collapseRow {
menuCell *cell = (menuCell *)[self.tableView cellForRowAtIndexPath:self.selectedRow];
[self.tableView beginUpdates];
self.selectedRow = nil;
[self.tableView endUpdates];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView beginUpdates];
if ([indexPath compare:self.selectedRow] == NSOrderedSame) {
self.selectedRow = nil;
} else {
self.selectedRow = indexPath;
}
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; {
if ([indexPath compare:self.selectedRow] == NSOrderedSame)
return 105;
else
return 50;
}
Am i doing anything wrong here? I know that the table view is updating when I tap a collection view cell, but it's not checking against the currently open row and setting the height using that.
The things we have a ViewController(ProductViewController), containing an instance of UITableView, the cells of table view are created in CustomCell class.
1) Since ProductViewController has the instance of UITableView, so using that instance ProductViewController can call methods on UITableView and it's cells.
2) Also, while creating table view we set the delegate and dataSource of tableview as
tableView.delegate = self;
tableView.dataSource = self;
self is nothing but the reference of the class in which table view is being created(in your case it's ProductViewController)
you implement the tableView's delegate and dataSource method in the ProductViewController class
and what the table view does, if it needs to call a method on your class ProductViewController' instance it uses the reference contained in it's delegate and dataSource properties, which is nothing but the reference of ProductViewController. So calling the method using delegate/dataSource by table view will execute the methods in ProductViewController(if the method exists there).
3) Now as you have created a class called CustomCell, using which you create the custom cells and add on the table view, the table can call methods on the cell's instance but if you need the other way round(cell calling the method of table view or instance of ProductViewController, you can't do so since the cell does not know the reference/address of the ProductViewController), so to call a method on the instance of ProductViewController which contains the table view and cell, the cell should know the address of the ProductViewController. And to provide the cell with the reference/ address of ProductViewController we use the concept of delegation as used by UITableView in 2 point point above.
You can use the below delegation pattern implementation to meet the requirement of point 3-
In CustomCell.h class declare a delegate as
#protocol CustomCellDelegate;
#interface CustomCell : UITableViewCell {
//declare member variables
...
id<CustomCellDelegate> cellDelegate;
}
...
#property (nonatomic, assign) id<CustomCellDelegate> cellDelegate;
...
// method declarations
#end
#protocol CustomCellDelegate <NSObject>
- (void)collapseRow;
#end
in CustomCell.m
#import "CustomCell.h"
#implementation CustomCell
#synthesize cellDelegate = _ cellDelegate;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
// methods to create cells
// methods to create collection view and it's items
// collection view item selection handler
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
// using delegate will call the method collapseRow in ProductViewController
[self. cellDelegate collapseRow];
}
#end
Also, adopt the delegate in ProductViewController.h as
#import "CustomCell.h"
#interface ProductViewController:UIViewController < CustomCellDelegate >
and in in ProductViewController.m
when you are creating the cell as
CustomCell *cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
pass the reference of ProductViewController instance as
cell.cellDelegate = self;
You can also use
- (void)collapseRow
{
NSArray* indexArray = [NSArray arrayWithObjects:self.selectedRow, nil];
self.selectedRow = nil;
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationNone];
}
I think the actual problem for you lies in this method
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
ProductViewController *productView = [[ProductViewController alloc]init];
[productView collapseRow];
}
In the above method you are creating a new instance of ProductViewController using ProductViewController *productView = [[ProductViewController alloc]init]; and this will call the collapseRow, but as it's not on the instance of the previous ProductViewController class so the row of the table contained in the previous instance will not get collapsed.
In above method you should use delegation pattern, when you are creating the cells for the collection view than during cell creation just pass the reference of ProductViewController instance(using delegation) and when any collection view cell is selected, just use the same delegate(which contains the reference of ProductViewController instance) to call the collapseRow method on the correct class rather than allocating the new one.
This should work:
- (void)collapseRow {
NSIndexPath *selectedRow = self.selectedRow;
[self.tableView beginUpdates];
self.selectedRow = nil;
[self.tableView reloadRowsAtIndexPaths:#[selectedRow] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
Or you could simply:
- (void)collapseRow {
self.selectedRow = nil;
[self.tableview reloadData];
}
but that would cause all the cells to be reloaded, so it's not as efficient.

Button inside tableViewCell not calling IBAction

I have created both UICollectionView and UITableView on my View
Inside tableViewCell's ViewController, there is an action listener :
.h:
- (IBAction)addItem:(id)sender
.m:
- (IBAction)addItem:(id)sender {
UIButton * button = ( UIButton * )sender;
NSLog(#"add item button clicked");
}
I wanted to execute this function by clicking both the Cell in my UICollectionView and a UIButton inside each tableViewCell
the following codes work perfectly for clicking my UICollectionView's cell
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
Item * selectedItem = [collectionDataArray objectAtIndex:indexPath.row];
//get item object from my collectionView's data array
if ([tableDataArray containsObject:selectedItem]){
//check if my tableView's data array having the same object
NSUInteger itemIndex = [tableDataArray indexOfObject:selectedItem];
NSIndexPath * indexpathOfTable = [NSIndexPath indexPathForRow:itemIndex inSection:0];
//get indexPath of the cell containing the item object
myTableCell * tableCell = (myTableCell*)[myTableView cellForRowAtIndexPath:indexpathOfTable];
[myTableCell addItem:self];
//executing action listener here
}else{
[tableDataArray addObject: selectedItem];
}
[myTableView reloadData];
}
didSelectRowAtIndexPath is working fine as well
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"cell clicked");
}
However, it will no longer execute the addItem function for the UIButton I added inside my UITableView no matter I add selector for the button when generating tableViewCell or I create UIButton with linked IBAction for my tableViewCell inside Interface Builder
I've implemented both UITableViewDelegateand UICollectionViewDelegate
I think it never detect my clicking gesture for my UITableView but detect the one for UICollectionView
Is there anyone help me on this? thanks a lot

dequeueReusableCellWithReuseIdentifier doesn't go through init nor initWithCoder functions

When creating cells for UICollectionView, dequeueReusableCellWithReuseIdentifier doesn't go through init nor initWithCoder function of CategoryView.
The view is creating, it has a proper type (CategoryView) but init nor initWithCoder of CategoryView is not called, so essential functionality is not executed. Is there some other init in this senario?
- (CategoryView *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CategoryView *cell = [cv dequeueReusableCellWithReuseIdentifier:#"CategoryView" forIndexPath:indexPath];
[cell someConfiguration];
return cell;
}
In this case, the problem was that the base class was not specified for your cell prototype in Interface Builder. So make sure the base class is set:
Then, when you call dequeueReusableCellWithReuseIdentifier, using the storyboard identifier you specified in the prototype cell, it calls initWithCoder when the cell is first instantiated. If the cell scrolls out of view and is later re-used for another NSIndexPath, the prepareForReuse is called:
#interface CategoryView : UICollectionViewCell
#end
#implementation CategoryView
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
NSLog(#"init");
}
return self;
}
- (void)prepareForReuse {
[super prepareForReuse];
NSLog(#"reuse");
}
#end
try - (instancetype)initWithFrame:(CGRect)frame
It works for me. I wrote UI programmatically.
Good luck!
UICollectionViews and UITableViews reuse cells to improve performance. initWithCoder: will only run once per reusable cell. As such, if you need something called every time a cell is displayed I would recommend writing a method as follows in your cellForRowAtIndexPath: method:
- (CategoryView *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CategoryView *cell = [cv dequeueReusableCellWithReuseIdentifier:#"CategoryView" forIndexPath:indexPath];
// self.parameters = an NSDictionary of the colors, text, etc. you need to the cell to know about
[cell configureWithParameters:self.parameters];
return cell;
}
Then, inside your configureWithParameters: method you can include colors, text, etc. that will help you setup your CategoryView.
You'll have to declare your configureWithParameters: method in your CateogryView.h as follows:
// CategoryView.h
- (void)configureCell:(NSDictionary *)parameters;
Then include your customizations in the .m as follows:
// CategoryView.m
- (void)configureCell:(NSDictionary *)parameters{
// Put Whatever initialization code you need here
// Example:
self.label.textColor = parameters["color"];
self.label.text = parameters["text"];
}
Make sure you have collection view delegate/datasource connected to your class.
Make sure you give base class for cell.
Make sure your cell has reuse identifier.
Make sure to register Nib for your reuse identifier. (You don't need to do that if your cell present in your collection view in storyboard).
And finally try changing
- (CategoryView *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
to
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
Now your
- (instancetype)initWithCoder:(NSCoder *)aDecoder
Should be called.
Assuming you've subclassed UICollectionViewCell, you can put your initialization code in awakeFromNib().

Which method should I use to do something between UICollectionViewCells?

I have implemented an UICollectionView image gallery. Each cell has some views and I would like to hide or show that views when I change the current cell, at least when the event starts. is there any method? or should I do something by delegate? I have paging enabled and custom cell and FlowLayout.
I have done everything almost like this tutorial
One way would be to keep the current selected cell index in a local variable in your viewController, and use that index to perform any actions when selecting another cell:
#property (nonatomic) NSIndexPath *selectedCellIndexPath;
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
if (![self.selectedCellIndexPath isEqual:indexPath]) {
UICollectionViewCell *lastSelectedCell = [collectionView cellForItemAtIndexPath:selectedIndexPath];
// Perform any change to lastSelectedCell before deselecting it
[collectionView deselectItemAtIndexPath:lastSelectedIndexPath animated:YES];
}
self.selectedCellIndexPath = indexPath;
UICollectionViewCell *selectedCell = [collectionView cellForItemAtIndexPath:indexPath];
// Change what you want in the newly selected cell;
}

Resources