View in English

  • Apple 开发者
    • 入门汇总

    探索“入门汇总”

    • 概览
    • 学习
    • Apple Developer Program

    及时了解最新动态

    • 最新动态
    • 开发者你好
    • 平台

    探索“平台”

    • Apple 平台
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    • App Store

    精选

    • 设计
    • 分发
    • 游戏
    • 配件
    • 网页
    • Home
    • CarPlay 车载
    • 技术

    探索“技术”

    • 概览
    • Xcode
    • Swift
    • SwiftUI

    精选

    • 辅助功能
    • App Intents
    • Apple 智能
    • 游戏
    • 机器学习与 AI
    • 安全性
    • Xcode Cloud
    • 社区

    探索“社区”

    • 概览
    • “与 Apple 会面交流”活动
    • 社区主导的活动
    • 开发者论坛
    • 开源

    精选

    • WWDC
    • Swift Student Challenge
    • 开发者故事
    • App Store 大奖
    • Apple 设计大奖
    • Apple Developer Centers
    • 文档

    探索“文档”

    • 文档库
    • 技术概述
    • 示例代码
    • 《人机界面指南》
    • 视频

    发布说明

    • 精选更新
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • Apple tvOS
    • Xcode
    • 下载

    探索“下载”

    • 所有下载
    • 操作系统
    • 应用程序
    • 设计资源

    精选

    • Xcode
    • TestFlight
    • 字体
    • SF Symbols
    • Icon Composer
    • 支持

    探索“支持”

    • 概览
    • 帮助指南
    • 开发者论坛
    • “反馈助理”
    • 联系我们

    精选

    • 《开发者账户帮助》
    • 《App 审核指南》
    • 《App Store Connect 帮助》
    • 即将实行的要求
    • 协议和准则
    • 系统状态
  • 快速链接

    • 活动
    • 新闻
    • 论坛
    • 示例代码
    • 视频
 

视频

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

