SwiftUIWindowBinder 支持在 SwiftUI 中访问宿主 Window 对象(UIWindow 或 NSWindow),无需任何设置。 没有应用程序代理或场景代理的 SwiftUI 应用程序仍然可以访问 Window,并且该窗口的作用域限定为多窗口应用程序中的每个文档。 这里也支持 Playground。
要在您的项目中使用 SwiftUIWindowBinder,请参阅如何使用 Swift Package Manager 或在 Xcode 中引用包,使用此存储库的 GitHub 链接。 安装完成后,您可以根据需要导入 SwiftUIWindowBinder。
要在 Package.swift
中手动添加,请在您的 dependencies
部分中使用以下引用
dependencies: [
.package(
name: "SwiftUIWindowBinder".
url: "https://github.com/happycodelucky/SwiftUIWindowBinder.git",
.upToNextMajor(from: "1.0.0"),
],
...
README.md 上的文档比较简单。 该软件包的内容不多,我们鼓励您探索代码,并提供关于更好方法或您可能喜欢的添加内容的修复/评论。
此软件包通过软件包本身中的一系列 Swift Playgrounds 页面提供了大量的文档(我希望如此)。 浏览文档,运行代码,并且一定要阅读关于注意事项!
Playground 示例仍在草稿中。 它们都是可运行的,只是要注意一些错别字。
出于良心,我不能在 README 上没有任何代码,所以请看下面。
要运行 SwiftIWindowBinder Playground 示例,您需要在 Xcode 中打开该软件包,并运行 Playgrounds
下的任何 playground。
请确保启用 'Render Documentation' 和 'Build Active Schema' 选项(默认情况下已启用),以便获得最佳表示,因为 Playgrounds 用作工作文档。
虽然该软件包支持 iOS 13、macOS 10.15 和 tvOS 13,但您需要使用 Xcode 12.2 才能运行 playground。 如果您使用的是 Catalina (macOS 10.15),则 playground 的 'macOS' 目标将无法运行,因为它需要 Big Sur (macOS 11) 才能运行一些 SwiftUI 代码。
这里只有两个真正的例子可以演示。 使用名为 WindowBinder
的东西和一个名为 WindowButton
的便利方法。 正如 playground 的总结文档所暗示的那样,这里可以做更多的事情(例如支持事件视图修饰符,如 onTapGesture
),但为了避免这种情况而选择了放弃。 如果您想了解更多信息,请阅读 Playgrounds ;)。
WindowBinder
是在您的 SwiftUI View
中捕获 Window
的核心。 顾名思义,它使用 Binding
参数将 Window
绑定到视图的 @State
属性(Window
是 UIWindow
或 NSWindow
的平台抽象类型别名)。
WindowBinder
是一个注入到 UIKit 或 AppKit 中实际视图层次结构中的视图,能够利用托管的UIWindow
或NSWindow
。
以下显示了 WindowBinder
的使用,绑定到 self.$window
,然后是内容视图的尾随闭包。 该闭包包含一个 Text 视图,其中包含使用绑定 window
属性的 onTapGesture
视图修饰符。
import SwiftUI
import SwiftUIWindowBinder
struct ContentView : View {
/// You will need a `@State` property in your view for the binding
@State var window: Window?
/// View body
var body: some View {
// Create a WindowBinder and bind it to the state property `window`
WindowBinder(window: $window) {
Text("Hello")
.padding()
.onTapGesture {
// `self.window` will be nil initially, until (this) View's actual view is in the
// hosted window hierarchy
guard let window = window else {
return
}
// Print the window description
print(window.description)
}
}
}
}
不要求您的视图以这种方式编写。 WindowBinder
不需要是根视图,甚至不需要包含任何视图元素,它只需要在您的视图中即可。 以下是一个可接受的替代方案。 内容闭包是为了避免对堆栈的需求而提供的便利。
struct ContentView : View {
@State var window: Window?
var body: some View {
ZStack(alignment: .center, content: {
WindowBinder(window: $window) { /* Nothing */ }
Text("Hello")
.padding()
.onTapGesture { /* ... */}
}
}
}
按钮可能是最常使用与窗口相关的操作的地方。 为了方便起见,WindowButton
封装了 WindowBinder
的逻辑,并提供了 action:
闭包,以便在交互时使用平台相关的 Window
。
修改上面的示例,我们得到一个更简单的 ContentView
import SwiftUI
import SwiftUIWindowBinder
struct ContentView : View {
/// View body
var body: some View {
// Our button that receives a `Window` when interacted with
WindowButton { window in
// Print the window description
print(window.description)
} label: {
Text("Hello")
}
// Just like Button, WindowButton can be styled just the same
.buttonStyle(DefaultButtonStyle())
}
}
与 WindowBinder
示例不同,没有对 window
的保护。 这是因为在没有宿主窗口的情况下,不会调用按钮 action:
闭包。 考虑到 SwiftUI/UIKit/AppKit 的架构,您需要做一些不寻常的事情才能与不在宿主窗口视图层次结构中的视图进行交互。
没错! 有很好的理由。 查看 总结 中的原因,如果您真的,真的想要它,请查看代码示例。
SwiftUI 正在发展,每个版本都获得了大量新功能。 该软件包代表了一种 polyfill 辅助(你好 JavaScript + Babel 命名法),直到我们拥有我们想要的官方包装器的那一天。
我不认为 UIKit 或 AppKit 会很快从 SwiftUI 之下消失(可能永远不会),而且我们中的一些人将继续发现需要像这样的软件包。
当然,bug、问题、拉取请求、更正、建议或评论都请随意...