View in English

  • 打开菜单 关闭菜单
  • Apple Developer
搜索
关闭搜索
  • Apple Developer
  • 新闻
  • 探索
  • 设计
  • 开发
  • 分发
  • 支持
  • 账户
在“”范围内搜索。

快捷链接

5 快捷链接

视频

打开菜单 关闭菜单
  • 专题
  • 相关主题
  • 所有视频
  • 关于

返回 WWDC25

大多数浏览器和
Developer App 均支持流媒体播放。

  • 简介
  • 转写文稿
  • 代码
  • 小组件的新功能

    WidgetKit 带来小组件、实时活动以及控制方面的众多更新,助你提升 App 性能。了解如何将小组件引入 visionOS,如何利用 CarPlay 车载让小组件在驾驶途中也能顺畅使用,以及如何使用强调渲染模式优化小组件的视觉效果。此外,探索如何让相关小组件呈现在 watchOS 的智能叠放中,以及如何使用推送通知确保小组件保持最新。

    章节

    • 0:00 - 简介
    • 1:03 - 小组件登陆新位置
    • 15:31 - 相关小组件
    • 18:12 - 推送小组件更新

    资源

    • Increasing the visibility of widgets in Smart Stacks
    • Optimizing your widget for accented rendering mode and Liquid Glass
    • RelevanceKit
    • Updating widgets with WidgetKit push notifications
    • Updating your widgets for visionOS
      • 高清视频
      • 标清视频

    相关视频

    WWDC25

    • 针对 CarPlay 车载优化你的 App
    • 针对 visionOS 设计小组件
    • watchOS 26 的新功能

    WWDC24

    • 为 Apple Watch 设计实时活动
    • 将 App 控制扩展到系统级别

    WWDC23

    • 小组件的新位置
    • 认识推送通知控制台

    WWDC20

    • 推送通知入门
  • 搜索此视频…

    大家好 我是 Tanner Oakes System Experience 团队 的一名工程师 小组件非常适合在系统各处 及时呈现相关资讯和操作选项 让你的 App 始终为用户所用 而无论它是否显示在前台 C 位 WidgetKit 不断升级扩展 不仅让你的小组件更强大 还让你的小组件能够亮相新位置 我朋友 Luca 有一款咖啡因记录 App 我一直在帮他更新 在这场讲座中 我将展示 这款 App 如何充分利用 小组件方面的所有新功能 在这个视频中 我将介绍 小组件、实时活动 和各种控件在不同平台上 扩展到了哪些新位置 我还将介绍一种新方法 让你将相关小组件内容 显示在 watchOS 的智能叠放中 最后 我将介绍如何利用推送通知 让你的小组件跨设备保持最新状态 小组件、实时活动和控件 现在支持更多新位置 但首先 我将介绍小组件 在传统显示位置的新外观 在 iOS 26 中 用户可将主屏幕配置为 以透明玻璃呈现方式 展示图标和小组件 还可以选择特定的色调进一步自定 比如这里的蓝色 用户也可以在 macOS Tahoe 中 对桌面上的小组件和 通知中心进行同样的配置 这些新的内容呈现形式在 iOS 和 macOS 上采用类似的构造方式 首先 小组件内容 采用强调渲染模式生成 将所有内容的色调调整成白色 然后 移除小组件背景 取而代之的是符合主题风格的 玻璃或着色效果 我的咖啡因追踪小组件 在强调模式下看起来很棒 无需我进行任何调整 不过 有的小组件可能需要一些调整 才能在强调模式下呈现出最棒的效果 我在 App 中添加了一个小组件 用来显示我最常喝的饮料 我最常喝的是抹茶拿铁 我的小组件会显示大图 底部显示饮料名称 文本之后呈现渐变效果 让图像上方的文本更清晰易读 应用强调渲染模式 会将小组件所有内容的色调 调整为白色 不透明内容会变成单一的白色 就像这里的拿铁咖啡杯 半透明的内容 比如这里的渐变效果 则会保留原有的不透明度 变为白色调 这可不是我想要的效果 现在根本看不出图像表示的是什么 而且也看不清文字 下面我来展示一下如何更新小组件 以便更好地应用强调渲染模式 这是我的小组件视图 ZStack 部分显示了饮料图像、 渐变效果和文本视图 首先 把 widgetRenderingMode 这个环境变量添加到小组件视图中 接下来添加条件 仅当小组件渲染为全色图像时 才显示大图和线性渐变效果 然后添加 VStack 让图像重新显示在文本之上 并设置条件 仅当小组件 以强调模式呈现时才显示图像 整体布局看起来真不错 现在只需要改善一下图像的显示效果 将 widgetAccentedRenderingMode 修饰符添加到图像中 将参数设为 desaturated widgetAccentedRenderingMode 是一种 SwiftUI 修饰符 可应用于小组件中的图像 通过设置传入的参数 我可以精确控制 图像在强调模式下的显示效果 widgetAccentedRenderingMode 支持五种不同的参数选项 下面 我们来看看各种模式 如何影响图像的呈现效果 并将 iOS 和 macOS 上的强调模式 与表盘上的强调模式比较一下

    对 widgetAccentedRenderingMode 而言 传入 nil 相当于完全不应用修饰符 这样可将内容主色应用于图像 对于 iOS 和 watchOS 小组件 这种设置会让我的图像变为纯白色 传入 accented 可将图像的色调 调整为强调色 在 iOS 和 macOS 上 主色和强调色都是白色 因此 这种设置会将图像变为纯白色 在 watchOS 上 强调色与表盘颜色保持一致 因此 这个 watchOS 小组件的图像 会显示为蓝色 传入 desaturated 可将图像中的颜色去饱和 这种效果会在 iOS 和 watchOS 上 呈现同样的样式 传入 accentedDesaturated 可应用两种效果 将图像的颜色去饱和 同时应用所选主题的强调色 在 iOS 上 这意味着 图像会显示得更白一些 而在 watchOS 上 去饱和的图像 现在会以所选主题的蓝色强调色呈现 传入 fullColor 会呈现 强调渲染模式下 完全不做修改的图像 为了与表盘完美融合 watchOS 不支持这一选项 对于大多数小组件 建议使用 desaturated 或 accentedDesaturated 以便图像内容与主屏幕的其余部分 更好地融合 建议使用 fullColor 来设计 用于表示媒体内容的图像 例如专辑封面或书籍封面 接下来 很高兴向你介绍如何 将你的小组件提升到全新维度 在 visionOS 26 中 由你构建的 visionOS App 现可包含小组件 如果你已经构建了带小组件的兼容 iPhone 或 iPad App 你的小组件将自动移植到 visionOS 支持 iOS 和 macOS 的 各种系统系列尺寸 visionOS 上的小组件 具备交互和动画功能 就像其他平台一样 我将介绍小组件在 visionOS 上的 工作方式以及 WidgetKit 的新选项 在 visionOS 中 用户可以将小组件 添加到房间 然后固定在表面上 小组件默认将呈现优化外观 直接显示在表面之上 也可以将小组件嵌入显示 让小组件呈现出直接嵌入表面的观感 如果这些样式不适合你的小组件 不妨用 supportedMountingStyles 修饰符来配置小组件 指定希望应用哪种选项 这个修饰符对 visionOS 和 iOS 小组件均适用 小组件默认呈现在玻璃纹理下方 对于 visionOS App 还可以指定纸质纹理 为小组件添加海报风格的外观 左侧悬停的是配置了玻璃效果的 常喝饮料小组件 右侧悬停的是同样的小组件 只是应用了纸质纹理 使用 widgetTexture 修饰符 来配置小组件 指定你希望小组件使用纸质材质渲染 还是呈现在玻璃面板之后 为了完善海报风小组件的视觉效果 visionOS 新推出了一个小组件系列 systemExtraLargePortrait 这里显示的纵向图像 是现有横向小组件系列 systemExtraLarge 的变体

    使用 supportedFamilies 修饰符 即可将纵向版本添加到小组件配置中 在visionOS中 小组件的颜色主题也可以自定 默认情况下 小组件将以全彩显示 如果我选择这个绿色主题 小组件的内容 会以强调渲染模式显示 小组件的边框和内容会将色调 调整为所选颜色 然后系统将移除背景 将背景替换为与所选颜色主题 相得益彰的实色 这种样式采用的渲染方法 与我们之前提到的适用于 iOS 和 macOS 的渲染方法相同 你可以运用刚才介绍的所有技巧 让自己的小组件在这些颜色主题下 呈现出最棒的效果 widgetAccentedRenderingMode 修饰符 用于自定图像的呈现形式 widgetRenderingMode 环境变量 用于根据指定条件应用重大修改

    在 visionOS 中 你可以将小组件移植到自己的环境 并放置在多个表面上 当你在空间中四处移动时 小组件会显示在固定的位置 即使显示在房间那头的墙面上 小组件仍然能够保持可见 就像实物一样 你离小组件越远 小组件就会变得越小 越难看清 与实物不同的是 你可以让 visionOS 中的小组件 根据距离进行相应的调整 方法是使用新的 LevelOfDetail API 我将介绍如何将 LevelOfDetail 添加到我的小组件中

    这里 现有的小组件显示了 当天的咖啡因摄入总量、 我喝的上一杯饮料 以及一个方便好用的按钮 用于快捷记录另一杯同类饮料 当小组件离我更远时 我希望咖啡因摄入总量能够 显示为更大的字号 这样更容易看清 我还想隐藏这个按钮 因为距离太远的话 可能很难点按按钮 这里是我的咖啡因追踪小组件 我想要更新 TotalCaffeineView 更改尺寸 并添加条件 指定何时在底部 显示或隐藏 LogDrinkView 首先 将这个环境属性 levelOfDetail 添加到视图中 你可以将 levelOfDetail 设为两个值 一个是 default 这是小组件的常规详细程度 在 visionOS 上 如果与小组件保持适当的距离 它会以默认详细程度显示 如果与小组件的物理距离足够远 详细程度会变为 simplified 让你能够以更简单、 更一目了然的方式呈现小组件 我要为 LogDrinkView 添加一个条件 当 levelOfDetail 为 default 时 才显示这个按钮 接下来需要更新咖啡因摄入量的字号

    在咖啡因摄入总量视图中 现在显示了标题 以及带格式的咖啡因摄入总量 首先 将 levelOfDetail 环境变量 添加到视图中 为了让字号变大 我要添加一个条件 根据详细程度 将咖啡因摄入量的字号 从标题更改为大标题

    现在 每当小组件离得足够远时 我的小组件就会转而呈现 更简单、更一目了然的版本 就像小组件中的时间线会变化一样 详细程度的变化也会以动画形式呈现 要进一步了解 何时需要自定空间样式、 详细程度方面有何建议 以及将小组件移植到 visionOS 这个新平台需要考虑其他哪些因素 请观看 “针对 visionOS 设计小组件” 现在我们“换个档” 聊聊新话题 如今 借助 CarPlay 车载 小组件和实时活动也能上路使用了 在 CarPlay 车载 Ultra 中 小组件显示在仪表板 左侧的一个或多个叠放中 从 iOS 26 开始 这项功能将登陆 所有支持 CarPlay 车载的汽车 用户可以在 CarPlay 车载的 “设置”App 中配置小组件 在 CarPlay 车载中 一目了然的信息、大号字体和清晰度 都非常重要 有助于在汽车显示屏上 构建清晰易读的小组件 因此 CarPlay 车载 以 StandBy 样式渲染小组件 使用参数为 fullColor 的 systemSmall 系列 并移除小组件背景 触控屏支持小组件交互功能 你可以访问开发者网站 下载 CarPlay 车载模拟器 对你的小组件进行测试 要了解调整小组件以便适应 StandBy 呈现形式的更多技巧 请观看 WWDC23 讲座 “小组件的新位置” 实时活动也可以 显示在 CarPlay 车载的主屏幕上 默认显示实时活动的灵动岛 前置视图和后置视图 这是我的咖啡订单追踪实时活动 在 CarPlay 车载中的显示效果 开头还不错 但只需再加几行代码 效果就会变得更棒 这是实时活动的代码 目前显示的是前置视图和后置视图 为了针对 CarPlay 车载 自定这个实时活动的呈现方式 我要添加修饰符 supplementalActivityFamilies 传入 small 作为 ActivityConfiguration 的参数 现在 CarPlay 车载不再显示 前置视图和后置视图 而是会显示 ActivityView 也就是 iPhone 锁定屏幕的同款视图 对于许多实时活动来说 这样设置也许就能呈现很棒的效果 但是我的视图有点拥挤 有些内容被截断了 好在我还可以进一步自定显示效果

    这是我的 ActivityView 我要在视图中添加 activityFamily 环境变量 添加变量后 在我的视图正文中 我可以添加条件来显示不同的内容 或调整布局 从而提供出色的体验 当 activityFamily 设为 small 时 我要呈现专为更小布局优化的 咖啡店订单视图 在其他情况下 则显示我的默认订单视图 只是添了几行代码 现在 我的实时活动 在 CarPlay 车载中的显示效果很棒 这样 只需快速瞥一眼 我就能知道 我的订单还有多久才能准备好 通过采用 supplementalActivityFamily 我还大大改善了我的实时活动 在配对 Apple Watch 上呈现的效果 你的 iPhone App 将 自动呈现同款效果 无需构建单独的 watchOS App 要进一步了解如何让实时活动 在 Apple Watch 的智能叠放中 呈现最棒的视觉效果 请观看 “为 Apple Watch 设计实时活动” 还可观看 “针对 CarPlay 车载优化你的 App” 进一步了解如何为 CarPlay 车载中 的小组件真正提速 实时活动的新位置 不止 CarPlay 车载一个 现在 macOS Tahoe 中会显示 来自配对 iPhone 的实时活动 就像在 iPhone 上的灵动岛一样 我的咖啡订单追踪实时活动 会在菜单栏呈现前置视图和后置视图 选择实时活动后 电脑上会显示 来自 iPhone 的锁定屏幕界面 点按锁定屏幕界面 会启动相应的 App 而这得益于 iPhone 镜像功能 macOS 上的实时活动支持 运行 iOS 18 及更高版本的 iPhone 无需更改代码 就像 macOS 上的 iPhone 小组件一样 这些小组件支持交互功能和深度链接 现在 我将介绍 macOS 和 watchOS 上控件的新位置 在 macOS 上 控件来源可以是 Mac 上运行的 App 使用 macOS SDK 或 Catalyst 构建均可 也可以是 Mac 的 Apple 芯片机型 上运行的 iOS App 你可以将控件添加到控制中心 iOS 上提供的小尺寸、 中尺寸和大尺寸呈现方式 也可以在 macOS 上进行配置 控件也可以直接放在菜单栏上 现在 我已经在 macOS 上的 App 中 添加了咖啡追踪控件 这样就可以直接从菜单栏 轻松更新我的咖啡日志了 在 watchOS 26 中 控件可以显示在三个位置 你可在控制中心配置控件 以便用户通过侧边按钮执行操作 对于 Apple Watch Ultra 你也可将控件设为 在按下操作按钮时启用 你还可在智能叠放中配置控件 让控件与其他小组件并排显示在一起 呈现控件的符号、标题和当前值 控件来源可以是 watchOS App 或者配对设备上的 iPhone App 有关构建控件的全面指南 请观看 “将 App 控制扩展到系统级别” 接下来 我将介绍 watchOS 26 上 智能叠放中的相关小组件 在 watchOS 的咖啡因追踪 App 中 我添加了一个小组件 帮我追踪我喜欢的咖啡店 推出的半价特惠时段 对于这款小组件 我希望改进两个地方 首先 因为我想要追踪 几家咖啡店的限时特惠 他们的特惠时段往往有所重叠 这就导致智能叠放中 我的小组件上的内容非常拥挤 其次 特惠时段往往 集中在一天的某个时段 因此 在一天中的其他时间 我的小组件并不是很有用 我其实希望我的限时特惠小组件 仅在切实相关时显示在智能叠放中 并为当前有效的每个特惠时段 显示更多详细信息 借助 watchOS 26 中的相关小组件 我就可以实现这种效果 我将介绍如何将限时特惠追踪工具 配置为相关小组件 要定义相关小组件 需要创建一个 Widget 类型 这里不要提供 StaticConfiguration 或 AppIntent 配置 而是要提供 RelevanceConfiguration 就像其他配置一样 这个配置需要 king 字符串、provider 对象 以及将自定 entry 转换为 SwiftUI 视图的闭包 将 provider 类型设为 RelevanceEntriesProvider placeholder 和 relevance 方法 类似于 TimelineEntriesProvider 对于 placeholder 我可以返回一个简单的 Entry 让它在我准备内容的时候显示出来 对于 relevance 首先获取我之前定义的 一系列对象

    对于我的限时特惠小组件 仅当介于特惠时段的开始时间 和结束时间之间时 这个配置才切实相关 因此 我要用这个上下文 根据每个限时特惠的日期间隔 来定义 relevance 属性 然后实现 entry 方法 与时间线小组件不同的是 RelevanceEntriesProvider 仅为配置提供单个 entry 我的配置中提供了 这一 entry 所需的所有数据 咖啡店数据和限时特惠的时间范围 这样就可以立即创建了 如果我需要任何其他数据或素材 我可以从这里获取 因为这个方法已标为 async 现在 有了我的相关小组件 我的限时特惠小组件 仅在相关时才会显示在智能叠放中 此外 如果同时有多个相关配置 智能叠放中会显示 这个小组件的多个实例 相关小组件是 watchOS 26 新推出的强大功能 让你能够将小组件内容 与其相关性直接联系起来 这些小组件本身就是很棒的附加功能 也非常适合搭配 现有的时间线小组件一起使用 要进一步了解相关小组件 请观看 Anne 为你带来的 “watchOS 26 的新功能”讲座 小组件现已拓展到更多位置和平台 无论具体显示在哪里 我都希望 我的小组件始终保持最新状态 我最近添加了一个服务器 这样就能跨设备保持 咖啡因日志数据的同步 我将介绍哪些选项 可用于刷新我的小组件 先来看看定时小组件重新载入 在这个图表中 左边显示了我的 App 套装 其中包含我的 App 以及我的小组件扩展 右边 我用这个方框 来表示 WidgetKit 当你在设备端配置好小组件时 例如将小组件设为显示在 iPhone 主屏幕或表盘之上 WidgetKit 向小组件扩展 请求时间线 扩展传回小组件时间线 其中还包含 TimelineReloadPolicy WidgetKit 用它来确定 下次重新载入这个小组件的恰当时机 TimelineReloadPolicy 非常适合用于 需要定期更新的小组件 比如显示咖啡店运营时间的小组件、 天气小组件或股票小组件 系统会为定时时间线重新载入 分配预算 以便保障性能和电池续航

    WidgetCenter API 是 App 可以 采用的另一个选项 App 中的数据更改 应该反映在相应的小组件中 你可以调用 WidgetCenter 的 reloadAllTimelines 或 reloadTimelines(ofKind:) 方法 这样会告诉 WidgetKit 小组件的内容过期了 需要重新载入 WidgetKit 随后会请求 小组件扩展提供时间线 以便更新小组件 这个选项非常适合小组件内容 主要在 App 内发生变化的用例 例如更新咖啡因日志更新、更改笔记 或标记完成提醒事项 由于调用这个 API 时 相应的 App 正在运行 系统不会为这个请求分配预算 但是 如果数据更改发生在服务器或 其他设备上 该怎么办呢? 这时 小组件推送更新就派上了用场 如果追踪到数据更改 服务器可以向 APNs 发送推送通知 从而告诉 WidgetKit 重新载入 这款 App 的小组件 与其他更新不同 WidgetKit 随后会请求小组件 提供更新的时间线 小组件推送更新非常适合 数据更改发生在设备之外的用例 就像 TimelineReloadPolicy 一样 系统也会为小组件推送通知更新 分配预算 以便保障性能和电池续航 得益于这种功能 小组件现在支持一整套重新载入选项 能够适应各种用例 这些选项彼此并不互斥 有些小组件可能需要结合使用 其中两个选项 甚至全部三个选项 得益于组件推送更新 小组件的咖啡因日志 可以跨设备保持最新状态 而无论日志更新来源于 iPad App、 Apple Vision Pro 小组件 还是 macOS 菜单栏控件 下面 我来展示一下 如何让小组件支持推送通知 我打算创建 WidgetPushHandler 将它添加到小组件配置中 在小组件扩展中添加推送通知授权 然后构建小组件更新推送请求 首先 创建一个符合 WidgetPushHandler 协议的结构体 这个类型决定了当推送令牌或 一组配置好的小组件发生变化时 我们如何向你发送通知 利用 pushTokenDidChange 方法 向服务器发送你的推送令牌 和小组件信息 接下来 我需要更新小组件配置 这里显示了咖啡因追踪小组件的配置 在我的小组件中 添加 pushHandler 修饰符 以登记小组件对推送通知的支持 对于这个修饰符 传入已经实现的 小组件 pushHandler 的类型

    最后 转到 Xcode 在 Signing & Capabilities 标签页 针对小组件扩展进行相关配置 在这里添加推送通知授权 以便小组件能够与 APNs 通信 现在 我的小组件已经配置好 可以接受推送更新了 我来展示一下如何发送 小组件更新推送通知 为了通过推送通知更新小组件 向 Apple 推送服务器 发送 HTTPS POST 请求 将 WidgetPushHandler 中 提供的小组件推送令牌 附加到请求路径的末尾 在标头部分 将 apns-push-type 设为 widgets 对于 apns-topic 标头 使用 App 的套装 ID 进行设置 并添加 .push-type.widgets 后缀 对于请求的正文部分 在 aps 词典中 将 content-changed 的键值 设为 true 要进一步了解推送通知 请观看“推送通知入门”讲座 还可观看 “认识推送通知控制台”讲座 了解如何轻松测试推送通知请求 小组件的推送更新有助于确保 小组件内容更及时地进行更新 不过 你需要酌情应用 因为它的目的并不是 完全取代其他通知体验 如果需要显示紧急或重要的更新 推荐提供用户通知 如果需要在限定时段定期更新 例如饮料订单、体育赛事比分 或航班信息的更新 推荐使用实时活动 如果需要小组件内容时刻保持最新 推荐使用小组件推送更新 小组件推送更新适用于 所有支持小组件的平台 发送小组件推送通知 将更新所有启用推送服务的 设备端小组件 请记住 系统会为这类 小组件重新载入分配预算 因此 务必要谨慎使用更新推送 比如你可以对服务器端更新进行节流 在开发和测试期间 你可以使用“设置”中的 WidgetKit 开发者模式 来忽略你的 App 的 推送和重新载入预算 今天我介绍了很多内容 建议你花点时间 探索这些小组件支持的新平台 观看前面列出的讲座视频 从中汲取创意灵感 确保你的小组件在 iOS 和 macOS 新的外观模式下呈现出色效果 最后 如果你的小组件数据更新 来自外部源或其他设备 可以添加推送通知 让相关内容保持最新状态 很高兴看到小组件的 所有新功能和新位置 我非常期待随时随地 使用你的小组件体验精彩功能 感谢观看

    • 2:44 - Observe .widgetRenderingMode

      struct MostFrequentBeverageWidgetView: View {
          @Environment(\.widgetRenderingMode) var renderingMode
          
          var entry: Entry
          
          var body: some View {
              ZStack {
                  if renderingMode == .fullColor {
                      Image(entry.beverageImage)
                          .resizable()
                          .aspectRatio(contentMode: .fill)
                  
                      LinearGradient(gradient: Gradient(colors: [.clear, .clear, .black.opacity(0.8)]), startPoint: .top, endPoint: .bottom)
                  }
                  
                  VStack {
                      if renderingMode == .accented {
                          Image(entry.beverageImage)
                              .resizable()
                              .widgetAccentedRenderingMode(.desaturated)
                              .aspectRatio(contentMode: .fill)
                      }
                      
                      BeverageTextView()
                  }
              }
          }
      }
    • 6:08 - visionOS Widget Configuration

      struct CaffeineTrackerWidget: Widget {
          var body: some WidgetConfiguration {
              StaticConfiguration(
                  kind: "BaristaWidget",
                  provider: Provider()
              ) { entry in
                  CaffeineTrackerWidgetView(entry: entry)
              }
              .configurationDisplayName("Caffeine Tracker")
              .description("A widget tracking your caffeine intake during the day.")
              .supportedMountingStyles([.elevated])
              .widgetTexture(.paper)
              .supportedFamilies([.systemExtraLargePortrait])
          }
      }
    • 8:56 - LevelOfDetail - CaffeineTrackerWidgetView

      struct CaffeineTrackerWidgetView : View {
          @Environment(\.levelOfDetail) var levelOfDetail
          
          var entry: CaffeineLogEntry
      
          var body: some View {
              VStack(alignment: .leading) {
                  TotalCaffeineView(entry: entry)
      
                  if let log = entry.log {
                      LastDrinkView(log: log)
                  }
      
                  if levelOfDetail == .default {
                      LogDrinkView()
                  }
              }
          }
      }
    • 9:46 - LevelOfDetail - TotalCaffeineView

      struct TotalCaffeineView: View {
          @Environment(\.levelOfDetail) var levelOfDetail
          
          let entry: CaffeineLogEntry
      
          var body: some View {
              VStack {
                  Text("Total Caffeine")
                      .font(.caption)
      
                  Text(totalCaffeine.formatted())
                      .font(caffeineFont)
              }
          }
          
          var caffeineFont: Font {
              if levelOfDetail == .simplified {
                  .largeTitle
              } else {
                  .title
              }
          }
          
          var totalCaffeine: Measurement<UnitMass> {
              entry.totalCaffeine
          }
      }
    • 11:49 - Add .supplementalActivityFamilies

      struct ShopOrderLiveActivity: Widget {
          var body: some WidgetConfiguration {
              ActivityConfiguration(for: Attributes.self) { context in
                  ActivityView(context: context)
              } dynamicIsland: { context in
                  DynamicIsland {
                      DynamicIslandExpandedRegion(.leading) {
                          ExpandedView(context: context)
                      }
                  } compactLeading: {
                      LeadingView(context: context)
                  } compactTrailing: {
                      TrailingView(context: context)
                  } minimal: {
                      MinimalView(context: context)
                  }
              }
              .supplementalActivityFamilies([.small])
          }
      }
    • 12:27 - Add .activityFamily

      struct ActivityView: View {
          @Environment(\.activityFamily) var activityFamily
          var context: ActivityViewContext<Attributes>
          
          var body: some View {
              switch activityFamily {
              case .small:
                  ShopOrderSmallView(context: context)
              default:
                  ShopOrderView(context: context)
              }
          }
      }
    • 16:20 - Define relevance widget with RelevanceConfiguration

      struct HappyHourRelevanceWidget: Widget {
          var body: some WidgetConfiguration {
              RelevanceConfiguration(
                  kind: "HappyHour",
                  provider: Provider()
              ) { entry in
                  WidgetView(entry: entry)
              }
          }
      }
    • 16:41 - Implement RelevanceEntriesProvider

      struct Provider: RelevanceEntriesProvider {
          func placeholder(context: Context) -> Entry {
              Entry()
          }
          
          func relevance() async -> WidgetRelevance<Configuration> {
              let configs = await fetchConfigs()
              var attributes: [WidgetRelevanceAttribute<Configuration>] = []
              
              for config in configs {
                  attributes.append(WidgetRelevanceAttribute(
                      configuration: config,
                      context: .date(interval: config.interval, kind: .default)))
              }
              
              return WidgetRelevance(attributes)
          }
          
          func entry(configuration: Configuration,
                     context: RelevanceEntriesProviderContext) async throws -> Entry {
              Entry(shop: configuration.shop, timeRange: configuration.timeRange)
          }
      }
    • 21:13 - Handle push token and widget configuration changes

      struct CaffeineTrackerPushHandler: WidgetPushHandler {
          func pushTokenDidChange(_ pushInfo: WidgetPushInfo, widgets: [WidgetInfo]) {
              // Send push token and subscription info to server
          }
      }
    • 21:30 - Add pushHandler to WidgetConfiguration

      struct CaffeineTrackerWidget: Widget {
          var body: some WidgetConfiguration {
              StaticConfiguration(
                  kind: Constants.widgetKind,
                  provider: Provider()
              ) { entry in
                  CaffeineTrackerWidgetView(entry: entry)
              }
              .configurationDisplayName("Caffeine Tracker")
              .pushHandler(CaffeineTrackerPushHandler.self)
          }
      }
    • 22:29 - Push Notification Request Body

      {
          "aps": {
              "content-changed": true
          }
      }

