置顶
菜鸟入门,各位大佬轻喷,如有谬误之处欢迎讨论建议,也欢迎各位道友与我同行
“不积跬步,无以至千里;不积小流,无以成江海”
继续
上文中我们引入了第三方库 Popup
,所以本文我们来讨论 Popup
的封装,实现一个 全局Toast
的功能
效果如下:
期望的调用方式如下:
showToast("测试");
hideToast();
思考
按照上文的调用方式描述,很显然我们不希望这几个方法被定义在本身的视图上,一定是在另一个文件中定义好,这里可以直接使用
所以我们将会用到 extension
同样的我们也不希望这个封装会大量地放到某一个视图中去,最好是直接在封装的地方就能做完,同时在原来的页面视图中只需要进行调用即可,最好是直接在初始化的时候用一次即可。
所以我们将会用到 ViewModifier
结合之前我们用到的 @AppStorage
,完全可以实现一个全局的 Toast
实现
我们新增一个文件:Toast.swift
用来存放所有的对 Toast
的封装,内容如下:
import SwiftUI
import ExytePopupView
/// extension 顾名思义即扩展
/// extension 可以给指定的对象、类型、struct、protocol 等扩展 func,不用再在具体的结构里面显式声明方法
/// 很好的一个横向解耦方式,很像PHP中的 Trait
/// 假如以后有相同方法的两个view,我们完全可以抽象到一起
extension View{
// 初始化启动Toast 返回一个 some View
public func enableToast() -> some View{
@AppStorage("showGlobalToast") var showGlboalToast:Bool = false;
@AppStorage("globalToastText") var globalToastText:String = "";
// 这里将全局变量重置,以避免正好在显示Toast的时候退出了app
// 导致下次打开app的时候就有一个 Toast
showGlboalToast = false
globalToastText = ""
// 这里调用了一个view modifier
return self.modifier(initToast())
}
// 显示一个Toast,传入内容
public func showToast(content:String){
@AppStorage("showGlobalToast") var showGlboalToast:Bool = false;
@AppStorage("globalToastText") var globalToastText:String = "";
// 修改全局变量
showGlboalToast = true;
globalToastText = content;
}
}
/// view modifier 相当于在外面修改某个视图的结构
/// body方法中提供了一个content就是调用你的那个视图
/// 你也可以理解为这是在跳转页面,但是把原始页面的实例给你了
struct initToast:ViewModifier{
@AppStorage("showGlobalToast") var showGlboalToast:Bool = false;
@AppStorage("globalToastText") var globalToastText:String = "";
public func body(content: Content) -> some View {
// 直接给 content 调用popup
content.popup(isPresented: $showGlboalToast, type:.default, position: .bottom, animation: .easeOut(duration: 0), autohideIn: 2, dragToDismiss: false,closeOnTap: false,closeOnTapOutside: true,backgroundColor: .clear
){
Text(globalToastText)
.frame(width: 150, height: 80)
.background(.gray)
.foregroundColor(.white)
.cornerRadius(20.0)
}
}
}
整个的封装过程就结束
接下来就是调用部分
修改 helloworldApp.swift
,即你的 APP
入口文件
import SwiftUI
@main
struct helloworldApp: App {
var body: some Scene {
WindowGroup {
IndexView()
// 调用封装中扩展的方法即可
.enableToast()
}
}
}
在新增 todo
的按钮上进行调用
Button("添加"){
// 这里可以直接调用 showToast()
showToast(content: "添加成功")
todos.add(name: newItem)
newItem = ""
}
因为我们是对 View
进行的扩展,所以,所有的页面和视图都可以调用这些方法
以上,我们实现了一个全局的 Toast
踩坑
我曾经思考过,封装一个方法在 View
上,可以用以下方式调用
self.body.showToast("asdfasdf")
这样我就可以省略入口文件的那个启用方法 enableToast()
但是实际上并不会成功,中途倒是没报几个错,但是 toast
的效果一直都没出来
.popup
一定要写在视图定义的后面,而不能在中途的方法调用中动态的执行
也许这就是所谓的 强类型 + 声明式视图
?😂
总结
-
extension
是扩展方法,就说所有的结构都能扩展,包括基础数据类型,用法与js
中的prototype
比较类似 -
ViewModifier
是对视图的修改,你可以拿到视图本身,添加方法调用,也可以再加一些其他的元素进去,可以理解为拿得到上个页面视图的页面的跳转 - 全局
Loading
可以参照上面的实现,无非就是在文字上面加一个Loading
的gif
动图,本文不再赘述。 - 包括一些全局的
prompt
、confirm
框等,也可以参照以上实现 - 貌似
popup
这个库引入得比较多余,我直接在modifier
里面加一个框框在中间,使用ZStack
这个框框显示在原始视图的上面不就完事儿了?但是怎么消除Toast
是个问题,或许SwiftUI自己处理了这些不显示的视图?这个后续再继续讨论。。。
欢迎关注微信公众号【思跃喵】,一起探讨。