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

213

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

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

18个回答

4

最近我一直在处理这个问题。我的问题是上面发布的使用heightForRowAtIndexPath:方法的解决方案可以在模拟器中适用于iOS 7.1,但是如果简单地切换到iOS 8.1,则完全会产生错误的结果。

我开始阅读更多关于自动调整大小的单元格(在iOS 8中引入,点此阅读)。显然,在iOS 8中使用UITableViewAutomaticDimension技术会有所帮助。我尝试使用该技术并删除了heightForRowAtIndexPath:的使用,哇,现在它在iOS 8中完美运行。但是iOS 7不行。我该怎么办?我需要在iOS 7中使用heightForRowAtIndexPath:而不是在iOS 8中使用。

这是我的解决方案(为简洁起见进行了修剪),借鉴了@JosephH上面发布的答案:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.estimatedRowHeight = 50.;
    self.tableView.rowHeight = UITableViewAutomaticDimension;

    // ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
        return UITableViewAutomaticDimension;

    } else {
        NSString *cellIdentifier = [self reuseIdentifierForCellAtIndexPath: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;
    }
}

- (NSString *)reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath {
    NSString * reuseIdentifier;
    switch (indexPath.row) {
        case 0:
            reuseIdentifier = EventTitleCellIdentifier;
            break;
        case 2:
            reuseIdentifier = EventDateTimeCellIdentifier;
            break;
        case 4:
            reuseIdentifier = EventContactsCellIdentifier;
            break;
        case 6:
            reuseIdentifier = EventLocationCellIdentifier;
            break;
        case 8:
            reuseIdentifier = NotesCellIdentifier;
            break;
        default:
            reuseIdentifier = SeparatorCellIdentifier;
            break;
    }

    return reuseIdentifier;
}

SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")实际上来自我正在使用的一组宏定义,我在某个地方找到了它们(非常有帮助)。它们被定义为:

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

4
如果您想设置固定的行高,可以按照以下方式进行操作:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 120;
}

2

在使用Swift 4的XCode 9时,遇到了同样的问题。

为单元格内的UI元素添加自动布局,自定义单元格行高将按照指定方式工作。


1
嗨,请问你是怎么做到的? - Fayçal
你只需要在单元格底部添加一个约束集。 - Justin
1
当我在Storyboard中执行这个操作并选择“自动”来设置单元格高度时,它希望将所有高度都设置为44,虽然当我运行时它看起来是正确的。我怎样才能使Storyboard正确地显示呢? - David

1

对于动态单元格,UITableView上设置的rowHeight始终会覆盖单个单元格的rowHeight。只需在行内计算动态高度即可。


0
作为评论添加,但为了更好地展示,我将其发布为答案:
如果您最初将表视图设置为“静态单元格”,然后将其更改为“动态原型”,似乎还存在问题。 我曾经遇到过这样的问题,即使是heightForRowAtIndexPath的委托方法也被忽略了。 我首先通过直接编辑Storyboard XML解决了该问题,最后重新从头开始构建了包含动态原型的Storyboard场景来解决。

0

你可以做的另一件事是进入文档大纲,选择嵌套原型单元格的表视图。然后在大小检查器中,将表视图行高更改为所需值,并取消勾选自动框。


0

鉴于我没有通过Interface Builder找到任何解决方案,我决定使用Swift中的两个动态单元格来发布一个编程解决方案,尽管最初的问题要求通过Interface Builder解决。无论如何,我认为这对Stack Overflow社区可能会有所帮助:

    import UIKit

    enum SignInUpMenuTableViewControllerCellIdentifier: String {
       case BigButtonCell = "BigButtonCell"
       case LabelCell = "LabelCell"
    }

    class SignInUpMenuTableViewController: UITableViewController {
            let heightCache = [SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell : CGFloat(50),
                              SignInUpMenuTableViewControllerCellIdentifier.LabelCell : CGFloat(115)]

    private func cellIdentifierForIndexPath(indexPath: NSIndexPath) -> SignInUpMenuTableViewControllerCellIdentifier {
        if indexPath.row == 2 {
            return SignInUpMenuTableViewControllerCellIdentifier.LabelCell
        } else {
            return SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell
        }
    }

   override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
       return self.heightCache[self.cellIdentifierForIndexPath(indexPath)]!
   }

   ...

  }

这很好,但除非我误读了代码,否则它仅依赖于魔数,并完全忽略您在IB中设置的动态单元格的任何值,因此...并不是OP问题的解决方案。 - Danny

0

我能找到的唯一真正的解决方案是这个

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = ...; // Instantiate with a "common" method you'll use again in cellForRowAtIndexPath:
    return cell.frame.size.height;
}

这个方法很有效,避免了在StoryBoard中重复使用可怕的switch/if逻辑。不确定性能如何,但我猜当到达cellForRow:时,单元格已经初始化,速度应该很快。当然,这里可能会有一些副作用,但对我来说看起来很好用。

我也在这里发布了这个帖子:https://devforums.apple.com/message/772464

编辑:Ortwin Gentz提醒我,heightForRowAtIndexPath:将为TableView的所有单元格调用,而不仅仅是可见的单元格。这听起来很合理,因为iOS需要知道总高度才能显示正确的滚动条。这意味着在小型TableView(如20个单元格)上可能没问题,但在1000个单元格的TableView上就别想了。

此外,之前使用XML的技巧:对我来说与第一条评论相同。正确的值已经存在。


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