Der Schriftzug hing weiter nicht an der Animation, und der Zwischenspeicher war nicht schuld: er lag im Actor, und ein `await` kostet immer mindestens einen Durchlauf. Das neue Bild erschien deshalb im ersten Bild ohne Schriftzug und im zweiten mit — er sprang also getrennt herein, obwohl er längst da war. Der Speicher liegt jetzt außerhalb des Actors und lässt sich beim Zeichnen lesen. Das gilt für alle Bilder: auch eine zurückscrollende Kachel blitzt nicht mehr kurz grau auf. Zur Seitenleiste auf dem Mac, gemessen statt vermutet: `automatic` löst sich laut Apples eigener Beschreibung auf iOS zu `overlap` auf (die Leiste schwebt über dem Inhalt) und auf macOS zu `tile` (sie schiebt ihn zur Seite und klebt am Fensterrand). Der Griff dorthin sitzt richtig — derselbe Weg hat die Leiste auf Kommando ganz ausgeblendet —, aber `overlap` wird auf dem Mac stillschweigend verworfen. Die Bitte bleibt trotzdem stehen; sie kostet nichts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
69 lines
2.9 KiB
Swift
69 lines
2.9 KiB
Swift
#if os(iOS)
|
|
import SwiftUI
|
|
import UIKit
|
|
|
|
/// Bittet die Seitenleiste, den Inhalt zu **überlagern** statt ihn zu verdrängen.
|
|
///
|
|
/// `UITabBarControllerSidebar.preferredLayout` steht ab Werk auf `automatic`,
|
|
/// und das löst sich laut Apples eigener Beschreibung je nach Gerät anders auf:
|
|
/// auf iOS zu `overlap` — die Leiste schwebt als eigene Fläche über dem Inhalt —
|
|
/// und auf macOS zu `tile`, wo sie den Inhalt zur Seite schiebt und selbst am
|
|
/// Fensterrand klebt. Das ist der Unterschied, den man zwischen iPad und Mac
|
|
/// sieht, und er kommt nicht aus dieser App.
|
|
///
|
|
/// **Unter Mac Catalyst gemessen: es hilft dort nicht.** Der Griff sitzt
|
|
/// richtig — zum Beweis hat derselbe Weg die Leiste auf Kommando ganz
|
|
/// ausgeblendet —, aber `overlap` wird auf dem Mac stillschweigend verworfen
|
|
/// und es bleibt bei `tile`. Der Aufruf bleibt trotzdem stehen: er ist die
|
|
/// richtige Bitte, er kostet nichts, und sollte „Designed for iPad" sich näher
|
|
/// am iPhone-Bau verhalten als Catalyst, greift er dort.
|
|
///
|
|
/// SwiftUI reicht die Eigenschaft nicht durch, also wird der zuständige
|
|
/// `UITabBarController` gesucht. Gesucht wird vom eigenen Platz aus nach oben
|
|
/// bis zur Wurzel und von dort nach unten — die Leiste liegt in der
|
|
/// SwiftUI-Sicht *über* uns, nicht unter uns, und die reine Elternkette führt
|
|
/// deshalb an ihr vorbei.
|
|
struct SidebarOverlapRequest: 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)
|
|
apply()
|
|
}
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
super.viewDidAppear(animated)
|
|
// Ein zweites Mal: beim ersten Anhängen steht der
|
|
// Tab-Controller noch nicht zwingend im Baum.
|
|
apply()
|
|
}
|
|
|
|
private func apply() {
|
|
guard let controller = Self.tabBarController(from: self) else { return }
|
|
guard controller.sidebar.preferredLayout != .overlap else { return }
|
|
controller.sidebar.preferredLayout = .overlap
|
|
}
|
|
|
|
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
|