【问题标题】:NSWindow contentView not cover full window size - macOS & SwiftUINSWindow contentView 不覆盖整个窗口大小 - macOS 和 SwiftUI
【发布时间】:2020-05-29 19:09:40
【问题描述】:

我正在使用 SwiftUI 启动一个新的 macOS 应用程序,但我遇到了一个大问题。 该应用程序需要完整尺寸的contentView(在titleBar 下方),但我无法完成。在使用 Storyboards 的新项目上工作正常,但使用 SwiftUI 则不行。

我的代码:

结果:

它应该看起来像这样:

有什么想法吗? 谢谢!

【问题讨论】:

  • 这有什么问题?你想要一个没有标题栏的窗口吗?
  • 是的,我需要一个没有标题栏但带有关闭、最小化和调整大小按钮的窗口。必须在标题栏和按钮下扩展两个视图(红色和黑色)

标签: macos swiftui fullscreen nswindow titlebar


【解决方案1】:

我只是在AppDelegate中使用了如下变体,ContentView等的内容可以是任意的

func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Create the SwiftUI view that provides the window contents.
    let contentView = ContentView()
        .edgesIgnoringSafeArea(.top) // to extend entire content under titlebar 

    // Create the window and set the content view. 
    window = NSWindow(
        contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
        styleMask: [.titled, .closable, .miniaturizable, .texturedBackground, .resizable, .fullSizeContentView],
        backing: .buffered, defer: false)
    window.center()
    window.setFrameAutosaveName("Main Window")

    window.titlebarAppearsTransparent = true // as stated
    window.titleVisibility = .hidden         // no title - all in content

    window.contentView = NSHostingView(rootView: contentView)
    window.makeKeyAndOrderFront(nil)
}

【讨论】:

  • 谢谢,这是更准确、更简单的答案
  • 有趣的是,在ContentView 之外指定边缘会产生不同的效果。这要么是一个错误,要么是 SwiftUI 的组合问题比我想象的要多。是时候进行更多实验了:)感谢您的回答!
  • 备案:如果我在ContentView 中有一个NavigationView,我还需要在NavigationView 上添加一个edgesIgnoringSafeArea
  • 我还添加了这个window.isReleasedWhenClosed = false 以防止在关闭窗口时崩溃。
【解决方案2】:

更多关于 SwiftUI App 生命周期的信息。

您需要将窗口样式设置为 HiddenTitleBarWindowStyle :

WindowGroup {
    ContentView()
}.windowStyle(HiddenTitleBarWindowStyle())

【讨论】:

    【解决方案3】:

    安全区域不会延伸到透明标题栏下方。您可以使用edgesIgnoringSafeArea 告诉您的内容视图边缘忽略安全区域。类似于您的示例的东西:

    struct ContentView: View {
      var body: some View {
        HStack(spacing: 0) {
          Text("Hello, World!")
            .frame(maxWidth: 200, maxHeight: .infinity)
            .background(Color.red)
          Text("Hello, World!")
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.black)
        }.edgesIgnoringSafeArea(.all)
      }
    }
    

    更新: 如果您想使用 NavigationView,您还必须在其内容中添加 edgesIgnoringSafeArea

    struct ContentView: View {
      var body: some View {
        NavigationView {
          Text("Hello, World!")
            .frame(maxWidth: 200, maxHeight: .infinity)
            .background(Color.red)
            .edgesIgnoringSafeArea(.all)
    
          Text("Hello, World!")
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.black)
            .edgesIgnoringSafeArea(.all)
    
        }.edgesIgnoringSafeArea(.all)
      }
    }
    

    不幸的是,此时它最初会显示一个标题栏,显然直到您强制重绘整个窗口。一旦将窗口移动到不同的显示或隐藏并再次显示,标题栏就会消失。所以,我的猜测是,这将在某个时候得到解决。

    现在,您可以通过添加以编程方式强制隐藏和显示

    DispatchQueue.main.async {
      self.window.orderOut(nil)
      self.window.makeKeyAndOrderFront(nil)
    }
    

    window.makeKeyAndOrderFront(nil) 之后applicationDidFinishLaunching。然而,它将添加一个非常短的动画。如果它困扰您,您可以使用 NSWindow.animationBehavior 或类似的东西将其关闭。

    更新 2:显然,如果您删除初始的 window.makeKeyAndOrderFront(nil) 并将其替换为上面的调度队列逻辑,它将不会动画。所以最后你会有

    func applicationDidFinishLaunching(_ aNotification: Notification) {
      let contentView = ContentView()
    
      window = NSWindow(
          contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
          styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView, .texturedBackground],
          backing: .buffered, defer: false)
      window.titlebarAppearsTransparent = true
      window.center()
      window.setFrameAutosaveName("Main Window")
      window.contentView = NSHostingView(rootView: contentView)
      // window.makeKeyAndOrderFront(self) <- don't call it here
      DispatchQueue.main.async {
        self.window.orderOut(nil)
        self.window.makeKeyAndOrderFront(nil)
      }
    }
    

    【讨论】:

    • 正确的方法是使用 NagivationView。在 macOS 中,您不应该使用 HStack 进行导航。它适用于您的解决方案,但不适用于 NavigationView。我将这个答案留在待命等待其他选项。谢谢!
    • 你没有说任何关于导航的事情 :) 我已经更新了我的答案。
    • 它的准确性如何?
    • 很难评估。两个答案都是正确的,但另一个有更少的代码,更通用,适用于整个 contentView 等我已经考虑了很多关于什么答案最好接受的问题。如果可以的话,我会给你们两个加分。对不起... :(
    • 我可以想象,但我知道您想建立声誉。
    【解决方案4】:

    纯 SwiftUI 中的最小解决方案。

    @main
    struct X_App: App {
    
    var body: some Scene {
        WindowGroup {
            ContentView()
           .edgesIgnoringSafeArea(.top) 
               
        }.windowStyle(.hiddenTitleBar)
     }}
    

    【讨论】:

      猜你喜欢
      • 2012-08-14
      • 2011-06-12
      • 2013-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-02
      • 1970-01-01
      • 2021-03-30
      相关资源
      最近更新 更多