当前位置: 首页 > news >正文

ios 小组件和数据共享

创建主工程就不必讲了
1 创建小组件
创建子工程 [new Target ] 选择 [ Widger Extension]
请添加图片描述
小组件入口是WidgetBundle文件,可以进行多个小组件的调试
请添加图片描述
TestWidget2文件是主要操作,小组件使用swiftUI布局,使用 AppIntent进行事件处理,TestDataUserDefalut 用的userdefault 和主程序数据同步,下面详细讲解

//
//  TestWidget2.swift
//  TestSwift6Demo
//
//  Created by ITHPNB04296 on 11/3/25.
//

import WidgetKit
import SwiftUI
import AppIntents


struct TestWidget2:Widget{
    let kind:String = "TestWidget2"
    var body: some WidgetConfiguration{
        StaticConfiguration(kind: kind, provider: MyWidget2Provider()) {entry in
            TestWidget2View(entry: entry ,ganmeStatus: entry.state)
                .containerBackground(for: ContainerBackgroundPlacement.widget) {
                    //背景色
                    Color.yellow
                }
        }
        .configurationDisplayName("TestWidget2")
        .description("show Testwidget2")
        .supportedFamilies([.systemMedium])
    }
    
   
}


struct TestWidget2View:View {
    var entry: MyWidget2Provider.Entry
    (\.widgetFamily) var family:WidgetFamily
    var ganmeStatus:Test2State
//    var selectedCharacter:CharacterDetail
    var body: some View {
        VStack{
            Text("\(ganmeStatus.date.description)")
            Button("\(ganmeStatus.state)") {
                print("点击按钮1 直接就跳转到主APP了")
                
            }
            
           
            if entry.running{
                createRunningView()
            }else{
                createStaticView()
            }
            
         
        }
        

    }
    
    func createRunningView()->some View{
           //点击一次更新了一次
        return VStack{
            Button("点击按钮2", intent: Test2Intent())
            //该Text 无法设置停止时间
//            Text(entry.date, style: Text.DateStyle.timer)
//                .font(Font.system(size: 15.0))
//                .fontWeight(.bold)
//                .multilineTextAlignment(.center)
//                .contentTransition(.numericText(countsDown: entry.state.duration > 0))
            
            //新Text样式,可以设置停止时间
            Text(timerInterval: entry.daterange,pauseTime: entry.pauseTime,countsDown: entry.countsDown,showsHours: entry.showsHours)
        }
    }
    
    
    func createStaticView()->some View{
        return VStack{
            Button("点击按钮2", intent: Test2Intent())
            Text(entry.date, style: Text.DateStyle.date)
                .font(Font.system(size: 15.0))
                .fontWeight(.bold)
                .multilineTextAlignment(.center)
            //过渡动画 countsDown 参数用来控制是否向下滚动
                .contentTransition(.numericText(countsDown: entry.state.duration > 0))
        }
        
    }
}

//添加按钮点击事件行为
struct Test2Intent:AppIntent{
  
    
    static var title: LocalizedStringResource = "XXX"
    
  
    //点击事件函数是异步的可以做一些耗时操作,比如网络请求等
    func perform() async throws -> some IntentResult {
        //也可以通过App group 和主程序共享数据
        
      
        //点击事件更新
        let isrunning = TestDataUserDefalut().get()
        TestDataUserDefalut().set(!isrunning)
        
        return .result()
    }
}

struct Test2State{
    var state:String
    var date:Date{
        return Date()
    }
    var duration:Int = 10
    
    
}

struct MyWidget2Entry:TimelineEntry{
    var date: Date
    
    //默认区间是0
    var daterange:ClosedRange<Date> = Date()...Date()
    //默认没有开启
    var running:Bool {
        //添加状态控制
        return TestDataUserDefalut().get()
    }
    /// The relevance of a widget’s content to the user.
    var relevance: TimelineEntryRelevance?
    
    //添加适配新的TimerText
    
    //停止时间
    var pauseTime: Date = Date()
    //倒计时
    var countsDown = false
    
    var showsHours = true
    
    
    //携带其他额外数据
    var state:Test2State
}


struct MyWidget2Provider: TimelineProvider{
    //没有数据时候占位,仅初始化时候调用一次,数据没加载上来时候,添加占位数据
    func placeholder(in context: Context) -> MyWidget2Entry {
        return MyWidget2Entry(date: Date(), state: Test2State(state: "init"))
    }
    //生成预览照,给系统组件库使用
    func getSnapshot(in context: Context, completion:   (MyWidget2Entry) -> Void) {
        let date = Date()
        let entry:MyWidget2Entry
        print("getSnapshot")
        entry = MyWidget2Entry(date: date, state: Test2State(state:"perview"))
        completion(entry)
    }
    
    //实时更新时间线,每次点击按钮时候调用两次 ? 调用两次没理解
    func getTimeline(in context: Context, completion:  (Timeline<MyWidget2Entry>) -> Void) {
        let date = Date()
        
        let endtime:Date = Calendar.current.date(byAdding: .minute, value: 10, to: date)!
        
        let entry = MyWidget2Entry(date: date, daterange: date...endtime,countsDown: true, state: Test2State(state:"start"))
        
        //下一次更新时候
        let nextUpdateDate = Calendar.current.date(byAdding: .second, value: 1, to: date)!
        //时间线,\
        /*
         atend -当时间线中的所有条目都已显示完毕后,才会重新加载时间线。固定时间点加载,比如天气
         never - 不会自动刷新小组件内容,只有主app主动去刷新数据
         after(Date) - 指定一个未来的日期和时间
         
         */
        let timeline = Timeline(entries: [entry], policy: TimelineReloadPolicy.after(nextUpdateDate))
        print("时间线方法1更新了 ")
        completion(timeline)
    }
    

}



