diff --git a/CamperMonitor/Assets.xcassets/CaliforniaSide.imageset/CaliforniaSide.png b/CamperMonitor/Assets.xcassets/CaliforniaSide.imageset/CaliforniaSide.png new file mode 100644 index 0000000..57a56c2 Binary files /dev/null and b/CamperMonitor/Assets.xcassets/CaliforniaSide.imageset/CaliforniaSide.png differ diff --git a/CamperMonitor/Assets.xcassets/CaliforniaSide.imageset/Contents.json b/CamperMonitor/Assets.xcassets/CaliforniaSide.imageset/Contents.json new file mode 100644 index 0000000..ceefcab --- /dev/null +++ b/CamperMonitor/Assets.xcassets/CaliforniaSide.imageset/Contents.json @@ -0,0 +1,10 @@ +{ + "images" : [ + { + "filename" : "CaliforniaSide.png", + "idiom" : "universal" + } + ], + "info" : { "author" : "xcode", "version" : 1 }, + "properties" : { "template-rendering-intent" : "template" } +} diff --git a/CamperMonitor/Assets.xcassets/VehicleRear.imageset/Contents.json b/CamperMonitor/Assets.xcassets/VansterRear.imageset/Contents.json similarity index 82% rename from CamperMonitor/Assets.xcassets/VehicleRear.imageset/Contents.json rename to CamperMonitor/Assets.xcassets/VansterRear.imageset/Contents.json index a0f8834..7848505 100644 --- a/CamperMonitor/Assets.xcassets/VehicleRear.imageset/Contents.json +++ b/CamperMonitor/Assets.xcassets/VansterRear.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "VehicleRear.png", + "filename" : "VansterRear.png", "idiom" : "universal" } ], diff --git a/CamperMonitor/Assets.xcassets/VehicleRear.imageset/VehicleRear.png b/CamperMonitor/Assets.xcassets/VansterRear.imageset/VansterRear.png similarity index 100% rename from CamperMonitor/Assets.xcassets/VehicleRear.imageset/VehicleRear.png rename to CamperMonitor/Assets.xcassets/VansterRear.imageset/VansterRear.png diff --git a/CamperMonitor/Assets.xcassets/VehicleSide.imageset/Contents.json b/CamperMonitor/Assets.xcassets/VansterSide.imageset/Contents.json similarity index 82% rename from CamperMonitor/Assets.xcassets/VehicleSide.imageset/Contents.json rename to CamperMonitor/Assets.xcassets/VansterSide.imageset/Contents.json index 7e18a2d..dfeb5ba 100644 --- a/CamperMonitor/Assets.xcassets/VehicleSide.imageset/Contents.json +++ b/CamperMonitor/Assets.xcassets/VansterSide.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "VehicleSide.png", + "filename" : "VansterSide.png", "idiom" : "universal" } ], diff --git a/CamperMonitor/Assets.xcassets/VehicleSide.imageset/VehicleSide.png b/CamperMonitor/Assets.xcassets/VansterSide.imageset/VansterSide.png similarity index 100% rename from CamperMonitor/Assets.xcassets/VehicleSide.imageset/VehicleSide.png rename to CamperMonitor/Assets.xcassets/VansterSide.imageset/VansterSide.png diff --git a/CamperMonitor/Views/AlignmentAssistantView.swift b/CamperMonitor/Views/AlignmentAssistantView.swift index cfd1495..44563fc 100644 --- a/CamperMonitor/Views/AlignmentAssistantView.swift +++ b/CamperMonitor/Views/AlignmentAssistantView.swift @@ -35,7 +35,8 @@ struct AlignmentAssistantView: View { LevelBubble(pitch: state.pitch, roll: state.roll) .frame(maxWidth: 320) case .vehicle: - VehicleTiltView(pitch: state.pitch, roll: state.roll) + VehicleTiltView(pitch: state.pitch, roll: state.roll, + style: device.vehicleGraphicStyle) } if !isLive { disconnectedBanner } diff --git a/CamperMonitor/Views/LevelSetupView.swift b/CamperMonitor/Views/LevelSetupView.swift index 876e317..daf0c81 100644 --- a/CamperMonitor/Views/LevelSetupView.swift +++ b/CamperMonitor/Views/LevelSetupView.swift @@ -25,6 +25,8 @@ struct LevelSetupView: View { store.devices.first { $0.id == device.id } ?? device } + private let graphicColumns = [GridItem(.adaptive(minimum: 76), spacing: 12)] + var body: some View { List { Section { @@ -80,6 +82,44 @@ struct LevelSetupView: View { } footer: { Text(calibrationHint) } + + Section { + LazyVGrid(columns: graphicColumns, spacing: 12) { + ForEach(VehicleGraphicStyle.allCases) { style in + Button { + var updated = currentDevice + updated.vehicleGraphicStyle = style + store.update(updated) + } label: { + VStack(spacing: 4) { + Image(style.sideImageName) + .resizable() + .scaledToFit() + .padding(10) + .frame(width: 60, height: 60) + .background(currentDevice.vehicleGraphicStyle == style + ? Color.accentColor.opacity(0.18) + : Color.secondary.opacity(0.08), + in: .rect(cornerRadius: 12)) + .foregroundStyle(currentDevice.vehicleGraphicStyle == style + ? Color.accentColor : Color.primary) + Text(style.title) + .font(.caption2) + .foregroundStyle(currentDevice.vehicleGraphicStyle == style + ? Color.accentColor : Color.secondary) + } + } + .buttonStyle(.plain) + .accessibilityLabel(style.title) + } + } + .padding(.vertical, 4) + } header: { + Text("Fahrzeuggrafik") + } footer: { + Text("Wird in der Fahrzeug-Darstellung des Neigungsmessers verwendet. " + + "Weitere Grafiken folgen.") + } } .navigationTitle("Neigungsmesser einrichten") .navigationBarTitleDisplayMode(.inline) diff --git a/CamperMonitor/Views/LevelView.swift b/CamperMonitor/Views/LevelView.swift index 165030b..1444c37 100644 --- a/CamperMonitor/Views/LevelView.swift +++ b/CamperMonitor/Views/LevelView.swift @@ -127,7 +127,8 @@ struct LevelControls: View { LevelBubble(pitch: state.pitch, roll: state.roll) .frame(maxHeight: 220) case .vehicle: - VehicleTiltView(pitch: state.pitch, roll: state.roll) + VehicleTiltView(pitch: state.pitch, roll: state.roll, + style: device.vehicleGraphicStyle) } if let instruction = state.instruction { diff --git a/CamperMonitor/Views/VehicleTiltView.swift b/CamperMonitor/Views/VehicleTiltView.swift index f979ce7..a315d67 100644 --- a/CamperMonitor/Views/VehicleTiltView.swift +++ b/CamperMonitor/Views/VehicleTiltView.swift @@ -15,22 +15,23 @@ import SwiftUI struct VehicleTiltView: View { let pitch: Double? let roll: Double? + var style: VehicleGraphicStyle = .vanster var body: some View { VStack(spacing: 20) { - tiltPanel(image: "VehicleSide", + tiltPanel(image: style.sideImageName, angle: pitch, title: "Längs", lowerLabel: "Front", upperLabel: "Heck", - aspect: VehicleTilt.sideAspect) + aspect: style.sideAspect) - tiltPanel(image: "VehicleRear", + tiltPanel(image: style.rearImageName, angle: roll, title: "Quer", lowerLabel: "links", upperLabel: "rechts", - aspect: VehicleTilt.rearAspect) + aspect: style.rearAspect) // Ohne diesen Hinweis nähme man den Bildwinkel für den echten. Text(String(format: "Neigung %.0f-fach überhöht dargestellt – " diff --git a/CamperMonitorWatch/Assets.xcassets/VehicleRear.imageset/Contents.json b/CamperMonitorWatch/Assets.xcassets/VansterRear.imageset/Contents.json similarity index 82% rename from CamperMonitorWatch/Assets.xcassets/VehicleRear.imageset/Contents.json rename to CamperMonitorWatch/Assets.xcassets/VansterRear.imageset/Contents.json index a0f8834..7848505 100644 --- a/CamperMonitorWatch/Assets.xcassets/VehicleRear.imageset/Contents.json +++ b/CamperMonitorWatch/Assets.xcassets/VansterRear.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "VehicleRear.png", + "filename" : "VansterRear.png", "idiom" : "universal" } ], diff --git a/CamperMonitorWatch/Assets.xcassets/VehicleRear.imageset/VehicleRear.png b/CamperMonitorWatch/Assets.xcassets/VansterRear.imageset/VansterRear.png similarity index 100% rename from CamperMonitorWatch/Assets.xcassets/VehicleRear.imageset/VehicleRear.png rename to CamperMonitorWatch/Assets.xcassets/VansterRear.imageset/VansterRear.png diff --git a/CamperMonitorWatch/Assets.xcassets/VehicleSide.imageset/Contents.json b/CamperMonitorWatch/Assets.xcassets/VansterSide.imageset/Contents.json similarity index 82% rename from CamperMonitorWatch/Assets.xcassets/VehicleSide.imageset/Contents.json rename to CamperMonitorWatch/Assets.xcassets/VansterSide.imageset/Contents.json index 7e18a2d..dfeb5ba 100644 --- a/CamperMonitorWatch/Assets.xcassets/VehicleSide.imageset/Contents.json +++ b/CamperMonitorWatch/Assets.xcassets/VansterSide.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "VehicleSide.png", + "filename" : "VansterSide.png", "idiom" : "universal" } ], diff --git a/CamperMonitorWatch/Assets.xcassets/VehicleSide.imageset/VehicleSide.png b/CamperMonitorWatch/Assets.xcassets/VansterSide.imageset/VansterSide.png similarity index 100% rename from CamperMonitorWatch/Assets.xcassets/VehicleSide.imageset/VehicleSide.png rename to CamperMonitorWatch/Assets.xcassets/VansterSide.imageset/VansterSide.png diff --git a/CamperMonitorWatch/Views/WatchVehicleView.swift b/CamperMonitorWatch/Views/WatchVehicleView.swift index f0c4c4b..ce95873 100644 --- a/CamperMonitorWatch/Views/WatchVehicleView.swift +++ b/CamperMonitorWatch/Views/WatchVehicleView.swift @@ -14,9 +14,9 @@ struct WatchVehicleView: View { /// Die angeschriebenen Gradzahlen bleiben echt. var body: some View { VStack(spacing: 8) { - panel(image: "VehicleSide", angle: pitch, title: "Längs", + panel(image: "VansterSide", angle: pitch, title: "Längs", lower: "Front", upper: "Heck", aspect: VehicleTilt.sideAspect) - panel(image: "VehicleRear", angle: roll, title: "Quer", + panel(image: "VansterRear", angle: roll, title: "Quer", lower: "links", upper: "rechts", aspect: VehicleTilt.rearAspect) } } diff --git a/Shared/Models/ConfiguredDevice.swift b/Shared/Models/ConfiguredDevice.swift index d84579f..cb4a3e2 100644 --- a/Shared/Models/ConfiguredDevice.swift +++ b/Shared/Models/ConfiguredDevice.swift @@ -105,6 +105,8 @@ struct ConfiguredDevice: Identifiable, Codable, Hashable, Sendable { //var fridgeZoneMode: FridgeZoneMode = .automatic /// Nur für den Neigungsmesser: wie er im Fahrzeug sitzt. var sensorOrientation = SensorOrientation.identity + /// Nur für den Neigungsmesser: welche Fahrzeuggrafik die Fahrzeug-Ansicht zeigt. + var vehicleGraphicStyle = VehicleGraphicStyle.vanster init(id: UUID = UUID(), name: String, @@ -135,5 +137,7 @@ struct ConfiguredDevice: Identifiable, Codable, Hashable, Sendable { // ?? .automatic sensorOrientation = try container.decodeIfPresent(SensorOrientation.self, forKey: .sensorOrientation) ?? .identity + vehicleGraphicStyle = try container.decodeIfPresent(VehicleGraphicStyle.self, + forKey: .vehicleGraphicStyle) ?? .vanster } } diff --git a/Shared/Models/VehicleGraphicStyle.swift b/Shared/Models/VehicleGraphicStyle.swift new file mode 100644 index 0000000..571387d --- /dev/null +++ b/Shared/Models/VehicleGraphicStyle.swift @@ -0,0 +1,52 @@ +import Foundation + +/// Welche Silhouette die Fahrzeug-Ansicht des Neigungsmessers zeigt. +/// +/// Jeder Fall ist ein zusammengehöriges Bilderset aus Seiten- und +/// Heckansicht in Assets.xcassets, beide als Vorlage ("template") +/// exportiert, damit `foregroundStyle` sie einfärben kann. Im Picker wird +/// nur die Seitenansicht gezeigt. +enum VehicleGraphicStyle: String, CaseIterable, Identifiable, Codable, Sendable { + case vanster + case california + + var id: String { rawValue } + + var title: String { + switch self { + case .vanster: return "Vanster" + case .california: return "California" + } + } + + var sideImageName: String { + switch self { + case .vanster: return "VansterSide" + case .california: return "CaliforniaSide" + } + } + + /// Für California liegt noch keine eigene Heckansicht vor, deshalb + /// vorerst die von Vanster. + var rearImageName: String { + switch self { + case .vanster, .california: return "VansterRear" + } + } + + /// Seitenverhältnis der jeweiligen Bilder – ohne das würde eine Grafik + /// mit anderen Proportionen in der gemeinsamen Bildhülle verzerrt oder + /// ungewollt verkleinert erscheinen. + var sideAspect: Double { + switch self { + case .vanster: return VehicleTilt.sideAspect + case .california: return 1 + } + } + + var rearAspect: Double { + switch self { + case .vanster, .california: return VehicleTilt.rearAspect + } + } +} diff --git a/Tools/README.md b/Tools/README.md index 52ef1c9..6115c0f 100644 --- a/Tools/README.md +++ b/Tools/README.md @@ -32,5 +32,11 @@ gefüllte Form, in der die Details erhalten bleiben. ```bash swiftc -O -o /tmp/make-vehicle-art Tools/make-vehicle-art.swift sips --resampleHeight 600 heckansicht.png --out /tmp/rear.png -/tmp/make-vehicle-art /tmp/rear.png CamperMonitor/Assets.xcassets/VehicleRear.imageset/VehicleRear.png +/tmp/make-vehicle-art /tmp/rear.png CamperMonitor/Assets.xcassets/VansterRear.imageset/VansterRear.png ``` + +Jedes Fahrzeug-Grafikset (siehe `VehicleGraphicStyle`) hat sein eigenes +Bildpaar `Side`/`Rear`. Hat die Vorlage schon einen +transparenten Hintergrund, geht die Quelldatei direkt ins Skript. Liegt sie +auf weißem Grund, muss der Rand vorher weg – sonst wird die ganze Fläche +mitgefärbt.