【iOS小组件实战】自定义充电动画

823 阅读2分钟

前言

经常看到别人充电时会有各种动态的充电动画效果,这里探索一下自定义充电动画的实现过程,末尾附demo链接。

限制

这种充电动画需要先解锁,锁屏无法执行

实现

1.充电动画展示

先创建一个用于充电时展示的动画视图

// 定义充电动画视图
struct ChargingAnimationView: View {
    let onClose: () -> Void

    @State private var scale: CGFloat = 1.0
    @State private var opacity: Double = 0.5
    
    var body: some View {
        ZStack {
            // 背景
            Color.black.edgesIgnoringSafeArea(.all)
            
            // 电池动画
            VStack {
                Image(systemName: "battery.100.bolt")
                    .resizable()
                    .scaledToFit()
                    .frame(width: 100, height: 100)
                    .foregroundColor(.green)
                    .scaleEffect(scale)
                    .opacity(opacity)
                
                Text("充电中...")
                    .foregroundColor(.white)
                    .font(.title)
                    .opacity(opacity)
            }
        }
        .onTapGesture {
            // 关闭window
            onClose()
        }
        .onAppear {
            // 创建循环动画
            withAnimation(Animation.easeInOut(duration: 1.0).repeatForever(autoreverses: true)) {
                scale = 1.2
                opacity = 1.0
            }
        }
    }
}

创建一个窗口管理器用于在window层展示充电动画视图

// 用于管理窗口的单例类
class WindowManager: ObservableObject {
    static let shared = WindowManager()
    private var additionalWindow: UIWindow?
    @Published var isWindowVisible = false
    
    func showWindow() {
        guard additionalWindow == nil else { return }
        
        // 获取当前 key window 的场景,会出现获取的activationState是非激活状态,这里暂时先不做处理
        guard let windowScene = UIApplication.shared.connectedScenes
            // .filter({ $0.activationState == .foregroundInactive })
            .first as? UIWindowScene else {
            return
        }
        
        let window = UIWindow(windowScene: windowScene)
        
        // 创建承载 SwiftUI 视图的控制器
        let contentView = ChargingAnimationView {
            self.hideWindow()
        }
        let hostingController = UIHostingController(rootView: contentView)
        
        window.rootViewController = hostingController
        // window.backgroundColor = .black  // 确保窗口有背景色
        // window.windowLevel = .alert + 1  // 设置较高的窗口层级
        window.frame = windowScene.coordinateSpace.bounds
        
        // 使窗口可见
        window.makeKeyAndVisible()
        window.isHidden = false
        
        self.additionalWindow = window
        self.isWindowVisible = true
    }
    
    func hideWindow() {
        additionalWindow?.isHidden = true
        additionalWindow = nil
        isWindowVisible = false
    }
}

定义好后测试一下动画效果

struct ContentView: View {
    @StateObject private var windowManager = WindowManager.shared
    
    var body: some View {
        VStack {
            Text("主窗口")
                .font(.title)
            
            Button("打开新窗口") {
                windowManager.showWindow()
            }
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(8)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

图片

2.注册快捷指令

import AppIntents
import SwiftUI

struct ChargingShortcut: AppIntent {
static var title = LocalizedStringResource("充电动画")
static var description = IntentDescription("充电时显示动画")

// 意图执行时,是否自动将应用拉起到前台
static var openAppWhenRun: Bool = true

    @MainActor
func perform() async throws -> some IntentResult {
// 打开充电动画效果
WindowManager.shared.showWindow()

return .result()
    }
}

注册完成在快捷指令中新建快捷指令搜索【充电动画】添加即可看到快捷指令效果

图片

3.快捷指令自动化

在快捷指令App中点击【自动化】->【充电器】->【已连接】->【添加操作】->搜索【充电动画】->取消【运行前询问

图片

图片

项目链接

代码放到了github上,需要的自取

快捷指令口令

见原文:【iOS小组件实战】自定义充电动画)

本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。