前语

iOS最近几年新特性

iOS14 视频画中画 AppLibrary 桌面小组件 相片隐私加强 应用限免 智能折叠 全新siri悬浮显现
iOS15 FaceTime支撑屏幕共享 信息和新增拟我表情 推出专心方法 告诉从头规划,图标变得更大 地图公共交通路线置顶,增加时刻显现 辨认图片上文字信息 支撑相片信息和相片上的文字进行查找
iOS16 iOS 16 确定界面 确定界面小组件 锁屏界面的实时活动 iPhone确定全屏幕音乐播放器 电池百分比出来啦 视频实况文本 快速查询Wi-Fi暗码
iOS17 设置您的待机屏幕 优先考虑交互式小部件 定制您的联系海报 创立您自己的贴纸 设置新的 Safari 配置文件 开启反追踪 共享您的 iCloud 钥匙串暗码

一、简介

带你走进灵动岛 | 京东云技能团队

实时活动(Live Activity),是iOS16新增的扩展组件功用,能够在灵动岛和确定屏幕上显现应用程序的实时数据。用于追踪事情和使命进度实时活动的开始和完毕都是离散的,详细画面场景如下:苹果

带你走进灵动岛 | 京东云技能团队

苹果在 iPhone 14 Pro 及 iPhone 14 Pro MAX 上推出了灵动岛。灵动岛将 iPhone 前置镜头和软件告诉结合在一起的全新规划,用出色的交互规划掩盖硬件的缺点,是一次交互玩法的改造。灵动岛能够经过点按、长按、轻扫来进行交互,最多支撑两个应用一起“登岛”。

灵动岛全称 Dynamic Island,作为 iOS 中实时活动(Live Activities)功用的一部分,用来展现需求实时更新的音讯。例如外卖配送信息,地图实时导航信息等。灵动岛有 3 种展现方法。

1.1 展现方法

1.1.1 紧凑(Compact)

当体系只有 1 个实时活动的内容时,灵动岛默许运用紧凑方法。紧凑方法下UI由头部(Leading side)和尾部(Trailing side)组成,如图所示。用户能够点击灵动岛打开 App 检查实时活动的内容

带你走进灵动岛 | 京东云技能团队

1.1.2 最小化(Minimal)

当体系有多个实时活动的内容时,灵动岛主动切换运用最小化方法。最小化方法下由附着的头部(Leading(attached))和分割开的尾部(Trailing(detached))组成,如图所示。和紧凑方法相同,最小化方法也支撑用户点击打开 App。

带你走进灵动岛 | 京东云技能团队

1.1.3扩展(Expanded)

当用户在紧凑或最小化方法轻扫或长按灵动岛时,灵动岛能够切换成扩展方法。用于向用户展现更多信息。扩展方法的 UI

带你走进灵动岛 | 京东云技能团队

规划尽量坚持和紧凑方法一致,用户从紧凑方法切换到扩展方法会有一个平滑的体会。

当咱们向 App Store 提交了适配灵动岛的 App 版本时,以上 3 种方法都需求适配。

带你走进灵动岛 | 京东云技能团队

二、场景约束

2.1款式约束

1、实时活动针对确定屏幕和灵动岛提供了不同的视图。确定屏幕能够呈现在所有支撑 iOS 16 的设备上。而灵动岛在支撑设备上,运用以下视图显现实时活动:紧凑前视图、紧凑尾视图、最小视图和扩展视图。

2、当用户触摸灵动岛,且灵动岛中有紧凑或最小视图,一起实时活动更新时,会呈现扩展视图。在不支撑灵动岛的设备上,扩展视图显现为实时活动更新的横幅。

3、为保证体系能够在每个方位显现 App 的实时活动,开发者必须支撑所有视图

主张:同场景多卡片因为款式趋同且折叠,不主张一起创立多卡片

灵动岛页面需求完结的部分有4个:

a、不支撑灵动岛的机型 或 锁屏时的 显现

b、紧凑级展现(即左右贴合灵动岛的展现)

c、多Live activity时的展现(即极小视图,左贴合,右别离)

d、扩展视图(长按灵动岛时触发)

补白:还有一个App一起存在的实时活动面板最多只能创立5个,这也是一个场景约束条件。Error requesting delivery Live Activity The operation couldn’t be completed. Maximum number of activities for target already exists

2.2 时刻约束

实时活动最多能够坚持八小时的活动状况,除非其应用程序或人员在此约束之前完毕活动。超越八小时约束后,体系主动完毕直播活动,并立即将其移出动态岛。可是,实时活动会保存在确定屏幕上,直到有人将其删除,或许在体系将其删除之前最多再保存四个小时(以先到者为准)。因而,实时活动在确定屏幕上保存最多 12 小时。

官方表述:developer.apple.com/documentati…

