From 4e57daaf8f145f2eadc420c828c54c1ee9aea643 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Fri, 13 Dec 2024 15:54:25 -0500 Subject: [PATCH] Improvements for iPad and Mac Catalyst --- Localizable.xcstrings | 6 +- .../MetricsColumnList.swift | 6 +- .../Views/Nodes/EnvironmentMetricsLog.swift | 25 +++--- .../EnvironmentDefaultColumns.swift | 21 +++-- .../Metrics Columns/MetricsColumnDetail.swift | 85 ++++++++++++------- 5 files changed, 85 insertions(+), 58 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index e8957a07..6f513769 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -132,9 +132,6 @@ }, "%@ dB" : { - }, - "%@ hPa" : { - }, "%@, %@" : { "localizations" : { @@ -6956,6 +6953,9 @@ }, "Documentation" : { + }, + "Done" : { + }, "Double Tap as Button" : { diff --git a/Meshtastic/Model/Metrics Visualization/MetricsColumnList.swift b/Meshtastic/Model/Metrics Visualization/MetricsColumnList.swift index cd843d6d..0476b6b8 100644 --- a/Meshtastic/Model/Metrics Visualization/MetricsColumnList.swift +++ b/Meshtastic/Model/Metrics Visualization/MetricsColumnList.swift @@ -42,7 +42,11 @@ class MetricsColumnList: ObservableObject, RandomAccessCollection, RangeReplacea } return returnValues } - + + func column(forAttribute attribute: String) -> MetricsTableColumn? { + return columns.first(where: { $0.attribute == attribute}) + } + // Collection conformance typealias Index = Int typealias Element = MetricsTableColumn diff --git a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift index af626a64..3b88d0bd 100644 --- a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift +++ b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift @@ -49,36 +49,32 @@ struct EnvironmentMetricsLog: View { .chartLegend(position: .automatic, alignment: .bottom) } } - let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current) - let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "") + + // Dynamic table column using SwiftUI Table requires TableColumnForEach which requires the target + // to be bumped to 17.4 -- Until that happens, the existing non-configurable table is used. if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { // Add a table for mac and ipad Table(environmentMetrics) { TableColumn("Temperature") { em in - Text(em.temperature.formattedTemperature()) + columnList.column(forAttribute: "temperature")?.body(em) } TableColumn("Humidity") { em in - Text("\(String(format: "%.0f", em.relativeHumidity))%") + columnList.column(forAttribute: "relativeHumidity")?.body(em) } TableColumn("Barometric Pressure") { em in - Text("\(String(format: "%.1f", em.barometricPressure)) hPa") + columnList.column(forAttribute: "barometricPressure")?.body(em) } TableColumn("Indoor Air Quality") { em in - HStack { - Text("IAQ") - IndoorAirQuality(iaq: Int(em.iaq), displayMode: IaqDisplayMode.dot ) - } + columnList.column(forAttribute: "iaq")?.body(em) } TableColumn("Wind Speed") { em in - let windSpeed = Measurement(value: Double(em.windSpeed), unit: UnitSpeed.kilometersPerHour) - Text(windSpeed.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0))))) + columnList.column(forAttribute: "windSpeed")?.body(em) } TableColumn("Wind Direction") { em in - let direction = cardinalValue(from: Double(em.windDirection)) - Text(direction) + columnList.column(forAttribute: "windDirection")?.body(em) } TableColumn("timestamp") { em in - Text(em.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized) + columnList.column(forAttribute: "time")?.body(em) } .width(min: 180) } @@ -96,6 +92,7 @@ struct EnvironmentMetricsLog: View { GridRow { ForEach(columnList.visible) { col in col.body(em) + .font(.caption) } } } diff --git a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift index 0821330f..4f1e2b5f 100644 --- a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift +++ b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift @@ -21,7 +21,6 @@ extension MetricsColumnList { minWidth: 25, maxWidth: 40, tableBody: { _, temp in Text(temp.formattedTemperature()) - .font(.caption) }), // Relative Humidity Series Configuration @@ -32,7 +31,6 @@ extension MetricsColumnList { minWidth: 25, maxWidth: 40, tableBody: { _, humidity in Text("\(String(format: "%.0f", humidity))%") - .font(.caption) }), // Barometric Pressure Series Configuration @@ -42,8 +40,11 @@ extension MetricsColumnList { abbreviatedName: "Bar", minWidth: 30, maxWidth: 50, tableBody: { _, pressure in - Text("\(String(format: "%.1f", pressure))") - .font(.caption) + if (UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac) { + Text("\(String(format: "%.1f hPa", pressure))") + } else { + Text("\(String(format: "%.1f", pressure))") + } }), // Indoor Air Quality Series Configuration @@ -54,7 +55,6 @@ extension MetricsColumnList { minWidth: 25, maxWidth: 50, tableBody: { _, iaq in IndoorAirQuality(iaq: Int(iaq), displayMode: .dot) - .font(.caption) }), // Wind Direction Series Configuration @@ -70,9 +70,14 @@ extension MetricsColumnList { let wind = Double(wind) Image(systemName: "location.north") .imageScale(.small) + .scaleEffect(0.9, anchor: .center) .rotationEffect(.degrees(wind)) - Text(abbreviatedCardinalValue(from: wind)) - .font(.caption) + .foregroundStyle(.blue) + if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { + Text(cardinalValue(from: wind)) + } else { + Text(abbreviatedCardinalValue(from: wind)) + } } }), @@ -93,7 +98,6 @@ extension MetricsColumnList { numberFormatStyle: .number.precision( .fractionLength(0)))) ) - .font(.caption) }), // Timestamp Series Configuration -- for use in table only @@ -113,7 +117,6 @@ extension MetricsColumnList { time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized ) - .font(.caption) }) ]) } diff --git a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/MetricsColumnDetail.swift b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/MetricsColumnDetail.swift index a2bcd9f8..b99bb37f 100644 --- a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/MetricsColumnDetail.swift +++ b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/MetricsColumnDetail.swift @@ -13,46 +13,69 @@ struct MetricsColumnDetail: View { @State private var currentDetent = PresentationDetent.medium + @Environment(\.dismiss) private var dismiss + var body: some View { - List { - Section("Chart") { - ForEach(seriesList) { series in - HStack { - Circle() - .fill(series.foregroundStyle(0.0...100.0) ?? AnyShapeStyle(.clear)) - .frame(width: 20.0, height: 20.0) - Text(series.name) - Spacer() - if series.visible { - Image(systemName: "checkmark") - .foregroundColor(.blue) - } - }.contentShape(Rectangle()) // Ensures the entire row is tappable - .onTapGesture { - seriesList.toggleVisibity(for: series) + ZStack { + List { + Section("Chart") { + ForEach(seriesList) { series in + HStack { + Circle() + .fill(series.foregroundStyle(0.0...100.0) ?? AnyShapeStyle(.clear)) + .frame(width: 20.0, height: 20.0) + Text(series.name) + Spacer() + if series.visible { + Image(systemName: "checkmark") + .foregroundColor(.blue) + } + }.contentShape(Rectangle()) // Ensures the entire row is tappable + .onTapGesture { + seriesList.toggleVisibity(for: series) + } + } + } + // Dynamic table column using SwiftUI Table requires TableColumnForEach which requires the target + // to be bumped to 17.4 -- Until that happens, the existing non-configurable table is used. + if !(UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac) { + Section("Table") { + ForEach(columnList.columns) { column in + HStack { + Text(column.name) + Spacer() + if column.visible { + Image(systemName: "checkmark") + .foregroundColor(.blue) + } + }.contentShape(Rectangle()) // Ensures the entire row is tappable + .onTapGesture { + columnList.objectWillChange.send() + columnList.toggleVisibity(for: column) + } } + } } } - Section("Table") { - ForEach(columnList.columns) { column in - HStack { - Text(column.name) - Spacer() - if column.visible { - Image(systemName: "checkmark") - .foregroundColor(.blue) - } - }.contentShape(Rectangle()) // Ensures the entire row is tappable - .onTapGesture { - columnList.objectWillChange.send() - columnList.toggleVisibity(for: column) - } + + // More friendly to tap a button to dismiss on these devices + if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { + Spacer() + Button { + self.dismiss() + } label: { + Text("Done") } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding([.leading, .trailing, .bottom]) } } .presentationDetents([.medium, .large], selection: $currentDetent) .presentationContentInteraction(.scrolls) - .presentationDragIndicator(.visible) + .presentationDragIndicator( + !(UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac) ? .visible : .hidden) .presentationBackgroundInteraction(.enabled(upThrough: .medium)) .interactiveDismissDisabled(false) }