拥抱SwiftUI中的子视图

时间:2019-06-30 06:08:56

标签: swift swiftui

戴夫·亚伯拉罕斯(Dave Abrahams)在WWDC19中关于“自定义视图”的讨论中解释了SwiftUI布局的一些机制,但是他遗漏了一些内容,因此我难以正确调整视图大小。

视图是否可以告诉其容器它没有占用任何空间,但是会使用给定的所有空间?换句话说,容器应拥抱其子视图。

具体示例,我想要类似c:

Some Texts inside a VStack

如果在Text中有一些VStack,如a)所示,则VStack将采用其宽度作为最宽子视图。

如果您按照b)的方式添加Rectangle,它将尽可能扩展,直到VStack填充容器。

这表示TextRectangle在布局方面属于不同的类别,Text具有固定的大小,而Rectangle是贪婪的。 但是如果我自己制作View,如何将其传达给我的容器?

我实际上想要达到的结果是c)。 VStack在确定其大小时应忽略Rectangle(或我的自定义视图),然后做到这一点,然后然后将其告知Rectangle或我的自定义视图可以容纳多少空间。 / p>

鉴于SwiftUI似乎是自下而上地布局,也许这是不可能的,但似乎应该有some的方式来实现这一目标。

2 个答案:

答案 0 :(得分:2)

没有修饰符(AFAIK)可以完成此操作,所以这是我的方法。如果您经常使用此功能,则值得创建自己的修饰符。

还要注意,这里我使用的是标准首选项,但锚首选项甚至更好。这里要解释一个繁重的话题。我正在写一篇有关它的文章,一旦完成,我会在这里发布一个链接。

同时,您可以使用下面的代码来完成所需的工作。

import SwiftUI

struct MyRectPreference: PreferenceKey {
    typealias Value = [CGRect]

    static var defaultValue: [CGRect] = []

    static func reduce(value: inout [CGRect], nextValue: () -> [CGRect]) {
        value.append(contentsOf: nextValue())
    }
}

struct ContentView : View {
    @State private var widestText: CGFloat = 0

    var body: some View {
        VStack {
            Text("Hello").background(RectGetter())
            Text("Wonderful World!").background(RectGetter())
            Rectangle().fill(Color.blue).frame(width: widestText, height: 30)
            }.onPreferenceChange(MyRectPreference.self, perform: { prefs in
                for p in prefs {
                    self.widestText = max(self.widestText, p.size.width)
                }
            })
    }
}

struct RectGetter: View {

    var body: some View {
        GeometryReader { geometry in
            Rectangle()
                .fill(Color.clear)
                .preference(key: MyRectPreference.self, value: [geometry.frame(in: .global)])
        }
    }
}

答案 1 :(得分:0)

所以我实际上找到了一种方法。首先,我尝试以各种配置将Spacers放置在视图周围,以尝试将其组合在一起,但这没有用。然后我意识到我也许可以使用.background修饰符,并且确实起作用了。似乎首先要让拥有视图计算其大小,然后才将其作为其框架,这正是我想要的。

这只是一个例子,有些技巧可以使高度正确,但这只是一个很小的细节,在我的特殊用例中,它是不需要的。如果您足够聪明,可能不在这里。

var body: some View {
    VStack(spacing: 10) {
        Text("Short").background(Color.green)
        Text("A longer text").background(Color.green)
        Text("Dummy").opacity(0)
    }
    .background(backgroundView)
    .background(Color.red)
    .padding()
    .background(Color.blue)
}

var backgroundView: some View {
    VStack(spacing: 10) {
        Spacer()
        Spacer()
        Rectangle().fill(Color.yellow)
    }
}

蓝色视图和所有彩色背景当然只是为了使其更易于查看。 这段代码产生了这一点:

enter image description here