SwiftUIWindowBinder

Swift Version iOS Version macOS Version tvOS Version watchOS Version GitHub license Maintained Release

概述

SwiftUIWindowBinder 支持在 SwiftUI 中访问宿主 Window 对象(UIWindowNSWindow),无需任何设置。 没有应用程序代理或场景代理的 SwiftUI 应用程序仍然可以访问 Window,并且该窗口的作用域限定为多窗口应用程序中的每个文档。 这里也支持 Playground。

安装

要在您的项目中使用 SwiftUIWindowBinder,请参阅如何使用 Swift Package Manager 或在 Xcode 中引用包,使用此存储库的 GitHub 链接。 安装完成后,您可以根据需要导入 SwiftUIWindowBinder

添加到 Package.swift

要在 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 上没有任何代码,所以请看下面。

Playgrounds

要运行 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

WindowBinder 是在您的 SwiftUI View 中捕获 Window 的核心。 顾名思义,它使用 Binding 参数将 Window 绑定到视图的 @State 属性(WindowUIWindowNSWindow 的平台抽象类型别名)。

WindowBinder 是一个注入到 UIKit 或 AppKit 中实际视图层次结构中的视图,能够利用托管的 UIWindowNSWindow

以下显示了 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

按钮可能是最常使用与窗口相关的操作的地方。 为了方便起见,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 的架构,您需要做一些不寻常的事情才能与不在宿主窗口视图层次结构中的视图进行交互。

等等,有 WindowButton,但没有事件视图修饰符?

没错! 有很好的理由。 查看 总结 中的原因,如果您真的,真的想要它,请查看代码示例。

享受吧!

SwiftUI 正在发展,每个版本都获得了大量新功能。 该软件包代表了一种 polyfill 辅助(你好 JavaScript + Babel 命名法),直到我们拥有我们想要的官方包装器的那一天。

我不认为 UIKit 或 AppKit 会很快从 SwiftUI 之下消失(可能永远不会),而且我们中的一些人将继续发现需要像这样的软件包。

当然,bug、问题、拉取请求、更正、建议或评论都请随意...