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

【ios】在 SwiftUI 中实现可随时调用的加载框

在 SwiftUI 项目中实现一个自定义的加载框(loading)功能,可以在任意位置调用,以便显示加载动画或者进度条。下面的教程将详细讲解如何创建一个可复用的 Loading 组件,并通过通知机制控制其显示和隐藏。

先上效果:

swift ui加载框效果演示

创建 Loading.swift 文件

在项目中创建一个名为 Loading.swift 的新文件,并粘贴以下代码:

import SwiftUIstruct Loading: ViewModifier {// 定义一些通知名称,用于在应用中控制弹窗的显示、隐藏和进度更新static let showNotification = Notification.Name("Loading.showNotification")static let hiddenNotification = Notification.Name("Loading.hiddenNotification")static let updateProgressNotification = Notification.Name("Loading.updateProgressNotification")// 全局的单例实例,确保在整个应用中只有一个 Loading 对象static let shared = Loading()// 一些状态变量,用于跟踪当前视图的显示状态和加载提示的相关信息@State private var isContentShowing = false  // 是否显示内容的标志@State private var isPresented = false       // 是否显示加载提示的标志@State private var progress: Double = 0.0    // 当前的进度值@State private var mode: LoadingMode = .standard // 当前的加载模式(标准或进度)@State private var labelText: String? = nil  // 可选的提示文本// 静态标识符,用于跟踪弹窗是否已经显示,以防止重复显示private static var isShowing = false// 定义一个枚举,表示加载提示的模式,可以是标准模式或带进度的模式enum LoadingMode {case standardcase progress}// body 方法用于构建自定义视图的内容func body(content: Content) -> some View {ZStack {// 显示原始内容contentif isPresented {// 黑透Color.black.opacity(0.5).ignoresSafeArea()// 使用 GeometryReader 获取屏幕尺寸,用于动态计算弹窗的位置和大小GeometryReader { geometry inVStack {if mode == .progress {ZStack {// 浅色带Circle().trim(from: 0.0, to: 1.0).stroke(Color.gray.opacity(0.3), lineWidth: 3).frame(width: 60, height: 60)// 环形进度条Circle().trim(from: 0.0, to: progress) // 进度值.stroke(Color.white, lineWidth: 3) // 进度条颜色和宽度.rotationEffect(.degrees(-90)) // 旋转90度,起点从顶部开始.frame(width: 60, height: 60) // 环形进度条的大小.animation(.easeInOut(duration: 0.5), value: progress) // 平滑过度动画Text("\(Int(progress * 100))%").foregroundColor(.white).font(.headline)}if let labelText = labelText {Text(labelText).foregroundColor(.white)}} else {// 标准模式下显示一个普通的加载指示器ProgressView().tint(Color.white).padding(.top, 10)// 如果有提示文本,就显示在加载指示器下面if let labelText = labelText {Text(labelText).foregroundColor(.white).padding(.top, 10)}}}.padding(.vertical, 10).padding(.horizontal, 20).background(Color.black.opacity(0.7)) // 添加一个半透明的黑色背景.cornerRadius(8) // 设置圆角.position(x: geometry.size.width / 2, y: geometry.size.height / 2) // 居中显示}}}// 当视图出现时,设置 isContentShowing 为 true.onAppear {isContentShowing = true}// 当视图消失时,设置 isContentShowing 为 false.onDisappear {isContentShowing = false}// 监听显示通知,当接收到显示通知时,显示加载提示.onReceive(NotificationCenter.default.publisher(for: Loading.showNotification)) { notification in// 如果内容没有显示,或者弹窗已经显示了,就不做任何操作guard isContentShowing, !Self.isShowing else { return }Self.isShowing = true // 标记弹窗为已显示// 解析通知中的用户信息,确定加载模式和提示文本if let userInfo = notification.userInfo,let mode = userInfo["mode"] as? LoadingMode {self.mode = modeif mode == .progress {self.progress = 0.0 // 如果是进度模式,重置进度为 0}} else {self.mode = .standard // 默认模式为标准模式}self.labelText = notification.userInfo?["label"] as? StringisPresented = true // 显示加载提示}// 监听隐藏通知,当接收到隐藏通知时,隐藏加载提示.onReceive(NotificationCenter.default.publisher(for: Loading.hiddenNotification)) { _ inguard isContentShowing else { return }isPresented = falseSelf.isShowing = false // 标记弹窗为已隐藏}// 监听进度更新通知,当接收到更新通知时,更新进度值.onReceive(NotificationCenter.default.publisher(for: Loading.updateProgressNotification)) { notification inguard isContentShowing, mode == .progress, let progressValue = notification.object as? Double else { return }self.progress = progressValue}}// 在主线程中执行static func postNotificationOnMainThread(name: Notification.Name, object: Any? = nil, userInfo: [AnyHashable: Any]? = nil) {if Thread.isMainThread {NotificationCenter.default.post(name: name, object: object, userInfo: userInfo)} else {DispatchQueue.main.async {NotificationCenter.default.post(name: name, object: object, userInfo: userInfo)}}}static func show(mode: LoadingMode = .standard, label: String? = nil) {postNotificationOnMainThread(name: Loading.showNotification, userInfo: ["mode": mode, "label": label as Any])}// 显示标准模式的加载提示static func showByStandard(label: String? = nil) {show(mode: .standard, label: label)}// 显示进度模式的加载提示static func showByProgress(label: String? = nil) {show(mode: .progress, label: label)}// 隐藏static func hidden() {postNotificationOnMainThread(name: Loading.hiddenNotification)}// 更新进度值static func updateProgress(_ progress: Double) {postNotificationOnMainThread(name: Loading.updateProgressNotification, object: progress)}
}// 给View扩展loadingable方法
extension View {func loadingable() -> some View {return self.modifier(Loading.shared)}
}