A Live Activity can be active for up to eight hours unless your app or a person ends it before this limit. After the 8-hour limit, the system automatically ends it. When a Live Activity ends, the system immediately removes it from the Dynamic Island. However, the Live Activity remains on the Lock Screen until a person removes it or for up to four additional hours before the system removes it — whichever comes first. As a result, a Live Activity remains on the Lock Screen for a maximum of twelve hours.

2.3 数据更新

每个实时活动运行在自己的沙盒中,与小组件不同的是,它无法访问网络或接收方位更新。若要更新实时活动的动态数据,少数(不能超越4KB)数据可经过长途推送告诉发送,或经过ActivityKit 框架后台活动改写数据。

ActivityKit 更新和 ActivityKit 推送告诉的更新动态数据巨细不能超越 4 KB。

2.4 网络约束

a、卡片自身禁止定位以及网络恳求,数据改写依靠本地改写,施行活动推送改写,同2)所述;

b、Live Activity内部禁用网络图片,传统的服务端传图片URL的方法无法满足实际运用,可是希望传入订单图片来个性化地表达而且区别不同订单。

iOS 16 beta版创立时能够经过将图片转为Data格局传入卡片,可是iOS16.1该计划仅限传入4KB左右的图片(API约束),因而暂时不考虑非本地图片计划,采用内置图片方法完结。

2.5 埋点约束

**场景状况:**因为默许状况点击是回主程序,而并不是固定页面,因而有必要自定义widgetUrl(如用于回到订单页面),也能够经过Link完结分区域的跳转,Link和widgetUrl共存时,点击Link区域会呼应Link,因而两者一起运用即可。

无法在widget内部直接增加埋点,而且灵动岛收起时,仅支撑增加同一个widgetUrl,关于收起状况增加Link并没有呼应。

**埋点方法:**因为点击直接跳转到主App,因而考虑将埋点参数参加URL参数即可,主App解析时埋点。可是无法记载包括用户检查、用户封闭(封闭卡片 持续发送推送也没有报错 因而无法判断)等行为的埋点。

关于灵动岛的区别,实际测试发现,在展开方法下,能够参加Link而且能够正常呼应,这与官方文档中的描述一致。

三、适配

3.1 UI适配

1、尺度

现在只有 iPhone 14 Pro 及 iPhone 14 Pro MAX 具有灵动岛功用。在两种机型上,灵动岛的圆角半径都为 44Points,这个数值和前置深感摄像头的半径是相同的。按照前述的 3 种方法,灵动岛的详细参数如下表格所示(表格触及的数值表示Points)。

机型 屏幕尺度 紧凑方法(头部) 紧凑方法(尾部) 最小化方法 展开方法
iPhone 14 Pro 393*852 52.33*36.67 52.33*36.67 36.67*36.67 371*(84-160)
iPhone 14 Pro Max 430*932 62.33*36.67 62.33*36.67 36.67*36.67 408*(84-160)

2、色彩

开发者无法更改灵动岛的背景色彩,只能更改文字色彩、素材色彩、灵动岛边框色彩等。UI 适配需求考虑体系的深色方法,必要状况能够运用两套 UI。

3.2开发适配

3.2.1开发框架简介

苹果在 iOS 16.1 正式对外开放了灵动岛适配框架ActivityKit,第三方 App 能够运用这些ActivityKit完结灵动岛适配工作。留意ActivityKit的 API 现在仅适用于 iPhone。灵动岛运用WidgetKitSwiftUI完结 UI 开发工作,ActivityKit在其中扮演创立Activity,恳求数据,更新数据,完毕Activity的角色。

带你走进灵动岛 | 京东云技能团队

3.2.2权限办理

灵动岛作为实时活动的一部分,需求实时活动权限才能正常展现。和告诉权限,相机权限等相似,实时活动权限需求 App

带你走进灵动岛 | 京东云技能团队

3.2.3 生命周期

Request

Update

Observe avtivity state

End

