Update from NavidromePlayer.zip (2026-04-04 07:45)
This commit is contained in:
parent
274b5dfe02
commit
a8d669824e
2 changed files with 36 additions and 127 deletions
|
|
@ -96,58 +96,6 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Scene Delegate
|
||||
// Takes ownership of window creation so we can use NavidromeHostingController.
|
||||
// When AppDelegate.application(_:configurationForConnecting:options:) returns a
|
||||
// UISceneConfiguration with delegateClass = NavidromeSceneDelegate.self, SwiftUI's
|
||||
// default window-bridging delegate is replaced — our delegate creates the window.
|
||||
// WindowGroup in App.body becomes a scene declaration that's never bridged to a
|
||||
// UIWindow, which is harmless.
|
||||
|
||||
final class NavidromeSceneDelegate: NSObject, UIWindowSceneDelegate {
|
||||
var window: UIWindow?
|
||||
private var cancellable: AnyCancellable?
|
||||
|
||||
func scene(_ scene: UIScene,
|
||||
willConnectTo session: UISceneSession,
|
||||
options connectionOptions: UIScene.ConnectionOptions) {
|
||||
guard let windowScene = scene as? UIWindowScene else { return }
|
||||
|
||||
let rootContent = AnyView(
|
||||
RootView()
|
||||
.environmentObject(ServerManager.shared)
|
||||
.environmentObject(AudioPlayer.shared)
|
||||
.environmentObject(OfflineManager.shared)
|
||||
.tint(Color(red: 1.0, green: 0.176, blue: 0.333))
|
||||
)
|
||||
|
||||
let hostingController = NavidromeHostingController(rootView: rootContent)
|
||||
|
||||
let window = UIWindow(windowScene: windowScene)
|
||||
window.rootViewController = hostingController
|
||||
window.makeKeyAndVisible()
|
||||
self.window = window
|
||||
|
||||
// Kick the status bar whenever the album color extractor publishes a new style
|
||||
cancellable = AlbumColorExtractor.shared.$preferredStatusBarStyle
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak hostingController] _ in
|
||||
hostingController?.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Custom Hosting Controller
|
||||
// Overrides preferredStatusBarStyle independently of the SwiftUI colorScheme.
|
||||
// NowPlayingView keeps .preferredColorScheme(.dark) so all SwiftUI views stay
|
||||
// visually dark. This override controls ONLY the status bar icon colour.
|
||||
|
||||
final class NavidromeHostingController: UIHostingController<AnyView> {
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
AlbumColorExtractor.shared.preferredStatusBarStyle
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct NavidromePlayerApp: App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
|
|
|
|||
|
|
@ -1,94 +1,55 @@
|
|||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Status Bar Style Manager
|
||||
//
|
||||
// Singleton that controls UIKit-level status bar appearance independently of
|
||||
// SwiftUI's .preferredColorScheme. Using .preferredColorScheme(.light/.dark) for
|
||||
// status bar control was wrong — it cascades through the entire view hierarchy and
|
||||
// flips all system colors. This class owns ONLY the status bar style via the
|
||||
// NavidromeHostingController override of preferredStatusBarStyle.
|
||||
//
|
||||
// NowPlayingView calls update(albumColor:isVisible:) on appear/dismiss/song change.
|
||||
|
||||
final class StatusBarStyleManager {
|
||||
static let shared = StatusBarStyleManager()
|
||||
|
||||
private(set) var currentStyle: UIStatusBarStyle = .default
|
||||
private weak var hostingController: NavidromeHostingController?
|
||||
|
||||
private init() {}
|
||||
|
||||
fileprivate func register(_ vc: NavidromeHostingController) {
|
||||
hostingController = vc
|
||||
}
|
||||
|
||||
/// Call whenever NowPlaying visibility or album art changes.
|
||||
func update(albumColor: Color, isVisible: Bool) {
|
||||
let style: UIStatusBarStyle
|
||||
if !isVisible {
|
||||
style = .default // follow system preference everywhere else
|
||||
} else {
|
||||
// NowPlaying always has a heavy black overlay so white icons are almost
|
||||
// always correct. Only flip to dark icons for extremely bright/white art
|
||||
// where the overlay might not fully neutralize a near-white background.
|
||||
var brightness: CGFloat = 0
|
||||
UIColor(albumColor).getHue(nil, saturation: nil, brightness: &brightness, alpha: nil)
|
||||
style = brightness > 0.85 ? .darkContent : .lightContent
|
||||
}
|
||||
guard style != currentStyle else { return }
|
||||
currentStyle = style
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.hostingController?.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Hosting Controller
|
||||
|
||||
/// UIHostingController subclass that delegates preferredStatusBarStyle to
|
||||
/// StatusBarStyleManager, giving us UIKit-level status bar control without
|
||||
/// touching SwiftUI's colorScheme environment.
|
||||
final class NavidromeHostingController: UIHostingController<AnyView> {
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
StatusBarStyleManager.shared.currentStyle
|
||||
}
|
||||
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { .fade }
|
||||
// Consume — don't forward to children so there's one source of truth
|
||||
override var childForStatusBarStyle: UIViewController? { nil }
|
||||
}
|
||||
import Combine
|
||||
|
||||
// MARK: - Scene Delegate
|
||||
// Takes ownership of window creation so we can use NavidromeHostingController.
|
||||
// When AppDelegate.application(_:configurationForConnecting:options:) returns a
|
||||
// UISceneConfiguration with delegateClass = NavidromeSceneDelegate.self, SwiftUI's
|
||||
// default window-bridging delegate is replaced — our delegate creates the window.
|
||||
// WindowGroup in App.body becomes a scene declaration that's never bridged to a
|
||||
// UIWindow, which is harmless.
|
||||
|
||||
/// Provides NavidromeHostingController as the window's root view controller.
|
||||
/// Registered via AppDelegate.application(_:configurationForConnecting:options:)
|
||||
/// which bypasses SwiftUI's automatic window creation for this scene.
|
||||
final class NavidromeSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
final class NavidromeSceneDelegate: NSObject, UIWindowSceneDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
func scene(
|
||||
_ scene: UIScene,
|
||||
willConnectTo session: UISceneSession,
|
||||
options connectionOptions: UIScene.ConnectionOptions
|
||||
) {
|
||||
private var cancellable: AnyCancellable?
|
||||
|
||||
func scene(_ scene: UIScene,
|
||||
willConnectTo session: UISceneSession,
|
||||
options connectionOptions: UIScene.ConnectionOptions) {
|
||||
guard let windowScene = scene as? UIWindowScene else { return }
|
||||
|
||||
// Recreate the same environment the WindowGroup body provides.
|
||||
// Singletons are used directly since they outlive any SwiftUI lifecycle.
|
||||
let content = AnyView(
|
||||
|
||||
let rootContent = AnyView(
|
||||
RootView()
|
||||
.environmentObject(ServerManager.shared)
|
||||
.environmentObject(AudioPlayer.shared)
|
||||
.environmentObject(OfflineManager.shared)
|
||||
.tint(Color(red: 1.0, green: 0.176, blue: 0.333))
|
||||
)
|
||||
|
||||
let hostingController = NavidromeHostingController(rootView: content)
|
||||
StatusBarStyleManager.shared.register(hostingController)
|
||||
|
||||
|
||||
let hostingController = NavidromeHostingController(rootView: rootContent)
|
||||
|
||||
let window = UIWindow(windowScene: windowScene)
|
||||
window.rootViewController = hostingController
|
||||
window.makeKeyAndVisible()
|
||||
self.window = window
|
||||
|
||||
// Re-trigger status bar appearance whenever album brightness changes
|
||||
cancellable = AlbumColorExtractor.shared.$preferredStatusBarStyle
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak hostingController] _ in
|
||||
hostingController?.setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Custom Hosting Controller
|
||||
// Overrides preferredStatusBarStyle independently of the SwiftUI colorScheme.
|
||||
// NowPlayingView keeps .preferredColorScheme(.dark) so all SwiftUI views stay
|
||||
// visually dark. This override controls ONLY the status bar icon colour.
|
||||
|
||||
final class NavidromeHostingController: UIHostingController<AnyView> {
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
AlbumColorExtractor.shared.preferredStatusBarStyle
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue