fix: reduce memory footprint — Canvas for dot grid, scope PreferenceKey

DotGridProgress replaced GeometryReader + 200+ Circle view nodes with a
single Canvas draw call. Canvas receives its own size parameter so no
GeometryReader is needed; SwiftUI interpolates the progress value through
the .animation modifier for smooth fill transitions.

FloatingTabState ScrollYKey made private to TabBarScrollModifier to limit
its PreferenceKey scope and prevent unintended propagation across unrelated
subtrees.
This commit is contained in:
kutesir
2026-05-29 13:38:52 +03:00
parent 875f562801
commit 98eaa9fb27

View File

@@ -1107,39 +1107,29 @@ struct DotGridProgress: View {
private let rows = 7 private let rows = 7
var body: some View { var body: some View {
GeometryReader { geo in Canvas { ctx, size in
let cols = max(1, Int((geo.size.width + gap) / (dotSize + gap))) let step = dotSize + gap
let cols = max(1, Int((size.width + gap) / step))
let total = cols * rows let total = cols * rows
let filled = Int(Double(total) * min(max(progress, 0), 1)) let filled = Int(Double(total) * min(max(progress, 0), 1))
VStack(alignment: .leading, spacing: gap) { for row in 0..<rows {
ForEach(0..<rows, id: \.self) { row in for col in 0..<cols {
DotRow(cols: cols, rowStart: row * cols, filled: filled, let idx = row * cols + col
dotSize: dotSize, gap: gap, cs: cs) let rect = CGRect(
x: CGFloat(col) * step,
y: CGFloat(row) * step,
width: dotSize, height: dotSize
)
ctx.fill(
Path(ellipseIn: rect),
with: .color(idx < filled ? AppColors.green : AppColors.green.opacity(0.12))
)
} }
} }
.animation(KisaniSpring.entrance, value: filled)
}
.frame(height: CGFloat(rows) * dotSize + CGFloat(rows - 1) * gap)
}
}
struct DotRow: View {
let cols: Int
let rowStart: Int
let filled: Int
let dotSize: CGFloat
let gap: CGFloat
let cs: ColorScheme
var body: some View {
HStack(spacing: gap) {
ForEach(0..<cols, id: \.self) { col in
let isFilled = (rowStart + col) < filled
Circle()
.fill(isFilled ? AppColors.green : AppColors.green.opacity(0.12))
.frame(width: dotSize, height: dotSize)
}
} }
.frame(maxWidth: .infinity)
.frame(height: CGFloat(rows) * (dotSize + gap) - gap)
.animation(KisaniSpring.entrance, value: progress)
} }
} }