import ActivityKit
struct AdventureAttributes: ActivityAttributes {
//不可变
    let hero: EmojiRanger
    /// The associated type that describes the dynamic content of a Live Activity.
    ///
    /// The dynamic data of a Live Activity that's encoded by `ContentState` can't exceed 4KB.
    struct ContentState: Codable & Hashable {
        let currentHealthLevel: Double
        let eventDescription: String
    }
}
let adventure = AdventureAttributes(hero: hero)
let initialState = AdventureAttributes.ContentState(
    currentHealthLevel: hero.healthLevel,
    eventDescription: "Adventure has begun!"
)
let content = ActivityContent(state: initialState, staleDate: nil, relevanceScore: 0.0)
let activity = try Activity.request(
    attributes: adventure,
    content: content,
    pushType: nil
)
let heroName = activity.attributes.hero.name
let contentState = AdventureAttributes.ContentState(
    currentHealthLevel: hero.healthLevel,
    eventDescription: "(heroName) has taken a critical hit!"
)
var alertConfig = AlertConfiguration(
    title: "(heroName) has taken a critical hit!",
    body: "Open the app and use a potion to heal (heroName)",
    sound: .default
)  
activity.update(
    ActivityContent<AdventureAttributes.ContentState>(
        state: contentState,
        staleDate: nil
    ),
    alertConfiguration: alertConfig
)
// Observe activity state asynchronously
func observeActivity(activity: Activity<AdventureAttributes>) {
    Task {
        for await activityState in activity.activityStateUpdates {
            if activityState == .dismissed {
                self.cleanUpDismissedActivity()
            }
        }
    }
}
// Observe activity state synchronously
let activityState = activity.activityState
if activityState == .dismissed {
    self.cleanUpDismissedActivity()
}
let hero = activity.attributes.hero
let finalContent = AdventureAttributes.ContentState(
    currentHealthLevel: hero.healthLevel,
    eventDescription: "Adventure over! (hero.name) has defeated the boss! Congrats!"
)
let dismissalPolicy: ActivityUIDismissalPolicy = .default
activity.end(
    ActivityContent(state: finalContent, staleDate: nil),
    dismissalPolicy: dismissalPolicy)
}
3.2.4UI
import WidgetKit
import SwiftUI
@main
struct EmojiRangersWidgetBundle: WidgetBundle {
    var body: some Widget {
        EmojiRangerWidget()
        LeaderboardWidget()
        AdventureActivityConfiguration()
    }
}
struct AdventureActivityConfiguration: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: AdventureAttributes.self) { context in
            // ...
            // Create the view that appears on the Lock Screen and as a
            // banner on the Home Screen of devices that don't support the
            // Dynamic Island.
        } dynamicIsland: { context in
 // Create the views that appear in the Dynamic Island.
            DynamicIsland {
                // Create the expanded view.
                // Leading region
                // Expanded region
                // Bottom region
            } compactLeading: {
                // Create the compact leading view.
                // ...
            } compactTrailing: {
                // Create the compact trailing view.
                // ...
            } minimal: {
                // Create the minimal view.
                // ...
            }
        }
    }
}

四、长途告诉更新数据

带你走进灵动岛 | 京东云技能团队

实时活动也支撑长途推送更新,依据文档以下9点要求完结(avtivity长途推送每小时有告诉预算(数量未清晰),超出后体系将封闭告诉)

1、保证发动activity时[request(attributes:contentState:pushType:)传入pushType参数(.token);

2、获取发动后的activity的推送令牌pushToken,传给服务端用来推送更新activity;(实时活动的pushToken不是音讯告诉的token,这个是独立出来的)

3、服务端推送的更新内容字段需求和ActivityAttributes的ContentState中定义的动态数据字段对应;

4、设置推送的报头apns-push-type的值为liveactivity;

5、设置推送的报头apns-topic的值为.push-type.liveactivity;

6、正确的推送对应的内容和状况;

7、运用pushTokenUpdates监听pushToken变化,如有变化,就令牌失效,需求将新的令牌传给服务器

8、当Activity完毕时,服务器端的pushToken将失效;

{
    "aps": {
        "timestamp": 1685952000,
        "event": "update",
        "content-state": {
            "currentHealthLevel": 0.0,
            "eventDescription": "Power Panda has been knocked down!"
        },
        "alert": {
            "title": "Power Panda is knocked down!",
            "body": "Use a potion to heal Power Panda!",
            "sound": "default"
        }
    }
}

留意:

1、不用为推送提供声响 , 如果推送推迟,在activity完毕后收到时将被忽略,avtivity每小时有告诉预算(数量未清晰),超出后体系将封闭告诉;

2、实时活动的pushToken不是音讯告诉的token,这个token上报到JDPush服务,需求独自办理和归类。

补白:

  1. 灵动岛的实时信息要有清晰的开始和完毕时刻点

  2. 当一个实时信息持续超越 8 小时,体系会从灵动岛移除这个 App 的信息

  3. 当一个实时活动完毕时,灵动岛上的展现信息也会立即被体系移除

  4. 防止在灵动岛上显现广告,究竟引起用户恶感能够被直接封闭

  5. App 要能够呼应灵动岛的点击信息,跳转到 App 中的正确子页面,而不是停留在 App 的主页

运用场景

1、需在屏幕驻留的文字、图画为主的信息:如地图导航、airdrop 传输状况等;

2、后台进行的音频类:如接电话、放音乐、录音、倒计时等;

3、即时交互反应:如充电、静音、人脸辨认等。超越这三类信息后,桌面可能会变得乱七八糟

带你走进灵动岛 | 京东云技能团队

参阅文章

ActivityKit官方文档

developer.apple.com/videos/play…

developer.apple.com/videos/play…

www.jianshu.com/p/f410eba6c…

www.bilibili.com/read/cv1854…

作者:京东零售李艳敏

来历:京东云开发者社区 转载请注明来历