I have UICollectionView that use custom UICollectionViewCell class called ClaimInfoCell. I want to fire its init method by overriding initWithCoder: but this method doesn't fired.
- (id)initWithCoder:(NSCoder*)aDecoder
{
if(self = [super initWithCoder:aDecoder])
{
// Do something
}
return self;
}
I checked in collectionView:cellForItemAtIndexPath: and everything is fine. NSLog-ing that cell, and it was the ClaimInfoCell's instance.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
ClaimInfoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"claimInfoCell" forIndexPath:indexPath];
return cell;
}
Already followed this and this answer, but I can't find the right that fits on mine.
I use storyboard and customize that cell on its collection view directly. So how I should declare my custom init method my cell? Any help would be appreciated. Thank you.
Related
I want to call didSelectItemAtIndexPath: for particular index path but I can't call it programmatically in cellForItemAtIndexPath because collection is not yet ready, I will get cell as nil. Do we have any delegate method or any other UIView method that is called after collection view is ready?
I have tried willDisplayCell: but it is not made for relevant work, couldn't find anything else.
I want to call didSelectItemAtIndexPath:
Don't. This is a delegate method. It is called by the runtime when the user selects an item. You must never call this yourself.
You have to do it programmatically utilising your manual logics. :)
The underlying concept is that get the indexes of selected cells and reload those specific cells only.
Declare a global var
NSMutableArray array_indexpath;
in your did select method add indexes of selected cells.
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
array_indexpath=[[NSMutableArray alloc]init];
[array_indexpath addObject:indexPath];
[self.myCollectionView reloadItemsAtIndexPaths:array_indexpath];
}
and in your cell for cellForItemAtIndexPath method check the indexes and reload it as required.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ShubhCalendarCollectionViewCell *cell =[collectionView dequeueReusableCellWithReuseIdentifier:#"ShubhCalendarCollectionViewCell" forIndexPath:indexPath];
cell.backgroundColor=[UIColor clearColor];
if (array_indexpath.count !=0)
{
for (int i=0; i<[array_indexpath count]; i++)
{
if ([array_indexpath objectAtIndex:i] == indexPath)
{
cell.backgroundColor=[UIColor greenColor];
}
}
}
return cell;
}
Hope it helps.. Happy Coding.. :)
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.
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().
I've created my own CustomTableView and CustomCell. The cell is in a xib, and I'm registering it when initializing the tableView, like this:
[self registerNib:[UINib nibWithNibName:#"CustomCell" bundle:nil]
forCellReuseIdentifier:kCustomCellIdentifier];
If I don't do this, I won't be able to define what ReuseIdentifier should "point" to this class. This is my cellForRow:
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = [self dequeueReusableCellWithIdentifier:
kCustomCellIdentifier forIndexPath:indexPath];
if(!cell)
{
cell = [[[NSBundle mainBundle] loadNibNamed:#"CustomCell"
owner:self options:nil] objectAtIndex:0];
cell.delegate = self;
[cell initialSetup]; //Other stuff
}
else
[cell resetCell];
//And other awesome stuff
return cell;
}
This 'works'. When I lauch my app, my own custom cells are showing.
However, it turns out the cell is NEVER returned as nil from [self dequeue... Thus, the if-statement if(!cell) is never true. I have additional setup inside this statement that I want to perform, but I don't know where the cell's are being initialized the first time now. If I remove registerNib, then this statement is true, but then it's true for all cells, and none will ever be dequeued.
I can probably work around this, and put my initialSetup (and other stuff) inside the -(id)initWithCoder..-method in the CustomCell-class, but I'd like to know where my cells are being initialized right now. Why do my cells exist before cellForRowAtIndexPath?
When you register a class or a nib in a table view using method registerClass:forCellReuseIdentifieror registerNib:forCellReuseIdentifier the tableview internally will create an instance of the cell if no one is available when you call dequeueReusableCellWithIdentifier:, so initialization code is no longer needed inside the delegate.
From the UITableView.h code:
// Beginning in iOS 6, clients can register a nib or class for each cell.
// If all reuse identifiers are registered, use the newer -dequeueReusableCellWithIdentifier:forIndexPath: to guarantee that a cell instance is returned.
// Instances returned from the new dequeue method will also be properly sized when they are returned.
- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);
Depending of the which register method is used the init methods called are:
initWithStyle:reuseIdentifier for cells registered using registerClass:
initWithCoder: for cells registered using registerNib:
If you are using registerNib: you can use too awakeFromNib method in the cell, that is also a good place to put initialization code of the cell. The main difference between using initWithCoder: or awakeFromNib its explained in this question.
When a cell is reused, you have the method prepareForReuse in the cell to make some cleanup in the cell and left it prepared to be configured again.
A good approach to work with all of this will be:
//ViewController code
- (void)viewDidLoad
{
...
[_tableView registerNib:[UINib nibWithNibName:#"CellSample" bundle:Nil] forCellReuseIdentifier:#"cellIdentifier"];
...
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
//configure the new cell, no if (!cell) needed
return cell;
}
//Cell code
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
//You can put initialization here
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
//But is better initialize here
}
- (void)prepareForReuse
{
//Reuse and reset the cell here
}
Hope it helps
When using dequeueReusableCellWithIdentifier: forIndexPath: you don't have to alloc the cell like you would if you were using default UITableViewCell. In your custom UITableViewCell subclass, this is called when it is initialized:
- (void)awakeFromNib {
[super awakeFromNib];
}
So add that in there and you should be good.
- (instancetype)initWithStyle:(UITableViewCellStyle)style
reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
//custom your cell
}
return self;
}
When recently adding a UICollectionView with a custom UICollectionViewFlowLayout subclass, the collectionView:cellForItemAtIndexPath: method is only being called on iOS7, and not on iOS6. In other words, everything works great in iOS7 but my custom collectionView items are not showing up in iOS6. Interestingly, the cells appear to be there (the collectionView scrolls), but all items are empty with a white background.
The collectionView was set up in a .xib file, dataSource and delegate were attached, and UICollectionViewDataSource and UICollectionViewDelegateFlowLayout were added after the #interface call in the view controller .h file.
The collectionView item size, section inset, line spacing, and inter-item spacing are all being set in the custom flow layout init method.
Some code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.collectionView.collectionViewLayout = [[TFSpringFlowLayout alloc] init];
[self.collectionView registerClass:[TFWorkoutCell class] forCellWithReuseIdentifier:CellIdentifier];
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
// This method is returning a value > 0
return _workouts.count;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
// This is being called on iOS7, but is never being called on iOS6
...removed for clarity
return cell;
}
EDIT: Issue Solved. My custom Flow Layout included some iOS7-specific overrides utilizing the new UIDynamicAnimator class. These were not causing a crash, but were preventing the cells from being drawn in iOS6.
Here is what the issue was for me, in case anybody else runs into this problem in the future.
My custom UICollectionViewFlowLayout contained several method overrides to implement iOS7's new UIKit Dynamics. These did not cause the application to crash, but prevented the cells from being drawn in iOS6.
Here is the offending code:
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
return [self.dynamicAnimator itemsInRect:rect];
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
return [self.dynamicAnimator layoutAttributesForCellAtIndexPath:indexPath];
}
And the simple change required for the fix:
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
if ([UIDynamicAnimator class])
return [self.dynamicAnimator itemsInRect:rect];
else
return [super layoutAttributesForElementsInRect:rect];
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
if ([UIDynamicAnimator class])
return [self.dynamicAnimator layoutAttributesForCellAtIndexPath:indexPath];
else
return [super layoutAttributesForItemAtIndexPath:indexPath];
}
Adding an if/else statement to check for iOS6 or iOS7 and only returning the appropriate response fixed the issue for me. Hope this helps someone else!