AutoLayout 动态 .xib 视图高度

AutoLayout Dynamic .xib View Height

我没能在 AutoLayout 中找到很多关于单个 .xib 文件的方法...

我有一个独立的 .xib 文件,它有 3 个视图——一个 header 视图(包含两个标签)、一个输入和一个页脚(包含两个按钮)。它看起来像这样:

header 视图中的标签具有约束,这些约束会影响 header 视图的垂直尺寸,进而影响整个视图的尺寸。 subheader 是一个 0 行的标签,这意味着它是 multi-line 并且是动态的。其他所有东西都有一个设置的高度,水平约束到父视图,顶部约束到兄弟(或者在 header 视图的情况下是父视图)。

我遇到的问题是,当我在代码中加载此 .xib 文件以进行显示时,根据 Xcode 的检查器中定义的内容,高度始终是静态的。是否可以根据宽度使整个视图的高度动态化(这会影响动态标签高度,从而影响视图的其余部分)?

例如 - 如果我从 .xib 加载此视图并将其宽度设置为 300,我该如何调整其高度以适应动态标签的新高度?我需要使用intrinsicContentSize方法来定义这个尺寸吗?

经过大量实验和阅读,我找到了答案。在某种构造函数中加载 .xib 时(在我的例子中是 class 级别的便捷方法),您必须确保调用 [view setTranslatesAutoresizingMaskIntoConstraints:NO]; 例如,我已经完成了以下操作:

+ (InputView *)inputViewWithHeader:(NSString *)header subHeader:(NSString *)subHeader inputValidation:(ValidationBlock)validation
{
    InputView *inputView = [[[NSBundle mainBundle] loadNibNamed:@"InputView" owner:self options:nil] lastObject];
    if ([inputView isKindOfClass:[InputView class]]) {
        [inputView setTranslatesAutoresizingMaskIntoConstraints:NO];
        [inputView configureWithHeader:header subHeader:subHeader inputValidation:validation];
        [inputView layoutIfNeeded];
        [inputView invalidateIntrinsicContentSize];
        return inputView;
    }
    return nil;
}

那么,有必要覆盖layoutSubviewsintrinsicContentSize。覆盖 layoutSubviews 允许我设置标签的 preferredMaxLayoutWidth,而覆盖 intrinsicContentSize 允许我根据约束和子视图计算大小!这是我的实现:

- (void)layoutSubviews {
    [super layoutSubviews];
    self.subHeaderLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
    [super layoutSubviews];
}

- (CGSize)intrinsicContentSize {
    CGFloat height = self.headerView.bounds.size.height;
    height += self.headerInputSpacer.constant;
    height += self.inputField.bounds.size.height;
    height += self.inputButtonSpacer.constant;
    height += self.buttonView.bounds.size.height;
    CGSize size = CGSizeMake([UIScreen mainScreen].bounds.size.width - 20, height);
    return size;
}

我相信有办法改进它,或者有更好的方法来实现它,但现在至少它的大小是正确的!对于不应具有用户定义框架的视图非常有用。

这是我的方法(Swift 版本):

将所有内容嵌入视图中。您的 xib 层次结构应该是:

  • Your xib
    • View
      • Everything else, your labels, buttons etc.

视图的约束应该约束到TopLeadingTrailingsuperview(你的xib),在你的"everything else"里面应该有对象的 bottom 对父视图 (View) 的约束。

在您的代码中:

加载笔尖

nib.frame.size.width = 80 // Set your desired width here

nib.label.text = "hello world" // Set your dynamic text here

nib.layoutIfNeeded() // This will calculate and set the heights accordingly

nib.frame.size.height = nib.view.frame.height + 16 // 16 is the total of top gap and bottom gap of auto layout

对于我的情况,我将这个 xib 加载到集合视图的单元格中,xib 的高度将动态调整,宽度将跟随单元格的宽度。最后,上面的代码块在我的集合视图的 cellForItemAt 方法中。

希望对您有所帮助。

不幸的是,我不确定我错过了什么。以上方法对我获取 xib 单元格的高度或让 layoutifneeded(), UITableView.automaticDimension 进行高度计算不起作用。我一直在搜索和尝试 3 到 4 个晚上,但没有答案。 不过,此处或另一个 post 上的一些答案确实为我提供了解决方法的提示。这是一种愚蠢的方法,但它确实有效。只需将所有单元格添加到数组中即可。然后在 xib 故事板中设置每个高度约束的出口。最后,在 heightForRowAt 方法中将它们相加。如果您不熟悉这些 API,这很简单。

Swift 4.2

CustomCell.Swift

@IBOutlet weak var textViewOneHeight: NSLayoutConstraint!
@IBOutlet weak var textViewTwoHeight: NSLayoutConstraint!
@IBOutlet weak var textViewThreeHeight: NSLayoutConstraint!

@IBOutlet weak var textViewFourHeight: NSLayoutConstraint!
@IBOutlet weak var textViewFiveHeight: NSLayoutConstraint!

MyTableViewVC.Swift

.
.
var myCustomCells:[CustomCell] = []
.
.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = Bundle.main.loadNibNamed("CustomCell", owner: self, options: nil)?.first as! CustomCell

.
.
myCustomCells.append(cell)
return cell

}


override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

   let totalHeight = myCustomCells[indexPath.row].textViewOneHeight.constant + myCustomCells[indexPath.row].textViewTwoHeight.constant +  myCustomCells[indexPath.row].textViewThreeHeight.constant + myCustomCells[indexPath.row].textViewFourHeight.constant + myCustomCells[indexPath.row].textViewFiveHeight.constant

  return totalHeight + 40 //some magic number


}