AgentSkillsCN

widgetkit

利用 Apple 的 WidgetKit 框架,构建 iOS/macOS/watchOS/visionOS 小部件、实时活动、手表复杂功能,以及控制组件。适用于创建小部件扩展、时间轴提供者、可配置小部件、锁屏小部件、智能堆叠小部件、结合 ActivityKit 的实时活动、带有按钮/开关的交互式小部件,或手表复杂功能时使用。涵盖所有小部件系列(系统 Small/Medium/Large/ExtraLarge,配件 Circular/Rectangular/Inline/Corner),以及多种渲染模式。

SKILL.md
--- frontmatter
name: widgetkit
description: Build iOS/macOS/watchOS/visionOS widgets, Live Activities, watch complications, and controls using Apple's WidgetKit framework. Use when creating widget extensions, timeline providers, configurable widgets, Lock Screen widgets, Smart Stack widgets, Live Activities with ActivityKit, interactive widgets with buttons/toggles, or watch complications. Covers all widget families (systemSmall/Medium/Large/ExtraLarge, accessoryCircular/Rectangular/Inline/Corner) and rendering modes.

WidgetKit Development Skill

Build glanceable, timely experiences across Apple platforms using WidgetKit.

Quick Start

Create Widget Extension

  1. File → New → Target → Widget Extension
  2. Deselect "Include Live Activity" and "Include Configuration App Intent" for static widgets
  3. Widget requires: Widget protocol, TimelineProvider, and SwiftUI views

Minimal Widget Structure

swift
@main
struct MyWidget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(
            kind: "com.app.mywidget",
            provider: Provider()
        ) { entry in
            MyWidgetView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("Shows key information")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: .now)
    }
    
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        completion(SimpleEntry(date: .now))
    }
    
    func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
        let entry = SimpleEntry(date: .now)
        let nextUpdate = Calendar.current.date(byAdding: .minute, value: 15, to: .now)!
        completion(Timeline(entries: [entry], policy: .after(nextUpdate)))
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
}

Widget Families & Sizes

FamilyPlatformsUse Case
systemSmalliOS, iPadOS, macOS, visionOSSingle tap target, glanceable info
systemMediumiOS, iPadOS, macOS, visionOSMultiple data points, interactive elements
systemLargeiOS, iPadOS, macOS, visionOSRich content, multiple interactions
systemExtraLargeiPadOS, macOS, visionOSDashboard-style layouts
accessoryCirculariOS Lock Screen, watchOSMinimal info, gauge-style
accessoryRectangulariOS Lock Screen, watchOS2-3 lines of text
accessoryInlineiOS Lock Screen, watchOSSingle line text + optional image
accessoryCornerwatchOS onlyCorner complications

Adapt to Widget Family

swift
struct MyWidgetView: View {
    @Environment(\.widgetFamily) var family
    
    var body: some View {
        switch family {
        case .systemSmall: CompactView()
        case .systemMedium: MediumView()
        case .systemLarge: DetailedView()
        case .accessoryCircular: GaugeView()
        case .accessoryRectangular: RectangularView()
        default: CompactView()
        }
    }
}

Rendering Modes

Widgets render differently based on context:

ModeWhen UsedBehavior
fullColorHome Screen (iOS 17-), macOS desktopFull color preserved
accentedHome Screen tinted/clear, visionOS, watchOSDivides into accent + primary groups
vibrantLock Screen, StandByDesaturated, blurred effect
swift
@Environment(\.widgetRenderingMode) var renderingMode

var body: some View {
    switch renderingMode {
    case .fullColor: FullColorView()
    case .accented: AccentedView()
    case .vibrant: VibrantView()
    @unknown default: FullColorView()
    }
}

Interactivity

Buttons & Toggles (iOS 17+)

swift
Button(intent: RefreshIntent()) {
    Label("Refresh", systemImage: "arrow.clockwise")
}

Toggle(isOn: $isEnabled, intent: ToggleIntent()) {
    Text("Enable")
}

Deep Links

swift
MyWidgetView()
    .widgetURL(URL(string: "myapp://detail/123")!)

// Or for multiple links in larger widgets:
Link(destination: URL(string: "myapp://item/1")!) {
    ItemView()
}

Configuration Types

TypeUse Case
StaticConfigurationNo user configuration needed
AppIntentConfigurationUser-configurable (iOS 17+)
ActivityConfigurationLive Activities

Reference Documentation

Key Constraints

  • No real-time updates: Use timelines; system batches updates
  • Limited budget: ~40-70 refreshes/day depending on usage
  • No continuous animations: Only transition animations up to 2 seconds
  • SwiftUI only: UIKit views not supported
  • Stateless: No persistent state between renders
  • No network in views: Fetch data in timeline provider only

Common Patterns

Share Data with Main App

swift
// Use App Groups
let sharedDefaults = UserDefaults(suiteName: "group.com.app.shared")

// Or shared container
let containerURL = FileManager.default.containerURL(
    forSecurityApplicationGroupIdentifier: "group.com.app.shared"
)

Force Widget Refresh

swift
import WidgetKit
WidgetCenter.shared.reloadTimelines(ofKind: "com.app.mywidget")
WidgetCenter.shared.reloadAllTimelines()

Placeholder for Sensitive Content

swift
.privacySensitive() // Redacts when device locked