如何在 SwiftUI 视图中访问自己的窗口? [英] How to access own window within SwiftUI view?

查看:27
本文介绍了如何在 SwiftUI 视图中访问自己的窗口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目标是在 SwiftUI 视图层次结构的任何级别轻松访问托管窗口.目的可能不同——关闭窗口、退出第一响应者、替换根视图或 contentViewController.与 UIKit/AppKit 的集成有时也需要通过窗口路径,所以......

The goal is to have easy access to hosting window at any level of SwiftUI view hierarchy. The purpose might be different - close the window, resign first responder, replace root view or contentViewController. Integration with UIKit/AppKit also sometimes require path via window, so…

我在这里遇到并尝试过的,

What I met here and tried before,

类似的东西

let keyWindow = shared.connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .map({$0 as? UIWindowScene})
        .compactMap({$0})
        .first?.windows
        .filter({$0.isKeyWindow}).first

或者通过在每个 SwiftUI 视图中添加 UIViewRepresentable/NSViewRepresentable 来获取使用 view.window 的窗口,看起来丑陋、沉重且不可用.

or via added in every SwiftUI view UIViewRepresentable/NSViewRepresentable to get the window using view.window looks ugly, heavy, and not usable.

那么,我该怎么做?

推荐答案

这里是我的实验结果,看起来很适合我,所以人们可能会发现它也有帮助.使用 Xcode 11.2/iOS 13.2/macOS 15.0 测试

Here is the result of my experiments that looks appropriate for me, so one might find it helpful as well. Tested with Xcode 11.2 / iOS 13.2 / macOS 15.0

这个想法是使用原生的 SwiftUI 环境概念,因为一旦注入的环境值自动对整个视图层次结构可用.所以

The idea is to use native SwiftUI Environment concept, because once injected environment value becomes available for entire view hierarchy automatically. So

1) 定义环境键.注意,需要记住避免在保持窗口上循环引用

1) Define Environment key. Note, it needs to remember to avoid reference cycling on kept window

struct HostingWindowKey: EnvironmentKey {

#if canImport(UIKit)
    typealias WrappedValue = UIWindow
#elseif canImport(AppKit)
    typealias WrappedValue = NSWindow
#else
    #error("Unsupported platform")
#endif

    typealias Value = () -> WrappedValue? // needed for weak link
    static let defaultValue: Self.Value = { nil }
}

extension EnvironmentValues {
    var hostingWindow: HostingWindowKey.Value {
        get {
            return self[HostingWindowKey.self]
        }
        set {
            self[HostingWindowKey.self] = newValue
        }
    }
}

2) 在根 ContentView 中注入托管窗口以代替窗口创建(在 AppDelegate 或 SceneDelegate 中,只需一次

2) Inject hosting window in root ContentView in place of window creation (either in AppDelegate or in SceneDelegate, just once

// window created here

let contentView = ContentView()
                     .environment(.hostingWindow, { [weak window] in
                          return window })

#if canImport(UIKit)
        window.rootViewController = UIHostingController(rootView: contentView)
#elseif canImport(AppKit)
        window.contentView = NSHostingView(rootView: contentView)
#else
    #error("Unsupported platform")
#endif

3) 只在需要的地方使用,只需声明环境变量

3) use only where needed, just by declaring environment variable

struct ContentView: View {
    @Environment(.hostingWindow) var hostingWindow

    var body: some View {
        VStack {
            Button("Action") {
                // self.hostingWindow()?.close() // macOS
                // self.hostingWindow()?.makeFirstResponder(nil) // macOS
                // self.hostingWindow()?.resignFirstResponder() // iOS
                // self.hostingWindow()?.rootViewController?.present(UIKitController(), animating: true)
            }
        }
    }
}

这篇关于如何在 SwiftUI 视图中访问自己的窗口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