Re-using a UICollectionViewCell - ios

I am dequeueing a vanilla UICollectionViewCell and setting its background color:
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellId forIndexPath:indexPath];
cell.backgroundColor = [UIColor redColor];
Of course, this color is being set every time a cell is recycled, even though it never changes.
Is there a way to only set the color when the cell is first created without subclassing UICollectionViewCell for such a small thing? I ask because UITableView allows for this without subclassing.

You can do that without subclass UICollectionViewCell in StoryBoard like this code
But UICollectionViewCell have nothing in its contentView, you should subclass then you can drag many other view you like to it and hook up with IBOutlet. Initial setting for these views are in awakeFromNib if Cell create in storyboard or Xib, in init if cell create by code.

Related

How to make a reusable cell for both TableView and CollectionView

I have a TableView and a CollectionView in different ViewController. Now I am showing an exactly same cell in both of them. So I want to create a reusable cell for convenience and easy maintenance.
I tried to create a xib and set the custom class to an UITableViewCell class, then I can register and load it in the UITableView. However, I cannot reuse this xib in the UICollectionView because CollectionView cannot load TableViewCell.
So my question is that is there any good way to make a reusable cell for both TableView and CollectionView?
UITableViewCell <- UIView
UICollectionViewCell <- UICollectionReusableView <- UIView
both are from UIView, so i think you can create a xib for UIView, and use that view in your UITableViewCell and UICollectionViewCell.
Eg: How to load a xib file in a UIView
and since both cell are going to have common code for some cases you can use category, to share the common code Eg: Sharing code between UITableViewCell and UICollectionViewCell

How to add more than two labels to prototype cell?

