使父视图的宽度成为最小孩子的宽度?

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。因此发生以下顺序:

    width1width2 被初始化为 0 VStack 将其宽度设置为min(0, 0) Text 内部视图缩小到宽度 0 WidthPreferenceKey.self 设置为 0 .onPreferenceChange 设置 width1width2,它们已经是 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](或类似的值) .onPreferenceChangewidth 设置为 42.5 VStack 将其宽度设置为 42.5 以满足其 .frame() 修饰符。

满足所有约束,SwiftUI 停止布局。请注意,.clipped() 会阻止显示长 Text 视图的边缘,即使它们在技术上超出了 VStack 的范围。

【讨论】:

以上是关于使父视图的宽度成为最小孩子的宽度?的主要内容,如果未能解决你的问题,请参考以下文章

父 flexbox 容器忽略孩子的 flexbox 最小宽度

Flutter: IntrinsicWidth类,将孩子的宽度调整为孩子的最大内在宽度

如何设置gridview的孩子的宽度以匹配屏幕宽度?

颤振让孩子在列内匹配另一个孩子的宽度

父类中的 CSS 样式,以便一个孩子有 25% 的宽度,另一个孩子有 75% [重复]

QML 布局不限制孩子的宽度