Steven Fisher
Steven Fisher

Reputation: 44876

Copying UITableViewCell

I'm reading a custom table cell in tableView:cellForRowAtIndexPath: from a nib file. This works great for my purposes, except it's quite slow.

Now, I know the right thing to do in the long term is to create the cell entirely in code, and to use a single view, and so on. But this is a prototype, and I don't want to put that much effort into it.

For now, I'd be happy if I was reading the nib only once in the UIViewController subclass, then tableView:cellForRowAtIndexPath: made copies of it. My assumption here is that copying would be faster than reading the nib.

Here's what I use to load the nib, which I call from viewDidLoad: (and retain after)

-(id)loadFromNamed:(NSString*)name {
    NSArray *objectsInNib = [[NSBundle mainBundle] loadNibNamed:name
                                                          owner:self
                                                        options:nil];
    assert( objectsInNib.count == 1 );
    return [objectsInNib objectAtIndex:0];
}

All is good so far. But the question is: How do I copy this over and over? Is it even possible?

I tried [_cachedObject copy] and [_cachedObject mutableCopy] but UITableViewCell doesn't support either copy protocol.

If I have to, I can just tell them to ignore the speed until I'm prepared to remove the nib entirely, but I'd rather get it going a little faster if there's a low-hanging fruit here.

Any ideas?

Upvotes: 7

Views: 9635

Answers (5)

Joe C
Joe C

Reputation: 2834

Here it is in Swift

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    var cell : UITableViewCell?
    let cellId = String(format: "Cell%d", indexPath.row)
    cell = alertTable!.dequeueReusableCellWithIdentifier(cellId) as! UITableViewCell?

    if cell == nil {
        let archivedData = NSKeyedArchiver.archivedDataWithRootObject(masterTableCell!)
        cell = NSKeyedUnarchiver.unarchiveObjectWithData(archivedData) as! UITableViewCell?
    }

    // do some stuff

    return cell!
}

Upvotes: 1

zubko
zubko

Reputation: 1797

I think coping of table cell can be used together with dequeuing mechanism, which will allow to create cell one time (from nib or programmatically or getting it loaded automatically from other nib and linking as an outlet in IB) and then clone it or dequeue it when needed.

UITableViewCell doesn't conform to NSCopying protocol, but it supports keyed archiving/unarchiving mechanism, so it can be used for cloning.

Based on answer " How to duplicate a UIButton in Objective C? " my data source delegate method looks like:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellID = @"CellIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellID];

    if (!cell) {
        NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:self.tableViewCell];
        cell = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
    }

    // ... config ...

    return cell;
}

And in my case self.tableViewCell is a cell that was loaded one time from view's nib file.

I don't tested what will be faster: "archive + unarchive" to clone or "load nib file + unarchive" which framework will do in case of -loadNibNamed:owner:options:, I used this method only with convenience considerations, but good chances that memory operation vs file operation will be faster.

EDIT: It appears not as easy as it seemed at first. As UIImage doesn't conforms to NSCoding, cells with configured UIImageViews can't be just copied without additional code. Yep, copying whole image is definitely not a good practice, cheers to Apple for pointing this.

Upvotes: 8

pfo
pfo

Reputation: 334

Not proud of this solution, but it works with the maximum number of possible IB bindings:

Interface (AlbumTableViewCell is a subclass of UITableViewCell of which an instance is defined in AlbumViewController's XIB file):

@interface AlbumsViewController : UITableViewController {
    IBOutlet AlbumTableViewCell *tableViewCellTrack;
}

@property (nonatomic, retain) AlbumTableViewCell *tableViewCellTrack;

Implementation (unarchive / archive makes a copy / clones the table view cell):

@implementation AlbumsViewController

@synthesize tableViewCellTrack;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    AlbumTableViewCell *cell = (AlbumTableViewCell *)[tableView dequeueReusableCellWithIdentifier: @"AlbumCell"];

    if (cell == nil) {
        AlbumsViewController *albumsViewController = [[[AlbumsViewController alloc] init] autorelease];
        [[NSBundle mainBundle] loadNibNamed: @"AlbumsViewController" owner: albumsViewController options: nil];

        cell = albumsViewController.tableViewCellTrack;
    }

    cell.labelTitle.text = ...;
    cell.labelArtist.text = ...;

    return cell;
}

Upvotes: 4

Teo Choong Ping
Teo Choong Ping

Reputation: 12798

Well, I'm not sure why all the tutorials out there doesn't specify this step.

When using your own custom UITableViewCell from Nib, calling dequeueReusableCellWithIdentifier is not enough. You have to specify the "Identifier" in the IB, just for for it in the Table View Cell tab section.

Then make sure the identifier you put in IB is the same as the identifier you use for the dequeueReusableCellWithIdentifier.

Upvotes: 3

Alex Wayne
Alex Wayne

Reputation: 187024

Use the cell cloning built into the table view. Apple knew generating a lot of table cells was slow. Check out the docs for this method:

- (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier

You create the cell once, then as new cells are requested, use that method to clone the existing cells. Then you just change what needs to be changed about the new cell and return the cell object.

Also check out the table view realted sample code provided by Apple that uses this method and show you the right way. The fact your cell was loaded from a nib shouldn't matter at all.


Minor clarification: I dont think the above method clone cells for you. Instead it takes cell object that have scrolled off the screen and simply moves them to a new spot. So it's literally reusing a cell. So be sure your custom table view can be set to all the new values it needs outside of the intialization.

Upvotes: 6

Related Questions