mpush_ios_demo/mpush_liveActivity_extension/mpushTakeoutLiveActivity.swift (186 lines of code) (raw):

// // mpushTakeoutLiveActivity.swift // mpush_liveActivity_extension // // Created by Miracle on 2025/3/27. // Copyright © 2025 alibaba. All rights reserved. // import ActivityKit import WidgetKit import SwiftUI struct mpushTakeoutAttributes: ActivityAttributes { // 内容状态定义动态参数 public struct ContentState: Codable, Hashable { // 配送状态,"1" - 备货中, "2" - 待配送, "3" - 配送中, "4" - 已完成 var status: String? // 配送距离(单位:米) var distance: String? // 配送剩余时间(单位:分钟) var progress: String? // 提示语 var prompt: String? } // 静态参数 - 创建后不会变化 // 商家名称 var merchantName: String // 商家图片 var merchantLogo: String } @available(iOS 16.1, *) struct mpushTakeoutLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: mpushTakeoutAttributes.self) { context in // Lock screen/banner UI goes here TakeoutLockStatusView(attributes: context.attributes, state: context.state) } dynamicIsland: { context in DynamicIsland { // Expanded UI goes here. Compose the expanded UI through // various regions, like leading/trailing/center/bottom DynamicIslandExpandedRegion(.center) { TakeoutExpandedView(attributes: context.attributes, state: context.state) } } compactLeading: { // 左侧显示商家图片和名称 HStack { let imageName = context.attributes.merchantLogo // 尝试从Assets中加载该名称的图片 if UIImage(named: imageName) != nil { // 本地Assets中存在该图片 Image(imageName) .resizable() .aspectRatio(contentMode: .fit) .frame(width:36, height: 36) .cornerRadius(18) } else { // 本地Assets中不存在该图片,使用默认shop图片 Image("shop") .resizable().aspectRatio(contentMode: .fit) .frame(width: 36, height:36) .cornerRadius(18) } Text(context.attributes.merchantName).font(.system(size: 12)).lineLimit(1) } } compactTrailing: { // 右侧显示对应状态 Text(statusTitle(state: context.state)).font(.system(size: 12)).lineLimit(1) } minimal: { // 显示对应状态 Text(statusTitle(state: context.state)).font(.system(size: 12)).lineLimit(1) } .widgetURL(URL(string: "pocdemo://dynamicisland")) .keylineTint(Color.red) } } func statusTitle(state: mpushTakeoutAttributes.ContentState) -> String { switch state.status { case "1": return "备货中" case "2": return "待配送" case "3": return "配送中" case "4": return "已完成" default: return "状态未知" } } } @available(iOS 16.1, *) struct TakeoutLockStatusView: View { let attributes: mpushTakeoutAttributes let state: mpushTakeoutAttributes.ContentState var body: some View { HStack(spacing: 12) { // 左侧商家信息(所有状态都显示) VStack(alignment: .center) { let imageName = attributes.merchantLogo // 尝试从Assets中加载该名称的图片 if UIImage(named: imageName) != nil { // 本地Assets中存在该图片 Image(imageName) .resizable() .aspectRatio(contentMode: .fit) .frame(width:36, height: 36) .cornerRadius(18) } else { // 本地Assets中不存在该图片,使用默认shop图片 Image("shop") .resizable().aspectRatio(contentMode: .fit) .frame(width: 36, height:36) .cornerRadius(18) } Text(attributes.merchantName).font(.system(size: 12)).lineLimit(1) }.frame(width:60) // 中间和右侧内容,根据状态显示不同内容 VStack(alignment: .leading, spacing: 4) { switch state.status { case "1": // 备货中 Text("商家备货中").font(.system(size: 16, weight: .medium)) if let promptText = state.prompt, !promptText.isEmpty { Text(promptText).font(.system(size: 13)).foregroundColor(.secondary) } case "2": // 待配送 Text("已备货,等待配送").font(.system(size: 16, weight: .medium)) case "3": // 配送中 Text("骑手配送中").font(.system(size: 16, weight: .medium)) HStack(spacing: 0) { Text("距您\(checkIfNumeric(input: state.distance))米").font(.system(size:13)).foregroundColor(.secondary) Text(",预计\(checkIfNumeric(input: state.progress))分钟送达").font(.system(size: 13)).foregroundColor(.secondary) } case "4": //已送达 Text("商品已送达").font(.system(size: 16, weight: .medium)) if let promptText = state.prompt, !promptText.isEmpty { Text(promptText).font(.system(size: 13)).foregroundColor(.secondary) } default: Text("配送状态未知").font(.system(size: 16, weight: .medium)) } }.frame(maxWidth: .infinity, alignment: .leading) } .padding() .background(Color(UIColor.systemBackground)) } } // 扩展模式视图 @available(iOS 16.1, *) struct TakeoutExpandedView: View { let attributes: mpushTakeoutAttributes let state: mpushTakeoutAttributes.ContentState var body: some View { VStack(alignment: .center) { HStack(alignment: .center, spacing: 12) { // 左侧商家信息(所有状态都显示) VStack(alignment: .center) { let imageName = attributes.merchantLogo // 尝试从Assets中加载该名称的图片 if UIImage(named: imageName) != nil { // 本地Assets中存在该图片 Image(imageName) .resizable() .aspectRatio(contentMode: .fit) .frame(width:36, height: 36) .cornerRadius(18) } else { // 本地Assets中不存在该图片,使用默认shop图片 Image("shop") .resizable().aspectRatio(contentMode: .fit) .frame(width: 36, height:36) .cornerRadius(18) } Text(attributes.merchantName).font(.system(size: 12)).lineLimit(1) }.frame(width:60) // 中间和右侧内容,根据状态显示不同内容 VStack(alignment: .leading, spacing: 4) { switch state.status { case "1": // 备货中 Text("商家备货中").font(.system(size: 16, weight: .medium)) if let promptText = state.prompt, !promptText.isEmpty { Text(promptText).font(.system(size: 13)).foregroundColor(.secondary) } case "2": // 待配送 Text("已备货,等待配送").font(.system(size: 16, weight: .medium)) case "3": // 配送中 Text("骑手配送中").font(.system(size: 16, weight: .medium)) HStack(spacing: 0) { Text("距您\(checkIfNumeric(input: state.distance))米").font(.system(size:13)).foregroundColor(.secondary) Text(",预计\(checkIfNumeric(input: state.progress))分钟送达").font(.system(size: 13)).foregroundColor(.secondary) } case "4": //已送达 Text("商品已送达").font(.system(size: 16, weight: .medium)) if let promptText = state.prompt, !promptText.isEmpty { Text(promptText).font(.system(size: 13)).foregroundColor(.secondary) } default: Text("配送状态未知").font(.system(size: 16, weight: .medium)) } }.frame(maxWidth: .infinity, alignment: .leading) } Spacer().frame(height: 8) //打开应用按钮 Button(action: {}) { HStack { Image("openDemo").resizable().aspectRatio(contentMode: .fit).frame(width: 36, height: 20) Text("Open the Demo App") }.frame(maxWidth: .infinity) .background(Color.blue.opacity(0.1)) .foregroundColor(.blue) .cornerRadius(12) }.frame(width: 300, height: 60) } } } fileprivate func checkIfNumeric(input: String?) -> String { // 检查是否为nil或空字符串 guard let input = input, !input.isEmpty else { return "未知" } // 创建匹配数字(包括小数和负数)的正则表达式 let numericRegex = #"^-?([0-9]+(\.[0-9]*)?|\.[0-9]+)$"# // 检查字符串是否匹配正则表达式 if let _ = input.range(of: numericRegex, options: .regularExpression) { return input } else { return "未知"} }