【问题标题】:SwiftUI macos NSWindow instanceSwiftUI macos NSWindow 实例
【发布时间】:2021-03-30 10:07:54
【问题描述】:

xcode 12.3swift 5.3SwiftUI App 生命周期一起使用来构建macOS 应用程序,访问和更改NSWindow 的外观和行为的最佳方法是什么?

编辑:我真正追求的是NSWindow 实例。

我添加了一个AppDelegate,但据我了解,NSWindow 可能是nil,因此无法修改,只需在此处创建一个类似于AppKit App Delegate 的生命周期方法会导致启动时出现两个窗口。

一种解决方案是阻止默认窗口出现,并将其全部留给applicationDidFinishLaunching 方法,但不确定这是否可行或合理。

WindowStyle 协议看起来是一个可能的解决方案,但不确定如何在现阶段通过CustomWindowStyle 最好地利用它,以及是否提供对NSWindow 实例的访问以进行细粒度控制。

class AppDelegate: NSObject, NSApplicationDelegate {        
    func applicationDidFinishLaunching(_ aNotification: Notification) {
      // In AppKit simply create the NSWindow and modify style.
      // In SwiftUI creating an NSWindow and styling results in 2 windows, 
      // one styled and the other default.
    }
}

@main
struct testApp: App {
    
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate : AppDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

【问题讨论】:

  • 给它分配一个 NSAppearance?参见例如developer.apple.com/documentation/appkit/…
  • 感谢您的建议,尽管我认为 NSAppearance 不足以涵盖我的用例,因为我需要控制 NSWindow 行为的其他方面。我将编辑我的问题,使这一要求更加明显。
  • 现在正在尝试做同样的事情。在 Google 上严重缺乏这方面的知识。这里的符合类型似乎也不适合我 - developer.apple.com/documentation/swiftui/windowstyle
  • 是的,我希望有更多关于符合类型以及如何通过实现WindowStyle 协议来实现类似的内容,但还没有找到任何东西,虽然我已经能够使用文档中定义的那些,即HiddenTitleBarWindowStyle 工作得很好,但我需要自己的实现来实现我所追求的。

标签: ios swift macos swiftui nswindow


【解决方案1】:

虽然我不完全确定这是否是正确的方法,但基于对以下问题的回答:https://stackoverflow.com/a/63439982/792406 我已经能够访问NSWindow 实例并修改其外观。

为了快速参考,这里有一个基于Asperi 提供的原始代码使用xcode 12.3swift 5.3 和SwiftUI 应用生命周期的工作示例。

@main
struct testApp: App {    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class Store {
    var window: NSWindow
    
    init(window: NSWindow) {
        self.window = window
        self.window.isOpaque = false
        self.window.backgroundColor = NSColor.clear
    }
}

struct ContentView: View {
    @State private var window: NSWindow?
    var body: some View {
        VStack {
            Text("Loading...")
            if nil != window {
                MainView(store: Store(window: window!))
            }
        }.background(WindowAccessor(window: $window))
    }
}

struct MainView: View {

    let store: Store
    
    var body: some View {
        VStack {
            Text("MainView with Window: \(store.window)")
        }.frame(width: 400, height: 400)
    }
}

struct WindowAccessor: NSViewRepresentable {
    @Binding var window: NSWindow?
    
    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        DispatchQueue.main.async {
            self.window = view.window
        }
        return view
    }
    
    func updateNSView(_ nsView: NSView, context: Context) {}
}

【讨论】:

  • 嘿 hillmark 我喜欢这个答案,但没有疑问,如果我想用其他视图替换 ContentView 而不打开另一个 NSWindow 怎么办
【解决方案2】:

有趣的方法@hillmark。

我还使用NSApplicationDelegateAdaptor 的方法使其工作。

我相信下面的代码只会帮助您处理单窗口 MacOS SwiftUI 应用程序 - 多窗口应用程序可能需要处理所有窗口,而不是只处理第一个窗口。

我也不确定我的方法有什么注意事项。

现在这正好解决了我的问题,但我也会检查你的方法。

import SwiftUI

final class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_ notification: Notification) {
        if let window = NSApplication.shared.windows.first {
            window.titleVisibility = .hidden
            window.titlebarAppearsTransparent = true
            window.isOpaque = false
            window.backgroundColor = NSColor.clear
        }
    }
}

@main
struct AlreadyMacOSApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
    var body: some Scene {
        WindowGroup {
            NewLayoutTest()
        }
    }
}

【讨论】:

  • 是的,我在多窗口路径上,现在发现托管窗口方法为我提供了灵活性,可以让每个窗口具有不同的样式等,并且具有良好的分离性。我的 appDelegate 正在慢慢清空,我对此持积极态度。实际上,我最终得到了一种类似于 react 的容器类型模式。
  • 试过你的方法。效果很好,我已经移到那里了。
  • 很高兴看到你在这个问题上回答你自己的问题。
【解决方案3】:

很多方法,但有一个陷阱阅读完整答案

 NSApplication.shared.keyWindow, NSApp.keyWindow, 
NSApp.mainWindow, 

有时,这些可能会返回 nil,尤其是在启动期间或应用处于非活动状态时

我认为这是因为从 Appkit 方面设置这些属性可能不是即时的

至于您的问题,您可以在主应用程序的 init() 或 appdelegate 适配器中使用它没关系

最好是访问所有窗口

 NSApp.windows.first

这将返回所有窗口,但顺序不可预测

如果您有多个窗口,您可以进行进一步过滤以找到所需的窗口,但这种方法避免了其他方法返回 nil 的疯狂行为

【讨论】:

    猜你喜欢
    • 2022-01-13
    • 2020-05-29
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-12
    • 2021-10-01
    相关资源
    最近更新 更多