Dynamic Island灵动岛调研

1.背景

对于IoT行业,iPhone14 Pro及Max的灵动岛可以用于展示设备的各种动作和计时,在iPhone锁定屏幕和动态岛上显示App的最新数据,这让人们可以一目了然地看到实时信息。

2.名词解释

词汇 含义
1 Widget 组件,iOS 14 重磅推出的新功能,使得用户可以在主屏幕添加小组件,快速浏览 app 提供的重要信息
2 Dynamic Island 灵动岛,iOS16.1推出的针对iphone14 pro系列机型的联动功能,快速浏览 app 提供的重要信息
3 Live Activity 实时活动,展示在灵动岛和锁屏界面的信息活动

Dynamic Island是ActivitiesKit的一部分,而它是Widget Extension的一部分。

3.配置条件

  • 需要至少Xcode 14.1 Beta版及iOS16.1才能使用ActivityKit 框架,iOS16.1是第一个开放ActivityKit的正式版本。
  • 在主工程的info.plist中加入键值NSSupportsLiveActivities ,并将其布尔值设置为YES
  • 因为灵动岛是属于小组件的一部分,所以项目中如果没有小组件的话要先创建小组件。有小组件的话可以添加些代码即可适配。


    16645173953743.png

4.详细介绍

4.1 布局
16645173873665.png
16645182762547.png
Simulator Screen Shot - iPhone 14 Pro Max - 2022-09-30 at 14.15.21.png

上图依次是

  • 紧凑视图:分为leading和traling视图,只有一个live activity事件时
  • 最小视图:两个及以上live activity事件时,左右显示的都是minmal视图
  • 扩展视图:分为leading、traling、center、bottom
  • 锁屏视图:锁屏的时候会展示在锁屏下方
var body: some WidgetConfiguration {
        ActivityConfiguration(for: GroceryDeliveryAppAttributes.self) { context in
            LockScreenView(context: context) //锁屏视图
        } dynamicIsland: { context in
            DynamicIsland {
                 //扩展视图
                 DynamicIslandExpandedRegion(.leading) {
                    dynamicIslandExpandedLeadingView(context: context)
                 }
                 
                 DynamicIslandExpandedRegion(.trailing) {
                     dynamicIslandExpandedTrailingView(context: context)
                 }
                 
                 DynamicIslandExpandedRegion(.center) {
                     dynamicIslandExpandedCenterView(context: context)
                 }
                 
                 DynamicIslandExpandedRegion(.bottom) {
                    dynamicIslandExpandedBottomView(context: context)
                 }
                
              } compactLeading: {//单个前视图
                  compactLeadingView(context: context)
              } compactTrailing: {//单个后视图
                  compactTrailingView(context: context)
              } minimal: { //最小化视图
                  minimalView(context: context)
              }
              .keylineTint(.cyan) //灵动岛边缘颜色
        }
    }

当只有一个live activity事件时


16645183250579.png
16645183283279.png

因为这个灵动岛左右是对称的,左边布局的拉宽会带动右边也拉宽,进而占据时间、信号、电量等图标。而用户肯定会对这种占据过多空间的live activity深恶痛绝,可能会立马干掉。

灵动岛:只能在App处于前台时从其启动实时活动。目前最多只能展示两个live activity,后来的不会显示,除非用户上下滑动移除了当前显示的live activity或者在锁屏界面移除或者代码调用移除,后来的才会依次展示在上面,因为用的模拟器调研,不知道系统的打电话功能是否优先级最高。注意这里的上下滑动只是隐藏了当前的live activity,他其实还在,锁屏的时候依然会显示。

锁屏: 没有灵动岛的设备同样会展示锁屏视图,高度最多显示160,其余会被系统裁剪掉。移除锁屏状态的视图,就相当于停止了live activity。

4.2 更新策略

每个“Live Activity”都在自己的沙盒中运行,虽然它是依托于小组件但是与小组件的时间线timeline更新不同,它无法访问网络或接收位置更新。要更新活动现场活动的动态数据,请使用应用程序中的ActivityKit框架,或允许您的现场活动接收远程推送通知。 ActivityKit 更新后和远程推送通知更新的动态数据大小不得超过 4KB

4.2.1 live activity更新
  1. 手动代码更新:
    16645187695082.png
  2. 推送通知更新:需要和服务端配置推送类型pushType,详情可见相关链接。如果通知被用户关闭了权限,应该只能采用手动更新来保证吧。

