【问题标题】:SwiftUI Scrollable Charts in IOS16IOS16 中的 SwiftUI 可滚动图表
【发布时间】:2022-12-23 09:18:11
【问题描述】:

使用新的 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)
        }
    }
}

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

我试过使用 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)
        }
    }
}

【问题讨论】:

    标签: swiftui charts ios16 swiftui-charts


    【解决方案1】:

    这种俗气的解决方法似乎可以解决问题。我将图表放在 ZStack 中,并用 HStack 覆盖图表。 HStack 包含一堆符合 Identifiable 协议的不可见对象。不可见对象的数量、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.indices, id: .self) { i 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(chartData[i].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))
            }
        }
    }
    

    恕我直言,这应该可以在 SwiftUI 框外工作。 Apple 的初始值设定项的 cmets 说它 creates a chart composed of a series of identifiable marks. 所以......如果标记是可识别的,那么期望 ScrollViewReader 与图表的标记一起工作并不是一件容易的事。

    但是不!

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多