Flixy heißt ab hier Halo
Reine Umbenennung, kein Verhalten geändert — bewusst als eigener Commit, damit „umbenannt" und „umgebaut" im Nachhinein auseinanderzuhalten sind. 712 mal `Flixy`, 778 mal `flixy`, 7 mal `FLIXY` in 169 Dateien, dazu 25 Pfade. Ziele: `Halo`, `HaloTV`, `HaloTopShelf`, `HaloUITests`, `HaloKit` mit `HaloDesign`, `HaloDownloads`, `HaloPlayback` und `HaloProbe`. `MediaKit`, `JellyfinKit` und `DownloadWidget` tragen den Namen nicht und bleiben. `docs/superpowers/specs/` ist ausgenommen. Datierte Entwurfsdokumente sind ein Protokoll getroffener Entscheidungen; sie nachträglich umzuschreiben hiesse, die Vergangenheit zu glätten. **Die Bundle-ID wandert mit** — ausdrücklich so gewollt, mit bekanntem Preis: `com.scarriffleservices.halo` ist für iOS eine *andere* App. Die installierte Fassung bleibt daneben stehen, mit Downloads und Anmeldung in ihrem Container; Halo startet leer. Einmal neu anmelden, heruntergeladene Folgen einmal neu laden. Dasselbe gilt für die App-Gruppe, die sich TV-App und Top Shelf teilen. **Eine Folge davon, die beim Entscheiden noch nicht auf dem Tisch lag:** der Aufräumschritt aus dem vorigen Commit kommt an die alten Plex-Token nicht mehr heran. Der Schlüsselbund grenzt nach Team und Bundle-Kennung ab — was unter `…flixy` liegt, ist für `…halo` unerreichbar. Erledigt wird es trotzdem, nur anders: **die alte App löschen.** iOS räumt deren Schlüsselbund-Einträge beim Deinstallieren mit ab. Der Code bleibt stehen, weil er für jeden Stand vor dieser Umbenennung richtig ist und im Jahr einen Startvergleich kostet. Die Projektdatei ist aus `project.yml` neu erzeugt, der alte Ordner gelöscht statt umbenannt — sie steht ohnehin nicht im Repo. Das Verzeichnis des Repos selbst heißt weiter `Flixy`; das umzubenennen ist ein Handgriff ausserhalb des Projekts. 368 Tests grün. iOS und tvOS bauen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
2fcf6821c8
commit
4b9a1e7b67
@@ -0,0 +1,81 @@
|
||||
#if os(iOS)
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
/// Blendet die vom System gezeichnete Seitenleiste und Tableiste aus.
|
||||
///
|
||||
/// Der Grund ist das Aussehen, nicht das Verhalten. `UITabBarController`
|
||||
/// zeichnet seine Seitenleiste auf dem iPad als schwebende Fläche und auf dem
|
||||
/// Mac angeklebt am Fensterrand — und das lässt sich nicht umstellen.
|
||||
/// `preferredLayout = .overlap` ist die dafür vorgesehene Bitte; macOS verwirft
|
||||
/// sie stillschweigend. Gemessen, nicht vermutet.
|
||||
///
|
||||
/// Statt deshalb die ganze Navigation selbst zu bauen, bleibt der
|
||||
/// `TabView` **stehen** und verliert nur sein Kleid: Auswahl, getrennte
|
||||
/// Navigationsstapel je Eintrag, erhaltener Scrollstand beim Wechseln,
|
||||
/// Zustandspflege — all das macht weiter das System. Gezeichnet wird darüber.
|
||||
///
|
||||
/// Dass das geht, war kein Ratschluss: derselbe Griff hat die Leiste
|
||||
/// versuchsweise schon einmal verschwinden lassen.
|
||||
struct TabChromeSuppressor: UIViewControllerRepresentable {
|
||||
func makeUIViewController(context: Context) -> UIViewController { Probe() }
|
||||
func updateUIViewController(_ controller: UIViewController, context: Context) {}
|
||||
|
||||
/// Ein Controller, der nichts zeigt und nur da ist, um im Baum zu stehen.
|
||||
private final class Probe: UIViewController {
|
||||
override func didMove(toParent parent: UIViewController?) {
|
||||
super.didMove(toParent: parent)
|
||||
suppress()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
// Ein zweites Mal: beim ersten Anhängen steht der Tab-Controller
|
||||
// noch nicht zwingend im Baum.
|
||||
suppress()
|
||||
}
|
||||
|
||||
override func viewWillLayoutSubviews() {
|
||||
super.viewWillLayoutSubviews()
|
||||
// Und bei jedem Umbau: das System stellt die Leisten wieder her,
|
||||
// wenn sich die Größenklasse ändert — beim Drehen etwa.
|
||||
suppress()
|
||||
}
|
||||
|
||||
private func suppress() {
|
||||
guard let controller = Self.tabBarController(from: self) else { return }
|
||||
|
||||
// Erst die Form festlegen, dann ausblenden — und zwar auf die
|
||||
// Tableiste, nicht auf die Seitenleiste.
|
||||
//
|
||||
// Nur `sidebar.isHidden = true` reichte nicht: auf dem Mac stand
|
||||
// die System-Seitenleiste danach wieder da, mit eigenem Pfeil und
|
||||
// eigenem Knopf, hinter unserer. Solange `mode` auf `tabSidebar`
|
||||
// steht, gehört die Seitenleiste zur Darstellung und das System
|
||||
// stellt sie wieder her. Steht `mode` auf `tabBar`, gibt es keine
|
||||
// Seitenleiste mehr, die zurückkommen könnte — und die Tableiste
|
||||
// ist ausgeblendet.
|
||||
if controller.mode != .tabBar { controller.mode = .tabBar }
|
||||
if !controller.isTabBarHidden { controller.isTabBarHidden = true }
|
||||
if !controller.sidebar.isHidden { controller.sidebar.isHidden = true }
|
||||
}
|
||||
|
||||
/// Sucht vom eigenen Platz aus nach oben bis zur Wurzel und von dort
|
||||
/// nach unten: die Leiste liegt in der SwiftUI-Sicht *über* uns, die
|
||||
/// reine Elternkette führt an ihr vorbei.
|
||||
private static func tabBarController(from start: UIViewController) -> UITabBarController? {
|
||||
var root = start
|
||||
while let parent = root.parent { root = parent }
|
||||
return search(root)
|
||||
}
|
||||
|
||||
private static func search(_ controller: UIViewController) -> UITabBarController? {
|
||||
if let found = controller as? UITabBarController { return found }
|
||||
for child in controller.children {
|
||||
if let found = search(child) { return found }
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user