在SwiftUI中拥抱子视图

Gus*_*afu 5 swift swiftui

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

View是否有一种方法可以告诉其容器它没有提出任何空间需求,但是会使用给定的所有空间?另一种说法是容器应拥抱其子视图。

具体的例子,我想要类似c的东西:

VStack中的一些文本

如果a)中TextVStacklike中包含一些,则VStack将会采用其宽度作为最宽的子视图。

如果您Rectangle在b)中添加了一个ough,它将尽可能扩展,直到VStack填充容器。

这表明Texts和Rectangles在布局Text方面属于不同的类别,具有固定的大小并且a Rectangle贪婪。但是,如果我要自己制作容器,该如何将其传达给容器View

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

鉴于SwiftUI似乎是自下而上地布局,也许这是不可能的,但似乎应该有some办法实现。

kon*_*iki 8

没有修饰符(AFAIK)来实现这一点,所以这是我的方法。如果这是您经常使用的东西,那么创建您自己的修饰符可能是值得的。

另请注意,这里我使用的是标准首选项,但锚点首选项更好。在这里解释是一个沉重的话题。我写了一篇文章,你可以在这里查看:https : //swiftui-lab.com/communicating-with-the-view-tree-part-1/

您可以使用下面的代码来完成您要查找的内容。

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)])
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Gus*_*afu 5

所以我实际上找到了一种方法来做到这一点。首先,我尝试在各种配置中将 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

蓝色视图和所有颜色背景当然只是为了更容易查看。这段代码产生这个:

在此处输入图片说明