没有contentView框架计算UITableViewCell高度的最佳方法

时间:2012-06-19 14:33:44

标签: ios objective-c iphone uitableview heightforrowatindexpath

概要

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

我的UITableViewController's之一包含以下内容: UITableViewCell与UITextView。

UITextView的宽度和高度应与UITableViewCell相同。

enter image description here

我创建了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个值)
  6. 并且不要忘记,由于UITableViewCell accessoryType,或者因为UITableView编辑状态,contentView框架可以更小。 (例如,如果我们的UITableViewCell具有任何编辑状态和任何accessoryView的任何高度的多线UILabel)

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

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

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

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

    有时候我会使用一些假的预先分配的UITableViewCell(什么只是愚蠢,但有时非常优雅,易于使用)......

    但我不喜欢这些。

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

    由于

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

虽然您可以在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)横向。

这一切都非常令人不安,我知道......干杯。