如何使父视图的宽度等于最小子视图的宽度?
我可以使用this question的答案来了解子视图的宽度,如下所示:
struct WidthPreferenceKey: PreferenceKey {
static var defaultValue = CGFloat(0)
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
typealias Value = CGFloat
}
struct TextGeometry: View {
var body: some View {
GeometryReader { geometry in
return Rectangle().fill(Color.clear).preference(key: WidthPreferenceKey.self, value: geometry.size.width)
}
}
}
struct Content: View {
@State var width1: CGFloat = 0
@State var width2: CGFloat = 0
var body: some View {
VStack {
Text("Short")
.background(TextGeometry())
.onPreferenceChange(WidthPreferenceKey.self, perform: { self.width1 = $0 })
Text("Loooooooooooong")
.background(TextGeometry())
.onPreferenceChange(WidthPreferenceKey.self, perform: { self.width2 = $0 })
}
.frame(width: min(self.width1, self.width2)) // This is always 0
}
}
但是VStack
的宽度始终为0。就像
.frame(width: min(self.width1, self.width2))
行被调用
在设置宽度之前。
有什么想法吗?
答案 0 :(得分:2)
这是一个逻辑错误,因为从技术上讲,系统完全按照您的指示进行操作,但是结果却不是您作为程序员所想要的。
基本上,VStack 想要尽可能缩小以适合其内容的大小。同样,文本视图愿意尽可能缩小(截断其内容)以适合其父视图。您给出的唯一硬性要求是VStack的初始帧的宽度为0。因此发生以下顺序:
width1
和width2
初始化为0 VStack
将其宽度设置为min(0, 0)
Text
视图缩小到宽度0 WidthPreferenceKey.self
设置为0 .onPreferenceChange
设置了width1
和width2
,它们已经为0 所有约束都得到满足,SwiftUI会愉快地停止布局。
让我们使用以下内容修改代码:
WidthPreferenceKey.Value
成为[CGFloat]
的别名,而不是CGFloat
。这样,您可以根据需要设置任意数量的首选项键设置器,它们只会不断累积到一个数组中。.onPreferenceChange
调用,它将找到所有读取值中的最小值,并设置单个@State var width: CGFloat
属性.fixedSize()
添加到Text
视图中。像这样:
struct WidthPreferenceKey: PreferenceKey {
static var defaultValue = [CGFloat]()
static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
value.append(contentsOf: nextValue())
}
typealias Value = [CGFloat]
}
struct TextGeometry: View {
var body: some View {
GeometryReader { geometry in
return Rectangle()
.fill(Color.clear)
.preference(key: WidthPreferenceKey.self,
value: [geometry.size.width])
}
}
}
struct Content: View {
@State var width: CGFloat = 0
var body: some View {
VStack {
Text("Short")
.fixedSize()
.background(TextGeometry())
Text("Loooooooooooong")
.fixedSize()
.background(TextGeometry())
}
.frame(width: self.width)
.clipped()
.background(Color.red.opacity(0.5))
.onPreferenceChange(WidthPreferenceKey.self) { preferences in
print(preferences)
self.width = preferences.min() ?? 0
}
}
}
现在发生以下情况:
width
初始化为0 VStack
将其宽度设置为0 Text
视图在VStack外部扩展到 ,因为我们已授予.fixedSize()
WidthPreferenceKey.self
设置为[42.5, 144.5]
(或近似值).onPreferenceChange
将width
设置为42.5
VStack
将其宽度设置为42.5
,以满足其.frame()
修饰符。满足所有约束,并且SwiftUI停止布局。请注意,.clipped()
会阻止显示长Text
的边缘,即使它们在技术上不在VStack
的范围之内。