@@ -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 |
} |