更多视频

  • 简介
  • 转写文稿
  • 代码
  • 将 App 控制扩展到系统级别

    将你的 App 控制引入控制中心、锁定屏幕以及更多位置。了解如何使用 WidgetKit 将你的 App 控制扩展到系统级别。我们将介绍如何构建控制功能、量身定制控制外观,以及让控制支持配置。

    章节

    • 0:00 - Introduction
    • 0:37 - Learn about controls
    • 3:04 - Build a control
    • 6:39 - Update toggle states
    • 12:25 - Make controls configurable
    • 14:40 - Add refinements

    资源

    • Creating a camera experience for the Lock Screen
    • Updating controls locally and remotely
    • Adding refinements and configuration to controls
    • Creating controls to perform actions across the system
    • Human Interface Guidelines: Controls
    • Forum: App & System Services
      • 高清视频
      • 标清视频

    相关视频

    WWDC24

    • 打造出色的锁定屏幕相机拍摄体验
  • 搜索此视频…

    大家好 我叫 Cliff 是 System Experience 团队的工程师 今天 我们将讨论如何构建控件 控件是 iOS 18 新增的小组件类型 我将首先介绍什么是控件 以及用户如何使用控件 然后介绍如何构建一个控件 让它执行操作 维护它的状态 支持相关配置 以及优化它与系统的整合

    控件提供了一种全新方式 可将 App 的功能 扩展到多个系统空间 包括“控制中心”、锁定屏幕 和操作按钮 控件是使用 WidgetKit 创建的 iOS 14 推出了 WidgetKit 让 App 可以显示 视觉效果丰富、 采用自定样式的内容 并提供详细信息 因此小组件成了显示“天气”信息 或是下一个“日历”日程的 理想方式 iOS 18 新增了控件 从而 进一步扩展了 WidgetKit 控件是让用户快速访问 App 操作的好方法 控件的主要用途是执行操作 和提供简明信息 例如打开手电筒 或提供“时钟”App 的深度链接 都是很棒的控件用例 如果你知道如何构建小组件 那么控件的构建方法和小组件类似 并且会采用和小组件 相同的基础架构 控件分为两种类型: 按钮和开关 按钮可执行离散的操作 其中可能包括启动你的 App 而开关可更改布尔值的状态 比如打开或关闭某些功能

    和交互式小组件一样 控件也使用 App Intent 来执行操作

    从根本上说 控件是 使用 App 提供的信息 以符合所在系统空间的视觉形式 呈现的操作

    App 向系统提供符号、 标题、色调和附加内容 这样用户就能将控件添加到 任何受支持的系统空间中 而系统空间会根据情境来显示控件

    在“控制中心”中 控件可以 以三种不同的大小显示 所以标题和值文本不一定会显示出来 我很喜欢使用计时器 让自己 既能专心工作 也能不时休息一下! 今天 我将构建一个 效率计时器控件 我可以用它来划分时间段 让工作和休息穿插进行 当计时器运行时 实时活动会显示剩余时间

    我可以从锁定屏幕启动计时器

    在“控制中心”中停止计时器

    甚至还可以使用操作按钮 来启动和停止计时器

    我将从头开始构建 这个效率计时器控件 首先构建一个基本的开关 然后利用控件的丰富功能集

    我已经为现有的效率小组件 创建了 WidgetBundle 所以首先要在 WidgetBundle 中 添加一个 TimerToggle() 条目 然后通过添加遵从 ControlWidget 的 TimerToggle 类型 在同一个小组件扩展中 定义 TimerToggle() 控件 为了定义控件 我将提供 要在控件中显示的信息 以及要执行的操作

    首先看看 StaticControlConfiguration 这意味着这个控件不可配置 我稍后再添加配置

    控件采用一个 kind 作为它的唯一标识符 还需要提供控件类型定义 也就是 ControlWidgetToggle

    然后 给控件起一个标题 并提供控件的状态

    还要提供当用户与控件交互时 要执行的操作 与交互式小组件一样 控件也使用 App Intent 来执行操作

    最后 提供符号 Image 来定义控件

    现在 所需的所有信息都齐了 系统能够显示这个控件了

    我可以把控件放在“控制中心”中 在这里 控件会显示相应的标题、 符号以及打开/关闭状态

    这个计时器控件还可以进一步优化 在处于运行或停止状态时 显示不同的符号

    为此 我将对闭包使用 isOn 参数 在控件处于打开状态时 显示流动沙漏符号 表明倒计时正在进行中

    非常好! 现在 控件只有在运行时才会 显示流动沙漏符号

    我还想优化一下这个状态的值文本 当前的值文本是 On 和 Off

    这是开关值文本的默认值 但说到计时器 用户通常会想到的状态是运行或停止 而不是打开 (On) 或关闭 (Off)

    我可以将 Image 更改为 Label 并在其中包含值文本和 systemImage 从而自定控件的值文本 现在 控件会显示 恰当、相关的值文本 来描述控件当前所处的状态 而不是显示默认的 On/Off 文本

    请注意 当控件位于锁定屏幕上时 或者当控件在“控制中心”中 以小尺寸显示时 都会只显示符号 而不显示这个值文本

    这个控件越来越顺眼了 但在打开状态下 符号色调 是默认的 systemBlue 这个色调不符合 我的效率 App 的品牌形象

    为了让控件采用彰显特色的颜色 而不是默认的 systemBlue 需要提供一个色调

    我将使用这款效率 App 的主题色 也就是紫色 用紫色代表效率! 当开关处于打开状态时 系统会 使用这个色调对符号进行着色

    这是具有完整样式的控件 在锁定屏幕上 以及操作按钮中的效果 所使用的代码 与用于“控制中心”的代码相同 当控件处于打开状态时 符号和值文本会采用指定色调 我们来看看开关的显示方式 和状态管理方式 到目前为止 我一直在使用 TimerManager 类 向控件提供当前计时器状态 在这个示例中 TimerManager 会在一个共享组容器中查找数据 这个容器具有和我的效率 App 相同的数据访问权限 并且可以同步获取 Running 状态 我们来了解一下 当控件的状态或内容有所变化时 系统如何重新载入控件

    当系统需要重新载入控件时 会在小组件扩展进程中 运行控件的 body 请求获取控件的当前值 并生成控件的内容 然后 控件的值和内容会传递回系统 用于显示控件 所以 小组件扩展可提供 控件的当前状态 以及与状态对应的内容

    有三种事件会导致系统 重新载入控件: 执行控件操作时、

    App 按需请求重新载入控件时 以及推送通知导致控件失效时 在这些事件中 我们首先看看 执行控件操作的事件 每次用户与控件交互时 系统都会在控件 App Intent 的 perform() 函数返回时 自动重新载入控件 要先确保所有更新都已完成 然后再返回

    在我的计时器控件中 操作是 ToggleTimerIntent()

    这个意图会将计时器的状态 设置为“Running” 并开始或停止 LiveActivity

    由于这个 App Intent 会将计时器的 “Running”状态设为系统提供的值 因此它属于 SetValueIntent 同时它也属于 LiveActivityIntent 因为它会修改计时器 LiveActivity perform 函数运行完毕后 系统会使用新状态来更新控件 但是 与计时器控件交互 并不是改变控件状态的唯一方法 我也可以打开效率 App 然后在 App 内开始或停止计时器 并且希望控件能够保持最新状态

    为此 当计时器状态发生变化时 我的 App 可以使用 ControlCenter API 将计时器控件的 kind 指定为 需要重新载入的控件 从而刷新控件 现在 在我的 App 中 启动计时器时 控件的状态可以及时更新!

    如果控件的状态或内容需要刷新 App 可以请求系统重新载入控件 适用于小组件和实时活动的刷新工具 也同样适用于控件 由于在控件开发过程中 需要频繁刷新控件的状态 因此可在 Developer > Settings 中 启用 WidgetKit Developer Mode 以便从控件中移除系统策略 现在 我的效率计时器 在这台设备上表现良好 我想让它在多台设备上运行 并让所有这些设备都能访问 服务器上的同一个计时器状态

    由于我的控件会反映 来自服务器的状态 而无法从设备上获取状态 因此我需要异步获取计时器状态 为此 我可以使用 ValueProvider

    ControlValueProvider 有两个必需项: currentValue() 和 previewValue

    currentValue() 是异步的 你可以从需要的位置获取数据 例如从数据库或服务器获取 在我的示例中 TimerManager 异步查询来自服务器的 计时器状态

    你也可以抛出错误 告诉系统无法计算状态 指出需要稍后重新载入控件

    previewValue 用于选择 用户在添加控件之前预览控件时 要显示的值 用户预览控件的情况 可能会发生在控件库中、 用户自定锁定屏幕时 以及操作按钮设置中 previewValue 应该是预先确定的 并且可以很快返回 而且这个值还应该对应于 控件的关闭状态

    我可以在接受 ValueProvider 的 其他控件构造器中 使用 ValueProvider 而提供的值会传递到 我定义开关的闭包中 我的示例随后会使用这个值 作为控件的 isOn 状态 在这个示例中 我使用了一个简单的布尔值 但是我也可以让这个值 包含更丰富的信息 我稍后会展示一个这方面的例子 当系统使用 ValueProvider 重新载入控件时 会首先运行 ValueProvider 来获取当前值 然后将这个值传递给控件闭包 以生成内容 整个过程都发生在小组件扩展进程中

    现在 我的效率计时器的状态 已经存在于服务器上了 而且可以从不同设备更改状态 例如 当我在 iPad 上 启动或停止计时器时 我希望这个设备外状态变化 能够在我的其他设备上 触发控件的重新载入 为了处理这种情况 我可以 使用 Push Notification API 为控件定义推送处理程序 这个处理程序会将控件配置为 在收到外部状态变化 推送通知时重新载入 推送处理文档更详细地说明了 这一过程的实现方法 和最佳做法

    现在 当我在 iPad 上 停止计时器时 iPhone 上的控件也会停止!

    我的效率计时器表现良好 但我想要对工作 和练习小提琴等个人努力 分别应用单独的计时器 以便我的 App 能够 对工作和个人努力分别进行跟踪 如果能够为其中每个计时器 构建不同的控件 然后将这两个控件 都放入“控制中心” 那就太棒了 由于控件和小组件一样 是使用 WidgetKit 构建的 为了实现这一点 我可以 将这个控件设为用户可配置 将一个计时器控件 添加到“控制中心”之后 我希望能够选择它是要 开始和停止我的工作计时器 还是个人计时器 这样一来 我就可以将一个控件 用于“控制中心”中的每个 工作计时器和个人计时器 首先 我可以更新 ValueProvider 以遵从新的协议 AppIntentControlValueProvider 这会让值依赖于某个意图的配置 决定配置的 App Intent 是 SelectTimerIntent 它允许用户选择让控件 与哪个计时器交互 请注意 我现在可以确保获取 配置的特定计时器的 Running 状态 现在返回的值 是一个自定结构体 其中既包含计时器 也包含 计时器的 Running 状态

    我可以使用可配置的 ValueProvider 以及 AppIntentControlConfiguration() 将控件设为可配置 现在 传递给闭包的值 是我的 timerState 结构体 我使用了计时器 和 Running 状态来完成开关 请注意 我想要显示 特定计时器的名称作为控件的标题 而现在计时器开关 App Intent 会作用于这个特定计时器

    现在 当用户自定“控制中心”时 我的控件允许用户选择 让控件与哪个计时器交互 现在 我让单独的工作计时器控件 和个人计时器控件 在“控制中心”中并排显示 分别控制不同的计时器

    如果你的控件要求配置能够正常运行 可以使用 promptsForUserConfiguration() 修饰符 在用户将控件 添加到某个系统空间时 自动提示用户对控件进行配置

    你可以进一步优化控件 在系统的默认值不符合你的用例时 提供最容易理解、最相关的内容 例如 当用户在执行操作之前 与操作按钮交互时 显示操作提示 控件当前的操作提示是“Hold for Running”和“Hold for Stopped”

    我们来详细了解一下 为什么会这样

    在我将控件的值标签文本自定为 “Running”/“Stopped”之前 系统合成的是默认的 Hold to Turn On、 Hold to Turn Off 操作提示 这与系统合成默认 On/Off 值文本的方式类似 在我自定值文本之后 系统也会 使用自定值文本来合成操作提示 系统合成了 Hold for Running 和 Hold for Stopped 操作提示 当然 这些提示是可以改进的 所以 我要进行自定 让它们契合实际用例

    使用 controlWidgetActionHint 修饰符来自定操作按钮提示文本 以选择要显示的操作提示 应该以动词开头 提供的提示代表着 达到特定状态所需的操作 所以 要达到计时器的 On 状态 需要的操作提示是“Start” 所以 让用户启动计时器的提示 就是 Hold to Start 要达到 Off 状态 需要的操作提示是“Stop” 所以 让用户停止计时器的操作提示 就是 Hold to Stop 对于计时器来说 这样的文本 听起来很棒 也让人觉得很自然

    使用 controlWidgetStatus 修饰符执行操作时 控件可以在“控制中心”中 显示瞬时状态 如果你的控件提供了额外的信息 来传达它的操作、 它的状态或是 状态有效性的持续时间 可以考虑添加一个状态 状态文本应谨慎使用 并且只能用于让用户注意 控件没有传达的相关信息

    从控件库添加控件时 现在这个控件名为 Productivity 和我的 App 同名

    控件的 App 名称 是控件的默认显示名称

    我要将控件的 displayName 自定为“Productivity Timer” 务必要针对每个控件的具体功能 来选择相应的 displayName 我还要添加一条说明 作为最后的优化 这条说明会在用户配置控件时显示 只需几个步骤 我就创建了一个效率计时器控件 它可以放在“控制中心”、锁定屏幕 或操作按钮中 并且可以在我的 所有设备上同步 控件是一项非常强大的功能 你现在可将控件构建到 iOS 和 iPadOS 18 上的 App 中 让用户能够在系统空间中 快速访问 App 的关键操作 请使用我们今天讨论的修饰符 根据控件执行的操作 来量身定制控件的样式 也请确保你的控件 采用鲜明独特的符号

    如果你要构建能让用户 使用相机来拍摄内容的控件 可以考虑构建 capture 扩展 并观看讲座 “打造出色的锁定 屏幕相机拍摄体验”

    谢谢观看!

    • 3:13 - Add the control to the Widget Bundle

      @main
      struct ProductivityExtensionBundle: WidgetBundle {
          
          var body: some Widget {
              ChecklistWidget()
              TaskCounterWidget()
              TimerToggle()
          }
          
      }
    • 3:29 - Complete the control

      struct TimerToggle: ControlWidget {
          var body: some ControlWidgetConfiguration {
              StaticControlConfiguration(
                  kind: "com.apple.Productivity.TimerToggle"
              ) {
                  ControlWidgetToggle(
                      "Work Timer",
                      isOn: TimerManager.shared.isRunning,
                      action: ToggleTimerIntent()
                  ) { _ in
                      Image(systemName:
                            "hourglass.bottomhalf.filled")
                  }
              }
          }
      }
    • 4:41 - Specify different symbols when on and off​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct TimerToggle: ControlWidget {
          var body: some ControlWidgetConfiguration {
              StaticControlConfiguration(
                  kind: "com.apple.Productivity.TimerToggle"
              ) {
                  ControlWidgetToggle(
                      "Work Timer",
                      isOn: TimerManager.shared.isRunning,
                      action: ToggleTimerIntent()
                  ) { isOn in
                      Image(systemName: isOn
                            ? "hourglass"
                            : "hourglass.bottomhalf.filled")
                  }
              }
          }
      }
    • 5:21 - Specify custom value text​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ and add a custom tint color

      struct TimerToggle: ControlWidget {
          var body: some ControlWidgetConfiguration {
              StaticControlConfiguration(
                  kind: "com.apple.Productivity.TimerToggle"
              ) {
                  ControlWidgetToggle(
                      "Work Timer",
                      isOn: TimerManager.shared.isRunning,
                      action: ToggleTimerIntent()
                  ) { isOn in
                      Label(isOn ? "Running" : "Stopped",
                            systemImage: isOn
                            ? "hourglass"
                            : "hourglass.bottomhalf.filled")
                  }
                  .tint(.purple)
              }
          }
      }
    • 8:14 - Implement timer toggling

      struct ToggleTimerIntent: SetValueIntent, LiveActivityIntent {
          static let title: LocalizedStringResource = "Productivity Timer"
          
          @Parameter(title: "Running")
          var value: Bool  // The timer’s running state
          
          func perform() throws -> some IntentResult {
              TimerManager.shared.setTimerRunning(value)
              return .result()
          }
      }
    • 8:54 - Refresh the control from within the app

      func timerManager(_ manager: TimerManager,
                        timerDidChange timer: ProductivityTimer) {
          ControlCenter.shared.reloadControls(
              ofKind: "com.apple.Productivity.TimerToggle"
          )
      }
    • 10:03 - Define a Value Provider

      struct TimerValueProvider: ControlValueProvider {
          
          func currentValue() async throws -> Bool {
              try await TimerManager.shared.fetchRunningState()
          }
          
          let previewValue: Bool = false
      }
    • 11:00 - Provide asynchronously fetched state with a Value Provider

      struct TimerToggle: ControlWidget {
          var body: some ControlWidgetConfiguration {
              StaticControlConfiguration(
                  kind: "com.apple.Productivity.TimerToggle",
                  provider: TimerValueProvider()
              ) { isRunning in
                  ControlWidgetToggle(
                      "Work Timer",
                      isOn: isRunning,
                      action: ToggleTimerIntent()
                  ) { isOn in
                      Label(isOn ? "Running" : "Stopped",
                            systemImage: isOn
                            ? "hourglass"
                            : "hourglass.bottomhalf.filled")
                  }
                  .tint(.purple)
              }
          }
      }
    • 13:06 - Make the Value Provider configurable

      struct ConfigurableTimerValueProvider: AppIntentControlValueProvider {
          func currentValue(configuration: SelectTimerIntent) async throws -> TimerState {
              let timer = configuration.timer
              let isRunning = try await TimerManager.shared.fetchTimerRunning(timer: timer)
              return TimerState(timer: timer, isRunning: isRunning)
          }
          
          func previewValue(configuration: SelectTimerIntent) -> TimerState {
              return TimerState(timer: configuration.timer, isRunning: false)
          }
      }
    • 13:40 - Make the timer configurable

      struct TimerToggle: ControlWidget {
          var body: some ControlWidgetConfiguration {
              AppIntentControlConfiguration(
                  kind: "com.apple.Productivity.TimerToggle",
                  provider: ConfigurableTimerValueProvider()
              ) { timerState in
                  ControlWidgetToggle(
                      timerState.timer.name,
                      isOn: timerState.isRunning,
                      action: ToggleTimerIntent(timer: timerState.timer)
                  ) { isOn in
                      Label(isOn ? "Running" : "Stopped",
                            systemImage: isOn
                            ? "hourglass"
                            : "hourglass.bottomhalf.filled")
                  }
                  .tint(.purple)
              }
          }
      }
    • 14:26 - Prompt for user configuration automatically

      struct SomeControl: ControlWidget {
          var body: some ControlWidgetConfiguration {
              AppIntentControlConfiguration(
                  // ...
              )
              .promptsForUserConfiguration()
          }
      }
    • 15:42 - Custom action hint -> hint treated as verb phrase

      struct TimerToggle: ControlWidget {
          var body: some ControlWidgetConfiguration {
              AppIntentControlConfiguration(
                  kind: "com.apple.Productivity.TimerToggle",
                  provider: ConfigurableTimerValueProvider()
              ) { timerState in
                  ControlWidgetToggle(
                      timerState.timer.name,
                      isOn: timerState.isRunning,
                      action: ToggleTimerIntent(timer: timerState.timer)
                  ) { isOn in
                      Label(isOn ? "Running" : "Stopped",
                            systemImage: isOn
                            ? "hourglass"
                            : "hourglass.bottomhalf.filled")
                      .controlWidgetActionHint(isOn ?
                                               "Start" : "Stop")
                  }
                  .tint(.purple)
              }
          }
      }
    • 16:56 - Specify a display name and add a description

      struct TimerToggle: ControlWidget {
          var body: some ControlWidgetConfiguration {
              AppIntentControlConfiguration(
                  kind: "com.apple.Productivity.TimerToggle",
                  provider: ConfigurableTimerValueProvider()
              ) { timerState in
                  ControlWidgetToggle(
                      timerState.timer.name,
                      isOn: timerState.isRunning,
                      action: ToggleTimerIntent(timer: timerState.timer)
                  ) { isOn in
                      Label(isOn ? "Running" : "Stopped",
                            systemImage: isOn
                            ? "hourglass"
                            : "hourglass.bottomhalf.filled")
                      .controlWidgetActionHint(isOn ?
                                               "Start" : "Stop")
                  }
                  .tint(.purple)
              }
              .displayName("Productivity Timer")
              .description("Start and stop a productivity timer.")
          }
      }

