欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > 【ios】在 SwiftUI 中实现可随时调用的加载框

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

2024/10/23 18:25:17 来源:https://blog.csdn.net/qq_38238956/article/details/142904146  浏览:    关键词:【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()}
}

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com