I have gone through the tutorial below and it works fine. My question is how do I add more than the two standard cells to the prototype cell?
http://thedarkdev.blogspot.co.uk/2013/09/web-service-apps-in-ios7-json-with.html
cell.textLabel.text = "Title Text";
cell.detailTextLabel.text = "Detail Text"
I am wanting to add another 4 labels and would like to lay them out using the storyboards.
Any ideas how to do this?
You can use a custom cell type and you'll be able to add as many labels as you want:
Create a empty UITableViewCell subclass that you'll use for this cell. Note, this subclass doesn't need any code inside its #implementation. We're only going to add outlets for its properties, and those will show up in its #interface, but the storyboard eliminates the need to write any code for the cell, itself.
Back in Interface Builder, go to the table view in your storyboard and make sure it has a cell prototype. (If it doesn't drag one from the object library on to the table view.)
Over on the "Identity" inspector panel on the right, set the base class of the cell prototype to be your UITableViewCell subclass as the cell prototype's "base class";
In the storyboard's "Attributes" inspector for the cell, set the cell "Storyboard identifier" to something you'll reference down in step 5 (I've used CustomCell here);
Set the cell "Style" to "Custom" rather than "Basic" or "Detailed":
add your labels to the cell.
I've added for labels to a single prototype cell here:
Use the "Assistant Editor" to show your code simultaneously with the storyboard. Select one of the labels you've added to the scene, change the code down below to be the UITableViewCell subclass you created in step 1, and you can now control-drag from the label to create IBOutlet references for the labels to the cell's custom subclass:
By the way, I'd advise against using IBOutlet names of textLabel or detailTextLabel (not only are they too generic, but it can get confused with the labels that appear in standard cell layouts).
Now your tableview controller can reference this subclass:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell"; // make sure this matches the "Identifier" in the storyboard for that prototype cell
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// retrieve the model data to be shown in this cell
// now fill in the four labels:
cell.firstNameLabel.text = ...;
cell.lastNameLabel.text = ...;
cell.emailLabel.text = ...;
cell.telephoneLabel.text = ...;
return cell;
}
So while there are a couple of steps to go through here, the net result is that you can design whatever cell layout you want, and with this very simple UITableViewCell subclass, your cellForRowAtIndexPath is incredibly simple, just referencing the IBOutlet references you connected in Interface Builder.

UICollectionViewCell reuse issue

I have a storyboard and one of my viewControllers has a CollectionView. I have a prototype cell that has a label inside. I created a class for that prototype cell in order to have access to the label via an IBOutlet property.
The problem is that I have many cells. Inside the initWithCoder constructor of the cell I add some cornerRadius.
When I push this viewController on the screen, it lags a lot. Without the corner radius it doesn't. I also noticed that initWithCoder gets called all the time, for each cell.
I tried to register the cell like this [self.myCollectionView registerClass:[MyCell class] forReuseIdentifier:#"MyReuseIdentifier"] but it doesn't work. I dont know how to use the registerNib method.
The reuse identifier is set in the storyboard prototype cell.
I don't know how to achieve the rounded corner effect without loss in perforrmance.
I have done my cell corner round in cellForItemAtIndexPath method like
cell.imageView.layer.cornerRadius = 10;
cell.imageView.layer.masksToBounds = YES;
and dont forget to import #import <QuartzCore/QuartzCore.h>

Lazy loading custom subviews for UICollectionViewCell

In -collectionView:cellForItemAtIndexPath: I’m adding custom subviews to the UICollectionViewCell like this:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
NSString *const cellIdentifier = #"cellIdentifier";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
MyCustomViewClass *carouselView = [[MyCustomViewClass alloc] init];
[cell.contentView addSubview:carouselView];
return cell;
}
According to the documentation, dequeueReusableCellWithReuseIdentifier:forIndexPath: “dequeues an existing cell if one is available or creates a new one based on the class or nib file you previously registered.”
The problem is that my implementation of cellForItemAtIndexPath is constantly creating new instances of MyCustomViewClass. Even though instances of the latter are removed from the collection view when they’re out of the screen, it still seems wrong to create a new one every single time dequeueReusableCellWithReuseIdentifier:forIndexPath: is called.
My question is, given that MyCustomViewClass instances are graphics-intensive and take up memory, what’s the best way to lazily load them? Do I have to implement my own queue? Or shall I make it a subclass of UICollectionViewCell?
Because you did this MyCustomViewClass *carouselView = [[MyCustomViewClass alloc] init];, everytime the system call dequeueReusableCellWithReuseIdentifier:forIndexPath: it will create a new instance of MyCustomViewClass for you. So what you need to do is to check whether an instance of MyCustomViewClass has already been added to that cell, if not then create a new one.
I have no idea about what your MyCustomViewClass does but you can create a custom UICollectionViewCell that has already that CustomViewClass associated.
If your CustomViewClass extends UIView is simpler. And if you are using storyboards it is even more simple. In your storyboard you don't need to create a custom UICollectionViewCell just for that. You can drag an UIVIew to your CollectionViewCell and set the customView to MyCustomViewClass. This way it will only be created once and then it will be reused.
If your MyCustomViewClass has some kind of state (imagine that is a status bar with a percentage) you can reset that state you have to extend the UICollectionViewCell and override prepareForReuse.

Initializing a custom UICollectionViewCell

I have a custom UICollectionViewCell that has a custom background view which is drawn using one of several colour schemes. The colour scheme for the background view is set in my custom initializer -(id)initWithFrame:andColourPalette: for the View.
I have a similar custom initialiser in my UICustomViewCell subclass but I can't figure out how to call this initialiser when I am setting up the cell in cellForItemAtIndexPath:
Can anyone help me do this? Or offer alternative solution for passing this Dictionary of colours into the Cell to pass on to the subView?
EDIT to show more detail:
This is what I have in my UICollectionView VC:
In ViewWillAppear:
[self.collectionView registerClass:[OPOLawCollectionViewCell class] forCellWithReuseIdentifier:CELL_ID];
self.colourPalette = [OPOColourPalette greenyColourPalette];
In cellForItemAtIndexPath:
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CELL_ID forIndexPath:indexPath];
OPOLawCollectionViewCell *lawCell = (OPOLawCollectionViewCell *)cell;
MainLevel *level = self.collectionData[indexPath.row];
lawCell.delegate = self;
lawCell.colourPalette = self.colourPalette;
In my Custom UICollectionViewCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// get background view
OPOLawBook *lawBookView = [[OPOLawBook alloc]initWithFrame:CGRectMake(0, 0, 200, 265) andColourPalette:self.colourPalette];
But that doesn't work - I guess because the propertys are not set up.
If I change the last line to this, then it works fine:
OPOLawBook *lawBookView = [[OPOLawBook alloc]initWithFrame:CGRectMake(0, 0, 200, 265) andColourPalette:[OPOColorPalette greenyColorPalette]];
So i guess I need to use a custom intialiser here but I cant figure out how to call it , or from where...
Thanks
Yuo have to register your customCells in collectionView:
[self.collectionView_ registerClass:[YourCustomClass class]
forCellWithReuseIdentifier:#"CustomCell"];
And then in your method cellForItemAtIndexPath:
YourCustomClass *cell = (YourCustomClass *)[collectionView
dequeueReusableCellWithReuseIdentifier:#"CustomCell" forIndexPath:indexPath];
It is done because collectionView might have 1000 cells and 10 visible. You don't keep all of them initialized and reuse when possible.
EDIT
You should set colorPaletter after you deque the reusable cell. Think of it as a container which can hold any color. You need to determine (by indexpath) what color to paint.
You shouldn't do below if your custom cell is in the Storyboard,
[self.collectionView registerClass:[OPOLawCollectionViewCell class] forCellWithReuseIdentifier:CELL_ID];
Because Storyboard take responsibility to register Cell_ID own.
Now, It will conflict to be generated invalid Cell if you use both.
Way off, every answer. The questioner is looking for a way to uniquely identify each cell upon initialization, which happens prior to dequeuing a cell, and prior to a cell's access to its index path property.
The only way to do this is to assign a unique reuse identifier to every cell based on what the index path value will be (assuming you will know what that will be—and, in your case, you will); then, when dequeuing the cell, use the index path to find the cell with the corresponding reuse identifier.
Does this negates the purpose of reuse identifiers? Absolutely not. You'll be reusing that cell every time you need to use it again. Reuse identifiers were not meant to limit you to a cookie-cutter cell for every cell in your collection view; they are also intended to be "unique use" identifiers.

Resources