Developer Footer

  • 视频
  • WWDC24
  • 将 App 控制扩展到系统级别
  • 打开菜单 关闭菜单
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    打开菜单 关闭菜单
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    打开菜单 关闭菜单
    • 辅助功能
    • 配件
    • Apple 智能
    • App 扩展
    • App Store
    • 音频与视频 (英文)
    • 增强现实
    • 设计
    • 分发
    • 教育
    • 字体 (英文)
    • 游戏
    • 健康与健身
    • App 内购买项目
    • 本地化
    • 地图与位置
    • 机器学习与 AI
    • 开源资源 (英文)
    • 安全性
    • Safari 浏览器与网页 (英文)
    打开菜单 关闭菜单
    • 完整文档 (英文)
    • 部分主题文档 (简体中文)
    • 教程
    • 下载
    • 论坛 (英文)
    • 视频
    打开菜单 关闭菜单
    • 支持文档
    • 联系我们
    • 错误报告
    • 系统状态 (英文)
    打开菜单 关闭菜单
    • Apple 开发者
    • App Store Connect
    • 证书、标识符和描述文件 (英文)
    • 反馈助理
    打开菜单 关闭菜单
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program (英文)
    • Mini Apps Partner 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。
    版权所有 © 2026 Apple Inc. 保留所有权利。
    使用条款 隐私政策 协议和准则