screen time api - FamilyActivityPicker 获取选中应用

使用 FamilyActivityPicker 获取选中应用的详细信息

在 iOS 15+ 中,FamilyActivityPicker 是家庭控制功能的重要组成部分,允许用户选择要监控或限制的应用。但选择后如何获取应用的详细信息(如图标、名称等)呢?本文将详细介绍相关 API 和使用方法。

相关 API 介绍

1. FamilyActivitySelection

  • 作用:表示用户选择的应用程序、网站或类别的集合
  • 包含属性
    • applications:选中的应用程序集合
    • categories:选中的应用类别集合
    • webDomains:选中的网站域名集合

2. Application

  • 作用:代表一个具体的应用程序
  • 关键属性
    • token:应用的唯一标识符
    • localizedDisplayName:本地化的应用名称
    • bundleIdentifier:应用的 Bundle ID

3. AppMetadata

  • 作用:提供应用程序的元数据信息
  • 获取方式 :通过 AppMetadata(token:) 初始化器

实现步骤

步骤 1:创建状态管理

swift 复制代码
import SwiftUI
import FamilyControls

struct ContentView: View {
    @State private var selection = FamilyActivitySelection()
    @State private var selectedApps: [AppInfo] = []
    
    var body: some View {
        VStack {
            // 选择器
            FamilyActivityPicker(selection: $selection)
            
            // 显示选中应用信息
            List(selectedApps, id: \.bundleIdentifier) { app in
                HStack {
                    if let image = app.icon {
                        Image(uiImage: image)
                            .resizable()
                            .frame(width: 40, height: 40)
                            .cornerable(8)
                    }
                    VStack(alignment: .leading) {
                        Text(app.name)
                            .font(.headline)
                        Text(app.bundleIdentifier)
                            .font(.caption)
                            .foregroundColor(.gray)
                    }
                }
            }
        }
        .onChange(of: selection) { newValue in
            loadSelectedAppsInfo()
        }
    }
}

步骤 2:定义应用信息模型

swift 复制代码
struct AppInfo {
    let token: ApplicationToken
    let name: String
    let bundleIdentifier: String
    let icon: UIImage?
}

步骤 3:加载应用详细信息

swift 复制代码
extension ContentView {
    private func loadSelectedAppsInfo() {
        selectedApps.removeAll()
        
        // 遍历选中的所有应用
        for application in selection.applications {
            let appInfo = createAppInfo(from: application)
            selectedApps.append(appInfo)
        }
    }
    
    private func createAppInfo(from application: Application) -> AppInfo {
        // 获取应用显示名称
        let displayName = application.localizedDisplayName ?? "未知应用"
        
        // 获取 Bundle Identifier
        let bundleID = application.bundleIdentifier ?? "未知 Bundle ID"
        
        // 获取应用图标
        let appIcon = loadAppIcon(for: application.token)
        
        return AppInfo(
            token: application.token,
            name: displayName,
            bundleIdentifier: bundleID,
            icon: appIcon
        )
    }
}

步骤 4:加载应用图标

swift 复制代码
extension ContentView {
    private func loadAppIcon(for token: ApplicationToken) -> UIImage? {
        do {
            // 通过 AppMetadata 获取应用信息
            let metadata = try AppMetadata(token: token)
            
            // 获取应用图标(需要处理权限和异步加载)
            // 注意:实际图标的获取可能需要额外的权限或使用其他方法
            return nil // 这里需要根据具体实现调整
            
        } catch {
            print("获取应用元数据失败: \(error)")
            return nil
        }
    }
}

完整示例代码

swift 复制代码
import SwiftUI
import FamilyControls

struct FamilyActivityAppView: View {
    @State private var selection = FamilyActivitySelection()
    @State private var selectedApps: [AppInfo] = []
    
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                Text("选择要管理的应用")
                    .font(.title2)
                    .padding()
                
                FamilyActivityPicker(selection: $selection)
                    .frame(height: 300)
                
                Divider()
                
                Text("已选择的应用 (\(selectedApps.count))")
                    .font(.headline)
                
