How to make a UITableViewCell with different subviews reusable?

后端 未结 4 1679
误落风尘
误落风尘 2020-12-25 10:07

I have a UITableView in which I display, naturally, UITableViewCells which are all of the same class, let\'s call it MyCell. So I have

相关标签:
4条回答
  • 2020-12-25 10:48

    There are a couple of different ways to do this. You need a way to access that subview and reset or change it on reuse.

    1. You could subclass UITableViewCell with your own class of cell that has a property for the train or car view. That way you could access and change that view when the cell is reused.

    2. Assign a different identifier to each type of cell:

    `

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
            static NSString *CarCellIdentifier = @"CarCell";
        static NSString *TrainCellIdentifier = @"TrainCell";
            if(indexPath == carCellNeeded) { 
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CarCellIdentifier];
                 if (cell == nil) {
                     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CarCellIdentifier] autorelease]; 
                 [cell addSubview:carView];
            }
            } else if(indexPath == trainCellNeeded){ 
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TrainCellIdentifier];
                    if (cell == nil) {
                      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TrainCellIdentifier] autorelease]; 
                [cell addSubview:trainView];
                 }
            }
          return cell; 
    }
    
    1. Or assign a special tag to that sub view you are adding and when the cell comes back around again to be reused you can access that specific subview by its tag.
    0 讨论(0)
  • 2020-12-25 10:58

    the simplest is to make a custom UITableViewCell subclass and create a xib for it. Set the root view in the xib as an uitableviewCell and set its class to your UITableViewCell subclass. Set the file owner class as your TableViewController subclass and add all the subviews you want to it. Then you can simply:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString * CellIdentifier = @"cellIdentifier";
    
        TableViewMessageCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    
        if (cell == nil)
        {
            cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([TableViewMessageCell class])
                                                  owner:self
                                                options:nil] lastObject];
        }
    
        Message * message = [self.fetchedResultsController objectAtIndexPath:indexPath];
        cell.message      = message;
    
        return cell;
    }
    
    0 讨论(0)
  • 2020-12-25 11:03

    I would add both custom subviews to the nib and connect them to an outlet. And depending on the content I would hide one of them when you configure the content of your cell.

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        static NSString *CellIdentifier = @"CellIdentifier";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (!cell) {
            cell = /* load from nib */
        }
        if (/*indexPath conditionForTrainCell*/) {
           cell.trainSubview.hidden = NO;
            cell.carSubview.hidden = YES;
            // configure train cell
        }
        else {
           cell.trainSubview.hidden = YES;
            cell.carSubview.hidden = NO;
            // configure car cell
        }
        return cell;
    }
    
    0 讨论(0)
  • 2020-12-25 11:05

    Instead of adding subviews to cells I'd suggest that you create for every kind of cell your own class. If you have the kinds: train, car, bike, boat and airplane I would create five subclasses.

    As I understand Apple the reuse mechanism with the identifier is just for that case: different types of cells get their own identifier, not every single cell a special one. Just to point how I interprete the whole thing.

    In Apple's Table View Programming Guide for iOS / Characteristics of Cell Objects, the 3rd paragrpah delivers some insight into the meaning of the reuse identifier.

    I've written myself a small TableViewCellFactory class which makes my life easier to create cells with the interface builder and have those in my app within minutes.

    First of all a small example on how to use cellForRowAtIndexPath and the factory as well as setting content for a cell.

    I create a fresh cell with the factory which needs the tableView so it can handle the reuse logic. Next thing is to let a method fill in the content for the cell. In this case it's a cell which shows a video clip with some text.

    Data Source delegate method and helper

    - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)anIndexPath
    {
        VideoClipTableViewCell *cell = [TableViewCellFactory videoClipTableViewCellWithTableView:aTableView];
    
        [self configureVideoClipCellWithCell:cell andIndexPath:anIndexPath];
    
        // code to decide what kind of cell not shown, but it could be here, just move the model
        // access code from the configure cell up here and decide on what you get
    
        return cell;
    }
    

    Next comes the data source helper to put content into the cell. Get the content from my model array and set the properties. Note, this does everything by reference, nothing is returned.

    - (void)configureVideoClipCellWithCell:(VideoClipTableViewCell *)aCell andIndexPath:(NSIndexPath *)anIndexPath
    {
        VideoClip *videoClip = [videoClips objectAtIndex:anIndexPath.row];
    
        aCell.videoTitleLabel.text = videoClip.title;
        aCell.dateLabel.text = videoClip.date;
    
        // more data setting ...
    }
    

    TableViewFactory

    This class consists mainly of convenience methods and some boilerplate methods to do the real work.

    // Convenience static method to create a VideoClipTableViewCell
    + (VideoClipTableViewCell *)videoClipTableViewCellWithTableView:(UITableView *)aTableView
    {
        return [self loadCellWithName:@"VideoClipTableViewCell" tableView:aTableView];
    }
    
    // method to simplify cell loading
    + (id)loadCellWithName:(NSString *)aName tableView:(UITableView *)aTableView
    {
        return [self loadCellWithName:aName 
                            className:aName
                           identifier:aName
                            tableView:aTableView];
    }
    
    // method with actually tries to create the cell
    + (id)loadCellWithName:(NSString *)aName 
                 className:(NSString *)aClassName 
                identifier:(NSString *)anIdentifier 
                 tableView:(UITableView *)aTableView
    {
        UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:anIdentifier];
    
        if (cell == nil) {
            UINib * nib = [UINib nibWithNibName:aName bundle:nil];  
    
            NSArray * nibContent = nil;
    
            nibContent = [nib instantiateWithOwner:nil options:nil];
    
            for (id item in nibContent) {
                if ([item isKindOfClass:NSClassFromString(aClassName)]) {
                    cell = item;
                }
            }
        }
        return cell;
    }
    

    I've thrown out the whole error and exception handling just to keep the example short. If someone is interested I'd add the code.

    Some important things about the usage is:

    • The connected class name, the reuse identifier and the nib name are all the same so a cell can be created with only one string constant, else the long loadCellWithName has to be used.

    • Don't forget to set the reuse identifier in interface builder.

    • The nib should contain only one TableViewCell (can be changed with some coding though)

    • Don't set outlets of the File's Owner, use those of the tableViewCell

    • Set the class identity of the cell to a corresponding class which should be created foremost

    • Look at the screenshot

    enter image description here

    Thoughts on subclassing own custom cells

    It's actually easy to subclass your own cell, add a few properties to it, make them available in IB with outlets, choose the new extended class in IB for your nib file.

    The main problem is interface itself. It's not easily done to have different kinds of cells based on a custom cell in interface builder. The first approach would be to copy the nib file, rename it and use it with all the existing references and link the new ones to differing outlets. But what happens if the base cell has to be changed? Going through all kinds of inheriting cells could be a tedious task.

    I just stumbled across Custom views in Interface Builder using IBPlugins on Cocoa with Love. It's a nice tutorial how to extend the components Library in IB. Our custom base cell could become an item in the library and become the template we've been looking for. I think this approach is the right way to choose. Still, looking at necessary steps, it's not just done within 5 minutes.

    Interface builder is a helpful tool, allowing us to rapidly create views, but when it comes to reusability through subclassing, there are big steps necessary to create maintainable views. A pity.

    Creating the views with code only I think one is better off with subclassing if it comes to more than one level of inheritance or many ancestor classes for just one base view.

    EDIT

    On the other hand, Apple warns about excessive use of subviews in a cell:

    However, if the content of a cell is composed of more than three or four subviews, scrolling performance might suffer. In this case (and especially if the cell is not editable), consider drawing directly in one subview of the cell’s content view. The gist of this guideline is that, when implementing custom table-view cells, be aware that there is a tradeoff between optimal scrolling performance and optimal editing or reordering performance.

    Right now any approach has its drawbacks and advantages:

    • Too man subviews will hit performance, easily done with IB

    • Drawing with code will result in a hard to maintain code base but will perform better

    • Skipping IB makes subclasssing of template cell classes easier

    • Hierarchy through subclassing difficult to achieve with IB with nib files

    0 讨论(0)
提交回复
热议问题