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

时间:2020-06-07 22:23:40

标签: macos swiftui

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


我之所以这样问,是因为我在任何地方都找不到该怎么做的信息:

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

最近的一次尝试使用这样的GeometryReader

GeometryReader { geometry in
    Text(verbatim: "\(geometry.frame(in: .global))")
}

但是原点总是(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)
    }
}

但是\.windowFrame不是真实的;它没有指向EnvironmentValues上的任何键路径。我找不到在哪里可以得到这样的价值。

1 个答案:

答案 0 :(得分:0)

如果您想要窗框:

SceneDelegate会跟踪所有窗口,因此您可以使用它来参考它们的框架制作一个EnvironmentObject并将其传递给View。在委托方法中更新环境对象的值: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)
      }
    })
  }
} 

尽管如此,通常您不需要绝对的框架。对齐向导使您可以将彼此放置在一起。

///对于macOS App,使用“框架更改通知”并将其作为环境对象传递到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
    }
  }
struct ContentView: View {
  @EnvironmentObject var windowInfo: WindowInfo

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