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

查看:125
本文介绍了如何在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天全站免登陆