iOS灵动岛开发实践

背景

这篇文章主要做一个引导,以及开发过程中遇到的疑难点进行讲述。具体的实操部分网上已经很多文章,可以去找找。

开发灵动岛必看官方文档:

  1. 灵动岛设计开发规范 ,这篇文档你们UI和产品会需要,你也需要看一下,讲述了灵动岛的尺寸、样式等。《Live Activities
  2. 灵动岛开发指导 ,这篇文章主要讲解灵动岛的相关接口使用,开发必看。《Displaying live data with Live Activities

问题点

1、灵动岛和实时活动如何实现动画?

目前灵动岛和实时活动还不支持自定义动画,如果自己写了动画,会被系统强制屏蔽。但iOS17开始,可以使用时序曲线实现一些线性的动画效果,具体可以去了解一下。

2、如何屏蔽或者修改系统自带的模糊淡入和淡出的动画效果?

可以使用控件的转场动画.contentTransition()接口,对内容动画进行调整。注意.transition()和.contentTransition()方法的区别,.transition()方法是针对整个控件的转场动画,如果只是控件内容发生改变,不会有什么效果的;.contentTransition()是对内容的转场动画,比如更新Text的内容,就用到它。.identity屏蔽系统动画,还有其他枚举,可以调整动画显示的类型。

scss 复制代码
//屏蔽系统自带的动画淡入淡出效果
Text(context.state.time)
    .multilineTextAlignment(.center)
    .foregroundColor(context.state.color)
    .font(.system(size: 14).weight(.semibold))
    .padding(.trailing, 7)
    .contentTransition(.identity)
3、如何在灵动岛或者实时活动添加按钮?

iOS17及以上才支持添加按钮点击事件,在此之前添加按钮到灵动岛或者实时活动页上,点击按钮时,点击事件是不生效的,会直接打开App。

4、灵动岛/实时活动如何和主App进行通讯,传递事件或者数据?

灵动岛/实时活动和主App进行通讯,分为两个方向的数据传递:主App-->灵动岛灵动岛-->主App 。 讲这个前,首先我们要清楚一个前提:当主App被杀掉时(没运行的时候),灵动岛和实时活动仍然是可以展示和更新的,而灵动岛和实时活动作为一个小组件,并不知道主App的生命周期。因此灵动岛-->主App同步发送数据或事件这条链路是行不通的。 那问题来了,我们要怎么实现数据传递呢?无法同步实现,我们可以异步实现是不?

  • 主App-->灵动岛

这个链路就比较容易实现,苹果提供了相关的接口:

swift 复制代码
    /// 显示灵动岛和实时活动
    @objc public func show(title:String, time:String, islandIcon:String, islandColor:String, content:String, isRecording: Bool) {
        let attributes = CCityWidgetAttributes(title: title)
        let contentState = CCityWidgetAttributes.ContentState(time: time, islandIcon: islandIcon, islandColor: islandColor, content: content, isRecording: isRecording)
        do {
            activity = try Activity<CCityWidgetAttributes>.request(attributes: attributes, contentState: contentState, pushType: nil)
        } catch {
            print(error.localizedDescription)
        }
    }
    
    /// 更新灵动岛内容
    @objc public func update(time:String, islandIcon:String, islandColor:String, content:String, isRecording: Bool) {
        let contentState = CCityWidgetAttributes.ContentState(time: time, islandIcon: islandIcon, islandColor:islandColor, content: content, isRecording: isRecording)
        Task {
            await self.activity?.update(using: contentState)
        }
    }
    
    /// 结束灵动岛显示
    @objc public func end() {
        Task {
            for activity in Activity<CCityWidgetAttributes>.activities{
                await activity.end(dismissalPolicy: .immediate)
            }
        }
    }
swift 复制代码
@available(iOS 16.1, *)
struct CCityWidgetAttributes: ActivityAttributes {

    public struct ContentState: Codable, Hashable {
        // 动态属性,可以在update方法中更新的属性,需要一直更新的内容,可以放到ContentState中。
        var time: String
        var islandIcon: String
        var islandColor: String
        var content: String
        var isRecording: Bool
        
        var color: Color {
            return Color(hexString: islandColor)
        }
    }
    // 静态属性,只有在show的时候才会更新的属性,不变的属性放在这里
    var title: String
}
  • 灵动岛-->主App

这个链路目前苹果并没有提供直接同步传递数据的方式,因此是不支持这个链路的数据通讯的。但是我们可以使用共享沙盒目录进行数据传递,即当用户点击灵动岛或者实时活动中的按钮时,将状态或者数据保存到共享沙盒中,然后当唤起主App时,主App读取沙盒中的数据,实现数据传递。相关逻辑可以参考《APP Extension 与 APP之间的数据共享》。

当然网上还有其他方案,比如通过本地socket进行数据发送来实现通讯,大致思路就是小组件和主App,作为socket的两端,相互发送数据来实现通讯,但是那样太重,没这必要。至于其他方式基本都被苹果禁用掉了,苹果也拒绝使用其他非常规手段,建议不用徒劳了。

4. 其他一些开发过程中遇到的问题。
  • 连续同步update灵动岛,数据只会更新一次,苹果内部接口应该对灵动岛的更新有一定的时间限制,因此建议将数据缓存一次性进行更新。
  • 当锁屏时(真正的锁屏,即没有解锁脸部识别),实时活动的更新频率会降低,比如几秒钟才会更新一次(哪怕你代码是每秒钟都调update接口),且更新动画也会被屏蔽,苹果应该是为了省电吧。
相关推荐
小溪彼岸3 天前
【iOS小组件】小组件尺寸及类型适配
swiftui·swift
文件夹__iOS8 天前
[SwiftUI 开发] @dynamicCallable 与 callAsFunction:将类型实例作为函数调用
ios·swiftui·swift
小溪彼岸8 天前
【iOS小组件】iOS17与低版本兼容适配
swiftui·swift
Mamong9 天前
SwiftUI疑难杂症(1):sheet content多次执行
ios·swiftui·swift
AUV110713 天前
Mac剪贴板历史全记录!
macos·swiftui·mac·效率工具·实用工具·剪贴板·clipboard
AUV110713 天前
Mac 上哪个剪切板增强工具比较好用? 好用剪切板工具推荐
macos·swiftui·mac·剪贴板·clipboard·剪贴板增强·app 推荐
多彩电脑15 天前
SwiftUI里的ForEach使用的注意事项
macos·ios·swiftui·swift
Swift社区17 天前
Apple 新品发布会亮点有哪些 | Swift 周报 issue 61
ios·swiftui·swift
humiaor18 天前
Xcode报错:No exact matches in reference to static method ‘buildExpression‘
swiftui·xcode
humiaor1 个月前
Xcode报错:Return from initializer without initializing all stored properties
swiftui·binding