大多数浏览器和
Developer App 均支持流媒体播放。
-
使用群组活动打造个性化体验
超越基本的流化和交互,探索如何使用群组活动框架的全部功能带来先进的 SharePlay 体验。我们将向您展示如何将一个简单的绘图 app 改编成实时共享画布,探索诸如 GroupSessionMessenger 之类的 API (这类 API 可以为组内参与者之间发送和接收自定义消息提供帮助),同时了解如何为自定义 SharePlay 体验进行最后润色。
资源
相关视频
WWDC23
WWDC21
-
下载
♪低音音乐播放♪ ♪ 威廉马特拉尔:嗨 我是威廉 我是负责团体活动的工程师 我将要跟安格斯和亚当一起介绍 以团体活动建立自定义体验 首先 我会先介绍 在这个课程中会使用到的app 以及解释为什么很适合在这个app上 加入支持团体活动 接着 我们会介绍活动建立 和对话管理步骤并且解释 和制作媒体体验之间的差异 最后 我们会提到一些方法 让你的用户在使用你的app时 能有更优良的体验 团体活动让你可以通过同播共享 在装置间建立共享体验 尽管重点集中于建立媒体体验 但是那不应该阻止你的想象力 尽情发挥 并且看看你的app 如何在多个装置间体验 我们会用到一些之前在 《以团体活动协调媒体体验》 介绍过的概念 我强烈建议大家去看一下
在这个课程中 我们会做一个叫做 “一起画画”的app 它可以让你…你猜对了 在FaceTime时一起画画 这是一个蛮简单的app 它几乎让整个屏幕成为画布 而且每个人都有个随机颜色 可以拿来画画 虽然它很简单 跟我的朋友们一起画画还是很有趣 可以对着其中一些人厉害的技巧 表示赞叹 或者嘲笑其中一些人画得多差劲 我自己绝对是属于后面那群人 让我向大家快速示范一下 我们要进行的内容
嘿 各位 怎么了? 亚当 我看到你有问题想问我 亚当赛义德:嗯 我和安格斯在聊天 他提到说你是很棒的艺术家 像是毕加索等级的 老兄 你一定要画给我们看一下! 安格斯伯顿:我看过一些威廉的画 真的让人印象深刻 威廉:安格斯 我觉得你有一点夸张 不过我很乐意画给你们看 让我们一起进去“一起画画”app 亚当:好的 我们可以画田园风景吗? 威廉:当然 嗯… 让我从画一个房子开始 或许吧 亚当:好的 安格斯:威廉 这个房子 看起来也太简单了 或许我能帮你加个太阳 让它好一点 亚当:我会试着加一棵树 威廉:那棵树真的太高了 亚当:不是 是太阳太低了 威廉:我来加一些草 亚当:我来多画几棵树 威廉:好的 嗯 我觉得到这里就好 我们都不是艺术家 我们现在不应该再画下去了 我们…晚点再说 好吗? 亚当:好的 安格斯:再见 威廉:大家可以看到 团体活动API的核心体验之一 就是让你们无法聚在一起的时候 可以一起做一些事 这种你能解锁的实时互动 和你的用户可以拥有的即刻反应 因为他们能够看见和听见其他人 因此能产生一些很神奇的时光 这是当你考虑如何将团体活动 整合到你的app中 需要考虑的事情 要采用团体活动 有两个步骤 活动建立和对话管理 我们已经在《协调媒体体验》中 详细地介绍过这部分了 在这个课程中 我们要来看看 建立自定义体验时 该如何改变这些步骤 让我们从活动建立开始 建立团体活动有两个部分 首先 要配置你的活动 接下来要激活那个活动 相较于媒体团体活动 自定义活动只有在配置部分不同 在配置活动时 你必须考虑到你希望 在所有参与者之间分享的特定体验 这个活动必须包含在整个体验中 能维持一致的所有信息 如果你已经看过 《以团体活动协调媒体体验》 这应该看起来很熟悉 我们已经定义 会遵守GroupActivity协议的 “一起画画”的结构 并且实作元数据属性 这里我们以标题建构相关的元数据 现在 要制作这个自定义活动 我们只需要在元数据上 设定正确的类型 通过把类型设定为通用 我们将这个活动配置为自定义活动 这就是相较于配置媒体活动 当你在配置自定义活动时 所需要做的事 让我们跳到Xcode 开始在“一起画画”app中 建立我们的自定义团体活动 首先 让我快速带你们看一下 我们开始要用到的编码 “一起画画”是一个SwiftUI app 而且会使用SwiftUI app生命周期 ContentView是app的主要视图 在这个视图上面 有一个指示器 会显示画画时会用到的颜色 在那下面是CanvasView 它会使用画布并且会负责 在画布上画出的所有笔触 和根据任何用户输入来更新画布 最后 在下面有一个ControlBar 它包含几个 在画画时很有用的控制 目前 我们在那里有单一按钮 可以清除画布和重头开始 画布本身是由笔触的数组所组成 其中每个笔触都有一个颜色 一个标识符和一系列的点 画布也有一个activeStroke 它代表目前本机用户 正在使用的笔触 以及用户会使用的笔触颜色 让我们从配置我们的活动开始 在开始之前 我必须先新增团体活动的权限 我会先进到项目设置 在“签名和功能”的页签中 我要新增一个功能 我会搜寻团体活动 接着选择它 现在我们有权限了 我们总算可以来配置我们的活动 要新增文件 我要进去文件>新增>文件… 然后选Swift文件 把它命名为“一起画画”… 然后按下“建立” 首先 我要输入我们的框架 接着 我们定义一个 叫做“一起画画”的新结构 它会遵守GroupActivity协议 GroupActivity协议 有两个应该要实作的属性 activityIdentifier和元数据 对于activityIdentifier 我会依赖预设实作 不过 元数据属性依然是必要的 所以让我们把它加上去
在这个计算的属性中 我会建立一个 GroupActivityMetadata对象 然后我会设定标题 同时我把类型设为通用 这对自定义活动来说是非常关键的 最后 我回传元数据对象
现在我们已经配置好活动了 我们依然需要在适当的时机激活它 我会新增一个按钮来激活它 有什么比我们的控制列 更适合做这件事呢? 我会在我们的HStack开始的位置 新增一个按钮 至于按钮的标签 我们会使用SF符号 在动作关闭中 我们会建立一个 自定义团体活动的新实例 并且在上面调用激活 这些就是激活我们的活动需要做的事 我刚刚示范了如何配置一个 自定义团体活动 以及如何激活它 这些就是活动建立步骤中 必要的两个部分 现在 我要把时间交给安格斯 他会跟大家介绍对话管理步骤 安格斯:谢谢 威廉 接下来 我们要聊聊 如何在你的应用程序中 使用团体活动来传送和接收 自定义数据 这是以团体活动建立独特的 同播共享体验的中心 根据之前的课程 《以团体活动协调媒体体验》 大家应该已经很熟悉这三个步骤 接收一个对话 准备播放和加入一个对话 我们需要替我们的自定义体验 配置一个对话来取代播放同步 不过在我们进入那个之前 让我们新增编码来接收团体对话 并且加入它 让我们回到Xcode 我们第一件要做的事 是找到ContentView 和输入GroupActivities
接下来 我们要建立一个异步任务 来接收我们的GroupSession 现在我们有GroupSession了 我们需要一个储存它的位置 让我们用一个新方法 “configureGroupSession” 来把它储存到 我们的画布对象上 现在 让我们去画布那边 并且实作 configureGroupSession方法
我们来到最上方 确认有输入GroupActivities 让我们到文件的下方 接着实作我们的新方法 这里 我们将接收到的 groupSession对象 指定到我们的类别上的新属性 同时注意到我们 在设定groupSession属性前 重新设定了画布 最后 但同样很重要的 让我们新增编码来加入groupSession 现在 我们应该可以 建立和编译我们的项目了 现在让我们来测试一下 我们到产品>建立
很好 既然我们已经建立了 接收和加入团体对话的编码 让我们来看看如何 在我们的应用程序中 配置用来传送和接收 自定义数据的对话 针对配置对话 我们会使用GroupSessionMessenger 它会提供一个简单的API 在团体对话中 从参与者那里 接收或寄送原始文件数据 或结构化的信息 或是将这些内容传送给他们 接着让我们来看看如何使用 GroupSessionMessenger 首先 我们会从我们的groupSession 建立一个GroupSessionMessenger 使用GroupSessionMessenger的 第一步 是定义在你的应用程序中 哪一种类型的数据 需要在参与者之间被交换 在“一起画画”中 我们需要用来 和其他装置分享的特定数据 是笔触本身 我们可以用三个属性来呈现笔触 标识符、颜色和坐标点 注意到我们让UpsertStrokeMessage 遵守可编码的协议 这是因为GroupSessionMessenger 让我们传送和接收结构化的信息 而且只要信息为可编码的 就会自动帮我们处理 序列化和去序列化 配置对话的第二步 是在GroupSessionMessenger上 使用信息API来接收数据 在“一起画画”中 我们必须要处理 接收UpsertStrokeMessages 这里显示的信息API可以用 可编码的类型 和回传一个异步序列 这会给我们一个该类型 包含信息的多元组 以及被这个信息包围的环境 这包含像是 哪位参与者传送了信息的信息 要配置对话的第三步是 在GroupSessionMessenger上 使用传送API来传送数据 在“一起画画”中 我们会在团体间 传送一个UpsertStrokeMessage 给所有参与者 注意到传送API是一个异步抛出方法 你的应用程序应该要适当地处理 它抛出的错误 现在 让我们去Xcode 并且新增 GroupSessionMessenger编码 我们要先去画布源文件 并且从我们的团体对话 建立一个GroupSessionMessenger
我们要将信息属性加到画布上 这会拦截我们刚刚建立的信息对象 接着我们需要定义 UpsertStrokeMessage 这会在参与者被传送和接收 让我们替它建立一个新的文件 我们要先去文件>新增>文件… 选择Swift文件… 让我们把它放到“模型”文件夹中 把它命名为“信息” 现在加入编码来定义 UpsertStrokeMessage 既然我们定义了 要以GroupSessionMessenger 传送和接收的信息 让我们写入编码来传送后接收它 我们要回到画布来做这件事
让我们到文件的下方 并且新增编码来接收信息
这里 我们建立一个中断作业 来从异步序列 接收UpsertStrokeMessages 以及调用新方法:“处理” 来处理这个信息 接着让我们来实作这部分 在这个编码中 我们会通过 查看它的标识符 来确认我们是否已经有一个笔触 如果有的话 就加上一个点 否则我们要建立一个新笔触 再加上一个点 并且将这个笔触 加到我们的笔触数组中 接着 我们要写入编码来传送信息 我们要到上面的方法 addPointToActiveStroke 太好了! 现在让我们来执行我们的app 看看共享的“一起画画”体验 实际执行的样子 我先找到我的两台装置 然后我要跟我自己进行FaceTime 我到“电话”app 接着打给我自己 我要在我另一台装置上接听电话 关掉麦克风 现在 在这台装置上 我要开始 一个共享“一起画画”体验 按下左下方的图示 在另一台装置上 我要加入团体对话 现在 我要跟自己玩井字游戏 我先动
看起来我赢了 很棒 看起来我们的编码没问题
既然我们讲了如何 使用GroupSessionMessenger 配置对话:首先定义信息 接着接收信息 最后传送信息 现在让我们来讲讲 在使用GroupSessionMessenger时 需要考虑的其他事情
本质上来说 GroupSessionMessenger 提供可靠和先进先出顺序的信息传递 给在团体中所有活跃的参与者 你传送的信息确实有一些限制 如果它们太大 传送API 就会丢出错误 GroupSessionMessenger 是给较小的负载使用 而且不应该用于串流大的资产 像是文件、图像或视频 在传送信息时另一件要考虑的事是 流量控制和速率限制 在快速演替中传送大量的信息 例如循环 可能会导致传送API丢出错误 最后 在定义 和GroupSessionMessenger 一起使用的信息时 考虑在你的应用程序协议中 加入版本控制支持 这能让你的应用程序 支持执行较旧版本软件的 装置的互操作性 现在 我要把时间交给亚当 他会告诉大家 如何完善你们的 GroupActivities体验 亚当:谢谢 安格斯! 现在让我们来讲一些 你们app的自定义体验会需要的 最后加工 首先 让我们来谈谈后加入者 后加入者是在对话开始后 才加入activitySession的装置 为了确保能得到适当的体验 后加入者需要取得最新的信息 这样所有装置才能经手相同的数据 这个情形对于确保能有 连贯的用户体验来说很重要 可是它却不是通用的 这个赶上过程所需的数据 取决于你的app和体验 我们来看看它是怎么应用在 “一起画画”app中 假设我们在团体对话中有两台装置 这两台装置有相同的信息 在它们的画布上画着一个笑脸 你可以看到是因为在画的时候 两台装置的在对话中 它们在体验中拥有相同的数据 现在 我们再加入另一台装置 这个时候 新的装置 在它们的GroupSession上调用加入 不过画布上什么都没有 所以我们画了一朵云 喔 那… 这可不行! 因为新的装置没有之前的上下文 现在我们的笑脸上有一朵云 让我们退回去 再重新试一次 我们该怎么修补它 让新装置在一加入的时候 上面就显示出笑脸呢? 一旦新装置在团体对话调用加入 其他所有加入在团体对话中的装置 都会看到GroupSession上的 activeParticipants属性启动了 接着 观察到那个信号的装置 会传送出它的赶上数据 到新加入的装置上 在这个案例中 指的是画布上的图 现在当新装置加入时 它就会看到那里已经有一个笑脸 所以我们可以围绕着它画! 那么 我们如何在编码中做这件事? 首先我们要了解在赶上信息中 我们的app需要传输什么样的数据 既然这是一个“一起画画”体验 我们的目标是确保 每台装置上画布都是相同的 让我们在Message.swift文件中 制作一个新信息 叫做“CanvasMessage” 这个结构会包含我们拥有的全部笔触 和一个叫做“pointCount”的变量 这会被当成启发法来计算 哪一个信息是最新的 太好了! 现在 我们要如何接收这个信息? 如果我们在Canvas.swift中 查看我们的画布模式 我们可以用安格斯刚才示范过的 GroupSessionMessenger 在configureGroupSession中 建立一个信息处理器
从这里 大家可以看到我们叫入 我们的处理函数 让我们实作这个吧 在这个编码中 大家可以看到 我们防止我们的pointCount启发法 让它只能接受比我们目前还新的 赶上信息 如果那个条件通过 就会往下进行 以catchupMessage的笔触 覆盖我们画布的笔触 现在 就像我们之前讨论过的 我们必须监听 activeParticipants的改变 来厘清是否有任何 需要我们沟通的新参与者 让我们来把它加入我们的 configureGroupSession函数 在这个处理器中 你能看到我们取用 新的activeParticipants 和旧的activeParticipants 之间的差量 这会确保我们只将赶上信息 传送给新加入的参与者 太棒了! 现在只要制作和传送信息就可以 这个信息会包含我们目前的画布状态 而且只会寄给新的参与者 就是这样! 这就是赶上信息! 现在我们具备在一个特定活动中 让团体对话进行的所有条件了 那如果我们想要把活动整个换掉 该怎么做呢? 这可以是换掉画画的画布 或是换掉电影这类的事 我们的API提供两种方式来改变活动 你可以建立新的团体对话 或是在你现有的团体对话中 帮每个人更新活动 让我们来谈谈这两种 第一个 也是优先用来 改变内容的方法 是通过调用开始这个团体对话的 同一个API 在GroupActivity上的 prepareForActivation 这个方法比较容易让参与者之间 推断出一致的状态 因为它对进入和离开GroupSession 提供了干净的屏障 所以你不用担心延迟状态 或是来自旧的GroupSession 那些你不需要的信息 当用户退出活动去找下一个 像是搜寻新的笔记或电影 这个方式就非常有用 这也会给系统一个重大改变的指示 并且用它来通知用户 在这个调用之后 和开始一个团体对话的方式一样 你会通过GroupActivity上的 对话异步序列 收到你的新的GroupSession 现在 假设你会在你的应用程序上面 一系列的活动之间转换 像是很多首歌曲会接着播放呢? 我们的GroupSession API 提供一个简单的方法 让你只要在GroupSession上面 设定活动属性 就可以帮所有人更新 从那里 你可以监听活动属性的改变 我们的API会确保 装置一直交会在相同的活动上 所以你不用担心这部分 现在我们在概念上理解这两种方式 在“一起画画”app中 我们应该用哪一个呢? 因为我们的app希望每次新的画布 都能有干净的画板 新的对话API绝对可以让我们 做到这件事 让我们跳到Xcode来看怎么实作它 第一步是要决定 我们想要如何触发新的对话 在我们的案例中 我们把它设定成 当用户按下“复原”按钮时 我们就会建立一个新的GroupSession 如果去看我们的ControlBar编码 会看到我们已经 有一个CapsuleButton 那会叫回我们的画布模式 并且复原本机的状态 让我们调整那个函数 来推翻GroupSession 并且建立一个新的 在这个编码中 我们会取消 所有在GroupSession中的作业 和可取消的内容 我们也会检查我们有没有 GroupSession 如果有的话 把它留在那里 并且在我们的“一起画画”类型上 叫入激活属性 然后我们接收GroupSession的 正常流程 就会从那里接手 我们就能开始了! 现在我们有个干净的方式 来转换到新的画布上 假设我们想更改我们的用户界面 来指示用户说 他们可以和朋友们试着使用 同播共享体验呢? 举例来说 在我们的画画app中 我们想要我们的画布从这个 更改成这个 也就是当我们符合GroupSession时 你会注意到上面出现一个 用来分享画布按钮 这个要怎么做呢? 通过GroupStateObserver API 当装置符合团体对话时 发布者就会告诉我们 接着我们可以通过它 动态地显示或隐藏我们的按钮 让我们来实作它吧! 就像我们刚刚看到的 我们希望 在我们应用程序的左下方有个小按钮 既然在我们应用程序的 ControlBar视图中 已经有一个分享按钮 让我们根据GroupStateObserver 来改变显示和隐藏按钮的行为 首先我们将groupStateObserver 新增到我们的视图 现在让我们将CapsuleButton 编写成只有在我们符合groupSession 以及我们还不在这个groupSession 里面时才会显示出来
这样就可以了! 现在我们的按钮只有在 对用户有帮助的时候 才会动态地出现 让我们复习一下 我们在这个课程看了哪些内容 我们介绍了建立一个简易画画app的 完整步骤 接着我们把它用在团体活动中 让团体活动比过去更同步 连结得更好 更重要的是 我们介绍了 能让大家发挥创意 以及用团体活动 建立各种自定义同播共享体验 所需要的所有步骤 我们讲到以通用类型 建立自定义活动 配置和使用GroupSession 以及GroupSessionMessenger 来做同步化的沟通 并且优化你的app应该使用的 案例和API 来达到丰富的用户体验 我希望大家喜欢和我们一起 建立这个自定义体验 很期待看到大家在团体活动框架上 尽情发挥创意! 各位在学习团体活动的下一步 应该是《设计团体活动》这个课程 如果你还没看过 《以团体活动建立媒体体验》 也请大家去看看 如果各位有任何问题 请通过团体活动实验室联络我们 最后 谢谢你们的收看 祝你们有个很棒的WWDC 我们很期待看到大家建立的内容! ♪
-
-
3:50 - Configuring your application’s activity
struct DrawTogether: GroupActivity { var metadata: GroupActivityMetadata { var metadata = GroupActivityMetadata() metadata.title = NSLocalizedString("Draw Together", comment: "Title of group activity") metadata.type = .generic return metadata } }
-
10:06 - Define, Receive and Send messages
let messenger = GroupSessionMessenger(session: groupSession) // 1. Define struct UpsertStrokeMessage: Codable { let id: UUID let color: Color let point: CGPoint } // 2. Receive for await (message, context) in messenger.messages(of: UpsertStrokeMessage.self) { // Handle message } // 3. Send do { try await messenger.send(UpsertStrokeMessage(id: stroke.id, color: .red, point: point)) } catch { // Handle error }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。