调用示例

在顶层视图中调用 loadingable()

为了使 Loading 能够在应用的任意位置调用,我们需要在主视图中添加 .loadingable() 修饰符。例如,在 ContentView 中:

loadingable只需要在顶层视图调用即可,往后不管嵌套多少层,只要是在这个视图下,都可以调用显示!!!以下是一个示例,具体怎么用看你自己了。

import SwiftUIstruct ContentView: View {var body: some View {ZStack {VStack {Spacer()Button("展示普通加载") {Loading.showByStandard(label: "加载中")simulateProgressUpdate()}.padding()Spacer()Button("展示进度加载") {Loading.showByProgress(label: "加载中")simulateProgressUpdate()}.padding()Spacer()}}.loadingable()}private func simulateProgressUpdate() {DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {Loading.updateProgress(0.2)}DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {Loading.updateProgress(0.4)}DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {Loading.updateProgress(0.6)}DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {Loading.updateProgress(0.8)}DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {Loading.updateProgress(1.0)}DispatchQueue.main.asyncAfter(deadline: .now() + 3) {Loading.hidden()}}
}struct ContentView_Previews: PreviewProvider {static var previews: some View {ContentView()}
}

为了实现这个效果,忙活了大半天,给点个赞呗~

相关文章:

【ios】在 SwiftUI 中实现可随时调用的加载框

在 SwiftUI 项目中实现一个自定义的加载框(loading)功能,可以在任意位置调用,以便显示加载动画或者进度条。下面的教程将详细讲解如何创建一个可复用的 Loading 组件,并通过通知机制控制其显示和隐藏。 先上效果&…...

字符、解释型语言、编程语言的互操作、输出

字符 同样是1,有人看到的是数字,有人看到的是字符,还有人看到的是一个小目标。 不同语言的字符 正则表达式把字符分成普通字符和元字符,元字符为了搭配匹配。比如.代表任意非换行字符,这对于通配很简便,用\…...

基于Python的自然语言处理系列(39):Huggingface中的解码策略

在自然语言生成任务中,如何选择下一步的单词或者词语对生成的文本质量影响巨大。Huggingface 提供了多种解码策略,可以在不同的场景下平衡流畅度、创造力以及生成效率。在这篇文章中,我们将逐步介绍 Huggingface 中的几种常见解码策略&#x…...

如何将视频格式转为mp4?好好看看下面这几个方法

如何将视频格式转为mp4?在数字化时代,视频已成为信息传播与娱乐消遣的重要载体。无论是学习、工作还是日常生活,我们几乎每天都会接触到各式各样的视频内容。然而,不同设备、平台或软件生成的视频文件往往采用不同的编码格式&…...

景区智慧公厕系统,监测公厕异味,自动清洁除臭

随着旅游业的快速发展,景区的公共厕所管理成为提升游客体验的重要环节。传统的公厕管理方式存在诸多不足,如卫生条件差、异味严重等问题。为了改善这些问题,许多景区开始采用智慧公厕系统。这种系统能够实时监测公厕内的异味,并自…...

GitLab CVE-2024-6389、CVE-2024-4472 漏洞解决方案

极狐GitLab 近日发布安全补丁版本17.3.2, 17.2.5, 17.1.7,修复了17个安全漏洞,本分分享其中两个漏洞 CVE-2024-6389、CVE-2024-4472 两个漏洞详情及解决方案。 极狐GitLab 正式推出面向 GitLab 老旧版本免费用户的专业升级服务,为 GitLab 老…...

hashCode的底层原理

HashCode是计算机科学中一个广泛使用的概念,特别是在Java等编程语言中,它扮演着重要的角色。为了详细解释hashCode的底层原理,以下从几个方面进行阐述: 一、hashCode的基本概念 HashCode,即哈希码,是一个将…...

