SwiftUI上线也有一段时间了,原因还是SwiftUI 支持的iOS系统版本过高(也不稳定很多奇奇怪怪的BUG)导致Store上SwiftUI写得App不是很多,导致关于SwiftUI的第三方工具库少的可怜。
找了半天没找到 SVProgressHUD 这样的第三方库搜索到的几篇制作相同的效果的文章竟然都要收费,对不起我只想白嫖啊
本文介绍自定义HUD气泡弹窗,需要你对SwiftUI有一定的掌握
1 SDProgressHUD
首先我们自定义一SDProgressHUD:的View(先做一个最简单的txt气泡View
)
struct SDProgressHUD:View
{
var body: some View
{
VStack(spacing:10)
{
Text("请稍等。。。。。")
}
.padding(18)
.background(Color.black.opacity(0.9),in:RoundedRectangle(cornerRadius: 8))
.foregroundColor(.white)
.frame(maxWidth:UIScreen.main.bounds.width-160)
}
}
1.1 SDProgressHUD 的用法
用ZStack将视图布局,做个按钮点击影藏显示
struct ContentView: View {
@State private var showingHUD = false
var body: some View {
ZStack(alignment: .top) {
NavigationView {
Button("Show/hide HUD") {
showingHUD.toggle()
}
}
if showingHUD {
SDProgressHUD()
.zIndex(1)
}
}
}
}
iShot_2022-09-09_15.59.54.gif
这里我们就得到一个简单的气泡弹框,我们需要给它加上动画,和自己倒计时消失机制
1.动画修通过饰符withAnimation
2.在onAppear(显示时)
添加定时器 动态修改属性控制显示影藏
struct ContentView: View {
@State private var showingHUD = false
var body: some View {
ZStack(alignment: .top) {
NavigationView {
Button("Show/hide HUD") {
showingHUD.toggle()
}
}
if showingHUD {
SDProgressHUD()
.zIndex(1)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
withAnimation {
showingHUD=false
}
}
}
}
}
}
}
iShot_2022-09-09_16.49.03.gif
随着项目的复杂度增加,可能很多地方都需要气泡弹窗,我们不可能每次需要加提示或者loading的时候都在当前页面加上ZStack去修改页面布局,这无疑是过于麻烦不现实的,所以我们在顶层视图上增加 通过注册环境对象
“.environmentObject()”
在有需要显示的子视图控制影藏显示即可
2.封装到顶层
2.1创建一个对象控制显示影藏
class ProgressState:ObservableObject
{
@Published var isPresented: Bool = false
private(set) var title: String=""
private(set) var duration:CGFloat=1.5
func Show(title:String,duration:CGFloat=1.5)
{
self.title=title
self.duration=duration
withAnimation {
self.isPresented=true
}
}
// FIXME:手动隐藏
func hide()
{
withAnimation {
self.isPresented=false
}
}
}
2.2为了在各个View中方便调用,继续封装,将SDProgressHUD
作为View的Extension
extension View{
func ProgressHUD(isPresented: Binding<Bool>) -> some View
{
ZStack(alignment:.center)
{
self
if isPresented.wrappedValue
{
Color.black.opacity(0.2)
SDProgressHUD()
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
withAnimation {
isPresented.wrappedValue=false
}
}
}
.zIndex(1)
}
}
.ignoresSafeArea()
}
}
2.3在顶层视图添加环境对象
import SwiftUI
@main
struct progressHUDApp: App {
//add ProgressHUD Model
@StateObject var progressState:ProgressState=ProgressState()
var body: some Scene {
WindowGroup {
ContentView()
.ProgressHUD(isPresented: $progressState.isPresented)
.environmentObject(progressState)
}
}
}
2.4在子视图中使用 你只要简单的获取到全局环境变量@EnvironmentObject var proState:ProgressState 修改显示影藏属性即可不用再去关心子视图布局的问题
struct ContentView: View {
@EnvironmentObject var proState:ProgressState
var body: some View {
Button("hide HUD") {
proState.isPresented=true
}
}
}
iShot_2022-09-09_17.38.20.gif
ok 基础教程结束,我们来仿一个项目中经常用到的
SVProgressHUD
效果
3.0 我们先看看SVProgressHUD 常用的几种效果
image.pngimport Foundation
import SwiftUI
//定义一个枚举控制布局
enum ProgressHUDType{
case success
case error
case loading // FIXME:loading这种方式 需要手动调用 hide 方法
case txt
case info
}
//创建一个Progress 状态类
class ProgressState:ObservableObject
{
@Published var isPresented: Bool = false
private(set) var title: String=""
private(set) var systemImage: String = ""
private(set) var duration:CGFloat=1.5
private(set) var type:ProgressHUDType = .loading
func Show(type:ProgressHUDType,title:String,duration:CGFloat=1.5)
{
self.type=type
self.title=title
self.duration=duration
withAnimation {
self.isPresented=true
}
}
// FIXME:手动隐藏
func hide()
{
withAnimation {
self.isPresented=false
}
}
}
//Progress 视图
struct SDProgressHUD:View
{
@EnvironmentObject var progressState:ProgressState
var body: some View
{
VStack(spacing:10)
{
if progressState.type == .txt
{
Text(progressState.title)
}else if progressState.type == .error
{
Image(systemName:"xmark")
.resizable()
.frame(width: 20, height: 20, alignment:.center)
Text(progressState.title)
}else if progressState.type == .success
{
Image(systemName:"checkmark")
.resizable()
.frame(width: 20, height: 20, alignment:.center)
Text(progressState.title)
}else if progressState.type == .loading
{
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white))
Text(progressState.title)
}
}
.padding(18)
.background(Color.black.opacity(0.9),in:RoundedRectangle(cornerRadius: 8))
.foregroundColor(.white)
.frame(maxWidth:UIScreen.main.bounds.width-160)
.onAppear {
if progressState.type != .loading
{
DispatchQueue.main.asyncAfter(deadline: .now() + progressState.duration) {
withAnimation {
progressState.isPresented=false
}
}
}
}
}
}
//封装方便调用
extension View{
func ProgressHUD(isPresented: Binding<Bool>) -> some View
{
ZStack(alignment:.center)
{
self
//为了演示防止点击后抖动的问题,正式项目中不需要"Color.clear"
Color.clear
if isPresented.wrappedValue
{
//需不需要背景蒙层
// Color.black.opacity(0.2)
SDProgressHUD()
.zIndex(1)
}
}
.ignoresSafeArea()
}
}
我们定义一个ProgressHUDType 枚举来控制不同的样式这里只做了几种我项目中常用的。
当然别忘了 在全局注册环境对象
import SwiftUI
@main
struct progressHUDApp: App {
//add ProgressHUD Model
@StateObject var progressState:ProgressState=ProgressState()
var body: some Scene {
WindowGroup {
ContentView()
.ProgressHUD(isPresented: $progressState.isPresented)
.environmentObject(progressState)
}
}
}
3.1 使用
struct ContentView: View {
@EnvironmentObject var proState:ProgressState
var body: some View {
VStack(spacing:20)
{
Button {
proState.Show(type: .txt, title: "无心爱良夜")
} label: {
Text("show Text")
.padding()
.overlay(RoundedRectangle(cornerRadius: 8, style: .continuous).stroke(.blue, lineWidth: 1))
}
Button {
proState.Show(type: .error, title: "error")
} label: {
Text("show error")
.padding()
.overlay(RoundedRectangle(cornerRadius: 8, style: .continuous).stroke(.blue, lineWidth: 1))
}
Button {
proState.Show(type: .success, title: "Success")
} label: {
Text("show Success")
.padding()
.overlay(RoundedRectangle(cornerRadius: 8, style: .continuous).stroke(.blue, lineWidth: 1))
}
HStack{
Button {
proState.Show(type: .loading, title: "loading")
} label: {
Text("show Loading")
.padding()
.overlay(RoundedRectangle(cornerRadius: 8, style: .continuous).stroke(.red, lineWidth: 1))
}
Button {
proState.hide()
} label: {
Text("hide Loading")
.padding()
.overlay(RoundedRectangle(cornerRadius: 8, style: .continuous).stroke(.red, lineWidth: 1))
}
}
.foregroundColor(.red)
.padding(.top,50)
}
}
}
iShot_2022-09-09_18.44.24.gifloading 类型的需要自己手动影藏,一般用于网络加载开始和结束,你懂得
ps,如文章如果有错误的地方请大佬指出
end
ok,以梦为马,不负韶华
网友评论