我想显示一个数学分数。为此,我有一个VStack,其中包含分子视图(称为ContainerView),用作分数线的矩形和分母视图(分子的相同类型)。由于分数是可编辑的,因此分子和分母的大小可以更改,因此分数线必须适应。我的第一个尝试是将分数线置于背景中,并使用几何读取器仅基于分子和分母的大小来获取分数的框架。这样,分数线位于背景的中心,具有我想要的大小。这是代码:
struct FractionView: View {
@EnvironmentObject var buffer: Buffer
var numeratorBufferIndex: Int
var denominatorBufferIndex: Int
var body: some View {
VStack(alignment: .center, spacing: 0) {
ContainerView(activeBufferIndex: numeratorBufferIndex)
.environmentObject(self.buffer)
ContainerView(activeBufferIndex: denominatorBufferIndex)
.environmentObject(self.buffer)
}
.background(GeometryReader { geometry in
Rectangle()
.frame(width: geometry.frame(in: .local).width, height: 2)
.foregroundColor(Color(.systemGray))
})
}
}
这三个属性用于渲染分子和分母。The result is this。 但是,由于它在背景中,因此不知道分子和分母的高度。当它们的大小like in this case不同时,这会带来问题。在图像中,您可以看到分子中还有另一个分数,因此其视图的高度更大。这引起问题,因为第一分数线穿过分子的一部分。
为避免这种情况,我认为最好的方法是将分数线直接放在主VStack中,因此它将分子和分母分开而不关心它们的高度。但是,矩形会占用所有可用空间,并且我不知道如何将其宽度限制为父级的宽度。这是新代码和what it looks like:
struct FractionView: View {
@EnvironmentObject var buffer: Buffer
var numeratorBufferIndex: Int
var denominatorBufferIndex: Int
var body: some View {
VStack(alignment: .center, spacing: 0) {
ContainerView(activeBufferIndex: numeratorBufferIndex)
.environmentObject(self.buffer)
Rectangle()
.frame(height: 2)
.foregroundColor(Color(.systemGray))
ContainerView(activeBufferIndex: denominatorBufferIndex)
.environmentObject(self.buffer)
}
}
}
总而言之,我必须找到一种方法来将矩形的宽度限制为VStack没有矩形时的宽度。我尝试按照this article的示例使用首选项。这就是我尝试过的
struct FractionView: View {
@EnvironmentObject var buffer: Buffer
@State var fractionFrame: CGFloat = 0
var numeratorBufferIndex: Int
var denominatorBufferIndex: Int
var body: some View {
VStack(alignment: .center, spacing: 0) {
ContainerView(activeBufferIndex: numeratorBufferIndex)
.environmentObject(self.buffer)
Rectangle()
.frame(width: fractionFrame, height: 2)
.foregroundColor(Color(.systemGray))
ContainerView(activeBufferIndex: denominatorBufferIndex)
.environmentObject(self.buffer)
}
.coordinateSpace(name: "VStack")
.background(GeometryObteiner())
.onPreferenceChange(MyFractionPreferenceKey.self) { (value) in
print("Il valore della larghezza è \(value)")
self.fractionFrame = value
}
}
}
struct GeometryObteiner: View {
var body: some View {
GeometryReader { geometry in
Rectangle()
.frame(width: geometry.frame(in: .global).width, height: geometry.frame(in: .global).height)
.foregroundColor(.clear)
.preference(key: MyFractionPreferenceKey.self, value: geometry.frame(in: .named("VStack")).width)
}
}
}
struct MyFractionPreferenceKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue: CGFloat = 10
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
但是似乎从未调用过onPreferenceChange,因为result始终是分数线具有默认值(10)的宽度。
也许解决方案比我想的要容易得多,但我无法弄清楚。我知道我写的内容可能有些混乱,如果您需要任何澄清,请在评论中问我。 预先感谢。
答案 0 :(得分:0)
此处对您的方案应用了与注释相同的想法。当然,它只是从头开始的,但是IMO可以根据需要进行调整并使其通用(例如,将当前使用的Text
替换为View
或FullFraction
本身来构造更复杂的分数)。
演示(由于DispatchQueue用于避免在视图绘制警告期间进行修改,因此应运行它以查看结果):
(屏幕截图演示的代码):
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")
}
}
}
struct DemoFraction_Previews: PreviewProvider {
static var previews: some View {
DemoFraction()
}
}