如何使用 Swift Charts 在可滚动折线图中始终显示自然周/月?

问题描述 投票:0回答:1

我正在使用 Swift Charts 构建折线图来绘制健康数据。 下面是我的代码,其中包含一些用于生成演示的模拟数据:

import SwiftUI
import Charts

struct Demo: View {
  struct HR: Identifiable {
    
    let day: Date
    let value: Int
    
    var id: Date {
      day
    }
  }
  
  let mockData: [HR]
  
  init() {
    func generateMockData() -> [HR] {
      let calendar = Calendar.current
      let today = calendar.startOfDay(for: Date())
      
      var data: [HR] = []
      
      for i in 0...27 {
        let day = calendar.date(byAdding: .day, value: -i, to: today)
        let value = Int.random(in: 50...100)
        let hr: HR = .init(day: day!, value: value)
        data.append(hr)
      }
      
      return data
    }
    let calendar = Calendar.current
    let today = calendar.startOfDay(for: Date())
    
    self.mockData = generateMockData()
    let beginningOfInterval = today.addingTimeInterval(-1 * 7 * 24 * 3600)
    self._scrollPosition = State(initialValue: beginningOfInterval.timeIntervalSince1970)
  }
  
  @State private var scrollPosition: TimeInterval = TimeInterval()
  
  
  var body: some View {
      Chart(mockData) { hr in
        LineMark(
          x: .value("Day", hr.day, unit: .day),
          y: .value("Value", hr.value)
        )
        .symbol(.circle)
      }
      .chartXAxis{
        AxisMarks(values: .stride(by: .day)) { _ in
          AxisValueLabel(
            format: .dateTime.weekday(),
            centered: true
          )
          AxisGridLine()
        }
      }
      .chartScrollableAxes(.horizontal)
      .chartXVisibleDomain(length: 3600 * 24 * 7)
      .chartScrollTargetBehavior(
        .valueAligned(
          matching: .init(hour: 0),
          majorAlignment: .matching(.init(weekday: 1)),
          limitBehavior: .never
        )
      )
      .scrollTargetBehavior(.viewAligned)
      .chartScrollPosition(x: $scrollPosition)
      .aspectRatio(1, contentMode: .fit)
      .padding()
  }
}

#Preview {
    Demo()
}

如您所见,我已使图表可滚动,并在停止滚动时捕捉到星期日。

问题是,当最后一天不是星期六时,我无法滚动图表以在可见区域显示自然周,最新值总是在图表的右边缘附近:

在Apple的健康应用程序中,您始终可以滚动以将当前的自然周显示在可见区域中,并且今天之后的日子将由空槽表示:

每月观看次数相同:

另一个有趣的细节是,在健康应用程序中,图表可以进一步推动(按住触摸)以显示代表更多未来日子的空间,并且网格线似乎无限延伸。

在我的代码中,进一步按住会显示空白区域:

我想知道是否可以使用 Swift Charts 实现与 Health App 相同的功能,特别是显示当前自然周/月?

swiftui swiftui-charts
1个回答
0
投票

找到部分解决方案:

我使用

.chartXScale(domain:)
设置开始和结束日期,尽管它们最初可能位于可见区域之外。

现在代码如下所示:

import SwiftUI
import Charts

struct Demo: View {
  struct HR: Identifiable {
    
    let day: Date
    let value: Int
    
    var id: Date {
      day
    }
  }
  
  let mockData: [HR]
  
  init() {
    func generateMockData() -> [HR] {
      let calendar = Calendar.current
      let today = calendar.startOfDay(for: Date())
      
      var data: [HR] = []
      
      for i in 0...27 {
        let day = calendar.date(byAdding: .day, value: -i, to: today)
        let value = Int.random(in: 50...100)
        let hr: HR = .init(day: day!, value: value)
        data.append(hr)
      }
      
      return data
    }
    
    self.mockData = generateMockData()
  }
  
  @State private var scrollPosition: TimeInterval = TimeInterval()
  
  
  var body: some View {
    let calendar = Calendar.current
    let today = calendar.startOfDay(for: Date())
    let weekday = calendar.component(.weekday, from: today)
    let daysToSubtract = weekday - calendar.firstWeekday
    let startOfWeek = calendar.date(byAdding: .day, value: -daysToSubtract, to: today)
    let start = calendar.date(byAdding: .day, value: -30, to: today)
    let end = calendar.date(byAdding: .day, value: 7, to: today)
    
      Chart(mockData) { hr in
        LineMark(
          x: .value("Day", hr.day, unit: .day),
          y: .value("Value", hr.value)
        )
        .symbol(.circle)
      }
      .chartXAxis{
        AxisMarks(preset: .aligned, values: .stride(by: .day)) { _ in
          AxisValueLabel(
            
            format: .dateTime.weekday(),
            centered: true
          )
          AxisGridLine()
        }
      }
      .chartScrollableAxes(.horizontal)
      .chartXVisibleDomain(length: 3600 * 24 * 7)
      .chartXScale(domain: start!...end!)
      .chartScrollTargetBehavior(
        .valueAligned(
          matching: .init(weekday: 1),
          majorAlignment: .matching(.init(weekday: 1)),
          limitBehavior: .always
        )
      )
      .chartScrollPosition(initialX: startOfWeek!)
      .chartScrollPosition(x: $scrollPosition)
      .aspectRatio(1, contentMode: .fit)
      .padding()
  }
}

#Preview {
    Demo()
}

我可以使用

.chartScrollPosition(initialX:)
来设置图表的初始位置。

现在剩下的问题是,在图表末尾按住不放,如何在未来几天显示无限网格线。

© www.soinside.com 2019 - 2024. All rights reserved.