如何在 SwiftUI 中获取窗口坐标?

Sup*_*tar 5 macos swiftui

我想制作一个视差背景视图,当窗口在屏幕上移动时,UI 后面的图像几乎保持静止。要在 macOS 上执行此操作,我想获取窗口的坐标。我如何获得窗口的坐标?


我问这个是因为我找不到任何说明如何做到这一点的地方:

正如我列出的那样,我发现所有这些要么与我的问题无关,要么只引用了窗口内的坐标,而不是屏幕内窗口的坐标。有些人提到了使用 AppKit 的方法,但我想尽可能避免这种情况。

我得到的最接近的是尝试使用GeometryReader这样的:

GeometryReader { geometry in
    Text(verbatim: "\(geometry.frame(in: .global))")
}
Run Code Online (Sandbox Code Playgroud)

但原点始终是(0, 0),尽管在我调整窗口时大小确实发生了变化。


我所设想的可能是这样的:

public struct ParallaxBackground<Background: View>: View {
    var background: Background

    @Environment(\.windowFrame)
    var windowFrame: CGRect

    public var body: some View {
        background
            .offset(x: windowFrame.minX / 10,
                    y: windowFrame.minY / 10)
    }
}
Run Code Online (Sandbox Code Playgroud)

\.windowFrame不是真实的;它没有指向EnvironmentValues. 我找不到在哪里可以获得这样的价值。

Cen*_*gen 1

如果你想要窗框:

SceneDelegate 跟踪所有窗口,因此您可以使用它来创建EnvironmentObject对其框架的引用并将其传递给您的视图。更新委托方法中的环境对象值:func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, ...

如果它是一个单窗口应用程序,那就更直接了。您可以在视图中使用 UIScreen.main.bounds (如果全屏)或计算变量:

var frame: CGRect { (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window?.frame ?? .zero }

但如果您正在寻找窗口中的视图框架,请尝试以下操作:

struct ContentView: View {
  @State var frame: CGRect = .zero

  var orientationChangedPublisher = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)

  var body: some View {
    VStack {
      Text("text frame georeader \(frame.debugDescription)")
    }
    .background(GeometryReader { geometry in
      Color.clear // .edgesIgnoringSafeArea(.all) // may need depending
        .onReceive(self.orientationChangedPublisher.removeDuplicates()) { _ in
          self.frame = geometry.frame(in: .global)
      }
    })
  }
} 
Run Code Online (Sandbox Code Playgroud)

但话虽如此,通常您不需要绝对框架。对齐参考线可让您将事物相对放置。

// 对于 macOS 应用程序,使用帧更改通知并作为环境对象传递给 SwiftUI 视图

class WindowInfo: ObservableObject {
  @Published var frame: CGRect = .zero
}

@NSApplicationMain

class AppDelegate: NSObject, NSApplicationDelegate {

  var window: NSWindow!

  let windowInfo = WindowInfo()

  func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Create the SwiftUI view that provides the window contents.
    let contentView = ContentView()
      .environmentObject(windowInfo)

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

    NotificationCenter.default.addObserver(forName: NSView.frameDidChangeNotification, object: nil, queue: nil) { (notification) in
      self.windowInfo.frame = self.window.frame
    }
  }
Run Code Online (Sandbox Code Playgroud)
struct ContentView: View {
  @EnvironmentObject var windowInfo: WindowInfo

  var body: some View {
    Group  {
      Text("Hello, World! \(windowInfo.frame.debugDescription)")
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)