4.2.2 对比widget更新

Widget 的刷新方式是很特别的,相当的克制。以下摘自官方文档:
Updating every minute is far too aggressive. Widgets have a limited number of updates and if you were to try to update every minute, you'll quickly run out of updates. While debugging with Xcode these limits are not imposed, but if you're running your app outside of Xcode you'd see the behavior you're describing where your widget would stop updating.
意思是一个组件一天会有一定限制的刷新频次数,超过这个次数,系统将不保证刷新是否成功。
WidgetKit通过以下两种方式之一要求时间线条目:

  • 单个即时快照,表示小部件的当前状态。
  • 条目数组,包括当前时刻,以及(如果已知)小部件状态将更改的任何未来日期。

其实就是Widget引入了timeline时间线的概念,如果已知这个小组件现在及未来一段时间的状态,就可以在getTimeline提供内容展示的entry组。并且更新策略选.atEnd就是提供的entry都展示完再去请求新的。
WidgetKit 在一个单独的进程中呈现视图。因此,即使小部件在屏幕上,小部件扩展也不会一直处于活动状态。尽管小部件并不总是处于活动状态,但可以通过多种方式使其内容保持最新。

被动:

  • WidgetKit 有时会重新加载小部件以帮助保持其内容最新。一些常见的场景包括:
  • 如果小部件位于用户很少访问的主屏幕页面上,则 WidgetKit 可能会降低该小部件的重新加载频率。稍后,当用户查看页面时,WidgetKit 可能会在小部件可见时重新加载它。
  • 对于使用位置服务的小部件,WidgetKit 会在位置发生重大变化后重新加载它们。

主动:
主程序调用的主动刷新:

WidgetCenter.shared.reloadTimelines(ofKind: "com.mygame.character-detail")

官方示例:

如果已知一个人健康状态当前为25%,且每小时固定增长25%,则可以安排一个如下计划生成timeline

//@escaping修饰则可以进行异步网络请求,下面的例子是静态的时候的

func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) {

        var date = Date()

        var healthLevel = 0.25

        var entries: [CharacterDetailEntry] = []       

        while healthLevel <= 1 {

            entries.append(CharacterDetailEntry(date: date, healthLevel: healthLevel))  //添加当前状态的entry 是要立马展示出来的

            healthLevel = min(1, healthLevel + 0.25)                                                        // 每次把健康状态升高0.25,最高为1

            date = Calendar.current.date(byAdding: .hour, value: 1, to: date)!                 // TiimeLine提供一段Entry,而entry的最小间隔是1分钟,及时设置成秒也没用。

        }

        let timeline = Timeline(entries: entries, policy: .atEnd) // .after   .never

        completion(timeline)

    }
4.3 移除策略

在灵动岛leading视图部位上下滑动可以隐藏当前的live activity , 也可以在应用的设置里面禁止live activity。

  • default:“实时活动”会在锁定屏幕上显示一段时间,以便用户一目了然地查看手机以查看最新信息。用户可以选择随时删除“实时活动”,或者系统会在“实时活动”结束后四小时自动移除它。

  • immediate:立即移除从锁定屏幕结束的“实时活动”

  • after: 系统会在给定日期后或自“实时活动”结束起四小时后删除已结束的现场活动——以先到者为准

除非App主动结束或用户手动结束,否则展现在灵动岛的“live activity”活动在不操作的情况下最多可以活跃8小时。如果在第8小时的时候用户主动关闭了default或after策略的live activity,锁定屏幕上的可以再多显示4个小时。所以即实时活动会灵动上岛最多保留八小时,在锁定屏幕上最多保留十二小时。 杀死App不会停止live activity.

欢迎大家交流并指出错误,谢谢

5. 参考文档

参考 链接
ActivityKit https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities
Live Activity Push Notification https://developer.apple.com/documentation/activitykit/update-and-end-your-live-activity-with-remote-push-notifications
Dynamic Island https://developer.apple.com/documentation/widgetkit/dynamicisland
Code https://github.com/jordibruin/Dynamic-Islands https://github.com/batikansosun/iOSLiveActivitiesLockScreen https://github.com/1998code/iOS16-Live-Activities

你可能感兴趣的:(Dynamic Island灵动岛调研)