SwiftUI-在ZStack中相对于另一个视图放置一个矩形

时间:2020-03-20 06:49:51

标签: swift swiftui

任务

我正在使用SwiftUI创建条形图,我正在寻找定位平均指标的最佳方法,即水平黄线,如下图所示:

enter image description here

目标

我需要能够相对于图表的条形图(也包括矩形)放置黄线(高度为1点的矩形)。为简单起见,让我们想象一下最高可能的柱线是100点,平均值是75。我的目标是在柱线底部上方的75点处绘制黄线。

问题

在我的示例中,我使用ZStack将线放置在图表顶部。我可以使用GeometryReader垂直放置线条,但是我不知道如何相对于条形底部放置线条。

这是我的游乐场代码:

import SwiftUI
import PlaygroundSupport

struct BarChart: View {
    var measurements : [Int]
    var pastColor: Color
    var todayColor: Color
    var futureColor: Color

    let labelsColor = Color(UIColor(white: 0.5, alpha: 1.0))

    func monthAbbreviationFromInt(_ day: Int) -> String {
        let da = Calendar.current.shortWeekdaySymbols
        return da[day]
    }

    var body: some View {
        ZStack {

            VStack {

                HStack {
                    Rectangle()
                        .fill(Color.yellow)
                        .frame(width: 12, height: 2, alignment: .center)

                    Text("Desired level")
                        .font(.custom("Helvetica", size: 10.0))
                        .foregroundColor(labelsColor)
                }
                .padding(.bottom , 50.0)

                HStack {

                    ForEach(0..<7) { day in

                        VStack {

                            Spacer()

                            Text(String(self.measurements[day]))
                                .frame(width: CGFloat(40), height: CGFloat(20))
                                .font(.footnote)
                                .lineLimit(1)
                                .minimumScaleFactor(0.5)
                                .foregroundColor(self.labelsColor)

                            Rectangle()
                                .fill(day > 0 ? self.futureColor : self.pastColor)
                                .frame(width: 20, height: CGFloat(self.measurements[day]*2))
                                .transition(.slide)
                                .cornerRadius(3.0)

                            Text("\(self.monthAbbreviationFromInt(day))\n\(day)")
                                .multilineTextAlignment(.center)
                                .font(.footnote)
                                .frame(height: 20)
                                .lineLimit(2)
                                .minimumScaleFactor(0.2)
                                .foregroundColor(self.labelsColor)

                        }
                        .frame(height: 200, alignment: .bottom )
                    }

                }
            }
            .padding()

            VStack {
                GeometryReader { geometry in
                    //Yellow line
                    Rectangle()
                        .path(in: CGRect(x: 0,
                                     y: 350, //This is now a random value, but I should detect and use the bottom coordinate of the bars to place the line at exactly 75 points above that coordinate
                        width: geometry.size.width,
                        height: 1))
                        .fill(Color.yellow)

                }
            }
        }
    }
}

如何获取条形底部的相对位置,以便正确放置线条?

您会建议另一种方法吗?

提前谢谢

1 个答案:

答案 0 :(得分:1)

示例

import SwiftUI

struct Data: Identifiable {
    let id = UUID()
    let value: CGFloat
    let color: Color
}
struct ContentView: View {
    let data = [Data(value: 35, color: .red),
                  Data(value: 100, color: .green),
                  Data(value: 70, color: .yellow),
                  Data(value: 25, color: .blue),
                  Data(value: 55, color: .orange)]

    var maxValue: CGFloat {
        data.map { (element) -> CGFloat in
            element.value
        }.max() ?? 1.0
    }
    var average: CGFloat {
        guard !data.isEmpty else { return 0 }
        let sum = data.reduce(into: CGFloat.zero) { (res, data) in
            res += data.value
        }
        return sum / CGFloat(data.count)
    }
    var body: some View {

        HStack {
            ForEach(data) { (bar) in
                Bar(t: bar.value / self.maxValue, color: bar.color, width: 20)
            }
        }
        .background(Color.pink.opacity(0.1))
        .overlay(
            GeometryReader { proxy in
                Color.primary.frame(height: 1).position(x: proxy.size.width / 2, y: proxy.size.height * ( 1 - self.average / self.maxValue))
            }
        ).padding(.vertical, 100).padding(.horizontal, 20)

    }
}

struct Bar: View {
    let t: CGFloat
    let color: Color
    let width: CGFloat

    var body: some View {
        GeometryReader { proxy in
            Path { (path) in
                path.move(to: .init(x: proxy.size.width / 2, y: proxy.size.height))
                path.addLine(to: .init(x: proxy.size.width / 2, y: 0))
                }
            .trim(from: 0, to: self.t)
            .stroke(lineWidth: self.width)
            .foregroundColor(self.color)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

和结果 enter image description here

或略微采用以显示标签

enter image description here