8

How can I hide the Title Bar in the new SwiftUI App Protocol?

Since the AppDelegate.swift and SceneDelegate.swift protocols are gone, I cant follow this documentation anymore: https://developer.apple.com/documentation/uikit/mac_catalyst/removing_the_title_bar_in_your_mac_app_built_with_mac_catalyst

I can't implement this code:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        #if targetEnvironment(macCatalyst)
        if let titlebar = windowScene.titlebar {
            titlebar.titleVisibility = .hidden
            titlebar.toolbar = nil
        }
        #endif
    }
}

Hope it's still possible with the new AppProtocol..

Thanks in advance

John
  • 297
  • 2
  • 12
  • 2
    You can use `withHostingWindow` (from https://stackoverflow.com/a/63276688/12299030) to find own window and then do what's needed. – Asperi Dec 10 '20 at 17:19
  • @Asperi, thanks for your response, thanks to your answer I was able to hide the titlebar. I'll post the answer, but I still hope Apple adds official support soon. – John Dec 10 '20 at 22:39

2 Answers2

14

This is how to hide the titlebar:

struct ContentView: View {
    var body: some View {
        ZStack {
            Text("Example UI")
        }
        .withHostingWindow { window in
            #if targetEnvironment(macCatalyst)
            if let titlebar = window?.windowScene?.titlebar {
                titlebar.titleVisibility = .hidden
                titlebar.toolbar = nil
            }
            #endif
        }
    }
}

extension View {
    fileprivate func withHostingWindow(_ callback: @escaping (UIWindow?) -> Void) -> some View {
        self.background(HostingWindowFinder(callback: callback))
    }
}

fileprivate struct HostingWindowFinder: UIViewRepresentable {
    var callback: (UIWindow?) -> ()

    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        DispatchQueue.main.async { [weak view] in
            self.callback(view?.window)
        }
        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {
    }
}
John
  • 297
  • 2
  • 12
  • UIKit is our everything! :D – imike Dec 14 '20 at 01:57
  • @imike Yes, i look forward to the time where SwiftUI will have everything to :) – John Dec 14 '20 at 17:10
  • A better approach is to create a subclass of UIView and implement its willMove:toWindow:, call the callback there, and skip the async. This way there is no flicker and the titlebar is hidden immediately when the view appears. – Jaka Jančar Feb 02 '21 at 17:44
  • I added the `if let titlebar` conditional from the `.withHostingWindow` modifier, right into my AppDelegate, in `didFinishLaunchingWithOptions` and it worked perfectly! – dbDev May 17 '23 at 18:27
1

On your Scene, set .windowStyle(_:) to HiddenTitleBarWindowStyle().

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .windowStyle(HiddenTitleBarWindowStyle())
    }
}

EDIT: Ah crap. While this API is supposedly available for Mac Catalyst according to the online documentation, it looks like it’s not actually marked as such in frameworks so you can’t use it.

Adam
  • 4,405
  • 16
  • 23