在 SwiftUI 中制作父视图大小的形状

Giu*_*Dev 4 layout ios swift swiftui

我想显示一个数学分数。为此,我有一个 VStack,其中包含分子视图(称为 ContainerView)、用作分数线的矩形和分母视图(与分子相同类型)。由于分数是可编辑的,因此分子和分母的大小可以改变,并且分数线必须相应地适应。我的第一次尝试是将分数线放在背景中,并使用几何阅读器仅根据分子和分母的大小来获取分数的框架。这样分数线就在背景的中心,大小是我想要的。这是代码:

\n
struct FractionView: View {\n\n@EnvironmentObject var buffer: Buffer\nvar numeratorBufferIndex: Int\nvar denominatorBufferIndex: Int\n\nvar body: some View {\n    VStack(alignment: .center, spacing: 0) {\n        ContainerView(activeBufferIndex: numeratorBufferIndex)\n            .environmentObject(self.buffer)\n        \n        ContainerView(activeBufferIndex: denominatorBufferIndex)\n            .environmentObject(self.buffer)\n    }\n    .background(GeometryReader { geometry in\n        Rectangle()\n            .frame(width: geometry.frame(in: .local).width, height: 2)\n            .foregroundColor(Color(.systemGray))\n    })\n}\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这三个属性用于渲染分子和分母。结果是这样的。\n但是,由于它在后台,所以它不知道分子和分母的高度。当它们具有不同的大小时,就像在本例中一样,这会产生问题。在图像中,您可以看到分子中有另一个分数,因此其视图的高度更大。这会导致问题,因为第一条分数线穿过分子的一部分。

\n

为了避免这种情况,我认为最好的方法是将分数线直接放在主 VStack 中,这样它就会除分子和分母而不关心它们的高度。然而,矩形形状占用了所有可用空间,我不知道如何将其宽度限制为父级的宽度。这是新代码及其外观:

\n
struct FractionView: View {\n\n@EnvironmentObject var buffer: Buffer\nvar numeratorBufferIndex: Int\nvar denominatorBufferIndex: Int\n\nvar body: some View {\n    VStack(alignment: .center, spacing: 0) {\n        ContainerView(activeBufferIndex: numeratorBufferIndex)\n            .environmentObject(self.buffer)\n        \n        Rectangle()\n            .frame(height: 2)\n            .foregroundColor(Color(.systemGray))\n        \n        ContainerView(activeBufferIndex: denominatorBufferIndex)\n            .environmentObject(self.buffer)\n    }\n}\n}\n
Run Code Online (Sandbox Code Playgroud)\n

总而言之,我必须找到一种方法将矩形的宽度限制为 VStack 没有它时的宽度。我尝试按照本文的示例使用首选项。

\n

这是我尝试过的:

\n
struct FractionView: View {\n\n@EnvironmentObject var buffer: Buffer\n@State var fractionFrame: CGFloat = 0\nvar numeratorBufferIndex: Int\nvar denominatorBufferIndex: Int\n\nvar body: some View {\n    VStack(alignment: .center, spacing: 0) {\n        ContainerView(activeBufferIndex: numeratorBufferIndex)\n            .environmentObject(self.buffer)\n        \n        Rectangle()\n            .frame(width: fractionFrame, height: 2)\n            .foregroundColor(Color(.systemGray))\n        \n        ContainerView(activeBufferIndex: denominatorBufferIndex)\n            .environmentObject(self.buffer)\n    }\n.coordinateSpace(name: "VStack")\n    .background(GeometryObteiner())\n    .onPreferenceChange(MyFractionPreferenceKey.self) { (value) in\n            print("Il valore della larghezza \xc3\xa8 \\(value)")\n            self.fractionFrame = value\n    }\n    \n}\n}\n\nstruct GeometryObteiner: View {\nvar body: some View {\n    GeometryReader { geometry in\n        Rectangle()\n            .frame(width: geometry.frame(in: .global).width, height: geometry.frame(in: .global).height)\n            .foregroundColor(.clear)\n            .preference(key: MyFractionPreferenceKey.self, value: geometry.frame(in: .named("VStack")).width)\n    }\n}\n}\n\nstruct MyFractionPreferenceKey: PreferenceKey {\ntypealias Value = CGFloat\n\nstatic var defaultValue: CGFloat = 10\n\nstatic func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {\n    value = nextValue()\n}\n}\n
Run Code Online (Sandbox Code Playgroud)\n

然而,似乎从未调用过 onPreferenceChange,因为结果始终是分数线具有默认值 (10) 的宽度。

\n

也许解决方案比我想象的要容易得多,但我无法弄清楚。我知道我写的可能有点令人困惑,如果您需要任何澄清,请在评论中询问我。

\n

Asp*_*eri 5

这里应用了与您的场景评论相同的想法。当然,它只是草图,但在我看来,它可以根据需要进行调整并使其通用(Text例如,替换当前使用的View或FullFraction本身以构造更复杂的分数)。

演示(因为 DispatchQueue 用于避免在视图绘制警告期间进行修改,您应该运行它来查看结果):

SwiftUI 数学分数

代码(截图演示):

struct FullFraction: View {
    var whole: String = ""
    var num: String
    var denom: String

    @State private var numWidth: CGFloat = 12.0 // just initial
    @State private var denomWidth: CGFloat = 12.0 // just initial
    var body: some View {
        HStack {
            Text(whole)
            VStack {
                numerator
                divider
                denominator
            }
        }
    }
    var numerator: some View {
        Text(num)
            .offset(x: 0, y: 8)
            .alignmentGuide(HorizontalAlignment.center, computeValue: { d in
                DispatchQueue.main.async {
                    self.numWidth = d.width
                }
                return d[HorizontalAlignment.center]
            })
    }
    var denominator: some View {
        Text(denom)
        .offset(x: 0, y: -4)
        .alignmentGuide(HorizontalAlignment.center, computeValue: { d in
            DispatchQueue.main.async {
                self.denomWidth = d.width
            }
            return d[HorizontalAlignment.center]
        })
    }

    var divider: some View {
        Rectangle().fill(Color.black).frame(width:max(self.numWidth, self.denomWidth), height: 2.0)
    }
}

struct DemoFraction: View {
    var body: some View {
        VStack {
            FullFraction(whole: "F", num: "(a + b)", denom: "c")
            Divider()
            FullFraction(whole: "ab12", num: "13", denom: "761")
            Divider()
            FullFraction(num: "1111", denom: "3")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)