Showing 1 changed files with 60 additions and 28 deletions
+60 -28
USB Meter/Views/Meter/Components/MeasurementChartView.swift
@@ -526,6 +526,7 @@ struct MeasurementChartView: View {
526 526
                                 availableTimeRange: availableTimeRange,
527 527
                                 selectorTint: selectorTint,
528 528
                                 compactLayout: compactLayout,
529
+                                xAxisLabelCount: xLabels,
529 530
                                 minimumSelectionSpan: minimumTimeSpan,
530 531
                                 configuration: resolvedRangeSelectorConfiguration(),
531 532
                                 selectedTimeRange: $selectedVisibleTimeRange,
@@ -2054,6 +2055,7 @@ private struct TimeRangeSelectorView: View {
2054 2055
     let availableTimeRange: ClosedRange<Date>
2055 2056
     let selectorTint: Color
2056 2057
     let compactLayout: Bool
2058
+    let xAxisLabelCount: Int
2057 2059
     let minimumSelectionSpan: TimeInterval
2058 2060
     let configuration: MeasurementChartRangeSelectorConfiguration
2059 2061
 
@@ -2072,25 +2074,25 @@ private struct TimeRangeSelectorView: View {
2072 2074
     }
2073 2075
 
2074 2076
     private var trackHeight: CGFloat {
2075
-        compactLayout ? 72 : 86
2077
+        Self.trackHeight(compactLayout: compactLayout)
2078
+    }
2079
+
2080
+    private static func trackHeight(compactLayout: Bool) -> CGFloat {
2081
+        compactLayout ? 42 : 50
2076 2082
     }
2077 2083
 
2078 2084
     static func recommendedReservedHeight(compactLayout: Bool) -> CGFloat {
2079 2085
         let rowHeight: CGFloat = compactLayout ? 28 : 32
2080
-        let trackHeight: CGFloat = compactLayout ? 72 : 86
2081
-        let boundaryHeight: CGFloat = compactLayout ? 16 : 18
2086
+        let trackHeight = Self.trackHeight(compactLayout: compactLayout)
2087
+        let axisLabelsHeight: CGFloat = compactLayout ? 18 : 20
2082 2088
         let spacing: CGFloat = compactLayout ? 6 : 8
2083
-        return rowHeight + spacing + rowHeight + spacing + trackHeight + spacing + boundaryHeight
2089
+        return rowHeight + spacing + rowHeight + spacing + trackHeight + spacing + axisLabelsHeight
2084 2090
     }
2085 2091
 
2086 2092
     private var cornerRadius: CGFloat {
2087 2093
         compactLayout ? 14 : 16
2088 2094
     }
2089 2095
 
2090
-    private var boundaryFont: Font {
2091
-        compactLayout ? .caption.weight(.semibold) : .footnote.weight(.semibold)
2092
-    }
2093
-
2094 2096
     private var symbolButtonSize: CGFloat {
2095 2097
         compactLayout ? 28 : 32
2096 2098
     }
@@ -2237,21 +2239,14 @@ private struct TimeRangeSelectorView: View {
2237 2239
                 .clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
2238 2240
                 .overlay(
2239 2241
                     RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
2240
-                        .stroke(Color.secondary.opacity(0.18), lineWidth: 1)
2242
+                        .stroke(Color.secondary.opacity(0.16), lineWidth: 1)
2241 2243
                 )
2242 2244
                 .contentShape(Rectangle())
2243 2245
                 .gesture(selectionGesture(totalWidth: geometry.size.width))
2244 2246
             }
2245 2247
             .frame(height: trackHeight)
2246 2248
 
2247
-            HStack {
2248
-                Text(boundaryLabel(for: availableTimeRange.lowerBound))
2249
-                Spacer(minLength: 0)
2250
-                Text(boundaryLabel(for: availableTimeRange.upperBound))
2251
-            }
2252
-            .font(boundaryFont)
2253
-            .foregroundColor(.secondary)
2254
-            .monospacedDigit()
2249
+            xAxisLabelsView
2255 2250
         }
2256 2251
     }
2257 2252
 
@@ -2730,18 +2725,55 @@ private struct TimeRangeSelectorView: View {
2730 2725
         return CGFloat(normalizedOffset) * width
2731 2726
     }
2732 2727
 
2733
-    private func boundaryLabel(for date: Date) -> String {
2734
-        date.format(as: boundaryDateFormat)
2735
-    }
2728
+    private var xAxisLabelsView: some View {
2729
+        let timeFormat: String = {
2730
+            switch context.size.width {
2731
+            case 0..<3600: return "HH:mm:ss"
2732
+            case 3600...86400: return "HH:mm"
2733
+            default: return "E HH:mm"
2734
+            }
2735
+        }()
2736 2736
 
2737
-    private var boundaryDateFormat: String {
2738
-        switch totalSpan {
2739
-        case 0..<86400:
2740
-            return "HH:mm"
2741
-        case 86400..<604800:
2742
-            return "MMM d HH:mm"
2743
-        default:
2744
-            return "MMM d"
2737
+        let labelCount = max(xAxisLabelCount, 2)
2738
+        let labels = (1...labelCount).map {
2739
+            Date(timeIntervalSince1970: context.xAxisLabel(for: $0, of: labelCount)).format(as: timeFormat)
2745 2740
         }
2741
+        let axisLabelFont: Font = compactLayout ? .caption2.weight(.semibold) : .footnote.weight(.semibold)
2742
+
2743
+        return GeometryReader { geometry in
2744
+            let labelWidth = max(geometry.size.width / CGFloat(max(labelCount - 1, 1)), 1)
2745
+
2746
+            ZStack(alignment: .topLeading) {
2747
+                Path { path in
2748
+                    for labelIndex in 1...labelCount {
2749
+                        let x = xPosition(for: labelIndex, totalLabels: labelCount, width: geometry.size.width)
2750
+                        path.move(to: CGPoint(x: x, y: 0))
2751
+                        path.addLine(to: CGPoint(x: x, y: 5))
2752
+                    }
2753
+                }
2754
+                .stroke(Color.secondary.opacity(0.22), lineWidth: 0.75)
2755
+
2756
+                ForEach(Array(labels.enumerated()), id: \.offset) { item in
2757
+                    let labelIndex = item.offset + 1
2758
+                    let centerX = xPosition(for: labelIndex, totalLabels: labelCount, width: geometry.size.width)
2759
+
2760
+                    Text(item.element)
2761
+                        .font(axisLabelFont)
2762
+                        .monospacedDigit()
2763
+                        .lineLimit(1)
2764
+                        .minimumScaleFactor(0.74)
2765
+                        .frame(width: labelWidth)
2766
+                        .position(
2767
+                            x: centerX,
2768
+                            y: geometry.size.height * 0.66
2769
+                        )
2770
+                }
2771
+            }
2772
+        }
2773
+        .frame(height: compactLayout ? 18 : 20)
2774
+    }
2775
+
2776
+    private func xPosition(for labelIndex: Int, totalLabels: Int, width: CGFloat) -> CGFloat {
2777
+        context.xGuidePosition(for: labelIndex, of: max(totalLabels, 2), width: width)
2746 2778
     }
2747 2779
 }