Minimize Dynamic Island, show direction letters instead of signs, fix inverted pitch bubble

- Dynamic Island now shows only the level icon (compact/minimal); the
  bubble, instruction text, and numbers move to the shared lock
  screen/CarPlay ActivityView, which iOS renders identically on both
  surfaces (no separate CarPlay layout API exists for this).
- Replace +/- signs with a leading direction letter (H/F for Heck-
  Front, R/L for rechts-links) via a new shared LevelDirectionFormatting
  helper, used by the level screen, alignment assistant, and dashboard
  card. The generic "Messwerte" list keeps raw signed values.
- Live Activity's instruction now reads as two lines ("Heck steht
  höher 1.8°" / "links steht höher 0.9°") instead of a sentence plus a
  separate number column.
- Fix the bubble level graphic's pitch axis being inverted (front/rear
  reversed) in all three copies (app, watch, Live Activity widget) —
  confirmed against the known-correct VehicleTiltView orientation.
- Dashboard card shows pitch and roll side by side, large, without the
  "Längsneigung"/"Querneigung" labels, for the leveling device.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
fototeddy
2026-09-05 01:03:37 +02:00
co-authored by Claude Sonnet 5
parent 9a3b5cb472
commit 4a8a40eea2
6 changed files with 107 additions and 58 deletions
@@ -0,0 +1,30 @@
import Foundation
/// Formatiert Neigungswerte mit vorangestelltem Richtungsbuchstaben statt
/// Vorzeichen "H 1.8°" statt "1.8°", "L 0.9°" statt "-0.9°".
///
/// Gemeinsam für App und Live-Activity-Widget, deshalb hier statt in
/// `Shared/`, das die Extension nicht mitkompiliert (siehe
/// `LevelActivityAttributes.swift`).
enum LevelDirectionFormatting {
/// "1.8°" Betrag ohne Vorzeichen, für Stellen, die die Richtung schon
/// im Wort oder Buchstaben ausdrücken.
static func magnitude(_ value: Double?) -> String {
guard let value else { return "" }
return String(format: "%.1f°", abs(value))
}
/// Längsneigung als Kachel: positiv (Heck höher) "H", negativ (Front
/// höher) "F". Muss zum Vorzeichen von `LevelState.pitch` passen.
static func pitchTile(_ value: Double?) -> String {
guard let value else { return "" }
return "\(value >= 0 ? "H" : "F") \(magnitude(value))"
}
/// Querneigung als Kachel: positiv (rechts höher) "R", negativ (links
/// höher) "L". Muss zum Vorzeichen von `LevelState.roll` passen.
static func rollTile(_ value: Double?) -> String {
guard let value else { return "" }
return "\(value >= 0 ? "R" : "L") \(magnitude(value))"
}
}