SwiftUI折线图RuleMark偏移及X轴HH:mm格式设置求助
SwiftUI折线图问题修复方案
问题1:RuleMark过度左右偏移修复
当前代码中proxy.value(at: location)会返回图表范围内任意位置的插值日期,包括数据点之外的区域,导致RuleMark可拖拽至数据范围外出现偏移。解决方案是匹配数据集内最接近的时间点,而非任意插值日期:
在DragGesture的onChanged中替换原有逻辑:
onChanged { value in let origin = geometry[proxy.plotAreaFrame].origin let location = CGPoint( x: value.location.x - origin.x, y: value.location.y - origin.y ) if let (date, _) = proxy.value(at: location, as: (Date, Double).self) { // 找到最接近当前位置的数据集时间点 let closestData = stockDatas.min { abs($0.timestamp.timeIntervalSince(date)) < abs($1.timestamp.timeIntervalSince(date)) } selectedDate = closestData?.timestamp selectedClose = closestData?.close } }
同时可通过限制X轴范围进一步避免偏移,为图表添加chartXScale:
.chartXScale(domain: stockDatas.map(\.timestamp).min()!...stockDatas.map(\.timestamp).max()!)
问题2:X轴日期格式设置为HH:mm且仅显示传入的时间数据
修改chartXAxis配置,指定轴标记仅使用数据中的时间点,并设置格式为小时分钟:
.chartXAxis { AxisMarks(values: stockDatas.map(\.timestamp)) { value in AxisGridLine() AxisTick() AxisLabel( content: { if let date = value.as(Date.self) { Text(date, format: .dateTime.hour().minute()) } }, position: .bottom ) } }
完整修改后的ContentView代码
struct ContentView: View { @State var stockDatas: [StockPriceData] = stockData @State private var selectedDate: Date? @State private var selectedClose: Double? var body: some View { ChartView .chartYScale(domain: stockDatas.map {$0.close}.min()!...stockDatas.map{$0.close}.max()!) .chartXScale(domain: stockDatas.map(\.timestamp).min()!...stockDatas.map(\.timestamp).max()!) .chartXAxis { AxisMarks(values: stockDatas.map(\.timestamp)) { value in AxisGridLine() AxisTick() AxisLabel( content: { if let date = value.as(Date.self) { Text(date, format: .dateTime.hour().minute()) } }, position: .bottom ) } } } private var ChartView: some View { Chart { ForEach(stockDatas) { data in LineMark(x: .value("time", data.timestamp), y: .value("price", data.close)) .foregroundStyle(Color.green) AreaMark(x: .value("time", data.timestamp), yStart: .value("Min", stockDatas.map{$0.close}.min()!), yEnd: .value("Max", data.close) ).foregroundStyle(LinearGradient(gradient: Gradient(colors: [Color.green, .clear]), startPoint: .top, endPoint: .bottom)).opacity(0.1) } if let selectedDate = selectedDate, let selectedClose = selectedClose { RuleMark(x: .value("Selected Date", selectedDate)) .foregroundStyle(.gray) .annotation(position: .top, alignment: .top) { VStack { Text(String(format: "Close: %.5f", selectedClose)) .font(.system(size: 12)) } .background(RoundedRectangle(cornerRadius: 5).fill(Color.gray)) .foregroundColor(Color.white) } } } .frame(width: UIScreen.main.bounds.width-23, height: 200) .chartOverlay { proxy in GeometryReader { geometry in Rectangle().fill(.clear).contentShape(Rectangle()) .gesture( DragGesture() .onChanged { value in let origin = geometry[proxy.plotAreaFrame].origin let location = CGPoint( x: value.location.x - origin.x, y: value.location.y - origin.y ) if let (date, _) = proxy.value(at: location, as: (Date, Double).self) { let closestData = stockDatas.min { abs($0.timestamp.timeIntervalSince(date)) < abs($1.timestamp.timeIntervalSince(date)) } selectedDate = closestData?.timestamp selectedClose = closestData?.close } } ) } } } }
额外说明
- RuleMark偏移修复:通过匹配数据集内的实际时间点,确保RuleMark仅在数据范围内的点上显示,避免过度偏移。
- X轴格式优化:指定
values为数据的时间数组,确保仅显示传入的时间点,同时通过format: .dateTime.hour().minute()将日期转为HH:mm格式。
内容的提问来源于stack exchange,提问作者user20166709
相关产品推荐
相关产品推荐

