【问题标题】:Retrieve custom prototype cell height from storyboard?从情节提要中检索自定义原型单元格高度?
【发布时间】:2012-11-19 13:07:08
【问题描述】:

当使用“动态原型”在情节提要上指定UITableView 内容时,可以将“行高”属性设置为自定义。

在实例化单元格时,不会考虑此自定义行高。这是有道理的,因为我使用哪个原型单元是由我的应用程序代码在单元被实例化时决定的。在计算布局时实例化所有单元格会带来性能损失,所以我理解为什么不能这样做。

然后的问题是,我能否以某种方式检索给定单元重用标识符的高度,例如

[myTableView heightForCellWithReuseIdentifier:@"MyCellPrototype"];

或类似的东西?还是我必须在我的应用程序代码中复制显式行高,随之而来的维护负担?

在@TimothyMoose 的帮助下解决了:

高度存储在单元格本身中,这意味着获取高度的唯一方法是实例化原型。这样做的一种方法是在正常单元回调方法之外预先使单元出列。这是我的小型 POC,它可以工作:

#import "ViewController.h"

@interface ViewController () {
    NSDictionary* heights;
}
@end

@implementation ViewController

- (NSString*) _reusableIdentifierForIndexPath:(NSIndexPath *)indexPath
{
    return [NSString stringWithFormat:@"C%d", indexPath.row];
}

- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(!heights) {
        NSMutableDictionary* hts = [NSMutableDictionary dictionary];
        for(NSString* reusableIdentifier in [NSArray arrayWithObjects:@"C0", @"C1", @"C2", nil]) {
            CGFloat height = [[tableView dequeueReusableCellWithIdentifier:reusableIdentifier] bounds].size.height;
            hts[reusableIdentifier] = [NSNumber numberWithFloat:height];
        }
        heights = [hts copy];
    }
    NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath];
    return [heights[prototype] floatValue];
}

- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 3;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath];
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:prototype];
    return cell;
}

@end

【问题讨论】:

  • 我使用这种方法已经有一段时间了,然后在一个新的故事板上,我得到的单元格的高度(和宽度)尺寸大多为零。即使是非零值也是意想不到的值。禁用尺寸类恢复了此功能。然而,令人失望的是,因为这个“修复”禁用了故事板中的重要功能。但是,这满足了我的迫切需要。
  • 我为solving this problem with size classes enabled找到了更完整的解决方案。

标签: ios uitableview uistoryboard


【解决方案1】:

对于静态(非数据驱动)高度,您只需将单元格出列一次并存储高度:

- (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];
}

对于动态(数据驱动)高度,可以在视图控制器中存储一个原型单元格,并在单元格的类中添加一个计算高度的方法,同时考虑原型实例的默认内容,例如子视图放置、字体等:

- (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];
}

当然,如果您使用自定义高度,您可能有多个单元原型,因此您可以将它们存储在字典或其他东西中。

据我所知,表格视图并没有尝试重用原型,大概是因为它在cellForRowAtIndexPath: 之外出列。这种方法对我们来说非常有效,因为它允许设计人员修改故事板中的单元格布局,而无需更改任何代码。

编辑:澄清了示例代码的含义,并为静态高度的情况添加了示例。

【讨论】:

  • 我不认识heightForData: 方法。它有什么作用?
  • heightForData: 是一种计算给定单元格数据高度的方法。
  • 我明白了。问题是,我想使用存储在故事板文件本身中的高度(静态高度)。不过,您的单元格预出队也可能适用于此。我会试试看。
  • 看看TLIndexPathTools。它通过自动创建原型和 a) 返回原型静态高度来计算单元高度,或者,b) 如果单元实现 TLDynamicSizeView 原型,则返回动态高度。请参阅Dynamic Height 示例项目。这确实是库的次要功能,但值得一试。
  • 这种方法的问题是当您的界面方向从纵向更改为横向时它不起作用。可能的解决方案:在计算 heightForRowAtIndexPath: 方法中的高度之前,将单元格的框架/宽度重置为表格的宽度。希望这会有所帮助。
【解决方案2】:

我前段时间为 UITableView 创建了一个类别,可能对此有所帮助。它使用asociated objects 存储“原型”单元格以重用原型,并提供一种方便的方法来获取故事板中分配的行的高度。释放表视图时释放原型。

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

用法

获取storyboard中设置的静态高度就这么简单:

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

假设一个多节表视图:

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;
}

最后,如果行高是动态的:

- (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];
}

【讨论】:

  • 这看起来真的很有用。谢谢!
  • 很好的实现,你应该把它放在 github 和 pot 上
猜你喜欢
  • 2016-12-03
  • 1970-01-01
  • 2014-04-01
  • 2016-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多