【问题标题】:SwiftUI - Position a rectangle relative to another view in ZStackSwiftUI - 相对于 ZStack 中的另一个视图定位一个矩形
【发布时间】:2020-07-01 07:52:55
【问题描述】:

任务

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

目标

我需要能够相对于图表的条形(也是矩形)定位黄线(高度为 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)

                }
            }
        }
    }
}

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

您还有其他建议吗?

提前谢谢你

【问题讨论】:

  • 如果你已经创建了所有的条和标签,你也知道如何创建这个黄色矩形。
  • 向我们展示你的 SwiftUI 逻辑;仅从屏幕截图中提出建议是冒险的。
  • @staticVoidMan 添加了我的游乐场代码
  • 好的,在提交我的答案之前我没有看到你自己的代码。我相信您能够根据自己的需要进行调整。

标签: swift swiftui


【解决方案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()
    }
}

和结果

或略微采用显示标签

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-13
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-30
    相关资源
    最近更新 更多