【发布时间】:2019-11-14 13:24:25
【问题描述】:
我有一个 tableview 单元格,其中包含一个自定义视图以及其他视图,并且使用了自动布局。
我的自定义视图的目的是将其子视图按行布局,如果当前子视图不适合当前行,则拆分为新行。它有点像多行标签,但有视图。我通过精确定位而不是自动布局实现了这一点。
由于我只知道layoutSubviews() 中的视图宽度,因此我需要计算那里的确切位置和行数。效果很好,但由于缺少intrinsicContentSize,我的视图的帧(零)不匹配。
因此,如果自上次布局通过后我的高度发生变化,我在计算的末尾添加了一个检查。如果是这样,我会更新在我的 intrinsicContentSize 属性中使用的 height 属性并调用 invalidateIntrinsicContentSize()。
我观察到最初layoutSubviews() 被调用了两次。第一遍效果很好,即使单元格的宽度小于应有的宽度,也会考虑intrinsicContentSize。第二遍使用实际宽度并更新intrinsicContentSize。但是父级(tableview 单元格中的 contentView)忽略了这个新的intrinsicContentSize。
所以基本上结果是子视图的布局和绘制正确,但是自定义视图的框架没有在父视图中更新/使用。
问题:
有没有办法通知父级固有大小的变化或指定的地方更新layoutSubviews()中计算的大小以便在父级中使用新的大小?
编辑:
这是我的自定义视图中的代码。
仅供参考:8 只是两个子视图之间的垂直和水平空间
class WrapView : UIView {
var height = CGFloat.zero
override var intrinsicContentSize: CGSize {
CGSize(width: UIView.noIntrinsicMetric, height: height)
}
override func layoutSubviews() {
super.layoutSubviews()
guard frame.size.width != .zero else { return }
// Make subviews calc their size
subviews.forEach { $0.sizeToFit() }
// Check if there is enough space in a row to fit at least one view
guard subviews.map({ $0.frame.size.width }).max() ?? .zero <= frame.size.width else { return }
let width = frame.size.width
var row = [UIView]()
// rem is the remaining space in the current row
var rem = width
var y: CGFloat = .zero
var i = 0
while i < subviews.count {
let view = subviews[i]
let sizeNeeded = view.frame.size.width + (row.isEmpty ? 0 : 8)
let last = i == subviews.count - 1
let fit = rem >= sizeNeeded
if fit {
row.append(view)
rem -= sizeNeeded
i += 1
guard last else { continue }
}
let rowWidth = row.map { $0.frame.size.width + 8 }.reduce(-8, +)
var x = (width - rowWidth) * 0.5
for vw in row {
vw.frame.origin = CGPoint(x: x, y: y)
x += vw.frame.width + 8
}
y += row.map { $0.frame.size.height }.max()! + 8
rem = width
row = []
}
if height != y - 8 {
height = y - 8
invalidateIntrinsicContentSize()
}
}
}
【问题讨论】:
-
嗨,马可。添加一些代码来显示您尝试过的内容将大大有助于获得答案。
-
查看minimal reproducible example 会有所帮助...但是,有一个建议:不要使用
intrinsicContentSize,而是尝试在调整大小循环的末尾添加一个高度约束变量...hConstraint.constant = y。 (不需要invalidateIntrinsicContentSize()) -
哎呀......让
hConstraint.constant = y - 8 -
@DonMag 我实际上做了对自定义视图有效的操作,但对单元格中的其他视图产生了影响,例如拉伸标签大于内容等等。很遗憾,这也不起作用。
-
@Marco - 我对 为什么 会发生这种情况有一个解释 - 不幸的是我没有解决方案。您的代码正在更新
intrinsicContentSize在 自动布局运行后...并且更改单元格的内容不会 自动触发tableView 重绘。您可能需要向控制器发送消息以在表格视图上运行.beginUpdates()/.endUpdates()对,但您最终可能会陷入无限循环。
标签: ios swift autolayout