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

iOS 小组件

基本知识

时间轴

小组件通过AppIntentTimelineProvider进行 UI 刷新

struct Provider: AppIntentTimelineProvider {func placeholder(in context: Context) -> SimpleEntry {// 添加占位的(选择添加的时候使用)// todo}func snapshot(for configuration: WidgetCfgIntent, in context: Context) async -> SimpleEntry {// 添加预览的时候会调用, 建议这里进行和timeline方法一样的数据处理。// todo}func timeline(for configuration: WidgetCfgIntent, in context: Context) async -> Timeline<SimpleEntry> {// 返回每个时间点的数据。var entries: [SimpleEntry] = []// 按需添加自己的时间片段// entries.append(entry)let nextTimeMin = 20let nextUpDate = Calendar.current.date(byAdding: .minute, value:nextTimeMin, to: .now) ?? Date(timeIntervalSince1970: Date().timeIntervalSince1970 + Double(nextTimeMin*60)) // 16-50分钟刷新一次, 不能设置时间太小,太小会被系统忽略return Timeline(entries: entries, policy: .after(nextUpDate))}// 推荐配置。func recommendations() -> [AppIntentRecommendation<WidgetCfgIntent>] {// todo}}

数据共享

与其他扩展一样,小组件可以通过Group 的UserDefault 共享数据。 还可以通过 SwiftData 共享数据。

交互

widgetURL:所有区域

Link: 不同元素

具有交互性的 Tog 或 Button(iOS 16):需要基于 AppIntent (与系统通用的 Intent 共用)参考https://developer.apple.com/documentation/widgetkit/adding-interactivity-to-widgets-and-live-activities

AppIntet 理解

Intent: 提供的一个小功能(通过入参、实现某个功能、返回什么结果),比如打开什么、记录什么

Entity:用来表示 App的内容,提供给 Intent 使用(对这个小功能的抽象数据)。

AppShortcut:用来包装 Intent,使之能被系统或者调用方发现。

APPIntent 如何返回数据


func perform() async throws -> some IntentResult & ReturnsValue<Int> {// 如果需要返回其他类型,在 Int 处替换。let returnValue: Int = 1//在这里添加你要执行的代码return .result(value: returnValue)}

APPIntent 的参数通常用@Parameter标记, 支持基本常用值, 也可以继承AppEntity或AppEnum–枚举进行自定义类型。

实现了 AppIntent 的功能默认自动提供给快捷指令使用。注意多语言和相关测试

小组件更新

主 APP 内:通过WidgetCenter刷新

// 通过 kind 刷新某个类型小组件
WidgetCenter.shared.reloadTimelines(ofKind: "com.mygame.gamestatus")
// 刷新所有小组件
WidgetCenter.shared.reloadAllTimelines()

系统刷新:

  • 通过时间轴刷新(15~50分钟一次,设置时间小了,系统也会忽略)
  • 执行交互型 Intent 后会触发刷新

支持小组件类型

struct DemoWidget: Widget {var body: some WidgetConfiguration {// AppIntentConfiguration 为可以配置的小组件AppIntentConfiguration(kind: kind, intent: WidgetCfgIntent.self, provider: Provider()) { entry inSwiftUIWidgetEntryView(entry: entry).widgetBackground(Color.container) // 添加自定义背景色(扩展方法),注意小组件最底层始终有一个底色。 无法做到像 Apple Home 那样的半透明背景效果(据说是只有系统才能用的 API)}.configurationDisplayName("展示名字").description("描述").supportedFamilies(supportFamilies) // 支持小组件类型。.disableContentMarginsIfNeeded() // 忽略边距(扩展方法)}private var supportFamilies:[WidgetFamily] {return [.systemSmall, .systemMedium, .systemLarge]}private var kind: String {return "com.xxxx.Widget"}
}

小组件配置

通过继承WidgetConfigurationIntent 实现。探索 App Intents 的功能更新 – 小专栏

一组一组配置

相关接口:https://developer.apple.com/documentation/appintents/intentitemcollection

struct DeviceDefaultProvider:DynamicOptionsProvider
// 注意这里需要返回 IntentItemCollection, 即分组显示
func results() async throws -> IntentItemCollection<DeviceEntity>  {
//        return ItemCollection(promptLabel: "----collection") {
//            ItemSection("title1", subtitle: "subtitle1", image: DisplayRepresentation.Image(named: "pic_xxxx")) {
//                DeviceEntity(deviceID: "111", name: "device111")
//                DeviceEntity(deviceID: "112", name: "device112")
//            }
//            ItemSection("title2", subtitle: "subtitle2", image: DisplayRepresentation.Image(named: "pic_em0xxxx")) {
//                DeviceEntity(deviceID: "211", name: "device211")
//                DeviceEntity(deviceID: "212", name: "device212")
//            }
//        }
}

效果图

IMG_6277

小组件扩展

import WidgetKit
import SwiftUIextension View {/// 统一设置备件@ViewBuilderfunc widgetBackground(_ backgroundView: some View) -> some View {if Bundle.main.bundlePath.hasSuffix(".appex"){ // 小组件才生效if #available(iOS 17.0, *) {containerBackground(for: .widget) {backgroundView}} else {background(backgroundView)}} else {background(backgroundView)}}
}extension WidgetConfiguration {func disableContentMarginsIfNeeded() -> some WidgetConfiguration {if #available(iOSApplicationExtension 17.0, *) {// 禁用边距return self.contentMarginsDisabled()} else {return self}}
}

小组件刷新失败

entities 查询返回数据空

背景:我在主 APP 修改了小组件显示数据源, 同时调用了WidgetCenter.shared.reloadAllTimelines刷新,但是回到小组件,数据依然没有变化,通过调试,控制台打印如下:

Error getting AppIntent from LNAction: AppIntent has missing parameter value for 'xxxx'. You may need to set a default value in the initializer of your @Parameter, or using the default method on your Query.
No AppIntent in timeline(for:with:)

上面这种情况属于 APP 内主动刷新失效, 但是系统的时间轴刷新有效。

通过反复测试发现,在桌面小组件自定义配置后才发生。

我的配置参数是基于 AppEntity 实现的自定义类型。

发现每次在调用 EntityQuery 的entities(for)方法,内部返回空数组,都会打印上面的错误。

struct DeviceEntityQuery: EntityQuery, Sendable {// 通过 ID 查询的会调用, (比如通过配置匹配,提供数据源等)func entities(for identifiers: [DeviceEntity.ID]) async throws -> [DeviceEntity] {//具体逻辑:通过 ID 过滤数据。 如果没匹配到,就返回空。}
}

APP主动刷新小组件 -> 通过自定义配置在 EntityQuery 方法中查询 -> 查询到,触发刷新小组件的生命周期方法,(没查询,比如返回空数组,则不会刷新小组件生命周期方法)

结论:在entities(for)的返回结果时,不要返回空数组,否则可能无法刷新。

解决方法:如果没有匹配到,不返回空数组,构建对应的 Entity 返回。

其他问题

widgetURL配置

现象:通过 WidgtURL装饰器配置的 URL 跳转异常。

原因:我在最底层配置得了 WidgetURL, 在子视图也配置了 Widget,导致跳转异常. 根据官网文档解释,如果有多层View 使用了 WidgetURL, 状态和跳转将是不可预料的(在 iOS18.0上,多数情况下都响应最底层的 WidgetURL)

解决方案:通过 Link 或者 Button(AppIntent 交互)处理连接。

Link(destination: URL(string: "xxxxx" ) {
// your view           
}

Summery 怎么多语言化

使用新的多语言文件 xcstrings

使用占位模式

Get the daylight duration on ${date} in ${location}

使用示例

Summary("Switch \(\.$device) \(\.$isOn)")// 可选, table: "Localizable1")

注意:变量名要一致

Next

将 App 控制扩展到系统级别: 将功能扩展到系统控制,通过类型小组件 AppIntent 的方式。

资料参考

Apple 官方指导:https://developer.apple.com/cn/documentation/widgetkit/

iOS 小组件系列教程:https://juejin.cn/post/7297513663435210771?searchId=2024082914404396F94C65488FE5528F7E

WidgetExamples – github

SwiftUI 控件 Demo – github

SwiftUI 细节学习参考 --肘子的记事本: 各种SwiftUI 控件、布局理解。

相关文章:

iOS 小组件

基本知识 时间轴 小组件通过AppIntentTimelineProvider进行 UI 刷新 struct Provider: AppIntentTimelineProvider {func placeholder(in context: Context) -> SimpleEntry {// 添加占位的&#xff08;选择添加的时候使用&#xff09;// todo}func snapshot(for configu…...

【2.使用VBA自动填充Excel工作表】

目录 前言什么是VBA如何使用Excel中的VBA简单基础入门控制台输出信息定义过程&#xff08;功能&#xff09;定义变量常用的数据类型Set循环For To 我的需求开发过程效果演示文件情况测试填充源文件测试填充目标文件 全部完整的代码sheet1中的代码&#xff0c;对应A公司工作表Us…...

算法记录——链表

2.链表 2.1判断是否是回文链表 1.方法一&#xff1a;利用栈反转链表 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode…...

EasyExcel实现百万数据批量导出

当数据量比较大时&#xff0c;例如数据量达到百万级&#xff0c;传统的一次读取到内存中在写入excel文件的方法便不再适用了&#xff0c;可能会导致内存溢出&#xff1b;而且一次性将数据写入一张sheet工作表也不太好。 但我们可以选择数据分片的方式批量写入多个工作表。 测试…...

兆易GD32E508的SHRTIM配置 主从定时器 产生2对相位可调互补PWM 带死区

如有技术问题及技术需求请加作者微信! GD32E5系列MCU是基于Arm Cortex-M33处理器的32位通用微控制器。Cortex-M33处理器基于Armv8架构,处理器主频最高可达180MHz,支持强大的可扩展指令集,包括通用数据处理I/O控制任务、增强的数据处理位域操作、DSP和浮点运算器(FPU)。 GD…...

数据归组工具

利用C#将数据 [ {"name":"A","fzh":1}, {"name":"A","fzh":2}, {"name":"A","fzh":3}, {"name":"B","fzh":4}, {"name":"B",&…...

JavaScript 中的闭包的形成及使用场景

JavaScript 中的闭包 闭包&#xff08;Closure&#xff09; 是 JavaScript 中一个非常重要且独特的概念&#xff0c;它指的是 函数能够记住并访问其词法作用域内的变量&#xff0c;即使这个函数在其词法作用域之外执行。 通俗地说&#xff0c;闭包是 一个函数可以“记住”它在…...

后端返回内容有换行标识,前端如何识别换行

<br/>的话 用 v-html \n 可以用css样式 white-space: pre-wrap 后端返回结果 前端...

服务器被挂马,导致网站首页被更改怎么解决

当服务器被挂马并导致网站首页被篡改时&#xff0c;说明服务器或网站的安全性遭到破坏。为了修复并防止未来的攻击&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1. 立即下线网站 目的&#xff1a;防止恶意软件进一步传播&#xff0c;保护用户数据和防止攻击者继续对网…...

Android 利用OSMdroid开发GIS

1、地址 Github地址&#xff1a;https://gitee.com/mirrors/osmdroid Git地址&#xff1a; GitCode - 全球开发者的开源社区,开源代码托管平台 Git下载包地址&#xff1a;Releases osmdroid/osmdroid GitHub 新建项目 osmdroid在线&#xff1a; &#xff08;1&#xff09…...

一文上手skywalking【上】

一、skywalking预览 1.1 skywalking 概述 ​ Apache SkyWalking, 适用于分布式系统的应用程序性能监控工具&#xff0c;专为微服务、云原生和基于容器的 &#xff08;Kubernetes&#xff09; 架构而设计。官方地址: https://skywalking.apache.org/ 适用于分布式系统的应用程…...

【JavaScript】JQuery基础知识及应用

一、JQuery的导入方法 https://editor.csdn.net/md/?articleId132214798 二、JQuery介绍 JQuery(JQ)&#xff1a;JS的一个类库&#xff08;方法库&#xff1a;包含了大量的、有助于项目开发的属性和方法&#xff09; 第一代版本1.xx.xx: 1.11.3 兼容所有浏览器的&#xff0…...

初始爬虫9

1.元素定位后的操作 “find_element“仅仅能够获取元素&#xff0c;不能够直接获取其中的数据&#xff0c;如果需要获取数据需要使用以下方法”。下面列出了两个方法&#xff1a; 获取文本 element.text 通过定位获取的标签对象的 text 属性&#xff0c;获取文本内容 获取属性…...

从细胞到临床:表观组学分析技术在精准医疗中的角色

中国科学院等科研院所的顶尖人才发起&#xff0c;专注于多组学、互作组、生物医学等领域的研究与服务。在Nature等国际知名期刊发表多篇论文&#xff0c;提供实验整体打包、免费SCI论文润色等四大优势服务。在表观组学分析技术方面&#xff0c;提供DAP-seq、ATAC-seq、H3K4me3 …...

带你0到1之QT编程:二十、QT与MySQL喜结连理,构建数据库应用开发

此为QT编程的第二十谈&#xff01;关注我&#xff0c;带你快速学习QT编程的学习路线&#xff01; 每一篇的技术点都是很很重要&#xff01;很重要&#xff01;很重要&#xff01;但不冗余&#xff01; 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点&#xff01; …...

梯度下降法及其性能评估

梯度下降法 梯度下降法是一种一阶迭代优化算法&#xff0c;用于寻找函数的局部最小值。在机器学习中&#xff0c;它通常用来最小化损失函数&#xff08;也称为成本函数或误差函数&#xff09;&#xff0c;以提高模型对数据的拟合程度。梯度下降法的基本思想是沿着目标函数当前…...

906. 超级回文数

1. 题目 906. 超级回文数 2. 解题思路 题目意思很简单&#xff0c;在给定范围中找到所有满足&#xff0c;它本身是回文&#xff0c;且它的平方也是回文的数字个数。 这题需要注意题目给定的范围&#xff0c;后面很有用&#xff1a; 因为回文范围是有限的&#xff0c;那么我…...

代码随想录算法训练营||二叉树

前/中/后序遍历 递归方式 参考文章 题目 思路&#xff1a;其实递归方式的前中后序遍历的方式都差不多&#xff0c;区别是在父节点的遍历时间。 前序代码 class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result new…...

线上报名小程序怎么做

在这个数字化、智能化的时代&#xff0c;信息技术的发展正以前所未有的速度改变着我们的生活。无论是学习、工作还是娱乐&#xff0c;互联网都成为了我们不可或缺的一部分。而在线上报名这一领域&#xff0c;小程序的出现更是为广大用户带来了前所未有的便捷与高效。今天&#…...

【测试岗】手撕代码 - 零钱兑换

322. 零钱兑换 题目描述 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

【Linux】自动化构建-Make/Makefile

前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具&#xff1a;make/makfile 1.背景 在一个工程中源文件不计其数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;mak…...

【Veristand】Veristand环境安装教程-Linux RT / Windows

首先声明&#xff0c;此教程是针对Simulink编译模型并导入Veristand中编写的&#xff0c;同时需要注意的是老用户编译可能用的是Veristand Model Framework&#xff0c;那个是历史版本&#xff0c;且NI不会再维护&#xff0c;新版本编译支持为VeriStand Model Generation Suppo…...

6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙

Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...