UITableViewCell reuse over View Controller instances - ios

Every time VC is showed - UITableView loads cells from storyboard. And then successfully reuse them while scrolling. But every time showing controller - performes loading from storyboard. I would like to avoid this behavior.
Is there any way to reuse cells between UITableView instances this way?

You can't achieve that using storyboards. You will have to create a XIB file with a custom UITableViewCell. You can then reuse it:
UITableViewCell* customCell = [tableView dequeueReusableCellWithIdentifier:#"CellId"];
if(!customCell)
{
customCell = [[NSBundle mainBundle]loadNibNamed:#"UITableViewCellXibName" owner:nil options:nil][0];
}

Related

IBOutlet of UITableViewCell subview is (NULL)

I have a custom UITableViewCell defined in a xib. It has two views in its Content View, a label and a text view.
In my table view controller, using either
JJDTextInputCell *cell = [[SDLTextInputCell alloc] init];
or
JJDTextInputCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TextInputCell" forIndexPath:indexPath];
the IBOutlet is null, so the table view does not show the default text or any text I try to display using
cell.descriptionLabel.text = #"foo";
What is the proper way to initialize custom UITableView cells created using interface builder?
AFAIK, there are two ways to dequeue table cell, you can find them here in the doc. Find them under Creating Table View Cells. You are using dequeueReusableCellWithIdentifier:forIndexPath: which always returns a valid cell BUT you need to use registerNib:forCellReuseIdentifier: function in pair with this. You have to register the class and then you will get a valid cell. Check out this question for reference.
If you are using XIB for custom cell you should use
[self.tableView registerNib:[UINib nibWithNibName:#"TableViewCell" bundle:nil] forCellReuseIdentifier:"CellIdentifier"];
for registering a cell instead of
[self.tableView registerClass:[TableViewCell class] forCellReuseIdentifier:"CellIdentifier"];
Initialize them from xib file.
JJDTextInputCell *cell = [[[NSBundle mainBundle] loadNibNamed:#"JJDTextInputCell" owner:self options:nil] objectAtIndex:0];

Using dequeueReusableCellWithIdentifier does not appear faster (or lower memory)?

When I want to create a table with custom cells, this is how I will write the standard codes:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CustomCell";
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
else
{
NSLog(#"reuse!");
}
cell.property1 = ....
cell.property2 = ....
return cell;
}
This is roughly how I will create the custom cells:
create a CustomCell.h (inheriting UITableViewCell) and CustomCell.m and a CustomCell.xib
in the CustomCell.xib, change the 'class' property of the top level UIView to 'CustomClass'
I have been doing this for sometime but today I finally decided to do a test to see if the table is really reusing the cells correctly.
Nope. The table is NOT reusing any cell!. The NSLog(#"reuse!") in the code snippet above is never triggered.
I decided to do a performance comparisons, with a tableview with 1 million cells, using these two methods:
Method #1 used the method described above. Take note that UIView is the top level view in my CustomCell.xib and no reuse identifier is configured on xib
cell not reused (NSLog reused! not printed)
peak memory about 3.4MB, peak CPU 60%
scrolling is smooth
Method #2 uses a UITableViewCell in the top level of the xib, and I put all my controls on the content view of this UITableViewCell. In the XIB, I configured the reuse identifier of this cell to be 'CustomCell'.
cell reused (NSLog reused printed multiple times)
peak memory about 6.1MB, peak CPU about 88%
scrolling is smooth
Two questions:
Why am i not seeing much lower CPU and memory usage on method #2, isn't method #1 wrong and method #2 right because method #1 is not reusing any cell at all? Or looking from another point of view, why method #1 is still scrolling so well even though it is stupidly loading nib again and again?
When creating a custom xib for a custom cell, does it make any difference whether the top level object is a UIViewnor UITableViewCell? (Looks like no difference?)
It seems that reuseIdentifier is not set properly in your XIB file.
You can set it programmatically also by adding
[yourTable registerNib:[UINib nibWithNibName:#"CustomCell" bundle:nil] forCellReuseIdentifier:#"CustomCell"];
somewhere before using table view. In viewDidLoad for example.
UPDATE
Question 1. If you do not set cell reuse identifier cells life circle is following:
1.It is created.
2.It is shown.
3.It is moved out of visible area.
4.It is not needed anymore so it is deleted.
If you use reuse identifier cells TYPICAL life circle is:
1.It is popped from reusable queue..
2.It is shown.
3.It is moved out of visible area.
4.It is pushed to reusable queue.
So performance difference is due to what is executed faster: create/release operation or pop/push. Memory usage should be approximately the same for both variants.
you need implement
-(NSString *)reuseIdentifier {
return #"CustomCell";
}
in you cell class CustomCell, because reuseIdentifier is readonly property you can't set it from outside

Custom buttons in XIB used as Custom UITableViewCell don't respond to taps (ios7)

So here I am upgrading a working ios6 app to ios7, and now I can't receive taps or other actions on custom buttons (or other subviews) inside my tableviewcells.
Edit:
My code:
Here is where I deploy my PlaceCell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"PlaceCell";
PlaceCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier];
if (!cell) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PlaceCell" owner:self options:nil];
cell = [nib lastObject];
cell.reuseIdentifier = cellIdentifier;
}
[cell configureCellWithPlace: [self.places objectAtIndex:indexPath.row]];
cell.delegate = self;
cell.userInteractionEnabled = YES;
return cell;
}
And then it is a normal custom cell with buttons which are connected to some actions by the interface.
It works perfectly with iOS6, but it does nothing with iOS7.
Thank you for your help.
Solved with:
[cell.contentView setUserInteractionEnabled: NO];
Put your button into cell's contentView.
This happens when your Cell's view in xib file is not a UITableViewCell, but only a UIView. Make sure that that the xib's top view is a UITableViewCell.
You can easily check it by looking into the first child of the main view inside the interface builder. If the first subview is not "Content View" then you should rebuild the cell with UITableViewCell on the top.
Also make sure that button is a subview of the "Content View".
I had a similar problem. I had dragged a UIView into the xib to use as my UITableViewCell. Even though I changed the classname to a subclass of a UITableViewCell in Interface Builder, the events on my buttons still didn't fire. Since it was originally a UIView, IB never knew about contentView and didn't add my controls to it.
Dragging a "real" UITableViewCell into the xib, changing its class to the one I wanted, and then rewiring up the IBOutlets fixed everything. Didn't need to mess with delaysContentTouches or other properties either.
Moral of the story: drag the right thing onto your xibs.
It seems to be that when you use interface builder to customize a cell subclass all the views added are added below the contentView. This is why setting userInteractionEnabled = NO on the content view works, because touch events are allow to pass through.
I used po [view recursiveDescription] with lldb to determine this.

Added table view cells follow 1 custom style?

I am wanting to create a custom UITableView cell. I would like to know how to do this. I understand how to actually create it and write code for it, but how can i create 1 style and then when i have more cells added, i want the same style. How can i do this? Is there a way to create 1 custom cell and have all the other cells that i want to add later follow this cells style?Thanks for the help!
In my projects I'm implementing method that creates custom style programmatically. Also it is possible to make custom cell via IB and when you need just take custom cell from it.
Don't forget that if you will write your code correctly then your cells will be reused and that method will be called only for number of cells that are visible in your table view.
may be this can help you http://iphone-bitcode.blogspot.com/2011/06/custom-tableview-cell.html
Write a separate .h/.m/.xib for the cell, and in the .xib set File's Owner to the class you want multiple copies of it in (your table view controller class, most likely). Attach it to an IBOutlet you created in the table view controller for new cells.
Then, each time you want a cell, try and dequeueReusableCellWithIdentifier: on your tableView, and if that doesn't work (you have no reusable ones), make a new cell using your custom class by simply loading the nib file. It will automatically create an instance of the cell and attach it to your IBOutlet, and then just retain the cell and set the outlet back to nil for the next time you need to create a cell. Essentially, I mean this (I have an IBOutlet UITableViewCell *cellOutlet):
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *reuseIdentifier = #"CustomCell";
UITableView *cell = [self.tableView
dequeueReusableCellWithIdentifier:reuseIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"MyCustomTableViewCell"
owner:self options:nil];
cell = cellOutlet;
self.cellOutlet = nil; // autoreleases
cell.reuseIdentifier = reuseIdentifier;
}
// configure the cell here
return cell;
}

Load a AQGridViewCell from XIB (not working)

I am using AQGridView class and I am trying to load a cell from an XIB. I have setup the XIB like a Custom Cell for a UITableView, but when I attempt to load the cell, it is simply blank. I was wondering if there was an easier way to get the XIB to load.
AQGridViewCell need to load the cell from an xib
- (AQGridViewCell *) gridView: (AQGridView *) gridView cellForItemAtIndex: (NSUInteger) index
{
static NSString * CellIdentifier = #"cellID";
gridCell * cell = (gridCell *)[gridView dequeueReusableCellWithIdentifier: CellIdentifier];
if ( cell == nil ){
gridCell = [[gridViewCell alloc] initWithFrame: CGRectMake(0,0,_gridView.frame.size.width/2-4,
_gridView.frame.size.height/2-8)
reuseIdentifier:CellIdentifier];
cell = gridCell;
self.gridCell = nil;
}
cell.title = #"Test Grid Item";
cell.date = #"Apr. 7, 2011";
return ( cell );
}
Here's an article that describes how to load an AQGridViewCell from nib, with example code. Check out the section called "A reusable AQGridViewCell".
(Thanks to pt2ph8 for pointing out contentView.)
From what I've understood, I think it shows as blank because what gets displayed is the cell's contentView. I ended up loading my custom view from IB and adding it as a subview of the cell's contentView when the cell is requested.
AQGridView's developers once claimed on GitHub that proper IB support will be added in the future, but that post is dated August 2010, so don't hold your breath.
This took me a while, but I figured a different way than the blog post jlstrecker mentioned.
Create a subclass of AQGridViewCell - let's call it
MyGridViewCell.
Create a nib for that cell, link it up in IB.
Pub a view ON TOP of the cell's view in IB. That's right, a view
on top of a view. Make the size the exact same.
For that view on
top of the view (let's call it view2), set the tag property (can
be done in IB) to 1.
Put everything you want to link up on top of
view2, decorate your cell, whatever you'd like.
Use the following code (of course, change it to your needs) in your subclass of AQGridViewController:
`
- (AQGridViewCell *)gridView:(AQGridView *)aGridView cellForItemAtIndex:(NSUInteger)index {
static NSString *CellIdentifier = #"MyGridViewCell";
MyGridViewCell *cell = (MyGridViewCell *)[self.gridView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = (ZZProductGridViewCell *)[[[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil] objectAtIndex:0];
}
[cell.contentView addSubview:[cell viewWithTag:1]]; //THIS IS THE IMPORTANT PART
return cell;
}
Enjoy!
I'm not familiar with AQGridView, but I believe you can leverage NSBundle's Nib loading capabilities. An excerpt from AdvancedTableViewCells sample project illustrates the idea:
RootViewController.h
#interface RootViewController : UITableViewController
{
ApplicationCell *tmpCell;
}
RootViewController.m
ApplicationCell *cell = (ApplicationCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"IndividualSubviewsBasedApplicationCell" owner:self options:nil];
cell = tmpCell;
self.tmpCell = nil;
}
Inside the IndividualSubviewsBasedApplicationCell.xib you would have to set the outlet of the UITableViewCell within to be the RootViewController's tmpCell property. Then, as a side effect of invoking NSBundle's loadNibNamed method, the tmpCell property gets set on the RootViewController via the Nib loading mechanism.
What you can do is do your xib (uiview) unpacking/loading in the subclass itself (which does have a different init method than a uitableviewcell)
you can also connect any outlets to this xib and add its entire view as a subview, or maybe replace contentview).
To make it even faster you can make uinib of this xib and reuse it to save disk i/o.
Build your cell normally using IB, then in your subclass of AQGridViewCell, add
- (void)awakeFromNib{
self.contentView.backgroundColor = [UIColor clearColor];
}

Resources