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:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user