【问题标题】:The best way to calculate UITableViewCell height without contentView frame在没有 contentView 框架的情况下计算 UITableViewCell 高度的最佳方法
【发布时间】:2012-06-21 15:02:02
【问题描述】:

摘要

鉴于我们并不总是知道单元格的框架或其内容视图将是什么(由于编辑、旋转、辅助视图等),计算高度的最佳方法是什么在tableView:heightForRowAtIndexPath:当单元格包含可变高度的文本字段或标签时?

我的一个UITableViewController's 包含以下演示文稿: UITableViewCell 与 UITextView。

UITextView 应该和 UITableViewCell 一样宽和高。

我创建了 UITableViewCell 子类,然后用 UITextView 初始化它(UITextView 是我的 UITableViewController 的私有字段)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 
     static NSString *CellIdentifier = @"TextViewCell";
     UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
     if (cell == nil) {
         cell = [[[BTExpandableTextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier textView:_notesTextView] autorelease];
     }    
     return cell;
}

我在 UITableViewCell 子类中实现了以下方法:

- (void)layoutSubviews{
    [super layoutSubviews];
    CGFloat height = [textView.text sizeWithFont:textView.font constrainedToSize:CGSizeMake(textView.frame.size.width, MAXFLOAT)].height + textView.font.lineHeight;
    textView.frame = CGRectMake(0, 0, self.contentView.frame.size.width, (height < textView.font.lineHeight * 4) ? textView.font.lineHeight * 4 : height);    
    [self.contentView addSubview:textView];
}

当然我实现了以下 UITableViewDataSource 方法(看!我正在使用 self.view.frame.size.width (但我真的需要 UITableViewCell contentView 框架宽度):

- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{

    CGFloat height  = [_notesTextView.text sizeWithFont:_notesTextView.font 
                                      constrainedToSize:CGSizeMake(self.view.frame.size.width, MAXFLOAT)].height;        
    CGFloat groupedCellCap = 20.0;     
    height          += groupedCellCap; 

    if(height < [BTExpandableTextViewCell minimumTextViewHeightWithFont:_notesTextView.font]){
        height = [BTExpandableTextViewCell minimumTextViewHeightWithFont:_notesTextView.font];
    }        
    return height;
}    

我还实现了以下方法(这不是那么重要,但无论如何都会发布它,只是为了解释单元格的高度是动态的,它会在 UITextView 中更改文本后缩小或扩大)

 - (void)textViewDidChange:(UITextView *)textView{
    CGFloat height = [_notesTextView.text sizeWithFont:_notesTextView.font 
                                 constrainedToSize:CGSizeMake(_notesTextView.frame.size.width, MAXFLOAT)].height;
    if(height > _notesTextView.frame.size.height){
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
    }
}

现在,我的问题是: 加载视图后,UITableViewController 按以下顺序调用方法:(为了简化,删除一些,如 titleForHeaderInSection 等)

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{ 

只有这样

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

看!我应该在 cellForRowAtIndexPath 之前返回正确的 UITableViewCell 高度! 这意味着:我不知道 UITableViewCell contentView 框架。而且我无法以编程方式获取它。

此宽度可以是以下之一:

  1. iPhone 普通桌子,纵向
  2. iPhone 普通桌子,横向
  3. iPhone 分组表,纵向
  4. iPhone 分组表,横向
  5. iPad 也是如此(另外 4 个值)

并且不要忘记 contentView 框架可以因为 UITableViewCell accessoryType 或者因为 UITableView 编辑状态而变小。 (例如,如果我们的 UITableViewCell 具有任何高度的多行 UILabel 在任何编辑状态和任何附件视图)

所以这个问题是根本问题:我无法获取单元格 contentView 框架宽度进行约束,因为我应该在单元格布局 contentView 之前返回这个高度。 (顺便说一句,这很合乎逻辑)但是这个 contentView 框架真的很重要。

当然,有时我可以准确地知道这个宽度并“硬编码”它 (例如:UITableViewCellAccessoryDisclosureIndicator 有 20 px 宽度,tableView 不能处于编辑状态,那么我可以写 self.view.frame.size.width - 20 任务完成)! 或者有时候 contentView 等于 UITableViewController 的 view frame!

有时我在 -tableView:heightForRowAtIndexPath: 方法中使用 self.view.frame.width ..(就像现在一样,它工作得很好,但由于分组的 UITableView 并不完美,应该减去一些常量值,它们2 个设备 * 2 个方向不同)

有时我在 UITableViewCell 中有一些#defined 常量(如果我确切知道宽度的话)...

有时我会使用一些虚拟的预分配 UITableViewCell(这很愚蠢,但有时非常优雅且易于使用)...

但我不喜欢那样的东西。

最好的决定是什么? 也许我应该创建一些辅助类,它将使用以下参数进行初始化: 附件视图、设备方向、设备类型、表视图编辑状态、表视图样式(普通、分组)、控制器视图框架等,其中将包括一些常量(如分组表视图偏移等)并使用它来查找预期的 UITableViewCell contentView 宽度? ;)

谢谢

【问题讨论】:

  • 您不是决定调用表视图数据源和委托方法的顺序的人。这是由数据源和委托 UITableView 类完成的,因此无论它们出现在代码中的哪个位置,Apple 都会根据需要调用它们...
  • 我知道 ;) 无论如何,在 heightForRowAtIndexPath 中检索单元格的宽度并不好,因为控制器显然在获得高度后布局单元格。这很合乎逻辑。与此无关。而且我们不应该试图弄乱它:)
  • 所以基本上你只想捕捉每次单元格改变宽度并相应地调整 UITextView?
  • 它是关于检索正确的 UITableViewCell contentView 宽度以限制 heightForRowAtIndexPath 中的高度。 “单元格更改宽度”也与此有关 - 例如,更改设备方向后单元格会更改宽度。我不想硬编码一些常量。就像在 iPad 上它的 self.view.frame.size.width - 20;在 iPhone 上它的 self.view.frame.size.width - 10;如果表处于编辑状态另一个“-20”...等
  • 我认为这是一个很好的问题,如果有点长 - 我在顶部添加了一个摘要,希望你不介意,我希望我明白你问题的重点在那里。如果您不喜欢,请随时重新编辑/回滚。

