如何开发iOS18的Controls控制组件?ControlWidget开发教程
在 iOS18 中,你可以将应用的控件扩展到系统级别,使其出现在控制中心、锁定屏幕等位置。本文将详细介绍如何使用 WidgetKit 构建和定制控件,并让控件支持配置,最终将其添加到系统界面中。
一、前期准备
在开始之前,确保你已经在 Xcode 中创建了一个 iOS 项目,并安装了最新版本的 Xcode。你还需要一些基本的 Swift 和 SwiftUI 知识。
二、将控件添加到 Widget Bundle
1. 什么是 Widget Bundle?
Widget Bundle
是一种容器,允许你将多个控件组合在一起。通过将控件添加到 Widget Bundle
,你可以更好地组织和管理这些控件,并方便地在应用中启用或禁用它们。
2. 创建一个 Widget Bundle
我们需要定义一个 WidgetBundle
来包含我们的控件。以下是具体的代码示例:
1 | @main |
@main
标记:表示这是应用的入口点。它将这个WidgetBundle
注册为应用的一部分。ProductivityExtensionBundle
:这是我们定义的WidgetBundle
名称。var body: some Widget
:这个属性表示该WidgetBundle
中包含的控件列表。你可以在这里添加任意数量的控件。
3. 控件的添加
在 body
中,我们添加了三个控件:
ChecklistWidget
:一个清单控件,用于显示任务列表。TaskCounterWidget
:一个任务计数控件,用于显示当前任务的数量。TimerToggle
:一个定时器切换控件,用于控制定时器的启动和停止。
通过这种方式,你可以将多个控件打包在一起,使它们可以在应用的不同部分方便地使用。
三、构建控件
1. 创建控件的基础结构
我们将使用 ControlWidget
协议来定义一个基础控件。这里我们以定时器切换控件为例进行说明。
1 | struct TimerToggle: ControlWidget { |
struct TimerToggle: ControlWidget
:定义了一个新的结构体TimerToggle
,它实现了ControlWidget
协议。StaticControlConfiguration
:这是一个静态配置,用于定义控件的外观和行为。kind
:指定控件的唯一标识符,这里使用了"com.apple.Productivity.TimerToggle"
。ControlWidgetToggle
:定义了一个切换控件,使用isOn
属性表示当前的状态。isOn
:绑定到TimerManager.shared.isRunning
,表示定时器是否在运行。action
:指定了一个ToggleTimerIntent
操作,当用户点击控件时触发该操作。{ isOn in ... }
:这是一个闭包,用于根据isOn
的值动态更新控件的图标。
2. 控件的工作原理
这个控件的核心是一个切换按钮。根据定时器的运行状态(isOn
),显示不同的图标(如沙漏或空心沙漏),并执行相应的操作(启动或停止定时器)。
3. 控件配置的关键点
StaticControlConfiguration
:静态配置,用于定义控件的结构。ControlWidgetToggle
:切换控件,具有isOn
属性和操作意图action
。
通过这种方式,你可以创建一个简单而功能强大的控件,并根据需要自定义其外观和行为。
四、自定义控件外观
1. 指定不同的符号
为了增强用户体验,我们可以根据定时器的状态显示不同的符号(例如:定时器运行时显示沙漏,停止时显示半沙漏)。
1 | struct TimerToggle: ControlWidget { |
Image(systemName:)
:这是一个SwiftUI
中的图像控件,用于显示系统图标。isOn
:根据isOn
的值选择不同的图标。systemName: isOn ? "hourglass" : "hourglass.bottomhalf.filled"
:如果定时器在运行,显示沙漏图标;否则显示半沙漏图标。
2. 添加自定义文本和颜色
你还可以为控件添加自定义文本和颜色,以提供更好的视觉效果和用户提示。
1 | struct TimerToggle: ControlWidget { |
Label
:这是一个带有文本和图标的控件,用于显示控件的状态。isOn ? "Running" : "Stopped"
:根据isOn
的值选择显示文本,运行时显示“Running”,停止时显示“Stopped”。systemImage
:根据isOn
的值选择不同的图标。
.tint(.purple)
:设置控件的主题色为紫色,使其更具视觉吸引力。
通过这些自定义选项,你可以创建一个更加丰富和直观的控件。
五、实现控件功能
1. 定义定时器切换逻辑
我们需要实现一个操作意图(Intent
),用于处理定时器的启动和停止操作。
1 | struct ToggleTimerIntent: SetValueIntent, LiveActivityIntent { |
struct ToggleTimerIntent: SetValueIntent, LiveActivityIntent
:定义了一个新的结构体ToggleTimerIntent
,它实现了SetValueIntent
和LiveActivityIntent
协议。@Parameter
:这是一个参数注解,用于定义意图中的参数。title: "Running"
:参数的标题。var value: Bool
:一个布尔值,表示定时器的运行状态。
func perform() throws -> some IntentResult
:定义了执行意图时的操作。TimerManager.shared.setTimerRunning(value)
:调用TimerManager
来设置定时器的运行状态。return .result()
:返回一个结果,表示意图执行成功。
这个意图允许用户通过控件启动或停止定时器,并将状态更新到控件中。
2. 从应用内部刷新控件
当定时器状态发生变化时,我们需要刷新控件的显示,以确保其显示的状态与实际一致。
1 | func timerManager(_ manager: TimerManager, |
func timerManager(_ manager: TimerManager, timerDidChange timer: ProductivityTimer)
:这是一个回调函数,当定时器状态发生变化时调用。ControlCenter.shared.reloadControls(ofKind: "com.apple.Productivity.TimerToggle")
:调用ControlCenter
来刷新指定类型的控件,这里是TimerToggle
。
通过这种方式,你可以确保控件的显示始终与定时器的实际状态保持一致。
六、值提供者与异步数据获取
1. 定义值提供者
为了提高控件的灵活性和响应性,我们可以使用值提供者(Value Provider
)来动态获取控件的状态。
1 | struct TimerValueProvider: ControlValueProvider { |
struct TimerValueProvider: ControlValueProvider
:定义了一个新的结构体TimerValueProvider
,它实现了ControlValueProvider
协议。func currentValue() async throws -> Bool
:这是一个异步函数,用于获取当前的定时器状态。try await TimerManager.shared.fetchRunningState()
:调用TimerManager
的异步方法,获取定时器的运行状态。
let previewValue: Bool = false
:定义了一个预览值,当无法获取实际值时使用。
2. 异步获取状态
我们可以将值提供者集成到控件中,以实现异步数据获取。
1 | struct TimerToggle: ControlWidget { |
provider: TimerValueProvider()
:使用我们定义的TimerValueProvider
作为值提供者。{ isRunning in ... }
:这是一个闭包,当获取到定时器状态时执行,其中isRunning
表示当前的定时器状态。
通过这种方式,你可以使控件动态响应定时器状态的变化,提供更好的用户体验。
七、使控件可配置
1. 定义可配置的值提供者
我们可以进一步扩展值提供者,使其支持配置不同的定时器。
1 | struct ConfigurableTimerValueProvider: AppIntentControlValueProvider { |
struct ConfigurableTimerValueProvider: AppIntentControlValueProvider
:定义了一个可配置的值提供者,实现了AppIntentControlValueProvider
协议。func currentValue(configuration: SelectTimerIntent) async throws -> TimerState
:这是一个异步函数,根据配置获取当前的定时器状态。let timer = configuration.timer
:获取配置中的定时器。let isRunning = try await TimerManager.shared.fetchTimerRunning(timer: timer)
:获取指定定时器的运行状态。
func previewValue(configuration: SelectTimerIntent) -> TimerState
:定义了一个预览值,用于在无法获取实际值时使用。
2. 实现可配置的定时器控件
我们可以使用可配置的值提供者创建一个更加灵活的定时器控件。
1 | struct TimerToggle: ControlWidget { |
AppIntentControlConfiguration
:这是一个支持应用意图的控件配置,允许根据用户配置动态更新控件。{ timerState in ... }
:这是一个闭包,根据获取到的定时器状态(timerState
)来更新控件。
通过这种方式,你可以创建一个更加灵活和强大的控件,允许用户根据需要配置不同的定时器。
八、自动提示用户配置
1. 自动提示用户配置
为了提高用户体验,我们可以自动提示用户对控件进行配置。
1 | struct SomeControl: ControlWidget { |
.promptsForUserConfiguration()
:这是一个方法,调用它可以在控件首次使用时自动提示用户进行配置。
通过这种方式,你可以让用户在使用控件时更加方便地进行配置,提升应用的易用性。
九、添加控件提示与描述
1. 添加操作提示和描述
为了让用户更好地理解控件的功能,我们可以为控件添加操作提示和描述。
1 | struct TimerToggle: ControlWidget { |
.controlWidgetActionHint
:这是一个方法,用于为控件添加操作提示,如“Start”或“Stop”。.displayName("Productivity Timer")
:设置控件的显示名称为“Productivity Timer”。.description("Start and stop a productivity timer.")
:添加控件的描述,说明其功能是启动和停止生产力定时器。
通过这些提示和描述,你可以让用户更清楚地了解控件的用途和操作方法,提升应用的用户体验。
参考视频
- 感谢你赐予我前进的力量