在Storyboard中自定义单元格行高设置不起作用

213

我正在尝试调整表视图中一个单元格的高度。我是通过在该单元格的“大小检查器”内部的“行高度”设置来调整其大小的。但当我在iPhone上运行应用程序时,该单元格具有从表视图的“行高度”设置中设置的默认大小。

如果我改变表视图的“行高度”,则所有单元格的大小都会发生改变。我不想这样做,因为我只想要一个自定义大小的单元格。我看到了很多关于编程解决问题的帖子,但如果可能的话,我更喜欢通过Storyboard来完成。

18个回答

295
在动态单元格上,UITableView 上设置的 rowHeight 值始终会覆盖单个单元格的 rowHeight 值。但是,在静态单元格上,单个单元格上设置的 rowHeight 值可以覆盖 UITableView 的值。不确定这是一个错误,苹果可能有意这样做?

36
正确答案是第三个,特别是因为这个答案适用于Interface Builder / Storyboards。如果在IB中选择单元格,则“大小检查器”会在顶部显示行高(带有“自定义”复选框),但如果选择整个表视图,“大小检查器”也会在那里显示行高(此时没有“自定义”)。正如pixelfreak所说,仅表视图设置用于动态单元格。(不确定是否是有意的)。 - Rhubarb
8
这个回答意味着解决方案就是将 UITableView 内容从“动态原型”更改为“静态单元格”,我尝试这样做后,整个项目都崩溃了,差点把我自己也搞垮了。 - abbood
4
有没有办法检索到这个数字?挖掘它的唯一方法是深入故事板并从那里找出来吗? - Yogurt
这绝对不是一个 bug,因为你的表格是动态的,系统怎么可能知道你会使用每个单元格的位置?以及每个原型将被使用多少次。 - Vincent Bernier
@pixelfreak,这对我在动态单元格上有效。最初,我将子视图添加到表视图中,并将行高设置为AutoDemension,现在我将其更改为适合返回的最大数据的任意值。 - natur3
显示剩余2条评论

84

如果您使用UITableViewController,请实现此方法:

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

在行的函数中,您可以选择高度。例如:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) {
       return 100;
    } 
    else {
       return 60;
    }
}
在这个例子中,第一行的高度为100像素,其他行的高度为60像素。
我希望这个例子能对你有所帮助。

2
Beber,是否总是需要同时执行以下两个步骤(在Storyboard中勾选Custom并编辑Row Height值 && 在heightForRowAtIndexPath中指定它)? - marciokoko
你能帮我解决这个问题吗?我需要动态高度的单元格,但是如果我使用这种方法,无论我返回什么,所有的单元格都会消失。除了 iPhone 5(设备)之外,在那里它按预期工作。你能理解为什么吗? - Ostmeistro

34

对于动态单元格,UITableView 上设置的 rowHeight 总是覆盖个别单元格的 rowHeight

这种行为在我看来是一个错误。每当您必须在两个地方管理 UI 时,它就容易出错。例如,如果您更改了故事板中的单元格大小,则必须记住在 heightForRowAtIndexPath: 中进行更改。在 Apple 修复此问题之前,当前最佳解决方法是重写 heightForRowAtIndexPath:,但是使用从故事板中实际原型单元格来确定高度,而不是使用 魔数 。以下是示例:

- (CGFloat)tableView:(UITableView *)tableView 
           heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /* In this example, there is a different cell for
       the top, middle and bottom rows of the tableView.
       Each type of cell has a different height.
       self.model contains the data for the tableview 
    */
    static NSString *CellIdentifier;
    if (indexPath.row == 0) 
        CellIdentifier = @"CellTop";
    else if (indexPath.row + 1 == [self.model count] )
        CellIdentifier = @"CellBottom";
    else
        CellIdentifier = @"CellMiddle";

    UITableViewCell *cell = 
              [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    return cell.bounds.size.height;
}

这将确保在运行时自动捕捉到原型单元格高度的任何更改,您只需要在一个地方管理您的UI:storyboard。


2
我已经发布了一个包括缓存的完整代码答案,请参见https://dev59.com/PGoy5IYBdhLWcg3wWcqh#16881312。 - JosephH
1
哇哇哇!我点赞了这个答案,但是要注意!正如评论中@Brennan所说,这种方法不仅会导致性能下降,还会在每次reloadData时引起内存分配增加,类似于内存泄漏!需要使用lensovet上面的解决方法!花了一天时间才发现这个内存泄漏! - skywinder
由于cell.bounds.size.height始终返回0.0,在Swift中无法工作。 - King-Wizard
1
系统调用tableView:heightForRowAtIndexPath方法时,单元格尚不存在。您应该能够使用传递给您的indexPath来索引到您的数据模型,并查看将在那里使用什么类型的单元格,然后计算该单元格的高度并返回它。这使得系统能够在创建任何单元格之前布置表中的单元格。 - King-Wizard
1
此外,您不应该调用自己的cellForRowAtIndexPath方法。表视图有一个cellForRowAtIndexPath方法,如果当前在屏幕上可见,则会返回该索引路径的单元格,但通常该索引路径没有与之关联的单元格。 - King-Wizard
heightForRowAtIndexPath 中调用 dequeueReusableCellWithIdentifier 会对我造成无限递归。 - xaphod

