Die Bedienung mit Zeiger und Tastatur hing an `#if targetEnvironment(macCatalyst)`. Das ist in dem Bau, der tatsächlich benutzt wird, **falsch**: „für iPad angepasst" ist buchstäblich der iOS-Bau, der auf Apple Silicon startet, und dort ist die Abfrage nicht erfüllt. Klick-zum-Anhalten, Leertaste und die Pfeiltasten waren damit genau dort abgeschaltet, wo sie gebraucht werden. Jetzt beantwortet `FlixyPlatform.isMac` die Frage zur Laufzeit und kennt beide Wege. Die Ansicht bleibt dabei eine einzige: `focusable(_:)` nimmt den Schalter entgegen, die Tastenhandler geben sonst `.ignored` zurück. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
150 lines
4.6 KiB
Swift
150 lines
4.6 KiB
Swift
import SwiftUI
|
|
#if canImport(UIKit)
|
|
import UIKit
|
|
#else
|
|
import AppKit
|
|
#endif
|
|
|
|
/// Was auf iPhone und Mac verschieden heißt — an einer Stelle.
|
|
///
|
|
/// Nicht jede Ansicht soll `#if os(iOS)` tragen. Ein Formular, das auf beiden
|
|
/// Plattformen dasselbe meint, soll auf beiden gleich aussehen; wo die
|
|
/// Systeme verschiedene Namen dafür haben, steht der Unterschied hier und
|
|
/// nirgends sonst. Sonst wandert er in zwanzig Dateien und wird beim
|
|
/// nächsten Bildschirm vergessen.
|
|
/// Auf was für einem Gerät läuft das hier gerade?
|
|
public enum FlixyPlatform {
|
|
/// Läuft die App auf einem Mac?
|
|
///
|
|
/// **Zwei Wege führen dorthin, und für den Übersetzer sehen sie
|
|
/// verschieden aus.** Catalyst ist ein eigener Bau und meldet sich als
|
|
/// `targetEnvironment(macCatalyst)`. „Für iPad angepasst" ist dagegen
|
|
/// buchstäblich der iOS-Bau, der auf Apple Silicon startet — dort ist
|
|
/// dieselbe Abfrage schlicht falsch.
|
|
///
|
|
/// Wer nur die erste Hälfte prüft, schreibt Code für den Mac, der auf dem
|
|
/// Mac nicht läuft: übersetzt sauber, ist aber genau dort abgeschaltet,
|
|
/// wo er gebraucht wird. Deshalb hier beide Wege, an einer Stelle.
|
|
public static let isMac: Bool = {
|
|
#if targetEnvironment(macCatalyst)
|
|
true
|
|
#elseif os(iOS)
|
|
ProcessInfo.processInfo.isiOSAppOnMac
|
|
#else
|
|
true
|
|
#endif
|
|
}()
|
|
}
|
|
|
|
extension View {
|
|
/// Eine schmale Titelzeile — auf dem Mac gibt es sie nicht.
|
|
public func flixyInlineTitle() -> some View {
|
|
#if os(iOS)
|
|
navigationBarTitleDisplayMode(.inline)
|
|
#else
|
|
self
|
|
#endif
|
|
}
|
|
|
|
/// Ein Blatt, das die halbe Höhe einnimmt.
|
|
///
|
|
/// Auf dem Mac ist ein Blatt ein Fenster: dort zählt keine Rastung,
|
|
/// sondern eine vernünftige Mindestgröße.
|
|
public func flixyMediumSheet() -> some View {
|
|
#if os(iOS)
|
|
presentationDetents([.medium])
|
|
#else
|
|
frame(minWidth: 420, minHeight: 460)
|
|
#endif
|
|
}
|
|
|
|
/// Dasselbe, aber auf Wunsch bis zur vollen Höhe aufziehbar.
|
|
public func flixyResizableSheet() -> some View {
|
|
#if os(iOS)
|
|
presentationDetents([.medium, .large])
|
|
#else
|
|
frame(minWidth: 460, minHeight: 520)
|
|
#endif
|
|
}
|
|
|
|
/// Ein Formular, das auf beiden Seiten gleich aussieht.
|
|
///
|
|
/// Auf dem Mac setzt SwiftUI sonst den Spalten-Stil: Beschriftungen rechts
|
|
/// ausgerichtet, keine abgesetzten Abschnitte, alles eng. Der gruppierte
|
|
/// Stil ist derselbe, den iOS ohnehin nimmt — und der Grund, warum die
|
|
/// Einstellungen auf dem Mac wie ein Systemdialog aus einem anderen
|
|
/// Jahrzehnt aussahen.
|
|
public func flixyForm() -> some View {
|
|
#if os(macOS)
|
|
formStyle(.grouped)
|
|
#else
|
|
self
|
|
#endif
|
|
}
|
|
|
|
/// Eingabefelder für Adressen und Namen: keine Großschreibung, keine
|
|
/// Korrektur — auf dem Mac gibt es beides ohnehin nicht als Tastatur.
|
|
public func flixyPlainEntry(isAddress: Bool = false) -> some View {
|
|
#if os(iOS)
|
|
self
|
|
.keyboardType(isAddress ? .URL : .default)
|
|
.textInputAutocapitalization(.never)
|
|
.autocorrectionDisabled()
|
|
#else
|
|
autocorrectionDisabled()
|
|
#endif
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
/// Keine eigene Fläche hinter der Titelzeile.
|
|
///
|
|
/// Auf der Startseite liegt darunter der Aufmacher — eine weichgezeichnete
|
|
/// Leiste verschleierte die Hälfte davon. Auf dem Mac gibt es die Leiste in
|
|
/// dieser Form nicht.
|
|
public func flixyClearToolbarBackground() -> some View {
|
|
#if os(iOS)
|
|
self
|
|
.toolbarBackground(.hidden, for: .navigationBar)
|
|
.toolbarColorScheme(.dark, for: .navigationBar)
|
|
#else
|
|
self
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// Etwas in die Zwischenablage legen.
|
|
///
|
|
/// UIKit und AppKit heißen verschieden und meinen dasselbe; die Diagnose soll
|
|
/// deswegen nicht mit `#if` beginnen.
|
|
public enum FlixyClipboard {
|
|
public static func copy(_ text: String) {
|
|
#if canImport(UIKit)
|
|
UIPasteboard.general.string = text
|
|
#else
|
|
NSPasteboard.general.clearContents()
|
|
NSPasteboard.general.setString(text, forType: .string)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
extension ToolbarItemPlacement {
|
|
/// Rechts in der Titelzeile.
|
|
public static var flixyTrailing: ToolbarItemPlacement {
|
|
#if os(iOS)
|
|
.topBarTrailing
|
|
#else
|
|
.primaryAction
|
|
#endif
|
|
}
|
|
|
|
/// Links in der Titelzeile.
|
|
public static var flixyLeading: ToolbarItemPlacement {
|
|
#if os(iOS)
|
|
.topBarLeading
|
|
#else
|
|
.navigation
|
|
#endif
|
|
}
|
|
}
|