IOS16 中的 SwiftUI 可滚动图表

fco*_*llf 5 charts swiftui ios16 swiftui-charts

使用新的 SwiftUI Charts 框架,我们可以制作比可见屏幕更大的图表,并将其放入 ScrollView 中以使其可滚动。像这样的东西:

var body : some View {
    
    GeometryReader { proxy in

        ScrollView(.horizontal, showsIndicators: false) {

            Chart {

                ForEach(data) { entry in

                    // ...
                }
            }
            .frame(width: proxy.size.width * 2)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有谁知道是否可以以编程方式移动滚动以显示图表的特定区域?

我尝试过使用ScrollViewReader,在 x 轴标签处设置 ID,并尝试使用该scrollTo函数导航到任何这些位置,但没有成功:

Chart {

    /// ...
    
}
.chartXAxis {

    AxisMarks(values: .stride(by: .day)) { value in
    
        if let date : Date = value.as(Date.self) {
            Text(date, style: .date)
                .font(.footnote)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

JJJ*_*idt 6

这个俗气的解决方法似乎可以解决问题。我将图表放入 ZStack 中,并用 HStack 覆盖图表。HStack 包含一堆符合 Identificable 协议的不可见对象。不可见物体的数量、ID 和位置与图表数据相符。

由于 ZStack 视图现在包含可识别的元素,ScrollViewReader 按预期工作。

import SwiftUI
import Charts

struct ChartData: Identifiable {
    var day: Int
    var value: Int
    var id: String { "\(day)" }
}

struct ContentView: View {
    @State var chartData = [ChartData]()
    @State var scrollSpot = ""
    let items = 200
    let itemWidth: CGFloat = 30
    
    var body: some View {
        VStack {
            ScrollViewReader { scrollPosition in
                ScrollView(.horizontal) {
                    
                    // Create a ZStack with an HStack overlaying the chart.
                    // The HStack consists of invisible items that conform to the
                    // identifible protocol to provide positions for programmatic
                    // scrolling to the named location.
                    ZStack {
                        // Create an invisible rectangle for each x axis data point
                        // in the chart.
                        HStack(spacing: 0) {
                            ForEach(chartData) { item in
                                Rectangle()
                                    .fill(.clear)

                                    // Setting maxWidth to .infinity here, combined
                                    // with spacing:0 above, makes the rectangles
                                    // expand to fill the frame specified for the
                                    // chart below.
                                    .frame(maxWidth: .infinity, maxHeight: 0)

                                    // Here, set the rectangle's id to match the
                                    // charted data.
                                    .id(item.id)
                            }
                        }
                        
                        Chart(chartData) {
                            BarMark(x: .value("Day", $0.day),
                                    y: .value("Amount", $0.value),
                                    width: 20)
                        }
                        .frame(width: CGFloat(items) * itemWidth, height: 300)
                    }
                }
                .padding()
                .onChange(of: scrollSpot, perform: {x in
                    if (!x.isEmpty) {
                        scrollPosition.scrollTo(x)
                        scrollSpot = ""
                    }
                })
            }
            .onAppear(perform: populateChart)
        
            Button("Scroll") {
                if let x = chartData.last?.id {
                    print("Scrolling to item \(x)")
                    scrollSpot = x
                }
            }
            
            Spacer()
        }
    }

    func populateChart() {
        if !chartData.isEmpty { return }
        for i in 0..<items {
            chartData.append(ChartData(day: i, value: (i % 10) + 2))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

恕我直言,这应该可以在 SwiftUI 盒子之外工作。Apple 对初始化程序的注释是这么说的:creates a chart composed of a series of identifiable marks.所以...如果标记是可识别的,那么期望 ScrollViewReader 能够处理图表的标记并不夸张。

但是不!

人们希望这是 Apple 的疏忽,因为该框架是新的,并且他们将在即将发布的版本中公开图表标记的 id。

  • 将 ScrollView 与 Swift Charts 结合使用的一个缺点是,如果您未位于图表末尾,则垂直轴标签不可见。我通过隐藏垂直轴标签并将整个 VStack 嵌入到另一个 HStack 中,然后在 HStack 中手动创建垂直轴标签来解决这个问题。不太理想,因为标签的垂直间距是手工卷起的并且与图表断开。 (2认同)
  • 有趣的方法,我不会想到这一点。有一点是肯定的,框架应该支持此功能,并且不需要所有这些摆弄。 (2认同)