使父视图的宽度成为最小孩子的宽度?
Posted
技术标签:
【中文标题】使父视图的宽度成为最小孩子的宽度?【英文标题】:Make parent view's width the width of the smallest child? 【发布时间】:2019-11-06 19:31:31 【问题描述】:如何使父视图的宽度成为最小子视图的宽度?
我可以使用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))
行
在设置宽度之前。
有什么想法吗?
【问题讨论】:
【参考方案1】:这是一个逻辑错误,因为系统在技术上完全按照您的指示行事,但结果并非您作为程序员的预期。
基本上,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
的范围。
【讨论】:
以上是关于使父视图的宽度成为最小孩子的宽度?的主要内容,如果未能解决你的问题,请参考以下文章
父 flexbox 容器忽略孩子的 flexbox 最小宽度
Flutter: IntrinsicWidth类,将孩子的宽度调整为孩子的最大内在宽度