【问题标题】: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)) 行 在设置宽度之前。

有什么想法吗?

【问题讨论】:

    标签: swiftui


    【解决方案1】:

    这是一个逻辑错误,因为系统在技术上完全按照您的指示行事,但结果并非您作为程序员的预期。

    基本上,VStack 希望尽可能地缩小以适应其内容的大小。同样,文本视图愿意尽可能地缩小(截断其内容)以适应其父视图。您给出的唯一硬性要求是 VStack 的初始帧的宽度为 0。因此发生以下顺序:

    1. width1width2 被初始化为 0
    2. VStack 将其宽度设置为min(0, 0)
    3. Text 内部视图缩小到宽度 0
    4. WidthPreferenceKey.self 设置为 0
    5. .onPreferenceChange 设置 width1width2,它们已经是 0

    所有的约束都满足了,SwiftUI 愉快的停止了布局。

    让我们用以下代码修改您的代码:

    1. WidthPreferenceKey.Value 设为[CGFloat] 而不是CGFloat 的类型别名。这样,您可以根据需要设置任意数量的首选项键设置器,它们会不断累积到一个数组中。
    2. 使用一个.onPreferenceChange调用,它将找到所有读取值中的最小值,并设置单个@State var width: CGFloat属性
    3. .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
            }
        }
    
    }
    

    现在发生以下情况:

    1. width 初始化为 0
    2. VStack 将其宽度设置为 0
    3. Text 视图在 VStack 之外扩展,因为我们已经获得了.fixedSize() 的许可
    4. WidthPreferenceKey.self 设置为 [42.5, 144.5](或类似的值)
    5. .onPreferenceChangewidth 设置为 42.5
    6. VStack 将其宽度设置为 42.5 以满足其 .frame() 修饰符。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-05
      • 2015-06-08
      • 1970-01-01
      • 2019-09-16
      • 2019-09-12
      相关资源
      最近更新 更多