简介
苹果在 iPhone 14 Pro 及 iPhone 14 Pro MAX 上推出了灵动岛。灵动岛将 iPhone 前置镜头和软件通知结合在一起的全新设计,用出色的交互设计掩盖硬件的缺陷,是一次交互玩法的革新。灵动岛可以通过点按、长按、轻扫来进行交互,最多支持两个应用同时“登岛”。
灵动岛全称 Dynamic Island,作为 iOS 中实时活动(Live Activities)功能的一部分,用来展示需要实时更新的消息。例如外卖配送信息,地图实时导航信息等。灵动岛有 3 种展现形式。
- 紧凑(Compact)
当系统只有 1 个实时活动的内容时,灵动岛默认使用紧凑模式。紧凑模式下UI由头部(Leading side)和尾部(Trailing side)组成,如图所示。用户可以点击灵动岛打开 App 查看实时活动的内容。
- 最小化(Minimal)
当系统有多个实时活动的内容时,灵动岛自动切换使用最小化模式。最小化模式下由附着的头部(Leading(attached))和分割开的尾部(Trailing(detached))组成,如图所示。和紧凑模式一样,最小化模式也支持用户点击打开 App。
- 扩展(Expanded)
当用户在紧凑或最小化模式轻扫或长按灵动岛时,灵动岛可以切换成扩展模式。用于向用户展示更多信息。扩展模式的 UI
UI适配
灵动岛使用了44点的圆角半径
| 机型 | 屏幕尺寸 | Compact leading | Compact trailing | Minimal (diameter) | Expanded (height given as a range) | Lock Screen |
|---|---|---|---|---|---|---|
| iPhone 14 Pro Max | 430 × 932 px | 62.33x36.67 | 62.33x36.67 | 36.67 | 408x84–160 | 408x160 |
| iPhone 14 Pro | 393 × 852 px | 52.33x36.67 | 52.33x36.67 | 36.67 | 371x84–160 | 371x160 |
开发框架
暂时无法在飞书文档外展示此内容
工程创建
Include Live Activity 这个要勾选,这个是灵动岛要用到的
Include Configuration Intent 如果你的 widget 支持用户配置属性,则需要勾选这个(例如天气组件,用户可以选择城市),不支持的话则不用勾选
在主工程的 info.plist 文件中添加 Supports Live Activities 并且设置为 YES
-
数据部分
- 继承ActivityAttributes,定义常用的数据来控制或改变UI及状态。这里包含的商品的基本信息,可以认为是不变的状态,可变的状态需要在 ContentState 中声明。
-
public struct BPLiveActivitiesData: ActivityAttributes { public typealias LiveActivitiesStatus = ContentState public struct ContentState: Codable, Hashable { var name: String var status : Int //1待接单, 2配送中 3已完成 var timer: ClosedRange<Date> } var numberOfPizzas: Int var totalAmount: String var orderNumber: String }
-
布局
- 继承Widget,通过ActivityConfiguration来管理锁屏及灵动岛的布局
-
struct BeautyPlusDynamicIslandLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: BPLiveActivitiesData.self) { context in // Lock screen/banner UI goes here // Lock screen/banner UI goes here VStack { Text(timerInterval: context.state.timer, countsDown: true) .bold() .font(.caption) .foregroundColor(.white.opacity(0.8)) .multilineTextAlignment(.center) Text(context.state.name).foregroundColor(Color.white) } .activityBackgroundTint(Color.cyan) .activitySystemActionForegroundColor(Color.black) } dynamicIsland: { context in DynamicIsland { // Expanded UI goes here. Compose the expanded UI through // various regions, like leading/trailing/center/bottom DynamicIslandExpandedRegion(.leading) { Text("Leading") } DynamicIslandExpandedRegion(.trailing) { Text("Trailing") } DynamicIslandExpandedRegion(.bottom) { Text("Bottom") // more content } } compactLeading: { Text("L") } compactTrailing: { Text("T") } minimal: { Text("Min") } .widgetURL(URL(string: "http://www.apple.com")) .keylineTint(Color.red) } } }
-
小组件初始化入口
-
@main struct BeautyPlusDynamicIslandBundle: WidgetBundle { var body: some Widget { BeautyPlusDynamicIsland() BeautyPlusDynamicIslandLiveActivity() } }
-
-
数据更新
-
Task{ let data = BPLiveActivitiesData.LiveActivitiesStatus(name: "变化了", status: 1, timer: Date()...Date().addingTimeInterval(60 * 60)) for activity in Activity<BPLiveActivitiesData>.activities { await activity.update(using: data) } }
-