SwiftUI 的 Pipify

该库引入了一个新的 SwiftUI 修饰符,使视图能够在画中画叠加层中显示。即使应用程序在后台,此叠加层也允许在屏幕上显示信息。

这对于像导航应用程序和股票市场应用程序这样依赖于向用户显示实时信息的应用程序来说非常棒。

注意:此项目目前依赖于 SwiftUI 4,因此最低部署目标为 iOS 16、tvOS 16 或 macOS 13,所有这些都处于测试阶段。 此库不适用于 watchOS。

入门

安装

我们目前支持通过 Swift Package Manager 进行安装。

https://github.com/getsidetrack/swiftui-pipify.git

项目配置

您需要在项目权限窗口中启用“后台模式”。 具体来说,您需要启用音频、AirPlay 和画中画选项。 如果没有此选项,画中画模式将无法启动。

Pipify 视图

“pipify 视图”是实际在画中画窗口中显示的视图。 这可以是您向其添加 pipify 修饰符的视图,也可以是完全不同的视图。

如果您不提供自定义 pipify 视图,那么我们将使用添加了修饰符的视图。 默认情况下,这将使用 Apple 的“morph”过渡,这将动画视图进入画中画控制器。

当提供自定义 pipify 视图时,我们将在屏幕外渲染它,这会导致画中画简单地淡入。

您的 pipify 视图可以是任何 SwiftUI 视图,但需要注意一些重要事项

  1. 创建此视图时,它将不可见地渲染,因此诸如 taskonAppear 之类的闭包可能会在您不希望的时候被调用。
  2. 大多数用户交互都不受支持 - 因此按钮、点击手势等将不起作用。
  3. 动画和过渡可能会导致意外行为,并且尚未经过全面测试。

用法

只需将 pipify 修饰符添加到您的 SwiftUI 视图即可。 根据您是否要提供自己的自定义 pipify 视图(见上文),有两种关键签名。

@State var isPresented = false

var body: some View {
    yourView
        .pipify(isPresented: $isPresented) // presents `yourView` in PIP
        
    // or

    yourView
        .pipify(isPresented: $isPresented) {
            SomeOtherView() // presents `SomeOtherView` in PIP
        }
}

在上面的示例中,您可以将 yourView 替换为您想要显示的任何内容。 这是您现有的代码。 状态绑定决定何时显示画中画窗口。 此 API 类似于 Apple 自己的解决方案,例如 sheet

如果您提供自定义 SwiftUI 视图作为您的 pipify 视图,那么您可以选择将我们的 pipify 控制器作为环境变量添加到该单独的视图中(上面的示例代码中的“SomeOtherView”)。

请注意,您不能在指定 pipify 修饰符的视图中使用 EnvironmentObject。

@EnvironmentObject var controller: PipifyController

这将使您可以访问某些变量,例如返回画中画窗口大小的 renderSize

或者,您可以将我们的自定义闭包附加到您的视图,以获取有关关键事件的通知。

yourPipifyView
    .onPipRenderSizeChanged { size in
        // size has changed
    }

存储库中包含一个基本示例项目。 想要分享您自己的示例吗? 提出一个包含您的示例的拉取请求。

测试

Pipify 将不会在不受支持的设备上启动,并且将在调试控制台中返回一条错误消息,指出它无法启动。 您可以使用 PipifyController.isSupported 检查设备是否兼容,该方法返回 true 或 false。 您可以使用它来显示或隐藏应用程序中的 pip 选项。

您必须在物理设备上测试此库。 由于我们无法控制的模拟器中的问题,在模拟器上运行时,您会看到各种不一致、缺乏支持和其他错误。

它是如何工作的?

该库利用了许多人可能称之为“黑客”的东西,但本质上是 Apple 画中画模式 (PiP) 的一项功能。

画中画已经存在很长时间了,于 2015 年首次随 iOS 9 发布,后来于 2019 年引入 macOS 10.15,最近于 2020 年引入 tvOS 14。 它使用户能够在使用其他应用程序时查看视频内容,例如在阅读推文时观看 YouTube 视频。

Pipify 通过本质上从 SwiftUI 视图创建视频流来扩展此功能。 我们会在您的视图每次更新时对其进行截图,并通过 Apple 的 AVKit 中的一系列函数推送它。 从这些截图(我们将其转换为视频流)中,我们可以启动画中画模式。

从用户的角度来看,该视图在一个应用程序上方的窗口中移动。 视频控件可能可见,可以通过点击隐藏它们。 用户可以随时临时隐藏或关闭画中画窗口。

⚠️没有理由相信此功能会违反 Apple 的 App Store 指南。 App Store 上有一些应用程序使用此功能的示例(示例),但也有应用程序因滥用 API 而被拒绝的情况(示例)。

我们建议开发人员谨慎行事,并考虑对用户来说的最佳体验是什么。 您可能需要探索将声音集成到应用程序中的方法。

感谢

感谢 Akihiro Urushihara 的 UIPiPView,这是构建此 SwiftUI 组件的灵感来源。