Charts: puntos + labels de valor en todas las gráficas de líneas
- Nuevo ChartValueLabels.swift: ChartValueBubble (cápsula) y ChartSelectionCard
(tarjeta multi-serie), con umbral compartido (<=12 puntos → labels SIEMPRE
visibles; las gráficas cortas aportan mucho más con los valores a la vista)
- PointMark añadido donde faltaba (Compare, Year vs Year, Drawdown, Volatility);
agrandado donde ya existía (Evolution, Prediction, Period vs Period, Rolling)
- Selección por tap/arrastre (chartXSelection) en las 8 gráficas: RuleMark + burbuja
prominente (una serie) o tarjeta con el valor de cada serie (multi-serie)
- Evolution reutiliza su scrubbing existente y ahora muestra burbuja en el punto
- Period vs Period: labels A arriba / B abajo para evitar solapes
- Drawdown/Volatility migrados de Chart(data:) a Chart{ForEach} para poder añadir
marcas de selección
Verificado en iPad Pro 13" (labels visibles en Evolution y Period vs Period).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
This commit is contained in:
@@ -2,6 +2,14 @@ import SwiftUI
|
||||
import Charts
|
||||
|
||||
struct ComparisonChartView: View {
|
||||
@State private var selectedDate: Date?
|
||||
|
||||
private static let selectionDateFormatter: DateFormatter = {
|
||||
let f = DateFormatter()
|
||||
f.dateFormat = "MMM yyyy"
|
||||
return f
|
||||
}()
|
||||
|
||||
@ObservedObject var viewModel: ChartsViewModel
|
||||
|
||||
|
||||
@@ -102,6 +110,21 @@ struct ComparisonChartView: View {
|
||||
.frame(height: 260)
|
||||
}
|
||||
|
||||
/// Permanent labels only when the chart stays readable (few points AND few series).
|
||||
private var showAllLabels: Bool {
|
||||
let maxPoints = viewModel.comparisonData.map { $0.points.count }.max() ?? 0
|
||||
return maxPoints <= ChartLabels.alwaysShowThreshold && viewModel.comparisonData.count <= 3
|
||||
}
|
||||
|
||||
private func selectionRows(for date: Date) -> [(label: String, value: String, color: Color)] {
|
||||
viewModel.comparisonData.compactMap { series in
|
||||
guard let point = series.points.min(by: {
|
||||
abs($0.date.timeIntervalSince(date)) < abs($1.date.timeIntervalSince(date))
|
||||
}) else { return nil }
|
||||
return (series.name, yAxisLabel(for: point.value), Color(hex: series.colorHex) ?? .appPrimary)
|
||||
}
|
||||
}
|
||||
|
||||
private var lineChart: some View {
|
||||
VStack(spacing: 12) {
|
||||
Chart {
|
||||
@@ -115,9 +138,34 @@ struct ComparisonChartView: View {
|
||||
)
|
||||
.foregroundStyle(seriesColor)
|
||||
.interpolationMethod(.catmullRom)
|
||||
|
||||
PointMark(
|
||||
x: .value("Date", point.date),
|
||||
y: .value("Value", point.value)
|
||||
)
|
||||
.foregroundStyle(seriesColor)
|
||||
.symbolSize(showAllLabels ? 32 : 16)
|
||||
.annotation(position: .top, spacing: 2) {
|
||||
if showAllLabels {
|
||||
ChartValueBubble(text: yAxisLabel(for: point.value), color: seriesColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let selectedDate {
|
||||
RuleMark(x: .value("Selected", selectedDate))
|
||||
.foregroundStyle(Color.secondary.opacity(0.35))
|
||||
.lineStyle(StrokeStyle(lineWidth: 1, dash: [3, 3]))
|
||||
.annotation(position: .top, spacing: 4) {
|
||||
ChartSelectionCard(
|
||||
title: Self.selectionDateFormatter.string(from: selectedDate),
|
||||
rows: selectionRows(for: selectedDate)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.chartXSelection(value: $selectedDate)
|
||||
.chartXAxis {
|
||||
AxisMarks(values: .stride(by: .month, count: xAxisStride)) { value in
|
||||
AxisGridLine(stroke: StrokeStyle(lineWidth: 0.5, dash: [3, 3]))
|
||||
|
||||
Reference in New Issue
Block a user