22

我已根据各种答案/评论的提示构建了代码,以便使其适用于使用原型单元格的故事板。

此代码:

  • 不需要在显而易见的位置之外设置单元格高度
  • 缓存高度以提高性能
  • 使用通用函数获取单元格标识符以避免重复逻辑

感谢Answerbot、Brennan和lensovet。

- (NSString *)cellIdentifierForIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = nil;

    switch (indexPath.section)
    {
        case 0:
            cellIdentifier = @"ArtworkCell";
            break;
         <... and so on ...>
    }

    return cellIdentifier;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];
    static NSMutableDictionary *heightCache;
    if (!heightCache)
        heightCache = [[NSMutableDictionary alloc] init];
    NSNumber *cachedHeight = heightCache[cellIdentifier];
    if (cachedHeight)
        return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    <... configure cell as usual...>

无法在Swift中工作,因为cell.bounds.size.height始终返回0.0。 - King-Wizard
在系统调用tableView:heightForRowAtIndexPath方法时,单元格还不存在。您应该能够使用传递给您的indexPath索引到您的数据模型中,并查看将在那里使用什么类型的单元格,然后计算该单元格的高度并返回它。这使得系统能够在创建任何单元格之前布置表中的单元格。 - King-Wizard
此外,您不应该调用自己的cellForRowAtIndexPath方法。表视图有一个cellForRowAtIndexPath方法,如果当前在屏幕上可见,则会返回该索引路径的单元格,但通常该索引路径没有与之关联的单元格。 - King-Wizard
也许在Objective-C中它可以工作,但在使用iOS 8.3的Swift中却不行。为什么?简单得很,因为heightForRowAtIndexPath在tableView:cellForRowAtIndexPath之前被多次调用,并且dequeueReusableCellWithIdentifier返回的是屏幕上显示的单元格,但此时该单元格尚未绘制,因此它返回0.0作为单元格的高度。所以这段代码在Swift中无法工作,抱歉。我仍然必须为每个原型单元格使用预定义的魔数。 - King-Wizard
使用两个动态(原型)单元格在Swift中运行以下代码,以重现错误:http://pastebin.com/cX7LbCru - King-Wizard
显示剩余2条评论

20

实际上,你需要在两个地方更改行高度,首先是单元格(你已经更改了它),现在选择表视图并检查大小检查器。


在TableView(而不是CellView)中,进入Size Inspector选项卡,可以设置行高。 - iman kazemayni
每次我忘记了这个细节都会让我抓狂。当使用故事板时,调整单元格为什么不会自动更新tableView呢! - Scooter

11

我认为这是一个bug。

尝试直接在Storyboard上通过鼠标拖动来调整高度,而不是使用实用程序检查器。

我用这种方法解决了这个问题。


10

如果你正在使用Swift,就像这样使用。不要使用storyboard来选择行高度。像这样通过编程方式设置表格行高:

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    if indexPath.row == 0 || indexPath.row == 1{
        let cell = self.tableView.dequeueReusableCellWithIdentifier("section1", forIndexPath: indexPath) as! Section1TableViewCell
        self.tableView.rowHeight = 150
        cell.label1.text = "hiiiiii"
        cell.label2.text = "Huiiilllllll"
        return cell

    } else {

        let cell = self.tableView.dequeueReusableCellWithIdentifier("section2", forIndexPath: indexPath) as! Section2TableViewCell
        self.tableView.rowHeight = 60
        cell.label3.text = "llll"
        return cell
    }

}

这个可以工作,但更一般的做法是:cell.sizeToFit(); self.tableView.rowHeight = cell.frame.height - Andy Chou

7
您可以通过以下代码从故事板中获取UITableviewCell(在UITableviewController - 静态单元格中)的高度。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
   CGFloat height = [super tableView:tableView heightForRowAtIndexPath:indexPath];

    return height;
}

6

在XML视图中打开Storyboard,尝试编辑所需元素的rowHeight属性。

当我尝试为我的原型行设置自定义rowHeight时,这对我有用。虽然通过检查器无法工作,但通过XML可以正常工作。


1
我右键单击并选择“打开为”->“源代码”。rowHeight已经设置为120。我认为故事板在这里存在一个错误,它忽略了自定义单元格高度。 - zirinisp

6
您可以使用具有自定义高度的原型单元格,然后调用cellForRowAtIndexPath:并返回其frame.height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self tableView:tableView
                    cellForRowAtIndexPath:indexPath];
    return cell.frame.size.height;
}

这个方法的添加非常成功,现在将所有自定义内容保留在故事板中,使其归属正确。 - daspianist
迄今为止最好的解决方案 - TheJeff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接