iPhone Duo (fase 1): navegación adaptativa por size class y layouts regular×regular

- ContentView: un único TabView (sidebarAdaptable en iOS 18+) para todas las
  size classes; desaparece el cambio de jerarquía iPhone/iPad, así abrir o
  cerrar el Duo conserva el estado de cada pestaña. @SceneStorage de la tab.
- Fuentes y Diario: NavigationSplitView (lista + detalle a la vez en ancho
  regular, stack colapsable en compacto) con selección nativa de List.
- Dashboard: Quick Update como .inspector (panel lateral junto a los gráficos
  en regular, sheet en compacto).
- Gráficos: chartHeightScale (+35% de alto en regular) vía chartFrame(height:),
  sin ignoresSafeArea en contenido interactivo.
- Liquid Glass (glassEffect) en las etiquetas flotantes del Diario en iOS 26+.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F1u4K16xy7eQVtgsYNZ9Vn
This commit is contained in:
alexandrev-tibco
2026-09-16 11:44:50 +02:00
parent a8a9ad64f3
commit 680255f69d
17 changed files with 485 additions and 547 deletions
@@ -57,14 +57,14 @@ struct AllocationPieChart: View {
VStack(alignment: .leading, spacing: 0) {
Text(item.category)
.font(.subheadline)
.foregroundColor(.primary)
.foregroundStyle(.primary)
let percentage = total > 0
? NSDecimalNumber(decimal: item.value / total).doubleValue * 100
: 0
Text(String(format: "%.1f%%", percentage))
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
.opacity(selectedSlice == nil || selectedSlice == item.category ? 1 : 0.5)
@@ -77,7 +77,7 @@ struct AllocationPieChart: View {
} label: {
Image(systemName: "chevron.right.circle.fill")
.font(.footnote)
.foregroundColor(.secondary.opacity(0.6))
.foregroundStyle(.secondary.opacity(0.6))
}
.buttonStyle(.plain)
}
@@ -92,14 +92,14 @@ struct AllocationPieChart: View {
}
} else {
Text("No allocation data available")
.foregroundColor(.secondary)
.frame(height: 200)
.foregroundStyle(.secondary)
.chartFrame(height: 200)
.frame(maxWidth: .infinity)
}
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
}
@@ -167,7 +167,7 @@ struct SemicircleAllocation: View {
y: center.y + radius * CoreGraphics.sin(angle))
Text("\(Int((frac * 100).rounded()))%")
.font(.system(size: lineWidth * 0.42, weight: .bold))
.foregroundColor(.white)
.foregroundStyle(.white)
.position(pos)
.opacity(selectedSlice == nil || selectedSlice == seg.category ? 1 : 0.4)
}
@@ -178,14 +178,14 @@ struct SemicircleAllocation: View {
VStack(spacing: 1) {
Text(c.label)
.font(.caption2)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
.lineLimit(1)
Text(c.value)
.font(.headline)
if let sub = c.sub {
Text(sub)
.font(.caption2)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
}
}
.position(x: center.x, y: center.y - lineWidth * 0.25)
@@ -243,7 +243,7 @@ struct AllocationTargetsComparisonChart: View {
if targetData.allSatisfy({ $0.target == 0 }) {
Text("Set allocation targets to compare your portfolio against your plan.")
.font(.subheadline)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
} else {
Chart {
ForEach(targetData, id: \.category) { item in
@@ -275,7 +275,7 @@ struct AllocationTargetsComparisonChart: View {
AxisValueLabel()
}
}
.frame(height: 220)
.chartFrame(height: 220)
ForEach(targetData, id: \.category) { item in
let drift = item.actual - item.target
@@ -289,20 +289,20 @@ struct AllocationTargetsComparisonChart: View {
Spacer()
Text("Actual \(String(format: "%.1f%%", item.actual))")
.font(.caption2)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text("Target \(String(format: "%.0f%%", item.target))")
.font(.caption2)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
Text("\(prefix)\(String(format: "%.1f%%", drift))")
.font(.caption2.weight(.semibold))
.foregroundColor(drift >= 0 ? .positiveGreen : .negativeRed)
.foregroundStyle(drift >= 0 ? Color.positiveGreen : Color.negativeRed)
}
}
}
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
@@ -351,7 +351,7 @@ struct AllocationListView: View {
Text(item.value.compactCurrencyString)
.font(.caption)
.foregroundColor(.secondary)
.foregroundStyle(.secondary)
.frame(width: 70, alignment: .trailing)
}
@@ -365,12 +365,12 @@ struct AllocationListView: View {
Rectangle()
.fill(Color.gray.opacity(0.1))
.frame(height: 6)
.cornerRadius(3)
.clipShape(RoundedRectangle(cornerRadius: 3))
Rectangle()
.fill(Color(hex: item.color) ?? .gray)
.frame(width: geometry.size.width * percentage, height: 6)
.cornerRadius(3)
.clipShape(RoundedRectangle(cornerRadius: 3))
}
}
.frame(height: 6)
@@ -379,7 +379,7 @@ struct AllocationListView: View {
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(AppConstants.UI.cornerRadius)
.clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius))
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
}