//更新共享数据
struct TestDataUserDefalut{
    func get()->Bool{
        let sharedDefaults = UserDefaults(suiteName: "group.testwidget1.demo")
        let isrunning = sharedDefaults?.value(forKey: "sharedRunning") as? Bool ?? false
        return isrunning
       
    }
    
    func set(_ state:Bool){
        let sharedDefaults = UserDefaults(suiteName: "group.testwidget1.demo")
        sharedDefaults?.set(state, forKey: "sharedRunning")
        sharedDefaults?.synchronize()
        
    }
}

小组件和主工程进行数据同步,也就是进程间通信
配置 App Groups
1. 打开 Xcode,选择你的主 App 目标(Target)。
2. 在 Signing & Capabilities 中,点击 + Capability。
3. 搜索 App Groups 并添加。
4. 创建一个新的 App Group(如 group.com.yourcompany.shared)。
5. 在 Widget Extension / App Clips 目标中重复上述步骤,选择相同的 App Group。

我的demo使用plist文件也就是userdefault 同步数据当然也可以使用数据库同步数据,我看同事使用SwiftData(ios 17之后才能使用)同步数据 swiftData官方文档 ,我选择的appgroup 名字是 “group.testwidget1.demo”,这个不是包名,TestWidget2 里面的 TestDataUserDefalut 就是小程序同步数据代码,下面看一下主程序App同步代码,添加包括刷新通知等,讲解比较详细

class ViewController: UIViewController {
    lazy var stateLab:UILabel = {
        let size = UIScreen.main.bounds.size
        let labwidth = 100.0
        let labheight = 50.0
        let lab  = UILabel(frame: CGRectMake((size.width - labwidth) / 2.0, (size.height - labheight) / 2.0, labwidth, labheight))
        view.addSubview(lab)
        lab.backgroundColor = UIColor.green
        lab.font = UIFont.systemFont(ofSize: 28)
        lab.textColor = UIColor.red
        lab.textAlignment = .center
        return lab
    }()
  
    func updateState(_ state:Bool){
        DispatchQueue.main.async {
            self.stateLab.text = state ? "打开" : "关闭"
        }
        
    }
    func addTest(){
        /*
         配置 App Groups
             1.    打开 Xcode,选择你的主 App 目标(Target)。
             2.    在 Signing & Capabilities 中,点击 + Capability。
             3.    搜索 App Groups 并添加。
             4.    创建一个新的 App Group(如 group.com.yourcompany.shared)。
             5.    在 Widget Extension / App Clips 目标中重复上述步骤,选择相同的 App Group。
         */
        
        let appGroup = "group.testwidget1.demo"
        let sharedDefaults = UserDefaults(suiteName: appGroup)
        let isrunning = sharedDefaults?.value(forKey: "sharedRunning") as? Bool ?? false
        updateState(isrunning)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        
        let sharedDefaults = UserDefaults(suiteName: "group.testwidget1.demo")
        let isrunning = sharedDefaults?.value(forKey: "sharedRunning") as? Bool ?? false
        sharedDefaults?.set(!isrunning, forKey: "sharedRunning")
        print("\(!isrunning)")
        updateState(!isrunning)
        //通知小组件刷新
        notifiUpdateWidget()
        
    }
    
    
    func notifiUpdateWidget(){
        //通知小组件刷新,刷新所有小组件
        WidgetCenter.shared.reloadAllTimelines()
        
        //刷新指定的小组件,小组件那边注册的kind
//        WidgetCenter.shared.reloadTimelines(ofKind: "TestWidget1")
        
        
        
        //获取配置去刷新小组件
//        WidgetCenter.shared.getCurrentConfigurations { result in
//            guard case .success(let success) = result else {
//                return
//            }
//            
            if let widget = success.first(
                where:{ widget in
                    let intent = widget.configuration as ConfigurationAppIntent
                    return intent?.character == ""
                }
            ){
                WidgetCenter.shared.reloadTimelines(ofKind: widget.kind)
            }
//        }
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        addTest()
        }
       }

苹果官方小组件文档

苹果官方swiftUI文档

相关文章:

  • 浅谈StarRocks数据库简介及应用
  • 插入排序算法的SIMD优化
  • 地下停车场调频广播覆盖:破解地下车库无线广播收听孤岛,技术赋能地下停车场FM调频无线广播覆盖
  • PixelCNN:基于自回归的图像生成模型及其数学原理
  • spring boot3 kafka集群搭建到使用
  • AI大模型测试用例生成平台
  • mysql 到 doris 挪移数据
  • IDEA中链接使用mysql数据库
  • 如何在 React 中实现错误边界?
  • 一场因黄焖鸡引发的技术方案大作战
  • js版本之ES12(2021)、ES13(2022)新特性(九)
  • 封装Axios拦截器实现用户无感刷新AccessToken实践指南
  • 简单创建一个Django项目并配置neo4j数据库
  • Scratch 3.0安装包,支持Win7/10/11、Mac电脑手机平板、少儿便编程的启蒙软件。
  • SQL99 多表查询
  • 成功破解加密机制,研究人员解锁LinuxESXi Akira勒索软件
  • 单片机技术
  • C++复试笔记(三)
  • flutter实践:断点调试踩坑
  • 循环遍历 Java 集合中元素的方法总结
  • 中国证监会副主席李明:目前A股估值水平仍处于相对低位
  • 杨建全已任天津市委副秘书长、市委市政府信访办主任
  • 在美国,为什么夏季出生的孩子更容易得流感?
  • 爬坡难下坡险,居民出行难题如何解?
  • 学者三年实地调查被判AI代笔,论文AI率检测如何避免“误伤”
  • 浙江演艺集团7部作品组团来沪,今夏开启首届上海演出季