Developer Footer

  • 视频
  • WWDC25
  • 小组件的新功能
  • 打开菜单 关闭菜单
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    打开菜单 关闭菜单
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    打开菜单 关闭菜单
    • 辅助功能
    • 配件
    • App 扩展
    • App Store
    • 音频与视频 (英文)
    • 增强现实
    • 设计
    • 分发
    • 教育
    • 字体 (英文)
    • 游戏
    • 健康与健身
    • App 内购买项目
    • 本地化
    • 地图与位置
    • 机器学习
    • 开源资源 (英文)
    • 安全性
    • Safari 浏览器与网页 (英文)
    打开菜单 关闭菜单
    • 完整文档 (英文)
    • 部分主题文档 (简体中文)
    • 教程
    • 下载 (英文)
    • 论坛 (英文)
    • 视频
    打开菜单 关闭菜单
    • 支持文档
    • 联系我们
    • 错误报告
    • 系统状态 (英文)
    打开菜单 关闭菜单
    • Apple 开发者
    • App Store Connect
    • 证书、标识符和描述文件 (英文)
    • 反馈助理
    打开菜单 关闭菜单
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program (英文)
    • News Partner Program (英文)
    • Video Partner Program (英文)
    • 安全赏金计划 (英文)
    • Security Research Device Program (英文)
    打开菜单 关闭菜单
    • 与 Apple 会面交流
    • Apple Developer Center
    • App Store 大奖 (英文)
    • Apple 设计大奖
    • Apple Developer Academies (英文)
    • WWDC
    获取 Apple Developer App。
    版权所有 © 2025 Apple Inc. 保留所有权利。
    使用条款 隐私政策 协议和准则