Bilder ohne Warten — und der Beweis, dass der Mac die Leiste selbst zeichnet
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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e5e01838f7
commit
e7de8a9055
@@ -170,6 +170,13 @@ struct RootView: View {
|
||||
/// ausfahrbares Blatt, eine geteilte Ansicht und eine Reihe oben. Sie
|
||||
/// konnten zusammen weniger als das eine Bauteil und mussten dreimal
|
||||
/// gepflegt werden.
|
||||
/// Läuft diese App gerade auf einem Mac?
|
||||
///
|
||||
/// Nicht über `#if` zu haben: „Designed for iPad" **ist** der iOS-Bau,
|
||||
/// unverändert, nur auf anderer Hardware. Erst zur Laufzeit ist der
|
||||
/// Unterschied sichtbar.
|
||||
static let isOnMac = ProcessInfo.processInfo.isMacCatalystApp
|
||||
|
||||
private var library: some View {
|
||||
TabView(selection: $selection) {
|
||||
Tab("sidebar.home", systemImage: "house", value: SidebarDestination.home) {
|
||||
@@ -241,6 +248,10 @@ struct RootView: View {
|
||||
}
|
||||
.tabViewStyle(.sidebarAdaptable)
|
||||
.tabViewCustomization($customization)
|
||||
// Auf dem Mac würde die Leiste sonst den Inhalt zur Seite schieben
|
||||
// und selbst am Fensterrand kleben. Auf dem iPad tut das System es
|
||||
// ohnehin so; der Aufruf schadet dort nicht.
|
||||
.background { SidebarOverlapRequest().frame(width: 0, height: 0) }
|
||||
// Das Konto sitzt unten in der Seitenleiste, nicht als Eintrag
|
||||
// zwischen den Mediatheken. Dort sucht man es.
|
||||
.tabViewSidebarBottomBar {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user