Retrieve custom prototype cell height from storyboard?

后端 未结 2 478
小蘑菇
小蘑菇 2020-11-27 05:51

When using \"Dynamic Prototypes\" for specifying UITableView content on the storyboard, there is a \"Row Height\" property that can be set to Custom.

Wh

相关标签:
2条回答
  • 2020-11-27 06:27

    I created a category for UITableView some time ago that may come helpful for this. It stores 'prototype' cells using asociated objects for reusing the prototypes and provides a convenience method for obtaining the height of the row assigned in storyboard. The prototypes are released when the table view is deallocated.

    UITableView+PrototypeCells.h

    #import <UIKit/UIKit.h>
    
    @interface UITableView (PrototypeCells)
    
    - (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier;
    - (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier;
    
    @end
    

    UITableView+PrototypeCells.m

    #import "UITableView+PrototypeCells.h"
    #import <objc/runtime.h>
    
    static char const * const key = "prototypeCells";
    
    @implementation UITableView (PrototypeCells)
    - (void)setPrototypeCells:(NSMutableDictionary *)prototypeCells {
        objc_setAssociatedObject(self, key, prototypeCells, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (NSMutableDictionary *)prototypeCells {
        return objc_getAssociatedObject(self, key);
    }
    
    - (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier {
        return [self prototypeCellWithReuseIdentifier:reuseIdentifier].frame.size.height;
    }
    
    - (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier {
        if (self.prototypeCells == nil) {
            self.prototypeCells = [[NSMutableDictionary alloc] init];
        }
    
        UITableViewCell* cell = self.prototypeCells[reuseIdentifier];
        if (cell == nil) {
            cell = [self dequeueReusableCellWithIdentifier:reuseIdentifier];
            self.prototypeCells[reuseIdentifier] = cell;
        }
        return cell;
    }
    
    @end
    

    Usage

    Obtaining the static height set in storyboard is as simple as this:

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        return [tableView heightForRowWithReuseIdentifier:@"cellIdentifier"];
    }
    

    Assuming a multi-section table view:

    enum {
        kFirstSection = 0,
        kSecondSection
    };
    
    static NSString* const kFirstSectionRowId = @"section1Id";
    static NSString* const kSecondSectionRowId = @"section2Id";
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        CGFloat height = tableView.rowHeight; // Default UITableView row height
        switch (indexPath.section) {
            case kFirstSection:
                height = [tableView heightForRowWithReuseIdentifier:kFirstSectionRowId];
                break;
            case kSecondSection:
                height = [tableView heightForRowWithReuseIdentifier:kSecondSectionRowId];
        }
        return height;
    }
    

    And finally if the row height is dynamic:

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        id thisRowData = self.allData[indexPath.row]; // Obtain the data for this row
    
        // Obtain the prototype cell
        MyTableViewCell* cell = (MyTableViewCell*)[self prototypeCellWithReuseIdentifier:@"cellIdentifier"];
    
        // Ask the prototype cell for its own height when showing the specified data
        return [cell heightForData:thisRowData];
    }
    
    0 讨论(0)
  • 2020-11-27 06:40

    For static (non-data-driven) height, you can just dequeue the cell once and store the height:

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSNumber *height;
        if (!height) {
            UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCustomCell"];
            height = @(cell.bounds.size.height);
        }
        return [height floatValue];
    }
    

    For dynamic (data-driven) height, you can store a prototype cell in the view controller and add a method to the cell's class that calculates the height, taking into account the default content of the prototype instance, such as subview placement, fonts, etc.:

    - (MyCustomCell *)prototypeCell
    {
        if (!_prototypeCell) {
            _prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCustomCell"];
        }
        return _prototypeCell;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        // Data for the cell, e.g. text for label
        id myData = [self myDataForIndexPath:indexPath];
    
        // Prototype knows how to calculate its height for the given data
        return [self.prototypeCell myHeightForData:myData];
    }
    

    Of course, if you're using custom height, you probably have multiple cell prototypes, so you'd store them in a dictionary or something.

    As far as I can tell, the table view doesn't attempt to reuse the prototype, presumably because it was dequeued outside of cellForRowAtIndexPath:. This approach has worked very well for us because it allows the designer to modify cells layouts in the storyboard without requiring any code changes.

    Edit: clarified the meaning of sample code and added an example for the case of static height.

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