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;
}
那么,有必要覆盖layoutSubviews
和intrinsicContentSize
。覆盖 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.
视图的约束应该约束到Top
、Leading
和Trailing
到superview
(你的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
}
我没能在 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;
}
那么,有必要覆盖layoutSubviews
和intrinsicContentSize
。覆盖 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.
视图的约束应该约束到Top
、Leading
和Trailing
到superview
(你的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
}