EasyApp

灵动岛

了解如何在 EasyAppSwiftUI 中实现灵动岛功能

简介

EasyAppWidgetLiveActivity 基于 ActivityKit 构建,支持锁屏横幅与灵动岛动态展示。本文介绍如何利用 EasyAppWidgetAttributes 和组件化视图在灵动岛上呈现扩展信息,并确保主应用与扩展间的数据同步。

架构概览

  • Widget 配置 EasyAppWidgetLiveActivity 使用 ActivityConfiguration,在 dynamicIsland 闭包中为扩展、紧凑与最小区域分别提供 SwiftUI 视图。
  • 视图组件 所有灵动岛区域视图定义在 EasyAppSwiftUI/EasyAppWidget/WidgetsComponents/LiveActivityComponents.swift
  • 数据模型 EasyAppWidgetAttributesEasyAppWidgetAttributes.ContentState 必须在主应用与 Widget Extension 中保持完全一致(字段、初始化器),否则 Live Activity 无法解码。

关键代码结构

struct EasyAppWidgetLiveActivity: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: EasyAppWidgetAttributes.self) { context in
            LiveActivityLockScreen(
                attributes: context.attributes,
                state: context.state
            )
            .activityBackgroundTint(.clear)
            .activitySystemActionForegroundColor(.primary)

        } dynamicIsland: { context in
            DynamicIsland {
                DynamicIslandExpandedRegion(.leading) {
                    LiveActivityExpandedLeading(
                        attributes: context.attributes,
                        state: context.state
                    )
                }
                DynamicIslandExpandedRegion(.trailing) {
                    LiveActivityExpandedTrailing(
                        attributes: context.attributes,
                        state: context.state
                    )
                }
                DynamicIslandExpandedRegion(.bottom) {
                    LiveActivityExpandedBottom(
                        attributes: context.attributes,
                        state: context.state
                    )
                }
            } compactLeading: {
                LiveActivityCompactLeading(attributes: context.attributes, state: context.state)
            } compactTrailing: {
                LiveActivityCompactTrailing(attributes: context.attributes, state: context.state)
            } minimal: {
                LiveActivityMinimal(attributes: context.attributes, state: context.state)
            }
            .widgetURL(URL(string: "easyapp://live-activity?path=live-activity"))
            .keylineTint(.blue)
        }
    }
}
  • .widgetURL 为灵动岛提供点击跳转入口,可根据业务替换深链路径。
  • .keylineTint 控制灵动岛轮廓色彩,建议与品牌主色保持一致。

区域视图说明

  • 扩展区域 (LiveActivityExpandedLeading/Trailing/Bottom) 负责展示主要内容、状态指标与操作按钮,可根据属性或状态扩展更多自定义视图。
  • 紧凑区域 (LiveActivityCompactLeading/Trailing) 将信息压缩为图标与短文本,确保在灵动岛收起状态下仍可识别。
  • 最小区域 (LiveActivityMinimal) 采用动画渐变背景与 Emoji,适合展示运行中的状态提示。
  • 锁屏横幅 (LiveActivityLockScreen) 与灵动岛共用属性数据,保持视觉一致性,必要时可在此增加更丰富的操作卡片。

数据与状态管理

  • EasyAppWidgetAttributes 描述 Live Activity 的静态属性,例如用户名或任务标题。
  • EasyAppWidgetAttributes.ContentState 保存实时字段(如 Emoji、进度值)。更新状态时,通过 Activity.update(using:) 推送到灵动岛。
  • 主应用和扩展中的 EasyAppWidgetAttributes 文件路径分别为:
    • EasyAppSwiftUI/Common/EasyAppWidgetAttributes.swift
    • EasyAppWidget/Common/EasyAppWidgetAttributes.swift 请保持两处文件完全同步,以避免序列化失败。

启动与调试

  1. 在主应用启动 Live Activity:
    let activity = try Activity<EasyAppWidgetAttributes>.request(
        attributes: EasyAppWidgetAttributes(name: "World"),
        contentState: EasyAppWidgetAttributes.ContentState(emoji: "😀"),
        pushType: nil
    )
  2. 运行 EasyAppWidgetExtension scheme,确保模拟器或设备支持灵动岛(iPhone 14 Pro 及以上)。
  3. 在 Xcode 的 #Preview 区域查看 .dynamicIsland(.compact/.expanded/.minimal) 预览,快速校验布局。
  4. 使用 activity.update(using:) 调整 ContentState,观察灵动岛的动态效果。

设计与交互建议

  • 遵循 Apple 人机交互指南:紧凑区域文字尽量保持在 6~8 个字符内,避免溢出。
  • 利用 AnimatedProgressBarPulsingStatusIndicator 等辅助视图在扩展区域展示实时状态。
  • 操作按钮应通过 .buttonStyle(PlainButtonStyle()) 保持灵动岛无多余高亮背景,确保易读性。
  • 若需要本地化,提前在 EasyAppWidgetAttributes 中准备对应文案或通过 LocalizedStringResource 管理。

常见问题

  • Live Activity 无法启动:确认主应用 Info.plist 包含 NSSupportsLiveActivities = true,并在真机环境测试。
  • 属性解码失败:检查主应用与扩展的 EasyAppWidgetAttributes.ContentState 是否字段一致(参考团队经验,切勿新增未同步的属性)。
  • 灵动岛不显示按钮:确认 DynamicIslandExpandedRegion 中的按钮未被条件隐藏,并在交互时调用实际逻辑。
  • 预览编译报错:如预览依赖运行时代码,可在预览目标中提供 mock 数据或加 #if DEBUG 限制。

更多灵动岛布局示例可在 LiveActivityComponents.swift 底部的多个 #Preview 中查阅,涵盖紧凑、扩展、锁屏等场景。

Last updated on