You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

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

嘿,我懂你想要的效果——让可滚动的Swift Charts折线图始终对齐自然周或自然月的边界,不会出现滚动到一半就显示半截周/月的情况对吧?结合你给出的代码片段,我给你梳理一套可行的实现方案:

核心思路

要实现这个效果,关键在于强制图表的X轴范围始终是完整的自然周/月区间,而不是跟着滚动随意截断。我们需要结合日历计算出对应区间,再绑定滚动状态来动态更新这个范围。

具体实现步骤

1. 补全基础代码并添加滚动状态

首先把你未完成的代码补全,加上跟踪滚动位置的状态变量:

import SwiftUI
import Charts

struct Demo: View {
    struct HR: Identifiable {
        let day: Date
        let value: Int
        var id: Date { day }
    }
    
    let mockData: [HR]
    @State private var scrollPosition: Date? // 跟踪滚动位置
    
    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)
                data.append(HR(day: day, value: value))
            }
            return data
        }
        self.mockData = generateMockData()
    }

2. 编写自然周/月区间计算逻辑

根据你的需求,编写计算完整自然周或自然月范围的方法,这里以自然周(周一为起始)为例:

// 计算当前应显示的自然周范围(周一到周日)
    private var currentWeekRange: ClosedRange<Date> {
        let calendar = Calendar.current
        // 用滚动位置或默认数据的第一个日期作为参考
        let referenceDate = scrollPosition ?? mockData.first?.day ?? Date()
        
        // 获取参考日期所在周的起始日(周一)
        guard let weekStart = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: referenceDate)) else {
            return Date()...Date()
        }
        // 计算周的结束日(周日)
        let weekEnd = calendar.date(byAdding: .day, value: 6, to: weekStart)!
        return weekStart...weekEnd
    }
    
    // 如果需要自然月范围,用这个方法
    private var currentMonthRange: ClosedRange<Date> {
        let calendar = Calendar.current
        let referenceDate = scrollPosition ?? mockData.first?.day ?? Date()
        
        // 获取当月第一天
        guard let monthStart = calendar.date(from: calendar.dateComponents([.year, .month], from: referenceDate)) else {
            return Date()...Date()
        }
        // 获取下月第一天再减一秒,得到当月最后一刻
        guard let nextMonthStart = calendar.date(byAdding: .month, value: 1, to: monthStart) else {
            return Date()...Date()
        }
        let monthEnd = nextMonthStart.addingTimeInterval(-1)
        return monthStart...monthEnd
    }

3. 绑定图表的X轴范围与滚动状态

在Chart视图中,强制X轴使用我们计算好的自然周/月范围,同时绑定滚动位置来动态更新:

var body: some View {
        Chart(mockData) { hr in
            LineMark(
                x: .value("Day", hr.day),
                y: .value("Heart Rate", hr.value)
            )
        }
        .chartXScale(domain: currentWeekRange) // 切换成currentMonthRange就是自然月模式
        .chartScrollableAxes(.horizontal)
        .chartScrollPosition(x: $scrollPosition)
        .chartXAxis {
            AxisMarks(position: .bottom) { value in
                AxisGridLine()
                AxisTick()
                AxisLabel(format: .dateTime.day().month(), centered: true)
            }
        }
        .padding()
    }
}

额外优化建议

  • 如果想要分页式滚动(滚动一次切换一个完整周/月),可以添加onChange(of: scrollPosition)监听,当滚动位置超出当前区间的一半时,自动切换到下一个/上一个周/月区间。
  • 不同地区的自然周起始日不同(比如有些地区以周日为一周开始),可以显式设置calendar.firstWeekday = 1(1代表周日,2代表周一)来适配需求。

备注:内容来源于stack exchange,提问作者Calvin

火山引擎 最新活动