美文网首页
iOS灵动岛开发实践

iOS灵动岛开发实践

作者: Minoz_min | 来源:发表于2023-10-30 18:18 被阅读0次

约束条件

1.Live Activity最多可以保持8小时的活动状态
2.已结束的Live Activity,在锁定屏幕上最多保留4个小时,所以实时活动在锁屏上最多保留12小时
3.最小化演示图像不能超过45x36.67pt
4.ActivityKit 更新和 ActivityKit 推送通知的更新动态数据大小不能超过 4 KB。
5.Live Activity的高度超过160pt,系统可能会截断该活动

灵动岛配置

1.创建Live Activity

1.在主项目中添加targetFile -> New -> Target...

选择Widget Extension.png

2.选择Widget Extension,勾选Include Live Activity,会自动创建模版代码

创建Live Activity.png

2.添加Info.plist配置

主项目Info.plist文件中添加Supports Live Activities配置,设置为YES

<key>NSSupportsLiveActivities</key>
<true/>

3.代码部分

主项目中

  1. 创建ActivityAttributes文件,用来提供灵动岛上展示的数据
import Foundation
import ActivityKit

struct LiveActvityWidgetAttributes: ActivityAttributes {
    typealias LiveActivityWidgetState = ContentState
    
    public struct ContentState: Codable, Hashable {
        // 可变的属性需要放在这里,activity调用update进行数据的更新
        var emoji: String
    }

    // 灵动岛的初始化数据,描述不可变的数据
    var name: String
    var logo: String
    var subName: String
}

2.将创建的LiveActvityWidgetAttributes共享给灵动岛扩展组件使用

共享文件配置.png

3.创建灵动岛并初始化数据

  • request(attributes:content:pushType:) 请求并启动Live activity
  • update(_:) 更新Live Activity的动态内容
  • end(_: dismissalPolicy:) 结束正在进行的Live activity
import UIKit
import ActivityKit

class ViewController: UIViewController {
    var liveActivity: Activity<LiveActvityWidgetAttributes>? = nil

    @IBOutlet weak var openButton: UIButton! {
        didSet {
            openButton.setTitle("开启灵动岛", for: .normal)
            openButton.setTitle("关闭灵动岛", for: .selected)
        }
    }
    @IBOutlet weak var updateButton: UIButton! {
        didSet {
            updateButton.setTitle("更新灵动岛", for: .normal)
            updateButton.setTitle("已更新灵动岛", for: .selected)
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    func actvityContent(emoji: String) -> ActivityContent<LiveActvityWidgetAttributes.LiveActivityWidgetState> {
        let state = LiveActvityWidgetAttributes.LiveActivityWidgetState(emoji: emoji)
        let content = ActivityContent<LiveActvityWidgetAttributes.LiveActivityWidgetState>(state: state, staleDate: nil)
        
        return content
    }

    @IBAction func onOpen(_ sender: Any) {
        guard ActivityAuthorizationInfo().areActivitiesEnabled else {
            print("不支持")
            return
        }
        
        openButton.isSelected.toggle()
        
        guard openButton.isSelected else {
             // 关闭灵动岛
            Task {
                await liveActivity?.end(nil, dismissalPolicy:.immediate)
            }
            updateButton.isSelected.toggle()
            return
        }
        
        do {
            // 创建灵动岛并初始化数据
            let attributes = LiveActvityWidgetAttributes(name: "灵动岛", logo: "apple.logo", subName: "这是长按展开显示的文案")
            let content = actvityContent(emoji: "😀")
            self.liveActivity = try Activity<LiveActvityWidgetAttributes>.request(attributes: attributes, content: content)
        } catch {
            debugPrint("打开灵动岛失败")
        }
    }
     // 更新灵动岛数据
    @IBAction func onUpdate(_ sender: Any) {
        guard openButton.isSelected else { return }
        
        updateButton.isSelected.toggle()
        
        Task {
            let content = actvityContent(emoji: "🤩")
            await liveActivity?.update(content)
        }
    }
}

扩展组件代码中

  • context.attributes 获取静态数据
  • context.state 获取动态数据
import ActivityKit
import WidgetKit
import SwiftUI

struct LiveActvityWidgetLiveActivity: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: LiveActvityWidgetAttributes.self) { context in
            // 小技巧:使用该方式进行日志打印
            let _ = debugPrint("context: \(context.attributes)")
            
            // 锁屏之后,显示的桌面通知栏位置,这里可以做相对复杂的布局
            VStack {
                Text("Hello \(context.state.emoji)")
            }
            .activityBackgroundTint(Color.cyan)
            .activitySystemActionForegroundColor(Color.black)

        } dynamicIsland: { context in
            // 灵动岛布局
            DynamicIsland {
                /*
                 长按灵动岛区域展开的UI
                 
                 有4个区域布局:左,右,中间(硬件下方),底部
                 */
                DynamicIslandExpandedRegion(.leading) {
                    VStack(alignment: .leading) {
                        Image(systemName: "\(context.attributes.logo)")
                            .resizable()
                            .frame(width: 30, height: 30)
                        Text("apple logo")
                    }
                }
                DynamicIslandExpandedRegion(.trailing) {
                    Image(systemName: "apple.terminal.on.rectangle.fill")
                }
                DynamicIslandExpandedRegion(.center) {
                    Text("\(context.attributes.name)")
                }
                DynamicIslandExpandedRegion(.bottom) {
                    VStack {
                        Image(systemName: "sun.max.fill")
                            .resizable()
                            .frame(width: 30, height: 30)
                            .foregroundColor(.yellow)
                        Text("\(context.attributes.subName)")
                    }
                }
            } compactLeading: {
                // 未展示开边的布局
                Image(systemName: "\(context.attributes.logo)")
            } compactTrailing: {
                // 未展示右边布局
                Text("\(context.state.emoji)")
            } minimal: {
                // 最小型样式,当有多个任务的情况下,位置在右边的一个圆圈区域
                Image(systemName: "figure.wave.circle.fill").foregroundColor(.red)
            }
            // 点击整个区域,通过deeplink将数据传递给主工程
            .widgetURL(URL(string: "http://www.apple.com"))
            .keylineTint(Color.red)
        }
    }
}

效果图

未展开样式.png
未展开更新数据效果.png
长按展示效果.png

参考文档:
ActivityKit文档
iOS灵动岛开发实践

相关文章

网友评论

      本文标题:iOS灵动岛开发实践

      本文链接:https://www.haomeiwen.com/subject/kwwbidtx.html