                List(selectedApps, id: \.bundleIdentifier) { app in
                    AppInfoRow(appInfo: app)
                }
                .listStyle(PlainListStyle())
            }
            .padding()
            .navigationTitle("应用管理")
        }
        .onChange(of: selection) { newValue in
            updateSelectedApps()
        }
    }
    
    private func updateSelectedApps() {
        Task {
            var apps: [AppInfo] = []
            
            for application in selection.applications {
                let appInfo = await createAppInfo(from: application)
                apps.append(appInfo)
            }
            
            await MainActor.run {
                selectedApps = apps
            }
        }
    }
    
    private func createAppInfo(from application: Application) async -> AppInfo {
        let displayName = application.localizedDisplayName ?? "未知应用"
        let bundleID = application.bundleIdentifier ?? "未知"
        
        // 异步加载应用图标
        let icon = await loadAppIcon(for: application.token)
        
        return AppInfo(
            token: application.token,
            name: displayName,
            bundleIdentifier: bundleID,
            icon: icon
        )
    }
    
    private func loadAppIcon(for token: ApplicationToken) async -> UIImage? {
        // 这里可以实现具体的图标加载逻辑
        // 可能需要使用其他系统 API 或第三方库
        return nil
    }
}

struct AppInfoRow: View {
    let appInfo: AppInfo
    
    var body: some View {
        HStack(spacing: 12) {
            if let image = appInfo.icon {
                Image(uiImage: image)
                    .resizable()
                    .frame(width: 40, height: 40)
                    .cornerRadius(8)
            } else {
                Rectangle()
                    .fill(Color.gray.opacity(0.3))
                    .frame(width: 40, height: 40)
                    .cornerRadius(8)
                    .overlay(
                        Image(systemName: "app")
                            .foregroundColor(.gray)
                    )
            }
            
            VStack(alignment: .leading, spacing: 4) {
                Text(appInfo.name)
                    .font(.headline)
                Text(appInfo.bundleIdentifier)
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            
            Spacer()
        }
        .padding(.vertical, 4)
    }
}

struct AppInfo {
    let token: ApplicationToken
    let name: String
    let bundleIdentifier: String
    let icon: UIImage?
}

注意事项

  1. 权限要求 :使用 FamilyActivityPicker 需要申请相应的家庭控制权限
  2. 图标获取限制:直接获取其他应用的图标可能受到系统限制
  3. 异步处理:应用信息的加载建议使用异步方式,避免阻塞 UI
  4. 错误处理:妥善处理可能出现的异常情况

通过以上步骤,你可以成功获取用户选择的应用的详细信息,并在界面中展示出来。

这个实现提供了完整的解决方案,包括 API 介绍、实现步骤和完整代码示例。你可以根据实际需求进行调整和优化。

相关推荐
传奇开心果编程10 小时前
【SwiftUI入门练中学】第1课 从零开始
学习·ui·ios·swiftui·swift
丿Wayne16 小时前
iOS Token 刷新实战(Actor 避免并发重复刷新)
ios·swift·token·actor·swift并发
终端安全笔记1 天前
iOS 27 给了租赁一个新工具,但它只认受监督的设备
android·网络·安全·ios
大熊猫侯佩2 天前
独立 App 首发完成 iPhone Duo 展开态适配
ios·swiftui·swift
光影少年2 天前
setImmediate 和 setTimeout(0) 的区别
android·前端·react.js·ios·前端框架
2501_916008892 天前
SSE 和 gRPC 流式接口如何抓包,调试 SSE 与 gRPC 流式接口
网络协议·计算机网络·网络安全·ios·adb·https·udp
HouWan2 天前
Flutter: MediaQuery.of(context) 为什么可能拖慢页面?
android·flutter·ios
SoaringHeart2 天前
Swift 进阶&封装:仿 Get 给SwiftUI 添加路由导航能力
swiftui·swift
用户38034165882972 天前
Broadcast Extension 里的 Live Activity:一次跨进程妥协
ios·音视频开发
匠测AI说2 天前
XCUITest 自动化全解:同进程白盒、XCTest 架构、自动同步机制与 iOS 端选型指南
测试工具·ios·自动化