Notes WWDC 2022 : Complications and widgets
Mes notes pour les sessions Complications and widgets: Reloaded et Go further with Complications in WidgetKit de la WWDC 2022.
New watchOS 9 complications are now widgets (based on iOS 14 WidgetKit
), with new families (prefixed with “accessory”). These new families are also available on the new iOS 16 Lock Screen.
case accessoryCircular
case accessoryRectangular // multiple lines
case accessoryInline // text-only, multiple sizes
// watchOS only
case accessoryCorner
Colors
3 rendering modes:
- Full colour (watchOS)
- Accented (watchOS)
- Vibrant (iOS)
@Environment(\.widgetRenderingMode) var renderingMode
For accented, use widgetAccentable
:
VStack(alignment: .leading) {
Text("Headline")
.font(.headline)
.widgetAccentable()
Text("Body 1")
Text("Body 2")
}.frame(maxWidth: .infinity, alignment: .leading)
For vibrant, avoid transparent colors, use darker colors instead.
New consistent background view: AccessoryWidgetBackground
ZStack {
AccessoryWidgetBackground()
VStack {
Text("MON")
Text("6")
.font(.title)
}
}
Project setup
For projects with existing widgets:
- Duplicate widget extension target
- Rename
- Update bundle identifier with Watch app prefix
- Target watchOS
- Embed extension in Watch app
- Add new supported families in widget configuration
- Use
#if os(watchOS)
for new families and previews - For intents, override
recommandations()
on timeline provider
Making glanceable views
Auto-updating view with live gauge:
ProgressView(interval: entry.character.injuryDate...entry.character.fullHealthDate, countdown: false, label: { Text(entry.character.name) }, currentValueLabel: { Avatar(character: entry.character, includeBackground: false) })
.progressViewStyle(.circular)
For consistency, use text styles:
.headline
.body
.caption
.title
For inline family, use ViewThatFits
:
ViewThatFits {
Text("\(entry.character.name) is resting, combat-ready in \(entry.character.fullHealthDate, style: .relative)")
Text("\(entry.character.name) ready in \(entry.character.fullHealthDate, style: .timer)")
Text("\(entry.character.avatar) \(entry.character.fullHealthDate, style: .timer)")
}
Privacy
For always-on displays:
@Environment(\.isLuminanceReduced)
For sensitive content: .privacySensitive()
modifier.
Unique to watchOS
For accessoryCorner
, the “auxiliary content” is typically the curved part in the corner, rendered by the watch face. Use new view modifier widgetLabel
, with text, gauge, or progress view.
struct CornerView: View {
let value: Double
var body: some View {
ZStack {
AccessoryWidgetBackground()
Image(systemName: "cup.and.saucer.fill")
.font(.title.bold())
.widgetAccentable()
}
.widgetLabel {
Gauge(value: value, in: 0...500) {
Text("MG")
} currentValueLabel: {
Text("\(Int(value))")
} minimumValueLabel: {
Text("0")
} maximumValueLabel: {
Text("500")
}
}
}
}
Can track if auxiliary content is shown:
@Environment(\.showsWidgetLabel) var showsWidgetLabel
Migration
No longer need to support the legacy 12 families from ClockKit
.
Add migrator:
var widgetMigrator: CLKComplicationWidgetMigrator {
self
}
With static or intent-based migration:
func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? {
CLKComplicationStaticWidgetMigrationConfiguration(kind: "CoffeeTracker", extensionBundleIdentifier: widgetBundle)
}
func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? {
CLKComplicationIntentWidgetMigrationConfiguration(kind: "CoffeeTracker", extensionBundleIdentifier: widgetBundle, intent: intent, localizedDisplayName: "Coffee Tracker")
}