hadoop_hdfs详解

HDFS秒懂 HDFS定义HDFS优缺点优点缺点 HDFS组成架构NameNodeDataNodeSecondary NameNodeClient NameNode工作机制元数据的存储启动流程工作流程 Secondary NameNode工作机制checkpoint工作流程 DataNode工作机制工作流程数据完整性 文件块大小块太小的缺点块太大的缺点 文件写入…...

【Linux】Linux命令行与环境变量

1.命令行 前⾯写C语⾔时,很少关注过 main 函数的参数,也没有考虑过 main 为什么会有参 数。 实际上在C语⾔中, main 函数⼀共有三个参数,在命令⾏部分先关注前两个参数: 1. argc:表示 main 函数接收到参…...

改变函数调用上下文:apply与call方法详解及实例

目录 改变函数调用上下文:apply与call方法详解及实例 一、什么是 apply 方法? 1、apply 语法 2、apply 示例 二、什么是 call 方法? 1、call 语法 2、call 示例 三、apply 和 call 的共同与差异 1、apply 和 call 的共同点 2、apply…...

k8s中的微服务

一、什么是微服务 用控制器来完成集群的工作负载,那么应用如何暴漏出去?需要通过微服务暴漏出去后才能被访问 Service是一组提供相同服务的Pod对外开放的接口。 借助Service,应用可以实现服务发现和负载均衡。 service默认只支持4层负载均…...

树莓派--AI视觉小车智能机器人--1.树莓派系统烧入及WiFi设置并进入jupyterlab

一、Raspberry Pi 系统烧入 使用树莓派,我们是需要有操作系统的。默认情况下,树莓派会在插入的SD卡上查找操作系统。这需要一台电脑将存储设备映像为引导设备,并将存储设备插入该电脑。大多数树莓派用户选择microSD卡作为引导设备。 1.1 下载…...

MacOS安装BurpSuite

文章目录 一、下载地址二、下载注册机三、安装教程四、启动burpsuit五、免责声明 一、下载地址 https://portswigger-cdn.net/burp/releases/download?productpro&version2024.7.1&typeMacOsx二、下载注册机 https://github.com/NepoloHebo/BurpSuite-BurpLoaderKey…...

【AI工具大全】《史上最全的AI工具合集》

一.AI编程类工具 1.CodeArts Snap CodeArts Snap是华为云研发的智能开发助手,覆盖软件开发全生命周期,提供代码生成、研发知识问答、智能协同等功能。通过自然语言编程,它能自动生成代码、解释代码逻辑、提供调试与检查,提升开发效率和软件质量。 2.ModelArts ModelArt…...

qt继承结构

一、 继承结构 所有的窗口类均继承自QWidget类,因此QWidget类本身包含窗口的特性。QWidget对象本身既可以作为独立窗口,又可以作为组件(子窗口)。 通过构造函数可以创建以上两种形态的QWidget: // 参数1:使…...

【HCIA复习作业】综合拓扑实验(已施工完)

一、实验要求 1.学校内部的HTTP客户端可以正常通过域名www.baidu.com访问到百度网络中的HTTP服务器 2.学校网络内部网段基于192.168.1.0/24划分,PC1可以正常访问3.3.3.0/24网段,但是Pc2不允许 3.学校内部路由使用静态路由,R1和R2之间两条链路…...

网络基础知识:交换机关键知识解析

了解交换机的关键知识对网络工程师至关重要。 以下是交换机的基础知识解析,包括其基本概念、工作原理和关键技术点: 01-交换机的基本概念 交换机是一种网络设备,用于在局域网(LAN)中连接多个设备,如计算机…...

基于System.js的微前端实现(插件化)

目录​​​​​​​ 写在前面 一、微前端相关知识 (一)概念 (二) 优势 (三) 缺点 (四)应用场景 (五)现有框架 1. qiankun 2. single-spa 3. SystemJ…...

MedSAM2调试安装与使用记录

目录 前言一、环境准备多版本cuda切换切换cuda版本二 安装CUDNN2.1 检查cudnn 二、使用步骤1.安装虚拟环境2.测试Gradio3.推理 总结 前言 我们在解读完MedSAM之后,迫不及待想尝尝这个技术带来的福音,因此验证下是否真的那么6。这不,新鲜的使…...

Linux 进程终止和进程等待

目录 0.前言 1. 进程终止 1.1 进程退出的场景 1.2 进程常见退出方法 1.2.1 正常退出 1.2.2 异常退出 2. 进程等待 2.1 进程等待的重要性 2.2 进程等待的方法 2.2.1 wait() 方法 2.2.2 waitpid() 方法 2.3 获取子进程 status 2.4 阻塞等待和非阻塞等待 2.4.1 阻塞等待 2.4.2 非阻…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

AI,如何重构理解、匹配与决策?

AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...