标签: ios objective-c iphone uitableview heightforrowatindexpath


【解决方案1】:

表格视图在创建任何UITableViewCellcell 之前使用tableView:heightForRowAtIndexPath: 方法来确定其contentSize。如果你停下来想一想,这是有道理的,因为你对UIScrollView 做的第一件事就是设置它的 contentSize。我之前遇到过类似的问题,我发现最好有一个辅助函数,可以将内容放入 UITableViewCell 并预测UITableViewCell 的高度。所以我认为你会想要创建某种数据结构,将文本存储在每个UITableViewCell 中,一个以 NSIndexPaths 作为键、将文本作为值的 NSDictionary 会很好。这样,您无需引用UITableViewCell 即可找到所需文本的高度。

【讨论】:

  • 我不知道如何避免引用单元格(原型),因为我需要获取文本容器的宽度来计算高度..
【解决方案2】:

虽然您可以在 UITableViewCell 子类的“-layoutSubviews”中真正动态地计算表格视图单元格中包含的标签的高度,但对于“-tableView:”中的单元格高度没有类似的方法(据我所知):表视图委托的 heightForRowAtIndexPath:'。

考虑一下:

- (void)layoutSubviews
{
    [super layoutSubviews];

    CGSize size = [self.textLabel.text sizeWithFont:self.textLabel.font
                                  constrainedToSize:CGSizeMake(self.textLabel.$width, CGFLOAT_MAX)
                                      lineBreakMode:self.textLabel.lineBreakMode];
    self.textLabel.$height = size.height;
}

不幸的是,在调用 '-tableView:heightForRowAtIndexPath:' 时还为时过早,因为 cell.textLabel.frame 尚未设置为 CGRectZero = {{0, 0}, {0, 0}}

AFAIK 您将无法使用内容视图的框架执行此操作,也无法汇总单个标签的框架...

我能想到的唯一方法是想出一个便利类、方法、常量等,试图在任何设备方向、任何设备上覆盖所有可能的宽度:

@interface UITableView (Additions)

@property (nonatomic, readonly) CGFloat padding;

@end

@implementation UITableView (Additions)

- (CGFloat)padding
{
    if (self.formStyle == PTFormViewStylePlain) {
        return 0;
    }

    if (self.$width < 20.0) {
        return self.$width - 10.0;
    }

    if (self.$width < 400.0 || [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        return 10.0;
    }

    return MAX(31.0, MIN(45.0, self.$width * 0.06));
}

@end

另外请注意,最近我们也有新款 iPhone 5 的 4 英寸宽度(568 而不是 480)横向显示。

这整件事很令人不安,我知道……干杯。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-27
    • 2021-08-27
    • 2011-08-19
    • 2014-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多