admin管理员组

文章数量:1410737

Note: This question refers to macOS and has nothing to do with iOS.

Context

My Mac app has a "log" window that appears when various parts of the app need to present errors or warnings to the user. In SwiftUI, the abbreviated structure looks like this:

@main
struct SomeApp: App
{
    @Environment(\.openWindow) private var openWindow
    @State private var appController = AppController()
    
    var body: some Scene
    {
        Window("SomeApp", id: SomeApp.mainWindowIdentifier) {
            MainWindowRootView()
                .environment(appController)
        }
        
        
        Window("Log", id: SomeApp.logWindowIdentifier) {
            LogWindowRootView()
        }
    }
}

Question

I know I can use openWindow(id:) to open the Log window. But what I really want is what I had in an AppKit NSWindowController:

if showWindow && !(window?.occlusionState.contains(.visible) ?? false)
{
    self.showWindow(self)
}

This ensures that if the log window is already partially visible (i.e. the user has it tiled behind some other windows on screen, but at least part of it can be seen) then it doesn't jump to the front and steal focus. It's a small detail, but one that makes the app a MUCH more pleasant citizen of macOS.

Is there a similar way to achieve this in pure SwiftUI? I want the window to open (become key) only if it's fully occluded.

Note: This question refers to macOS and has nothing to do with iOS.

Context

My Mac app has a "log" window that appears when various parts of the app need to present errors or warnings to the user. In SwiftUI, the abbreviated structure looks like this:

@main
struct SomeApp: App
{
    @Environment(\.openWindow) private var openWindow
    @State private var appController = AppController()
    
    var body: some Scene
    {
        Window("SomeApp", id: SomeApp.mainWindowIdentifier) {
            MainWindowRootView()
                .environment(appController)
        }
        
        
        Window("Log", id: SomeApp.logWindowIdentifier) {
            LogWindowRootView()
        }
    }
}

Question

I know I can use openWindow(id:) to open the Log window. But what I really want is what I had in an AppKit NSWindowController:

if showWindow && !(window?.occlusionState.contains(.visible) ?? false)
{
    self.showWindow(self)
}

This ensures that if the log window is already partially visible (i.e. the user has it tiled behind some other windows on screen, but at least part of it can be seen) then it doesn't jump to the front and steal focus. It's a small detail, but one that makes the app a MUCH more pleasant citizen of macOS.

Is there a similar way to achieve this in pure SwiftUI? I want the window to open (become key) only if it's fully occluded.

Share Improve this question edited Mar 7 at 17:13 Bryan asked Mar 7 at 7:13 BryanBryan 5,8474 gold badges46 silver badges81 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Achieving the same behavior as the NSWindowController in AppKit can be a bit tricky since SwiftUI doesn't provide direct access to window properties like occlusion state.

However, we can try to create a custom NSWindowController to manage the log window.

import Cocoa
class LogWindowController: NSWindowController {
    static let shared = LogWindowController(windowNibName: "LogWindow")

    func showIfNeeded() {
        if let window = self.window, !window.occlusionState.contains(.visible) {
            self.showWindow(nil)
        }
    }
}

Now, we can create a SwiftUI view to trigger the window controller

import SwiftUI

struct ContentView: View {
    var body: some View {
        Button("Show Log Window") {
            LogWindowController.shared.showIfNeeded()
        }
    }
}

Integrating with SwiftUI app structure:

@main
struct SomeApp: App {
    var body: some Scene {
        Window("SomeApp", id: "mainWindow") {
            ContentView()
        }
    }
}

At least, you can control the visibility of the log window without it stealing focus if it's already partially visible.

Again, this is just a work around and it might not be the exact fit. Hope it helps

本文标签: swiftCan I access window occlusionState in a SwiftUI appStack Overflow