Improvements for iPad and Mac Catalyst

This commit is contained in:
Jake-B 2024-12-13 15:54:25 -05:00
parent 76dfc9647e
commit 4e57daaf8f
5 changed files with 85 additions and 58 deletions

View file

@ -132,9 +132,6 @@
},
"%@ dB" : {
},
"%@ hPa" : {
},
"%@, %@" : {
"localizations" : {
@ -6956,6 +6953,9 @@
},
"Documentation" : {
},
"Done" : {
},
"Double Tap as Button" : {

View file

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

View file

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

View file

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

View file

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