Asp*_*eri 8 macos ios swift swiftui
目标是在 SwiftUI 视图层次结构的任何级别轻松访问托管窗口。目的可能不同——关闭窗口、退出第一响应者、替换根视图或 contentViewController。与 UIKit/AppKit 的集成有时也需要通过窗口路径,所以……
我在这里遇到并尝试过的东西,
像这样的东西
let keyWindow = shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
Run Code Online (Sandbox Code Playgroud)
或者通过在每个 SwiftUI 视图中添加 UIViewRepresentable/NSViewRepresentable 来使用view.window看起来丑陋、沉重且不可用的窗口。
因此,我将如何做到这一点?
Asp*_*eri 15
这是我的实验结果,看起来很适合我,所以人们可能会发现它也有帮助。使用 Xcode 11.2 / iOS 13.2 / macOS 15.0 测试
这个想法是使用原生的 SwiftUI 环境概念,因为一旦注入的环境值自动可用于整个视图层次结构。所以
1) 定义环境键。注意,需要记住避免在保持窗口上进行引用循环
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
2) 在根 ContentView 中注入托管窗口以代替窗口创建(在 AppDelegate 或 SceneDelegate 中,只需一次
// 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
Run Code Online (Sandbox Code Playgroud)
3)只在需要的地方使用,只需声明环境变量
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)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
通过接收访问当前窗口NSWindow.didBecomeKeyNotification:
.onReceive(NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification)) { notification in
if let window = notification.object as? NSWindow {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
将窗口添加为环境对象中的属性。这可以是您用于其他应用程序范围数据的现有对象。
final class AppData: ObservableObject {
let window: UIWindow? // Will be nil in SwiftUI previewers
init(window: UIWindow? = nil) {
self.window = window
}
}
Run Code Online (Sandbox Code Playgroud)
创建环境对象时设置该属性。将对象添加到视图层次结构基础的视图中,例如根视图。
let window = UIWindow(windowScene: windowScene) // Or however you initially get the window
let rootView = RootView().environmentObject(AppData(window: window))
Run Code Online (Sandbox Code Playgroud)
最后,在您的视图中使用窗口。
struct MyView: View {
@EnvironmentObject private var appData: AppData
// Use appData.window in your view's body.
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7428 次 |
| 最近记录: |