NSWindow的内容视图不能覆盖整个窗口大小 - macOS和SwiftUI

15
我正在使用SwiftUI创建一个新的macOS应用程序,但是我遇到了一个大问题。该应用程序需要一个完整尺寸的contentView(在titleBar下方),但我无法实现。在使用Storyboards的新项目中可以正常工作,但使用SwiftUI时不行。
我的代码: 输入图像描述 结果: 输入图像描述 而它应该看起来像这样: 输入图像描述 有什么想法吗? 谢谢!

它有什么问题吗?你想要一个没有标题栏的窗口吗? - Asperi
是的,我需要一个没有标题栏但带有关闭、最小化和调整大小按钮的窗口。两个视图(红色和黑色)必须延伸到标题栏和按钮下面。 - mhergon
4个回答

22

我刚刚在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的组合问题比我想象的更多。是时候进行更多实验了 :) 感谢您的回答! - ingoem
2
记录一下:如果我在ContentView中有一个NavigationView,那么我仍然需要在NavigationView上使用edgesIgnoringSafeArea - ingoem
1
同时,我还添加了 window.isReleasedWhenClosed = false 来防止在关闭窗口时崩溃。 - johnny peter
不幸的是,这似乎在macOS 12上无法工作。我无法使侧边栏占据整个窗口高度。 - Bryan
1
@Bryan 我刚遇到了材料/视觉效果视图未覆盖整个窗口的问题。我猜你的侧边栏也是一个材料/视觉效果视图?在调试时,我注意到使用Color可以正常工作,但是使用材料/视觉效果时,我必须使用.frame(minWidth: ,minHeight:) - iMaddin

5
安全区域不包括透明标题栏下方。您可以使用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)
}

applicationDidFinishLaunching中使用window.makeKeyAndOrderFront(nil)后面接上,这将添加一个非常短的动画效果。如果您觉得它有点令人不舒服,可以通过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)
  }
}

正确的方法是使用NavigationView。在macOS中,您不应该使用HStack进行导航。它可以与您的解决方案正常工作,但无法与NavigationView一起使用。 我将保留此答案,等待其他选项。谢谢! - mhergon
你没有提到导航方面 :) 我已经更新了我的答案。 - ingoem
它为什么不够准确? - ingoem
很难评估。两个答案都是正确的,但另一个代码更少,更通用,适用于整个contentView等等。 我已经认真考虑了哪个答案是最好的。如果可以的话,我会给你们两个人分数。对不起... :( - mhergon
我可以想象,但我明白你想要建立声誉。 - mhergon

3

在 SwiftUI 应用程序生命周期方面提供更多信息。

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

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

1

使用纯SwiftUI的最小解决方案。

@main
struct X_App: App {

var body: some Scene {
    WindowGroup {
        ContentView()
       .edgesIgnoringSafeArea(.top) 
           
    }.windowStyle(.hiddenTitleBar)
 }}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接