From 32f9af1ae60eb5e2456e0b41f7d0164cfe4cd646 Mon Sep 17 00:00:00 2001 From: xaoxuu Date: Fri, 18 Aug 2023 20:55:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHDemo/PHDemo/AlertVC.swift | 144 +++++++++--------- PHDemo/PHDemo/CapsuleVC.swift | 60 +++++--- PHDemo/PHDemo/SheetVC.swift | 36 ++--- PHDemo/PHDemo/ToastVC.swift | 35 +++-- Sources/ProHUD/Alert/AlertButton.swift | 2 +- Sources/ProHUD/Alert/AlertConfiguration.swift | 52 +++---- .../ProHUD/Alert/AlertConvenienceLayout.swift | 8 +- Sources/ProHUD/Alert/AlertDefaultLayout.swift | 7 +- Sources/ProHUD/Alert/AlertManager.swift | 48 ++---- Sources/ProHUD/Alert/AlertProvider.swift | 48 ++++++ .../Alert/{Alert.swift => AlertTarget.swift} | 41 ++--- Sources/ProHUD/Alert/AlertViewModel.swift | 10 ++ Sources/ProHUD/Alert/AlertWindow.swift | 6 +- .../ProHUD/Capsule/CapsuleConfiguration.swift | 82 +++++----- .../ProHUD/Capsule/CapsuleDefaultLayout.swift | 37 +++-- Sources/ProHUD/Capsule/CapsuleManager.swift | 71 +-------- Sources/ProHUD/Capsule/CapsuleProvider.swift | 53 +++++++ .../{Capsule.swift => CapsuleTarget.swift} | 68 ++------- Sources/ProHUD/Capsule/CapsuleViewModel.swift | 40 +++++ Sources/ProHUD/Capsule/CapsuleWindow.swift | 6 +- ...{Controller.swift => BaseController.swift} | 20 +-- Sources/ProHUD/Core/Models/Action.swift | 4 +- .../ProHUD/Core/Models/BaseViewModel.swift | 4 +- .../ProHUD/Core/Models/Configuration.swift | 10 +- .../ProHUD/Core/Protocols/CommonLayout.swift | 2 +- .../ProHUD/Core/Protocols/DefaultLayout.swift | 2 +- Sources/ProHUD/Core/Protocols/HUD.swift | 20 --- .../Core/Protocols/LoadingAnimation.swift | 2 +- Sources/ProHUD/Core/Protocols/Provider.swift | 64 ++++++++ Sources/ProHUD/Core/Protocols/Target.swift | 19 +++ Sources/ProHUD/Core/Utils/AppContext.swift | 4 +- Sources/ProHUD/Core/Utils/ConsolePrint.swift | 2 +- .../ProHUD/Core/Utils/RotateAnimation.swift | 2 +- Sources/ProHUD/Core/Views/Button.swift | 4 +- Sources/ProHUD/Sheet/SheetButton.swift | 2 +- Sources/ProHUD/Sheet/SheetConfiguration.swift | 104 ++++++------- .../ProHUD/Sheet/SheetConvenienceLayout.swift | 8 +- Sources/ProHUD/Sheet/SheetDefaultLayout.swift | 5 +- Sources/ProHUD/Sheet/SheetManager.swift | 40 +---- Sources/ProHUD/Sheet/SheetProvider.swift | 48 ++++++ .../Sheet/{Sheet.swift => SheetTarget.swift} | 31 ++-- Sources/ProHUD/Sheet/SheetViewModel.swift | 10 ++ Sources/ProHUD/Sheet/SheetWindow.swift | 6 +- Sources/ProHUD/Toast/ToastButton.swift | 2 +- Sources/ProHUD/Toast/ToastConfiguration.swift | 83 +++++----- .../ProHUD/Toast/ToastConvenienceLayout.swift | 6 +- Sources/ProHUD/Toast/ToastDefaultLayout.swift | 8 +- Sources/ProHUD/Toast/ToastManager.swift | 38 +---- Sources/ProHUD/Toast/ToastProvider.swift | 48 ++++++ .../Toast/{Toast.swift => ToastTarget.swift} | 35 ++--- Sources/ProHUD/Toast/ToastViewModel.swift | 12 ++ Sources/ProHUD/Toast/ToastWindow.swift | 6 +- 52 files changed, 823 insertions(+), 682 deletions(-) create mode 100644 Sources/ProHUD/Alert/AlertProvider.swift rename Sources/ProHUD/Alert/{Alert.swift => AlertTarget.swift} (73%) create mode 100644 Sources/ProHUD/Alert/AlertViewModel.swift create mode 100644 Sources/ProHUD/Capsule/CapsuleProvider.swift rename Sources/ProHUD/Capsule/{Capsule.swift => CapsuleTarget.swift} (51%) create mode 100644 Sources/ProHUD/Capsule/CapsuleViewModel.swift rename Sources/ProHUD/Core/Controllers/{Controller.swift => BaseController.swift} (86%) delete mode 100644 Sources/ProHUD/Core/Protocols/HUD.swift create mode 100644 Sources/ProHUD/Core/Protocols/Provider.swift create mode 100644 Sources/ProHUD/Core/Protocols/Target.swift create mode 100644 Sources/ProHUD/Sheet/SheetProvider.swift rename Sources/ProHUD/Sheet/{Sheet.swift => SheetTarget.swift} (69%) create mode 100644 Sources/ProHUD/Sheet/SheetViewModel.swift create mode 100644 Sources/ProHUD/Toast/ToastProvider.swift rename Sources/ProHUD/Toast/{Toast.swift => ToastTarget.swift} (81%) create mode 100644 Sources/ProHUD/Toast/ToastViewModel.swift diff --git a/PHDemo/PHDemo/AlertVC.swift b/PHDemo/PHDemo/AlertVC.swift index fc8b37d..d33ba3d 100644 --- a/PHDemo/PHDemo/AlertVC.swift +++ b/PHDemo/PHDemo/AlertVC.swift @@ -18,7 +18,7 @@ class AlertVC: ListVC { title = "Alert" header.detailLabel.text = "弹窗控件,用于强阻塞性交互,用户必须做出选择或者等待结果才能进入下一步,当多个实例出现时,会以堆叠的形式显示,新的实例会在覆盖旧的实例上层。" - Alert.Configuration.shared { config in + AlertConfiguration.global { config in config.reloadData { vc in if vc.identifier == "custom" { return true @@ -29,7 +29,7 @@ class AlertVC: ListVC { list.add(title: "纯文字") { section in section.add(title: "只有一句话") { - Alert(.message("只有一句话").duration(2)).push() + Alert(.message("只有一句话").duration(2)) } section.add(title: "标题 + 正文") { let title = "这是标题" @@ -43,7 +43,7 @@ class AlertVC: ListVC { list.add(title: "图文弹窗") { section in section.add(title: "纯图标") { - Alert(.loading(3)).push() + Alert(.loading(3)) } section.add(title: "图标 + 文字") { Alert(.loading.message("正在加载")) { alert in @@ -82,20 +82,21 @@ class AlertVC: ListVC { button.titleLabel?.font = .systemFont(ofSize: 15) } alert.vm.title = "你正在使用移动网络观看" - } .onViewDidLoad { vc in - guard let alert = vc as? Alert else { - return + alert.onViewDidLoad { vc in + guard let alert = vc as? AlertTarget else { + return + } + alert.add(contentSpacing: 30) + let v = UIView() + v.backgroundColor = UIColor("#f2f2f2") + alert.add(subview: v).snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.height.equalTo(1) + } + alert.add(contentSpacing: 16) + alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc"))) + } - alert.add(contentSpacing: 30) - let v = UIView() - v.backgroundColor = UIColor("#f2f2f2") - alert.add(subview: v).snp.makeConstraints { make in - make.left.right.equalToSuperview() - make.height.equalTo(1) - } - alert.add(contentSpacing: 16) - alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc"))) - } } section.add(title: "只有一段文字 + 无背景色按钮") { @@ -111,20 +112,21 @@ class AlertVC: ListVC { button.titleLabel?.font = .systemFont(ofSize: 15) } alert.vm.message = "为了维护社区氛围,上麦用户需进行主播认证" - } .onViewDidLoad { vc in - guard let alert = vc as? Alert else { - return + alert.onViewDidLoad { vc in + guard let alert = vc as? AlertTarget else { + return + } + alert.add(contentSpacing: 30) + let v = UIView() + v.backgroundColor = UIColor("#f2f2f2") + alert.add(subview: v).snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.height.equalTo(1) + } + alert.add(contentSpacing: 16) + alert.add(action: "取消", style: .plain(textColor: UIColor("#939999"))) + alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc"))) } - alert.add(contentSpacing: 30) - let v = UIView() - v.backgroundColor = UIColor("#f2f2f2") - alert.add(subview: v).snp.makeConstraints { make in - make.left.right.equalToSuperview() - make.height.equalTo(1) - } - alert.add(contentSpacing: 16) - alert.add(action: "取消", style: .plain(textColor: UIColor("#939999"))) - alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc"))) } } section.add(title: "只有一段文字 + 3个无背景色按钮") { @@ -144,41 +146,42 @@ class AlertVC: ListVC { stack.spacing = 0 stack.axis = .vertical // 竖排按钮 } - } .onViewDidLoad { vc in - guard let alert = vc as? Alert else { - return + alert.onViewDidLoad { vc in + guard let alert = vc as? AlertTarget else { + return + } + func createLine() -> UIView { + let v = UIView() + v.backgroundColor = UIColor("#f2f2f2") + return v + } + let btn1 = alert.add(action: "确认,以后不需要再提醒", style: .plain(textColor: UIColor("#14cccc"))) + btn1.contentEdgeInsets.top = 16 + 1 // 间距 + 线的高度 + btn1.contentEdgeInsets.bottom = 16 + let line1 = createLine() + btn1.insertSubview(line1, at: 0) + line1.snp.makeConstraints { make in + make.top.left.right.equalToSuperview() + make.height.equalTo(1) + } + + let btn2 = alert.add(action: "确认,每次消费前提醒", style: .plain(textColor: UIColor("#14cccc"))) + let line2 = createLine() + btn2.insertSubview(line2, at: 0) + line2.snp.makeConstraints { make in + make.top.left.right.equalToSuperview() + make.height.equalTo(1) + } + + let btn3 = alert.add(action: "取消", style: .plain(textColor: UIColor("#939999"))) + let line3 = createLine() + btn3.insertSubview(line3, at: 0) + line3.snp.makeConstraints { make in + make.top.left.right.equalToSuperview() + make.height.equalTo(1) + } + } - func createLine() -> UIView { - let v = UIView() - v.backgroundColor = UIColor("#f2f2f2") - return v - } - let btn1 = alert.add(action: "确认,以后不需要再提醒", style: .plain(textColor: UIColor("#14cccc"))) - btn1.contentEdgeInsets.top = 16 + 1 // 间距 + 线的高度 - btn1.contentEdgeInsets.bottom = 16 - let line1 = createLine() - btn1.insertSubview(line1, at: 0) - line1.snp.makeConstraints { make in - make.top.left.right.equalToSuperview() - make.height.equalTo(1) - } - - let btn2 = alert.add(action: "确认,每次消费前提醒", style: .plain(textColor: UIColor("#14cccc"))) - let line2 = createLine() - btn2.insertSubview(line2, at: 0) - line2.snp.makeConstraints { make in - make.top.left.right.equalToSuperview() - make.height.equalTo(1) - } - - let btn3 = alert.add(action: "取消", style: .plain(textColor: UIColor("#939999"))) - let line3 = createLine() - btn3.insertSubview(line3, at: 0) - line3.snp.makeConstraints { make in - make.top.left.right.equalToSuperview() - make.height.equalTo(1) - } - } } @@ -203,7 +206,7 @@ class AlertVC: ListVC { } list.add(title: "图标 + 文字 + 按钮") { section in section.add(title: "操作成功") { - Alert(.success(3).title("操作成功").message("这条消息将在3s后消失")).push() + Alert(.success(3).title("操作成功").message("这条消息将在3s后消失")) } section.add(title: "操作失败") { Alert { alert in @@ -287,12 +290,13 @@ class AlertVC: ListVC { Alert(.loading) { alert in alert.vm.title = "在弹出过程中增加元素" alert.add(action: "OK", style: .gray) - } .onViewWillAppear { vc in - guard let alert = vc as? Alert else { - return + alert.onViewWillAppear { vc in + guard let alert = vc as? AlertTarget else { + return + } + alert.vm.message = "这是一段后增加的文字\n动画效果会有细微差别" + alert.reloadTextStack() } - alert.vm.message = "这是一段后增加的文字\n动画效果会有细微差别" - alert.reloadTextStack() } } } @@ -337,7 +341,7 @@ class AlertVC: ListVC { let vc = UIViewController() vc.title = "页面" vc.view.backgroundColor = .systemYellow - let alert = Alert(.loading.title("正在加载").message("这个弹窗被放在指定容器中")) + let alert = Alert(.loading.title("正在加载").message("这个弹窗被放在指定容器中")).target alert.add(action: "返回上一页") { alert in vc.dismiss(animated: true) } diff --git a/PHDemo/PHDemo/CapsuleVC.swift b/PHDemo/PHDemo/CapsuleVC.swift index d534b06..335d72f 100644 --- a/PHDemo/PHDemo/CapsuleVC.swift +++ b/PHDemo/PHDemo/CapsuleVC.swift @@ -16,20 +16,30 @@ class CapsuleVC: ListVC { title = "Capsule" header.detailLabel.text = "状态胶囊控件,用于状态显示,一个主程序窗口每个位置(上中下)各自最多只有一个状态胶囊实例。" - Capsule.Configuration.shared { config in + CapsuleConfiguration.global { config in // config.cardCornerRadius = .infinity // 设置一个较大的数字就会变成胶囊形状 } list.add(title: "默认布局:纯文字") { section in section.add(title: "一条简短的消息") { - Capsule(.message("一条简短的消息")).push() + // 设置vm或者handler都会自动push,这里测试传入vm: + Capsule(.message("一条简短消息")) } section.add(title: "一条稍微长一点的消息") { - Capsule(.message("一条稍微长一点的消息")).push() + // 设置vm或者handler都会自动push,这里测试传入handler: + Capsule { capsule in + // handler中可以进行复杂设置(详见下面的其它例子) + capsule.vm = .message("一条稍微长一点的消息") + } } section.add(title: "(默认)状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") { - Capsule(.message("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")).push() + // 也可以创建一个空白实例,在需要的时候再push + let obj = Capsule().target + obj.vm = .message("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") + // ... 在需要的时候手动push + obj.push() } section.add(title: "(限制1行)状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") { + // 同时设置vm和handler也可以 Capsule(.message("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")) { capsule in capsule.config.customTextLabel { label in label.numberOfLines = 1 @@ -39,14 +49,27 @@ class CapsuleVC: ListVC { } list.add(title: "默认布局:图文") { section in - section.add(title: "一条简短的消息") { - Capsule(.info("一条简短的消息")).push() + section.add(title: "下载进度") { + let capsule = Capsule().target + capsule.vm = .message("正在下载").icon(.init(systemName: "arrow.down.circle.fill")).duration(.infinity) + capsule.update(progress: 0) + capsule.push() + updateProgress(in: 4) { percent in + capsule.update(progress: percent) + } completion: { + capsule.update { toast in + toast.vm = .message("下载成功") + .icon(.init(systemName: "checkmark.circle.fill")) + .duration(5) + .tintColor(.systemGreen) + } + } } - section.add(title: "一条稍微长一点的消息") { - Capsule(.systemError.title("500").message("一条稍微长一点的消息")).push() + section.add(title: "接口报错提示") { + Capsule(.systemError.title("[500]").message("服务端错误")) } section.add(title: "(默认)状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") { - Capsule(.info("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")).push() + Capsule(.info("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")) } section.add(title: "(限制1行)状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") { Capsule(.info("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")) { capsule in @@ -59,14 +82,10 @@ class CapsuleVC: ListVC { list.add(title: "不同位置、不同动画") { section in section.add(title: "顶部,默认滑入") { - Capsule(.info("一条简短的消息")) { capsule in - - } + Capsule(.info("一条简短的消息")) } section.add(title: "中间,默认缩放") { - Capsule(.middle.info("一条简短的消息")) { capsule in - - } + Capsule(.middle.info("一条简短的消息")) } section.add(title: "中间,黑底白字,透明渐变") { Capsule(.middle.info("一条简短的消息")) { capsule in @@ -98,7 +117,7 @@ class CapsuleVC: ListVC { section.add(title: "底部,渐变背景,默认回弹滑入") { Capsule(.bottom.enter("点击进入")) { capsule in capsule.config.tintColor = .white - capsule.config.cardEdgeInsets = .init(top: 16, left: 24, bottom: 16, right: 24) + capsule.config.cardEdgeInsets = .init(top: 12, left: 20, bottom: 12, right: 20) capsule.config.customTextLabel { label in label.textColor = .white label.font = .boldSystemFont(ofSize: 16) @@ -116,9 +135,10 @@ class CapsuleVC: ListVC { mask.layer.insertSublayer(gradientLayer, at: 0) } capsule.config.cardCornerRadius = .infinity - }.onTapped { capsule in - Alert(.message("收到点击事件").duration(1)).push() - capsule.pop() + capsule.onTapped { capsule in + Alert(.message("收到点击事件").duration(1)) + capsule.pop() + } } } } @@ -126,7 +146,7 @@ class CapsuleVC: ListVC { } -extension Capsule.ViewModel { +extension CapsuleTarget.ViewModel { static func info(_ text: String?) -> Self { .init() diff --git a/PHDemo/PHDemo/SheetVC.swift b/PHDemo/PHDemo/SheetVC.swift index fc9c0f7..e368553 100644 --- a/PHDemo/PHDemo/SheetVC.swift +++ b/PHDemo/PHDemo/SheetVC.swift @@ -73,13 +73,14 @@ class SheetVC: ListVC { sheet.add(spacing: 24) sheet.add(action: "确认") sheet.add(action: "取消", style: .gray) - } onTappedBackground: { sheet in - print("点击了背景") - Toast.lazyPush(identifier: "alert") { toast in - toast.vm = .error - toast.vm.title = "点击了背景" - toast.vm.message = "点击背景将不会dismiss,必须在下方做出选择才能关掉" - toast.vm.duration = 2 + sheet.onTappedBackground { sheet in + print("点击了背景") + Toast.lazyPush(identifier: "alert") { toast in + toast.vm = .error + toast.vm.title = "点击了背景" + toast.vm.message = "点击背景将不会dismiss,必须在下方做出选择才能关掉" + toast.vm.duration = 2 + } } } } @@ -180,16 +181,17 @@ class SheetVC: ListVC { mask.effect = .none mask.backgroundColor = .clear } - } .onViewWillAppear { vc in - guard let sheet = vc as? Sheet else { return } - let imgv = UIImageView(image: UIImage(named: "landscape")) - imgv.contentMode = .scaleAspectFill - imgv.clipsToBounds = true - imgv.layer.cornerRadiusWithContinuous = 16 - sheet.contentView.insertSubview(imgv, at: 0) - imgv.snp.makeConstraints { make in - make.edges.equalToSuperview() - make.height.equalTo(300) + sheet.onViewWillAppear { vc in + guard let sheet = vc as? SheetTarget else { return } + let imgv = UIImageView(image: UIImage(named: "landscape")) + imgv.contentMode = .scaleAspectFill + imgv.clipsToBounds = true + imgv.layer.cornerRadiusWithContinuous = 16 + sheet.contentView.insertSubview(imgv, at: 0) + imgv.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.height.equalTo(300) + } } } } diff --git a/PHDemo/PHDemo/ToastVC.swift b/PHDemo/PHDemo/ToastVC.swift index 5616ac1..6c51fab 100644 --- a/PHDemo/PHDemo/ToastVC.swift +++ b/PHDemo/PHDemo/ToastVC.swift @@ -32,15 +32,14 @@ func updateProgress(in duration: TimeInterval, callback: @escaping (_ percent: C let isTesting: Bool = true -class TestToast: Toast { +class TestToastTarget: ToastTarget { override func push() { - guard isTesting else { - return - } + guard isTesting else { return } super.push() } } +typealias TestToast = HUDProvider class ToastVC: ListVC { @@ -54,7 +53,7 @@ class ToastVC: ListVC { header.detailLabel.text = message - Toast.Configuration.shared { config in + ToastConfiguration.global { config in config.contentViewMask { mask in mask.backgroundColor = .clear mask.effect = UIBlurEffect(style: .systemChromeMaterial) @@ -66,15 +65,15 @@ class ToastVC: ListVC { list.add(title: "默认布局") { section in section.add(title: "标题 + 正文") { - TestToast(.title(title).message(message)).push() + TestToast(.title(title).message(message)) } section.add(title: "一段长文本") { - Toast(.message(message)).push() + Toast(.message(message)) } section.add(title: "图标 + 标题 + 正文") { let s1 = "笑容正在加载" let s2 = "这通常不会太久" - let toast = Toast(.loading.title(s1).message(s2)) + let toast = Toast(.loading.title(s1).message(s2)).target toast.push() toast.update(progress: 0) updateProgress(in: 4) { percent in @@ -99,10 +98,10 @@ class ToastVC: ListVC { } } section.add(title: "图标 + 一段长文本") { - Toast(.note.message(message)).push() + Toast(.note.message(message)) } section.add(title: "网络图标 + 一段文本") { - Toast(.message("这是网络图标").icon(.init(string: "https://xaoxuu.com/assets/xaoxuu/avatar/rect-256@2x.png"))).push() + Toast(.message("这是网络图标").icon(.init(string: "https://xaoxuu.com/assets/xaoxuu/avatar/rect-256@2x.png"))) } } @@ -113,7 +112,7 @@ class ToastVC: ListVC { Toast(.msg.title(title).message(message)) { toast in toast.onTapped { toast in toast.pop() - Alert(.success(1).message("操作成功")).push() + Alert(.success(1).message("操作成功")) } } } @@ -150,7 +149,7 @@ class ToastVC: ListVC { alert.pop() }) toast.pop() - Alert(.success(1).message("Good choice!")).push() + Alert(.success(1).message("Good choice!")) } Toast.find(identifier: "loading") { toast in toast.vm = .success(2).message("加载成功") @@ -227,28 +226,28 @@ class ToastVC: ListVC { Toast { toast in toast.vm = .title("圆角半径").message("可以在共享配置中设置,则全局生效") toast.add(action: "8", style: .gray) { toast in - Toast.Configuration.shared { config in + ToastConfiguration.global { config in config.cardCornerRadius = 8 } toast.pop() foo() } toast.add(action: "16", style: .gray) { toast in - Toast.Configuration.shared { config in + ToastConfiguration.global { config in config.cardCornerRadius = 16 } toast.pop() foo() } toast.add(action: "24", style: .gray) { toast in - Toast.Configuration.shared { config in + ToastConfiguration.global { config in config.cardCornerRadius = 24 } toast.pop() foo() } toast.add(action: "32", style: .gray) { toast in - Toast.Configuration.shared { config in + ToastConfiguration.global { config in config.cardCornerRadius = 32 } toast.pop() @@ -330,7 +329,7 @@ class ToastVC: ListVC { toast.title = "共享配置" toast.vm.message = "建议在App启动后进行通用配置设置,所有实例都会先拉取通用配置为默认值,修改这些配置会影响到所有实例。" toast.add(action: "默认", style: .gray) { toast in - Toast.Configuration.shared { config in + ToastConfiguration.global { config in config.customTitleLabel { titleLabel in titleLabel.font = .systemFont(ofSize: 19, weight: .bold) } @@ -339,7 +338,7 @@ class ToastVC: ListVC { foo() } toast.add(action: "大号标题") { toast in - Toast.Configuration.shared { config in + ToastConfiguration.global { config in config.customTitleLabel { titleLabel in titleLabel.font = .systemFont(ofSize: 28, weight: .medium) } diff --git a/Sources/ProHUD/Alert/AlertButton.swift b/Sources/ProHUD/Alert/AlertButton.swift index ac31d93..8297f8e 100644 --- a/Sources/ProHUD/Alert/AlertButton.swift +++ b/Sources/ProHUD/Alert/AlertButton.swift @@ -13,7 +13,7 @@ public class AlertButton: Button { .init(top: 12, left: 24, bottom: 12, right: 24) } - public override func update(config: ProHUD.Configuration, action: Action) { + public override func update(config: CommonConfiguration, action: Action) { titleLabel?.font = .boldSystemFont(ofSize: 15) layer.cornerRadiusWithContinuous = 8 super.update(config: config, action: action) diff --git a/Sources/ProHUD/Alert/AlertConfiguration.swift b/Sources/ProHUD/Alert/AlertConfiguration.swift index f5a8365..a3595a1 100644 --- a/Sources/ProHUD/Alert/AlertConfiguration.swift +++ b/Sources/ProHUD/Alert/AlertConfiguration.swift @@ -7,35 +7,31 @@ import UIKit -public extension Alert { +public class AlertConfiguration: CommonConfiguration { - class Configuration: ProHUD.Configuration { - - /// 堆叠深度 - public var stackDepth: CGFloat = 30 - - public var enableShadow: Bool = true - - static var customShared: ((_ config: Configuration) -> Void)? - - /// 共享配置(只能设置一次,影响所有实例) - /// - Parameter callback: 配置代码 - public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) { - customShared = callback - } - - var customBackgroundViewMask: ((_ mask: UIView) -> Void)? - - /// 设置背景蒙版 - /// - Parameter callback: 自定义内容卡片蒙版代码 - public func backgroundViewMask(_ callback: @escaping (_ mask: UIView) -> Void) { - customBackgroundViewMask = callback - } - - override var animateDurationForBuildInByDefault: CGFloat { - animateDurationForBuildIn ?? 0.6 - } - + /// 堆叠深度 + public var stackDepth: CGFloat = 30 + + public var enableShadow: Bool = true + + static var customGlobalConfig: ((_ config: AlertConfiguration) -> Void)? + + /// 全局共享配置(只能设置一次,影响所有实例) + /// - Parameter callback: 配置代码 + public static func global(_ callback: @escaping (_ config: AlertConfiguration) -> Void) { + customGlobalConfig = callback + } + + var customBackgroundViewMask: ((_ mask: UIView) -> Void)? + + /// 设置背景蒙版 + /// - Parameter callback: 自定义内容卡片蒙版代码 + public func backgroundViewMask(_ callback: @escaping (_ mask: UIView) -> Void) { + customBackgroundViewMask = callback + } + + override var animateDurationForBuildInByDefault: CGFloat { + animateDurationForBuildIn ?? 0.6 } } diff --git a/Sources/ProHUD/Alert/AlertConvenienceLayout.swift b/Sources/ProHUD/Alert/AlertConvenienceLayout.swift index 024ef91..80706f1 100644 --- a/Sources/ProHUD/Alert/AlertConvenienceLayout.swift +++ b/Sources/ProHUD/Alert/AlertConvenienceLayout.swift @@ -7,7 +7,7 @@ import UIKit -extension Alert: InternalConvenienceLayout { +extension AlertTarget: InternalConvenienceLayout { // MARK: 增加 @discardableResult public func add(action: Action) -> Button { @@ -155,7 +155,7 @@ extension Alert: InternalConvenienceLayout { } // MARK: more -public extension Alert { +public extension AlertTarget { /// 增加一个按钮 /// - Parameters: @@ -164,10 +164,10 @@ public extension Alert { /// - identifier: 唯一标识符 /// - handler: 点击事件 /// - Returns: 按钮实例 - @discardableResult func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ alert: Alert) -> Void)? = nil) -> Button { + @discardableResult func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ alert: AlertTarget) -> Void)? = nil) -> Button { if let handler = handler { let action = Action(identifier: identifier, style: style, title: title) { vc in - if let vc = vc as? Alert { + if let vc = vc as? AlertTarget { handler(vc) } } diff --git a/Sources/ProHUD/Alert/AlertDefaultLayout.swift b/Sources/ProHUD/Alert/AlertDefaultLayout.swift index 3f3b174..825e0f6 100644 --- a/Sources/ProHUD/Alert/AlertDefaultLayout.swift +++ b/Sources/ProHUD/Alert/AlertDefaultLayout.swift @@ -7,9 +7,9 @@ import UIKit -extension Alert: DefaultLayout { +extension AlertTarget: DefaultLayout { - var cfg: ProHUD.Configuration { + var cfg: CommonConfiguration { return config } @@ -17,6 +17,7 @@ extension Alert: DefaultLayout { if self.cfg.customReloadData?(self) == true { return } + view.tintColor = vm.tintColor ?? config.tintColor let isFirstLayout: Bool if contentView.superview == nil { isFirstLayout = animated @@ -132,7 +133,7 @@ extension Alert: DefaultLayout { } -extension Alert { +extension AlertTarget { func setupImageView() { // 移除动画 diff --git a/Sources/ProHUD/Alert/AlertManager.swift b/Sources/ProHUD/Alert/AlertManager.swift index 4835f23..c3c84d0 100644 --- a/Sources/ProHUD/Alert/AlertManager.swift +++ b/Sources/ProHUD/Alert/AlertManager.swift @@ -7,14 +7,15 @@ import UIKit -extension Alert: HUD { +extension AlertTarget { @objc open func push() { - guard Configuration.isEnabled else { return } + guard AlertConfiguration.isEnabled else { return } let window = createAttachedWindowIfNotExists() guard window.alerts.contains(self) == false else { return } + setDefaultAxis() view.transform = .init(scaleX: 1.2, y: 1.2) view.alpha = 0 navEvents[.onViewWillAppear]?(self) @@ -34,12 +35,12 @@ extension Alert: HUD { self.navEvents[.onViewDidAppear]?(self) } window.alerts.append(self) - Alert.updateAlertsLayout(alerts: window.alerts) + AlertTarget.updateAlertsLayout(alerts: window.alerts) } @objc open func pop() { navEvents[.onViewWillDisappear]?(self) - Alert.removeAlert(alert: self) + AlertTarget.removeAlert(alert: self) let duration = config.animateDurationForBuildOut ?? config.animateDurationForBuildOutByDefault UIView.animateEaseOut(duration: duration) { self.view.alpha = 0 @@ -63,29 +64,9 @@ extension Alert: HUD { } } -} - -public extension Alert { - - /// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例 - /// - Parameters: - /// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符) - /// - handler: 实例创建代码 - static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ alert: Alert) -> Void, onExists: ((_ alert: Alert) -> Void)? = nil) { - let id = identifier ?? (file + "#\(line)") - if let vc = find(identifier: id).last { - vc.update(handler: onExists ?? handler) - } else { - Alert { alert in - alert.identifier = id - handler(alert) - } - } - } - /// 更新HUD实例 /// - Parameter callback: 实例更新代码 - func update(handler: @escaping (_ alert: Alert) -> Void) { + @objc open func update(handler: @escaping (_ alert: AlertTarget) -> Void) { handler(self) reloadData() UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) { @@ -93,23 +74,12 @@ public extension Alert { } } - /// 查找HUD实例 - /// - Parameter identifier: 唯一标识符 - /// - Returns: HUD实例 - @discardableResult static func find(identifier: String, update handler: ((_ alert: Alert) -> Void)? = nil) -> [Alert] { - let arr = AppContext.alertWindow.values.flatMap({ $0.alerts }).filter({ $0.identifier == identifier }) - if let handler = handler { - arr.forEach({ $0.update(handler: handler) }) - } - return arr - } - } // MARK: - layout -fileprivate extension Alert { - static func updateAlertsLayout(alerts: [Alert]) { +fileprivate extension AlertTarget { + static func updateAlertsLayout(alerts: [AlertTarget]) { for (i, a) in alerts.reversed().enumerated() { let scale = CGFloat(pow(0.9, CGFloat(i))) UIView.animate(withDuration: 1.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5, options: [.allowUserInteraction, .curveEaseInOut], animations: { @@ -125,7 +95,7 @@ fileprivate extension Alert { AlertWindow.createAttachedWindowIfNotExists(config: config) } - static func removeAlert(alert: Alert) { + static func removeAlert(alert: AlertTarget) { guard var alerts = alert.attachedWindow?.alerts else { return } diff --git a/Sources/ProHUD/Alert/AlertProvider.swift b/Sources/ProHUD/Alert/AlertProvider.swift new file mode 100644 index 0000000..f97d71b --- /dev/null +++ b/Sources/ProHUD/Alert/AlertProvider.swift @@ -0,0 +1,48 @@ +// +// AlertProvider.swift +// +// +// Created by xaoxuu on 2023/8/18. +// + +import UIKit + +open class AlertProvider: HUDProvider { + @discardableResult public required init(_ vm: ViewModel?, initializer: ((_ alert: Target) -> Void)?) { + super.init(vm, initializer: initializer) + } + + @discardableResult public required convenience init(initializer: ((_ alert: Target) -> Void)?) { + self.init(nil, initializer: initializer) + } + + /// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例 + /// - Parameters: + /// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符) + /// - handler: 实例创建代码 + public static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ alert: AlertTarget) -> Void, onExists: ((_ alert: AlertTarget) -> Void)? = nil) { + let id = identifier ?? (file + "#\(line)") + if let vc = find(identifier: id).last { + vc.update(handler: onExists ?? handler) + } else { + Self.init { alert in + alert.identifier = id + handler(alert) + } + } + } + + /// 查找HUD实例 + /// - Parameter identifier: 唯一标识符 + /// - Returns: HUD实例 + @discardableResult public static func find(identifier: String, update handler: ((_ alert: AlertTarget) -> Void)? = nil) -> [AlertTarget] { + let arr = AppContext.alertWindow.values.flatMap({ $0.alerts }).filter({ $0.identifier == identifier }) + if let handler = handler { + arr.forEach({ $0.update(handler: handler) }) + } + return arr + } + +} + +public typealias Alert = AlertProvider diff --git a/Sources/ProHUD/Alert/Alert.swift b/Sources/ProHUD/Alert/AlertTarget.swift similarity index 73% rename from Sources/ProHUD/Alert/Alert.swift rename to Sources/ProHUD/Alert/AlertTarget.swift index 8368f99..1a520f5 100644 --- a/Sources/ProHUD/Alert/Alert.swift +++ b/Sources/ProHUD/Alert/AlertTarget.swift @@ -7,11 +7,11 @@ import UIKit -open class Alert: ProHUD.Controller { +open class AlertTarget: BaseController, HUDTargetType { - public lazy var config: Configuration = { - var cfg = Configuration() - Configuration.customShared?(cfg) + public lazy var config: AlertConfiguration = { + var cfg = AlertConfiguration() + AlertConfiguration.customGlobalConfig?(cfg) return cfg }() @@ -73,10 +73,8 @@ open class Alert: ProHUD.Controller { return stack }() - @objc(AlertViewModel) open class ViewModel: BaseViewModel {} - /// 视图模型 - @objc public var vm = ViewModel() + @objc public var vm: AlertViewModel = .init() public override var title: String? { didSet { @@ -84,35 +82,22 @@ open class Alert: ProHUD.Controller { } } - @discardableResult @objc public init(_ vm: ViewModel, handler: ((_ alert: Alert) -> Void)? = nil) { + required public override init() { super.init() - self.vm = vm - handler?(self) - DispatchQueue.main.async { - if handler != nil { - self.setDefaultAxis() - self.push() - } - } - } - - @discardableResult @objc public convenience init(handler: ((_ alert: Alert) -> Void)?) { - self.init(.init(), handler: handler) - } - - public override func viewDidLoad() { - super.viewDidLoad() - view.tintColor = config.tintColor - reloadData(animated: false) - navEvents[.onViewDidLoad]?(self) } required public init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + public override func viewDidLoad() { + super.viewDidLoad() + reloadData(animated: false) + navEvents[.onViewDidLoad]?(self) + } + } // MARK: 动画扩展 -extension Alert: LoadingAnimation {} +extension AlertTarget: LoadingAnimation {} diff --git a/Sources/ProHUD/Alert/AlertViewModel.swift b/Sources/ProHUD/Alert/AlertViewModel.swift new file mode 100644 index 0000000..4e0df2b --- /dev/null +++ b/Sources/ProHUD/Alert/AlertViewModel.swift @@ -0,0 +1,10 @@ +// +// AlertViewModel.swift +// +// +// Created by xaoxuu on 2023/8/18. +// + +import UIKit + +@objc open class AlertViewModel: BaseViewModel {} diff --git a/Sources/ProHUD/Alert/AlertWindow.swift b/Sources/ProHUD/Alert/AlertWindow.swift index 9a03b7a..ec2e6f0 100644 --- a/Sources/ProHUD/Alert/AlertWindow.swift +++ b/Sources/ProHUD/Alert/AlertWindow.swift @@ -9,11 +9,11 @@ import UIKit class AlertWindow: Window { - var alerts: [Alert] = [] + var alerts: [AlertTarget] = [] override var usingBackground: Bool { true } - static func createAttachedWindowIfNotExists(config: Configuration) -> AlertWindow { + static func createAttachedWindowIfNotExists(config: AlertConfiguration) -> AlertWindow { let windowScene = AppContext.windowScene if let windowScene = windowScene, let w = AppContext.alertWindow[windowScene] { return w @@ -34,7 +34,7 @@ class AlertWindow: Window { } -extension Alert { +extension AlertTarget { var attachedWindow: AlertWindow? { view.window as? AlertWindow } diff --git a/Sources/ProHUD/Capsule/CapsuleConfiguration.swift b/Sources/ProHUD/Capsule/CapsuleConfiguration.swift index 6c8c5b6..35e03b6 100644 --- a/Sources/ProHUD/Capsule/CapsuleConfiguration.swift +++ b/Sources/ProHUD/Capsule/CapsuleConfiguration.swift @@ -7,51 +7,47 @@ import UIKit -public extension Capsule { +public class CapsuleConfiguration: CommonConfiguration { - typealias CustomAnimateHandler = ((_ window: UIWindow, _ completion: @escaping () -> Void) -> Void) + public typealias CustomAnimateHandler = ((_ window: UIWindow, _ completion: @escaping () -> Void) -> Void) - class Configuration: ProHUD.Configuration { - - static var customShared: ((_ config: Configuration) -> Void)? - - /// 共享配置(只能设置一次,影响所有实例) - /// - Parameter callback: 配置代码 - public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) { - customShared = callback - } - - override var cardCornerRadiusByDefault: CGFloat { - cardCornerRadius ?? 16 - } - - override var cardEdgeInsetsByDefault: UIEdgeInsets { - cardEdgeInsets ?? .init(top: 12, left: 16, bottom: 12, right: 16) - } - - override var cardMaxWidthByDefault: CGFloat { cardMaxWidth ?? 320 } - - override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? 120 } - - override var animateDurationForBuildInByDefault: CGFloat { - animateDurationForBuildIn ?? 0.8 - } - - override var animateDurationForBuildOutByDefault: CGFloat { - animateDurationForBuildOut ?? 0.8 - } - - var animateBuildIn: CustomAnimateHandler? - public func animateBuildIn(_ handler: CustomAnimateHandler?) { - animateBuildIn = handler - } - - var animateBuildOut: CustomAnimateHandler? - public func animateBuildOut(_ handler: CustomAnimateHandler?) { - animateBuildOut = handler - } - - + static var customGlobalConfig: ((_ config: CapsuleConfiguration) -> Void)? + + /// 全局共享配置(只能设置一次,影响所有实例) + /// - Parameter callback: 配置代码 + public static func global(_ callback: @escaping (_ config: CapsuleConfiguration) -> Void) { + customGlobalConfig = callback } + override var cardCornerRadiusByDefault: CGFloat { + cardCornerRadius ?? 16 + } + + override var cardEdgeInsetsByDefault: UIEdgeInsets { + cardEdgeInsets ?? .init(top: 12, left: 16, bottom: 12, right: 16) + } + + override var cardMaxWidthByDefault: CGFloat { cardMaxWidth ?? 320 } + + override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? 120 } + + override var animateDurationForBuildInByDefault: CGFloat { + animateDurationForBuildIn ?? 0.8 + } + + override var animateDurationForBuildOutByDefault: CGFloat { + animateDurationForBuildOut ?? 0.8 + } + + var animateBuildIn: CustomAnimateHandler? + public func animateBuildIn(_ handler: CustomAnimateHandler?) { + animateBuildIn = handler + } + + var animateBuildOut: CustomAnimateHandler? + public func animateBuildOut(_ handler: CustomAnimateHandler?) { + animateBuildOut = handler + } + + } diff --git a/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift b/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift index dd2c4f6..fff99ef 100644 --- a/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift +++ b/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift @@ -7,9 +7,9 @@ import UIKit -extension Capsule: DefaultLayout { +extension CapsuleTarget: DefaultLayout { - var cfg: ProHUD.Configuration { + var cfg: CommonConfiguration { return config } @@ -18,18 +18,14 @@ extension Capsule: DefaultLayout { return } + view.tintColor = vm.tintColor ?? config.tintColor + // content loadContentViewIfNeeded() loadContentMaskViewIfNeeded() // image - imageView.removeFromSuperview() - if let icon = vm.icon { - contentStack.insertArrangedSubview(imageView, at: 0) - imageView.image = icon - } else { - contentStack.removeArrangedSubview(imageView) - } + setupImageView() // text textLabel.removeFromSuperview() @@ -94,4 +90,27 @@ extension Capsule: DefaultLayout { }) } + private func setupImageView() { + // 移除动画 + stopRotate(animateLayer) + animateLayer = nil + animation = nil + + // 移除进度 + progressView?.removeFromSuperview() + + if vm.icon == nil && vm.iconURL == nil { + contentStack.removeArrangedSubview(imageView) + } else { + contentStack.insertArrangedSubview(imageView, at: 0) + } + imageView.image = vm.icon + if let iconURL = vm.iconURL { + config.customWebImage?(imageView, iconURL) + } + if let rotation = vm.rotation { + startRotate(rotation) + } + + } } diff --git a/Sources/ProHUD/Capsule/CapsuleManager.swift b/Sources/ProHUD/Capsule/CapsuleManager.swift index 291d271..8261eda 100644 --- a/Sources/ProHUD/Capsule/CapsuleManager.swift +++ b/Sources/ProHUD/Capsule/CapsuleManager.swift @@ -7,10 +7,10 @@ import UIKit -extension Capsule: HUD { +extension CapsuleTarget { @objc open func push() { - guard Configuration.isEnabled else { return } + guard CapsuleConfiguration.isEnabled else { return } let isNew: Bool let window: CapsuleWindow let position = vm.position @@ -156,78 +156,15 @@ extension Capsule: HUD { } } - -} - -public extension Capsule { - - /// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例 - /// - Parameters: - /// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符) - /// - handler: 实例创建代码 - static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ capsule: Capsule) -> Void, onExists: ((_ capsule: Capsule) -> Void)? = nil) { - let id = identifier ?? (file + "#\(line)") - if let vc = find(identifier: id).last { - vc.update(handler: onExists ?? handler) - } else { - Capsule { capsule in - capsule.identifier = id - handler(capsule) - } - } - } - /// 更新HUD实例 /// - Parameter handler: 实例更新代码 - func update(handler: @escaping (_ capsule: Capsule) -> Void) { + @objc open func update(handler: @escaping (_ capsule: CapsuleTarget) -> Void) { handler(self) + reloadData() UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) { self.view.layoutIfNeeded() } } - /// 查找HUD实例 - /// - Parameter identifier: 唯一标识符 - /// - Returns: HUD实例 - @discardableResult static func find(identifier: String, update handler: ((_ capsule: Capsule) -> Void)? = nil) -> [Capsule] { - let arr = AppContext.capsuleWindows.values.flatMap({ $0.values }).compactMap({ $0.capsule }).filter({ $0.identifier == identifier }) - if let handler = handler { - arr.forEach({ $0.update(handler: handler) }) - } - return arr - } - } - - -//extension Capsule { -// -// func translateIn(completion: (() -> Void)?) { -// UIView.animateEaseOut(duration: config.animateDurationForBuildInByDefault) { -// if self.config.stackDepthEffect { -// if isPhonePortrait { -// AppContext.appWindow?.transform = .init(translationX: 0, y: 8).scaledBy(x: 0.9, y: 0.9) -// } else { -// AppContext.appWindow?.transform = .init(scaleX: 0.92, y: 0.92) -// } -// AppContext.appWindow?.layer.cornerRadiusWithContinuous = 16 -// AppContext.appWindow?.layer.masksToBounds = true -// } -// } completion: { done in -// completion?() -// } -// } -// -// func translateOut(completion: (() -> Void)?) { -// UIView.animateEaseOut(duration: config.animateDurationForBuildOutByDefault) { -// if self.config.stackDepthEffect { -// AppContext.appWindow?.transform = .identity -// AppContext.appWindow?.layer.cornerRadius = 0 -// } -// } completion: { done in -// completion?() -// } -// } -// -//} diff --git a/Sources/ProHUD/Capsule/CapsuleProvider.swift b/Sources/ProHUD/Capsule/CapsuleProvider.swift new file mode 100644 index 0000000..1a0bd9a --- /dev/null +++ b/Sources/ProHUD/Capsule/CapsuleProvider.swift @@ -0,0 +1,53 @@ +// +// CapsuleProvider.swift +// +// +// Created by xaoxuu on 2023/8/18. +// + +import UIKit + +open class CapsuleProvider: HUDProvider { + + /// 根据ViewModel和自定义的初始化代码创建一个Target并显示 + /// - Parameters: + /// - vm: 数据模型 + /// - initializer: 初始化代码 + @discardableResult public required init(_ vm: ViewModel?, initializer: ((_ capsule: Target) -> Void)?) { + super.init(vm, initializer: initializer) + } + + @discardableResult public required convenience init(initializer: ((_ capsule: Target) -> Void)?) { + self.init(nil, initializer: initializer) + } + + /// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例 + /// - Parameters: + /// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符) + /// - handler: 实例创建代码 + public static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ capsule: CapsuleTarget) -> Void, onExists: ((_ capsule: CapsuleTarget) -> Void)? = nil) { + let id = identifier ?? (file + "#\(line)") + if let vc = find(identifier: id).last { + vc.update(handler: onExists ?? handler) + } else { + Self.init { capsule in + capsule.identifier = id + handler(capsule) + } + } + } + + /// 查找HUD实例 + /// - Parameter identifier: 唯一标识符 + /// - Returns: HUD实例 + @discardableResult public static func find(identifier: String, update handler: ((_ capsule: CapsuleTarget) -> Void)? = nil) -> [CapsuleTarget] { + let arr = AppContext.capsuleWindows.values.flatMap({ $0.values }).compactMap({ $0.capsule }).filter({ $0.identifier == identifier }) + if let handler = handler { + arr.forEach({ $0.update(handler: handler) }) + } + return arr + } + +} + +public typealias Capsule = CapsuleProvider diff --git a/Sources/ProHUD/Capsule/Capsule.swift b/Sources/ProHUD/Capsule/CapsuleTarget.swift similarity index 51% rename from Sources/ProHUD/Capsule/Capsule.swift rename to Sources/ProHUD/Capsule/CapsuleTarget.swift index bb22727..ff465f3 100644 --- a/Sources/ProHUD/Capsule/Capsule.swift +++ b/Sources/ProHUD/Capsule/CapsuleTarget.swift @@ -7,11 +7,11 @@ import UIKit -open class Capsule: Controller { +open class CapsuleTarget: BaseController, HUDTargetType { - public lazy var config: Configuration = { - var cfg = Configuration() - Configuration.customShared?(cfg) + public lazy var config: CapsuleConfiguration = { + var cfg = CapsuleConfiguration() + CapsuleConfiguration.customGlobalConfig?(cfg) return cfg }() @@ -33,6 +33,8 @@ open class Capsule: Controller { return imgv }() + public var progressView: ProgressView? + /// 文本 public lazy var textLabel: UILabel = { let lb = UILabel() @@ -44,56 +46,12 @@ open class Capsule: Controller { return lb }() - @objc(CapsuleViewModel) open class ViewModel: BaseViewModel { - - @objc public enum Position: Int { - case top - case middle - case bottom - } - - @objc public var position: Position = .top - - public func position(position: Position) -> Self { - self.position = position - return self - } - public static var top: Self { - let obj = Self.init() - obj.position = .top - return obj - } - public static var middle: Self { - let obj = Self.init() - obj.position = .middle - return obj - } - public static var bottom: Self { - let obj = Self.init() - obj.position = .bottom - return obj - } - - } + public var vm: CapsuleViewModel = .init() - /// 视图模型 - @objc public var vm = ViewModel() + private var tapActionCallback: ((_ capsule: CapsuleTarget) -> Void)? - private var tapActionCallback: ((_ capsule: Capsule) -> Void)? - - @discardableResult @objc public init(_ vm: ViewModel, handler: ((_ capsule: Capsule) -> Void)? = nil) { + required public override init() { super.init() - self.vm = vm - handler?(self) - DispatchQueue.main.async { - if handler != nil { - self.push() - } - } - } - - @discardableResult @objc public convenience init(handler: ((_ capsule: Capsule) -> Void)?) { - self.init(.init(), handler: handler) } required public init?(coder: NSCoder) { @@ -102,7 +60,6 @@ open class Capsule: Controller { public override func viewDidLoad() { super.viewDidLoad() - view.tintColor = vm.tintColor ?? config.tintColor view.layer.shadowRadius = 8 view.layer.shadowOffset = .init(width: 0, height: 5) view.layer.shadowOpacity = 0.1 @@ -117,13 +74,13 @@ open class Capsule: Controller { } - public func onTapped(action: @escaping (_ capsule: Capsule) -> Void) { + public func onTapped(action: @escaping (_ capsule: CapsuleTarget) -> Void) { self.tapActionCallback = action } } -fileprivate extension Capsule { +fileprivate extension CapsuleTarget { /// 点击事件 /// - Parameter sender: 手势 @@ -132,3 +89,6 @@ fileprivate extension Capsule { } } + + +extension CapsuleTarget: LoadingAnimation { } diff --git a/Sources/ProHUD/Capsule/CapsuleViewModel.swift b/Sources/ProHUD/Capsule/CapsuleViewModel.swift new file mode 100644 index 0000000..5c5157b --- /dev/null +++ b/Sources/ProHUD/Capsule/CapsuleViewModel.swift @@ -0,0 +1,40 @@ +// +// CapsuleViewModel.swift +// +// +// Created by xaoxuu on 2023/8/18. +// + +import UIKit + +@objc open class CapsuleViewModel: BaseViewModel { + + @objc public enum Position: Int { + case top + case middle + case bottom + } + + @objc public var position: Position = .top + + public func position(position: Position) -> Self { + self.position = position + return self + } + public static var top: Self { + let obj = Self.init() + obj.position = .top + return obj + } + public static var middle: Self { + let obj = Self.init() + obj.position = .middle + return obj + } + public static var bottom: Self { + let obj = Self.init() + obj.position = .bottom + return obj + } + +} diff --git a/Sources/ProHUD/Capsule/CapsuleWindow.swift b/Sources/ProHUD/Capsule/CapsuleWindow.swift index 87f1d06..3d986f4 100644 --- a/Sources/ProHUD/Capsule/CapsuleWindow.swift +++ b/Sources/ProHUD/Capsule/CapsuleWindow.swift @@ -9,9 +9,9 @@ import UIKit class CapsuleWindow: Window { - var capsule: Capsule + var capsule: CapsuleTarget - init(capsule: Capsule) { + init(capsule: CapsuleTarget) { self.capsule = capsule super.init(frame: .zero) windowScene = AppContext.windowScene @@ -36,7 +36,7 @@ class CapsuleWindow: Window { } -extension Capsule { +extension CapsuleTarget { var attachedWindow: CapsuleWindow? { view.window as? CapsuleWindow } diff --git a/Sources/ProHUD/Core/Controllers/Controller.swift b/Sources/ProHUD/Core/Controllers/BaseController.swift similarity index 86% rename from Sources/ProHUD/Core/Controllers/Controller.swift rename to Sources/ProHUD/Core/Controllers/BaseController.swift index ac8a830..9564294 100644 --- a/Sources/ProHUD/Core/Controllers/Controller.swift +++ b/Sources/ProHUD/Core/Controllers/BaseController.swift @@ -1,13 +1,13 @@ // -// Controller.swift -// +// BaseController.swift +// // // Created by xaoxuu on 2022/8/29. // import UIKit -open class Controller: UIViewController { +open class BaseController: UIViewController { /// ID标识 public var identifier = String(Date().timeIntervalSince1970) @@ -56,29 +56,29 @@ open class Controller: UIViewController { case onTappedBackground = "onTappedBackground" } - var navEvents = [NavEvent: ((Controller) -> Void)]() + var navEvents = [NavEvent: ((BaseController) -> Void)]() - @discardableResult public func onViewDidLoad(_ callback: ((_ vc: Controller) -> Void)?) -> Controller { + @discardableResult public func onViewDidLoad(_ callback: ((_ vc: BaseController) -> Void)?) -> BaseController { navEvents[.onViewDidLoad] = callback return self } - @discardableResult public func onViewWillAppear(_ callback: ((_ vc: Controller) -> Void)?) -> Controller { + @discardableResult public func onViewWillAppear(_ callback: ((_ vc: BaseController) -> Void)?) -> BaseController { navEvents[.onViewWillAppear] = callback return self } - @discardableResult public func onViewDidAppear(_ callback: ((_ vc: Controller) -> Void)?) -> Controller { + @discardableResult public func onViewDidAppear(_ callback: ((_ vc: BaseController) -> Void)?) -> BaseController { navEvents[.onViewDidAppear] = callback return self } - @discardableResult public func onViewWillDisappear(_ callback: ((_ vc: Controller) -> Void)?) -> Controller { + @discardableResult public func onViewWillDisappear(_ callback: ((_ vc: BaseController) -> Void)?) -> BaseController { navEvents[.onViewWillDisappear] = callback return self } - @discardableResult public func onViewDidDisappear(_ callback: ((_ vc: Controller) -> Void)?) -> Controller { + @discardableResult public func onViewDidDisappear(_ callback: ((_ vc: BaseController) -> Void)?) -> BaseController { navEvents[.onViewDidDisappear] = callback return self } @@ -91,7 +91,7 @@ open class Controller: UIViewController { } // MARK: - 事件 -extension Controller { +extension BaseController { func addTouchUpAction(for button: UIButton, action: @escaping () -> Void) { button.addTarget(self, action: #selector(didTappedButton(_:)), for: .touchUpInside) diff --git a/Sources/ProHUD/Core/Models/Action.swift b/Sources/ProHUD/Core/Models/Action.swift index 23775aa..ffe88aa 100644 --- a/Sources/ProHUD/Core/Models/Action.swift +++ b/Sources/ProHUD/Core/Models/Action.swift @@ -37,9 +37,9 @@ open class Action: NSObject { open var title: String? - open var handler: ((_ vc: HUD) -> Void)? + open var handler: ((_ vc: HUDControllerType) -> Void)? - public init(identifier: String? = nil, style: Style = .tinted, title: String?, handler: ((_ vc: HUD) -> Void)? = nil) { + public init(identifier: String? = nil, style: Style = .tinted, title: String?, handler: ((_ vc: HUDControllerType) -> Void)? = nil) { self.identifier = identifier self.style = style self.title = title diff --git a/Sources/ProHUD/Core/Models/BaseViewModel.swift b/Sources/ProHUD/Core/Models/BaseViewModel.swift index 5ff2f68..689e825 100644 --- a/Sources/ProHUD/Core/Models/BaseViewModel.swift +++ b/Sources/ProHUD/Core/Models/BaseViewModel.swift @@ -7,8 +7,10 @@ import UIKit +public protocol HUDViewModelType {} + /// 数据模型 -open class BaseViewModel: NSObject { +open class BaseViewModel: NSObject, HUDViewModelType { /// 图标 @objc open var icon: UIImage? diff --git a/Sources/ProHUD/Core/Models/Configuration.swift b/Sources/ProHUD/Core/Models/Configuration.swift index 9e31419..655bd04 100644 --- a/Sources/ProHUD/Core/Models/Configuration.swift +++ b/Sources/ProHUD/Core/Models/Configuration.swift @@ -7,7 +7,7 @@ import UIKit -public class Configuration: NSObject { +open class CommonConfiguration: NSObject { /// 全局功能开关 public static var isEnabled: Bool = true @@ -171,11 +171,11 @@ public class Configuration: NSObject { customWebImage = handler } - var customReloadData: ((_ vc: Controller) -> Bool)? + var customReloadData: ((_ vc: BaseController) -> Bool)? /// 自定义刷新规则( ⚠️ 自定义此函数之后,整个容器将不再走默认布局规则,可实现完全自定义) /// - Parameter callback: 自定义刷新规则代码 - public func reloadData(_ callback: @escaping (_ vc: Controller) -> Bool) { + public func reloadData(_ callback: @escaping (_ vc: BaseController) -> Bool) { customReloadData = callback } @@ -188,5 +188,9 @@ public class Configuration: NSObject { customContentViewMask = callback } + override init() { + + } + } diff --git a/Sources/ProHUD/Core/Protocols/CommonLayout.swift b/Sources/ProHUD/Core/Protocols/CommonLayout.swift index af60831..bd652e3 100644 --- a/Sources/ProHUD/Core/Protocols/CommonLayout.swift +++ b/Sources/ProHUD/Core/Protocols/CommonLayout.swift @@ -7,7 +7,7 @@ import Foundation -public protocol CommonLayout: Controller { +public protocol CommonLayout: BaseController { func reloadData() } diff --git a/Sources/ProHUD/Core/Protocols/DefaultLayout.swift b/Sources/ProHUD/Core/Protocols/DefaultLayout.swift index f6021a1..d077ba9 100644 --- a/Sources/ProHUD/Core/Protocols/DefaultLayout.swift +++ b/Sources/ProHUD/Core/Protocols/DefaultLayout.swift @@ -9,7 +9,7 @@ import UIKit protocol DefaultLayout: CommonLayout { - var cfg: Configuration { get } + var cfg: CommonConfiguration { get } func reloadData(animated: Bool) diff --git a/Sources/ProHUD/Core/Protocols/HUD.swift b/Sources/ProHUD/Core/Protocols/HUD.swift deleted file mode 100644 index 8c529a5..0000000 --- a/Sources/ProHUD/Core/Protocols/HUD.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// ProHUD.swift -// -// -// Created by xaoxuu on 2022/8/29. -// - -import UIKit - -@objc public protocol HUD { - @objc func push() - @objc func pop() -} - -//public extension HUD { -// func push(workspace: Workspace?) { -// AppContext.workspace = workspace -// push() -// } -//} diff --git a/Sources/ProHUD/Core/Protocols/LoadingAnimation.swift b/Sources/ProHUD/Core/Protocols/LoadingAnimation.swift index 1a08e02..2d0575e 100644 --- a/Sources/ProHUD/Core/Protocols/LoadingAnimation.swift +++ b/Sources/ProHUD/Core/Protocols/LoadingAnimation.swift @@ -8,7 +8,7 @@ import UIKit /// 加载动画 -public protocol LoadingAnimation: Controller { +public protocol LoadingAnimation: BaseController { var imageView: UIImageView { get } var progressView: ProgressView? { get set } diff --git a/Sources/ProHUD/Core/Protocols/Provider.swift b/Sources/ProHUD/Core/Protocols/Provider.swift new file mode 100644 index 0000000..7246a4a --- /dev/null +++ b/Sources/ProHUD/Core/Protocols/Provider.swift @@ -0,0 +1,64 @@ +// +// Provider.swift +// +// +// Created by xaoxuu on 2023/8/18. +// + +import UIKit + +public protocol HUDProviderType { + + associatedtype ViewModel = HUDViewModelType + associatedtype Target = HUDTargetType + + /// 根据ViewModel和自定义的初始化代码创建一个Target并显示 + /// - Parameters: + /// - vm: 数据模型 + /// - initializer: 初始化代码 + @discardableResult init(_ vm: ViewModel?, initializer: ((_ target: Target) -> Void)?) + +} + +open class HUDProvider: HUDProviderType { + + /// HUD实例 + public var target: Target + + /// 根据ViewModel和自定义的初始化代码创建一个Target并显示 + /// - Parameters: + /// - vm: 数据模型 + /// - initializer: 初始化代码 + @discardableResult public required init(_ vm: ViewModel?, initializer: ((_ target: Target) -> Void)?) { + var t = Target() + if let vm = vm as? Target.ViewModel { + t.vm = vm + } + initializer?(t) + self.target = t + if (vm == nil && initializer == nil) == false { + DispatchQueue.main.async { + t.push() + } + } + } + + /// 根据自定义的初始化代码创建一个Target并显示 + /// - Parameter initializer: 初始化代码 + @discardableResult public convenience init(initializer: ((_ target: Target) -> Void)?) { + self.init(nil, initializer: initializer) + } + + /// 根据ViewModel创建一个Target并显示 + /// - Parameter vm: 数据模型 + @discardableResult public convenience init(_ vm: ViewModel?) { + self.init(vm, initializer: nil) + } + + /// 创建一个空白的实例,不立即显示,需要手动调用target.push()来显示 + @discardableResult public convenience init() { + self.init(nil, initializer: nil) + } + + +} diff --git a/Sources/ProHUD/Core/Protocols/Target.swift b/Sources/ProHUD/Core/Protocols/Target.swift new file mode 100644 index 0000000..82749e2 --- /dev/null +++ b/Sources/ProHUD/Core/Protocols/Target.swift @@ -0,0 +1,19 @@ +// +// Target.swift +// +// +// Created by xaoxuu on 2023/8/18. +// + +import UIKit + +@objc public protocol HUDControllerType { + @objc func push() + @objc func pop() +} + +public protocol HUDTargetType: HUDControllerType { + associatedtype ViewModel = HUDViewModelType + var vm: ViewModel { get set } + init() +} diff --git a/Sources/ProHUD/Core/Utils/AppContext.swift b/Sources/ProHUD/Core/Utils/AppContext.swift index 0071857..6db9658 100644 --- a/Sources/ProHUD/Core/Utils/AppContext.swift +++ b/Sources/ProHUD/Core/Utils/AppContext.swift @@ -36,7 +36,7 @@ public struct AppContext { static var toastWindows: [UIWindowScene: [ToastWindow]] = [:] static var alertWindow: [UIWindowScene: AlertWindow] = [:] static var sheetWindows: [UIWindowScene: [SheetWindow]] = [:] - static var capsuleWindows: [UIWindowScene: [Capsule.ViewModel.Position: CapsuleWindow]] = [:] + static var capsuleWindows: [UIWindowScene: [CapsuleTarget.ViewModel.Position: CapsuleWindow]] = [:] static var current: AppContext? { guard let windowScene = windowScene else { return nil } @@ -120,7 +120,7 @@ extension AppContext { var toastWindows: [ToastWindow] { Self.toastWindows[windowScene] ?? [] } - var capsuleWindows: [Capsule.ViewModel.Position: CapsuleWindow] { + var capsuleWindows: [CapsuleTarget.ViewModel.Position: CapsuleWindow] { Self.capsuleWindows[windowScene] ?? [:] } } diff --git a/Sources/ProHUD/Core/Utils/ConsolePrint.swift b/Sources/ProHUD/Core/Utils/ConsolePrint.swift index 768b085..917ef5f 100644 --- a/Sources/ProHUD/Core/Utils/ConsolePrint.swift +++ b/Sources/ProHUD/Core/Utils/ConsolePrint.swift @@ -9,7 +9,7 @@ import Foundation func consolePrint(_ items: Any...) { #if DEBUG - guard Configuration.enablePrint else { return } + guard CommonConfiguration.enablePrint else { return } print(">> ProHUD:", items) #endif } diff --git a/Sources/ProHUD/Core/Utils/RotateAnimation.swift b/Sources/ProHUD/Core/Utils/RotateAnimation.swift index dac7fbb..4ad6693 100644 --- a/Sources/ProHUD/Core/Utils/RotateAnimation.swift +++ b/Sources/ProHUD/Core/Utils/RotateAnimation.swift @@ -59,7 +59,7 @@ extension LoadingAnimation { } /// 动画扩展 -extension Controller { +extension BaseController { @objc func pauseLoadingAnimation() { if let layer = animateLayer { animation = layer.animation(forKey: .rotateKey) diff --git a/Sources/ProHUD/Core/Views/Button.swift b/Sources/ProHUD/Core/Views/Button.swift index 00c67ff..bf234a4 100644 --- a/Sources/ProHUD/Core/Views/Button.swift +++ b/Sources/ProHUD/Core/Views/Button.swift @@ -29,7 +29,7 @@ open class Button: UIButton { fatalError("init(coder:) has not been implemented") } - public convenience init(config: ProHUD.Configuration, action: Action) { + public convenience init(config: CommonConfiguration, action: Action) { self.init(frame: .zero) layer.cornerRadiusWithContinuous = 8 self.update(config: config, action: action) @@ -37,7 +37,7 @@ open class Button: UIButton { /// 更新按钮 /// - Parameter style: 样式 - open func update(config: ProHUD.Configuration, action: Action) { + open func update(config: CommonConfiguration, action: Action) { self.action = action setTitle(action.title, for: .normal) switch action.style { diff --git a/Sources/ProHUD/Sheet/SheetButton.swift b/Sources/ProHUD/Sheet/SheetButton.swift index 6a0d745..7d7efbc 100644 --- a/Sources/ProHUD/Sheet/SheetButton.swift +++ b/Sources/ProHUD/Sheet/SheetButton.swift @@ -13,7 +13,7 @@ public class SheetButton: Button { .init(top: 14, left: 28, bottom: 14, right: 28) } - public override func update(config: ProHUD.Configuration, action: Action) { + public override func update(config: CommonConfiguration, action: Action) { titleLabel?.font = .boldSystemFont(ofSize: 18) layer.cornerRadiusWithContinuous = 12 super.update(config: config, action: action) diff --git a/Sources/ProHUD/Sheet/SheetConfiguration.swift b/Sources/ProHUD/Sheet/SheetConfiguration.swift index 17067dc..58665df 100644 --- a/Sources/ProHUD/Sheet/SheetConfiguration.swift +++ b/Sources/ProHUD/Sheet/SheetConfiguration.swift @@ -7,61 +7,57 @@ import UIKit -public extension Sheet { +public class SheetConfiguration: CommonConfiguration { - class Configuration: ProHUD.Configuration { - - /// 堆叠效果 - public var stackDepthEffect: Bool = false - - /// 卡片距离屏幕的间距 - public var windowEdgeInset: CGFloat = 16 - - /// 是否是全屏的页面 - public var isFullScreen = false - - /// 副标题字体 - - var customSubtitleLabel: ((_ label: UILabel) -> Void)? - - public func customSubtitleLabel(handler: @escaping (_ label: UILabel) -> Void) { - customSubtitleLabel = handler - } - - static var customShared: ((_ config: Configuration) -> Void)? - - /// 共享配置(只能设置一次,影响所有实例) - /// - Parameter callback: 配置代码 - public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) { - customShared = callback - } - - var customBackgroundViewMask: ((_ mask: UIView) -> Void)? - - /// 设置背景蒙版 - /// - Parameter callback: 自定义内容卡片蒙版代码 - public func backgroundViewMask(_ callback: @escaping (_ mask: UIView) -> Void) { - customBackgroundViewMask = callback - } - - override var cardEdgeInsetsByDefault: UIEdgeInsets { - cardEdgeInsets ?? .init(top: 24, left: 24, bottom: 24, right: 24) - } - - override var cardMaxWidthByDefault: CGFloat { cardMaxWidth ?? 500 } - - override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? (AppContext.appBounds.height - 50) } - - override var animateDurationForBuildInByDefault: CGFloat { - animateDurationForBuildIn ?? 0.5 - } - - override var animateDurationForBuildOutByDefault: CGFloat { - animateDurationForBuildOut ?? 0.5 - } - - override var cardCornerRadiusByDefault: CGFloat { cardCornerRadius ?? 32 } - + /// 堆叠效果 + public var stackDepthEffect: Bool = false + + /// 卡片距离屏幕的间距 + public var windowEdgeInset: CGFloat = 16 + + /// 是否是全屏的页面 + public var isFullScreen = false + + /// 副标题字体 + + var customSubtitleLabel: ((_ label: UILabel) -> Void)? + + public func customSubtitleLabel(handler: @escaping (_ label: UILabel) -> Void) { + customSubtitleLabel = handler } + static var customGlobalConfig: ((_ config: SheetConfiguration) -> Void)? + + /// 全局共享配置(只能设置一次,影响所有实例) + /// - Parameter callback: 配置代码 + public static func global(_ callback: @escaping (_ config: SheetConfiguration) -> Void) { + customGlobalConfig = callback + } + + var customBackgroundViewMask: ((_ mask: UIView) -> Void)? + + /// 设置背景蒙版 + /// - Parameter callback: 自定义内容卡片蒙版代码 + public func backgroundViewMask(_ callback: @escaping (_ mask: UIView) -> Void) { + customBackgroundViewMask = callback + } + + override var cardEdgeInsetsByDefault: UIEdgeInsets { + cardEdgeInsets ?? .init(top: 24, left: 24, bottom: 24, right: 24) + } + + override var cardMaxWidthByDefault: CGFloat { cardMaxWidth ?? 500 } + + override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? (AppContext.appBounds.height - 50) } + + override var animateDurationForBuildInByDefault: CGFloat { + animateDurationForBuildIn ?? 0.5 + } + + override var animateDurationForBuildOutByDefault: CGFloat { + animateDurationForBuildOut ?? 0.5 + } + + override var cardCornerRadiusByDefault: CGFloat { cardCornerRadius ?? 32 } + } diff --git a/Sources/ProHUD/Sheet/SheetConvenienceLayout.swift b/Sources/ProHUD/Sheet/SheetConvenienceLayout.swift index 151afac..7f9c434 100644 --- a/Sources/ProHUD/Sheet/SheetConvenienceLayout.swift +++ b/Sources/ProHUD/Sheet/SheetConvenienceLayout.swift @@ -7,7 +7,7 @@ import UIKit -extension Sheet: ConvenienceLayout { +extension SheetTarget: ConvenienceLayout { // MARK: 增加 @discardableResult public func add(action: Action) -> Button { @@ -126,7 +126,7 @@ extension Sheet: ConvenienceLayout { } // MARK: more -public extension Sheet { +public extension SheetTarget { /// 增加一个标题 /// - Parameter text: 文本 @@ -185,10 +185,10 @@ public extension Sheet { /// - identifier: 唯一标识符 /// - handler: 点击事件 /// - Returns: 按钮实例 - @discardableResult func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ sheet: Sheet) -> Void)? = nil) -> Button { + @discardableResult func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ sheet: SheetTarget) -> Void)? = nil) -> Button { if let handler = handler { let action = Action(identifier: identifier, style: style, title: title) { vc in - if let vc = vc as? Sheet { + if let vc = vc as? SheetTarget { handler(vc) } } diff --git a/Sources/ProHUD/Sheet/SheetDefaultLayout.swift b/Sources/ProHUD/Sheet/SheetDefaultLayout.swift index 8885dce..15a89e3 100644 --- a/Sources/ProHUD/Sheet/SheetDefaultLayout.swift +++ b/Sources/ProHUD/Sheet/SheetDefaultLayout.swift @@ -7,9 +7,9 @@ import UIKit -extension Sheet: DefaultLayout { +extension SheetTarget: DefaultLayout { - var cfg: ProHUD.Configuration { + var cfg: CommonConfiguration { return config } @@ -17,6 +17,7 @@ extension Sheet: DefaultLayout { if self.cfg.customReloadData?(self) == true { return } + view.tintColor = vm.tintColor ?? config.tintColor // background if backgroundView.superview == nil { view.insertSubview(backgroundView, at: 0) diff --git a/Sources/ProHUD/Sheet/SheetManager.swift b/Sources/ProHUD/Sheet/SheetManager.swift index 037718b..b726ac0 100644 --- a/Sources/ProHUD/Sheet/SheetManager.swift +++ b/Sources/ProHUD/Sheet/SheetManager.swift @@ -7,10 +7,10 @@ import UIKit -extension Sheet: HUD { +extension SheetTarget { @objc open func push() { - guard Configuration.isEnabled else { return } + guard SheetConfiguration.isEnabled else { return } let isNew: Bool let window: SheetWindow var windows = AppContext.current?.sheetWindows ?? [] @@ -57,29 +57,9 @@ extension Sheet: HUD { } } -} - -public extension Sheet { - - /// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例 - /// - Parameters: - /// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符) - /// - handler: 实例创建代码 - static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ sheet: Sheet) -> Void, onExists: ((_ sheet: Sheet) -> Void)? = nil) { - let id = identifier ?? (file + "#\(line)") - if let vc = find(identifier: id).last { - vc.update(handler: onExists ?? handler) - } else { - Sheet { sheet in - sheet.identifier = id - handler(sheet) - } - } - } - /// 更新HUD实例 /// - Parameter handler: 实例更新代码 - func update(handler: @escaping (_ sheet: Sheet) -> Void) { + @objc open func update(handler: @escaping (_ sheet: SheetTarget) -> Void) { handler(self) reloadData() UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) { @@ -87,21 +67,9 @@ public extension Sheet { } } - /// 查找HUD实例 - /// - Parameter identifier: 唯一标识符 - /// - Returns: HUD实例 - @discardableResult static func find(identifier: String, update handler: ((_ sheet: Sheet) -> Void)? = nil) -> [Sheet] { - let arr = AppContext.sheetWindows.values.flatMap({ $0 }).compactMap({ $0.sheet }).filter({ $0.identifier == identifier }) - if let handler = handler { - arr.forEach({ $0.update(handler: handler) }) - } - return arr - } - } - -extension Sheet { +extension SheetTarget { func translateIn(completion: (() -> Void)?) { UIView.animateEaseOut(duration: config.animateDurationForBuildInByDefault) { diff --git a/Sources/ProHUD/Sheet/SheetProvider.swift b/Sources/ProHUD/Sheet/SheetProvider.swift new file mode 100644 index 0000000..8e5a9ec --- /dev/null +++ b/Sources/ProHUD/Sheet/SheetProvider.swift @@ -0,0 +1,48 @@ +// +// SheetProvider.swift +// +// +// Created by xaoxuu on 2023/8/18. +// + +import UIKit + +open class SheetProvider: HUDProvider { + @discardableResult public required init(_ vm: ViewModel?, initializer: ((_ sheet: Target) -> Void)?) { + super.init(vm, initializer: initializer) + } + + @discardableResult public required convenience init(initializer: ((_ sheet: Target) -> Void)?) { + self.init(nil, initializer: initializer) + } + + /// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例 + /// - Parameters: + /// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符) + /// - handler: 实例创建代码 + public static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ sheet: SheetTarget) -> Void, onExists: ((_ sheet: SheetTarget) -> Void)? = nil) { + let id = identifier ?? (file + "#\(line)") + if let vc = find(identifier: id).last { + vc.update(handler: onExists ?? handler) + } else { + Self.init { sheet in + sheet.identifier = id + handler(sheet) + } + } + } + + /// 查找HUD实例 + /// - Parameter identifier: 唯一标识符 + /// - Returns: HUD实例 + @discardableResult public static func find(identifier: String, update handler: ((_ sheet: SheetTarget) -> Void)? = nil) -> [SheetTarget] { + let arr = AppContext.sheetWindows.values.flatMap({ $0 }).compactMap({ $0.sheet }).filter({ $0.identifier == identifier }) + if let handler = handler { + arr.forEach({ $0.update(handler: handler) }) + } + return arr + } + +} + +public typealias Sheet = SheetProvider diff --git a/Sources/ProHUD/Sheet/Sheet.swift b/Sources/ProHUD/Sheet/SheetTarget.swift similarity index 69% rename from Sources/ProHUD/Sheet/Sheet.swift rename to Sources/ProHUD/Sheet/SheetTarget.swift index c35c08d..738455a 100644 --- a/Sources/ProHUD/Sheet/Sheet.swift +++ b/Sources/ProHUD/Sheet/SheetTarget.swift @@ -7,13 +7,13 @@ import UIKit -open class Sheet: Controller { +open class SheetTarget: BaseController, HUDTargetType { weak var window: SheetWindow? - public lazy var config: Configuration = { - var cfg = Configuration() - Configuration.customShared?(cfg) + public lazy var config: SheetConfiguration = { + var cfg = SheetConfiguration() + SheetConfiguration.customGlobalConfig?(cfg) return cfg }() @@ -33,7 +33,7 @@ open class Sheet: Controller { return stack }() - private var onTappedBackground: ((_ sheet: Sheet) -> Void)? + private var tapActionCallback: ((_ sheet: SheetTarget) -> Void)? public override var title: String? { didSet { @@ -41,16 +41,10 @@ open class Sheet: Controller { } } - @discardableResult @objc public init(handler: @escaping (_ sheet: Sheet) -> Void, onTappedBackground action: ((_ sheet: Sheet) -> Void)? = nil) { + public var vm: SheetViewModel = .init() + + required public override init() { super.init() - - onTappedBackground = action - handler(self) - - DispatchQueue.main.async { - self.push() - } - } required public init?(coder: NSCoder) { @@ -59,7 +53,6 @@ open class Sheet: Controller { public override func viewDidLoad() { super.viewDidLoad() - view.tintColor = config.tintColor // 点击 let tap = UITapGestureRecognizer(target: self, action: #selector(_onTappedBackground(_:))) @@ -73,11 +66,15 @@ open class Sheet: Controller { } + public func onTappedBackground(action: @escaping (_ sheet: SheetTarget) -> Void) { + self.tapActionCallback = action + } + } -extension Sheet { +extension SheetTarget { @objc func _onTappedBackground(_ sender: UITapGestureRecognizer) { - if let act = onTappedBackground { + if let act = tapActionCallback { act(self) } else { self.pop() diff --git a/Sources/ProHUD/Sheet/SheetViewModel.swift b/Sources/ProHUD/Sheet/SheetViewModel.swift new file mode 100644 index 0000000..e55e2a2 --- /dev/null +++ b/Sources/ProHUD/Sheet/SheetViewModel.swift @@ -0,0 +1,10 @@ +// +// SheetViewModel.swift +// +// +// Created by xaoxuu on 2023/8/18. +// + +import UIKit + +@objc open class SheetViewModel: BaseViewModel {} diff --git a/Sources/ProHUD/Sheet/SheetWindow.swift b/Sources/ProHUD/Sheet/SheetWindow.swift index def4164..a1f34c1 100644 --- a/Sources/ProHUD/Sheet/SheetWindow.swift +++ b/Sources/ProHUD/Sheet/SheetWindow.swift @@ -9,9 +9,9 @@ import UIKit class SheetWindow: Window { - var sheet: Sheet + var sheet: SheetTarget - init(sheet: Sheet) { + init(sheet: SheetTarget) { self.sheet = sheet if let scene = AppContext.windowScene { super.init(windowScene: scene) @@ -29,7 +29,7 @@ class SheetWindow: Window { } -extension Sheet { +extension SheetTarget { func getContextWindows() -> [SheetWindow] { guard let windowScene = windowScene else { return [] diff --git a/Sources/ProHUD/Toast/ToastButton.swift b/Sources/ProHUD/Toast/ToastButton.swift index 523d7b1..8630892 100644 --- a/Sources/ProHUD/Toast/ToastButton.swift +++ b/Sources/ProHUD/Toast/ToastButton.swift @@ -13,7 +13,7 @@ public class ToastButton: Button { .init(top: 10, left: 24, bottom: 10, right: 24) } - public override func update(config: ProHUD.Configuration, action: Action) { + public override func update(config: CommonConfiguration, action: Action) { titleLabel?.font = .boldSystemFont(ofSize: 15) layer.cornerRadiusWithContinuous = 8 super.update(config: config, action: action) diff --git a/Sources/ProHUD/Toast/ToastConfiguration.swift b/Sources/ProHUD/Toast/ToastConfiguration.swift index 5c25585..7ac28e1 100644 --- a/Sources/ProHUD/Toast/ToastConfiguration.swift +++ b/Sources/ProHUD/Toast/ToastConfiguration.swift @@ -7,51 +7,46 @@ import UIKit -public extension Toast { +public class ToastConfiguration: CommonConfiguration { - class Configuration: ProHUD.Configuration { - - /// 元素与元素之间的距离 - public var margin = CGFloat(8) - - var customInfoStack: ((_ stack: StackView) -> Void)? - public func customInfoStack(handler: @escaping (_ stack: StackView) -> Void) { - customInfoStack = handler - } - /// 行间距 - public var lineSpace = CGFloat(4) - - static var customShared: ((_ config: Configuration) -> Void)? - - /// 共享配置(只能设置一次,影响所有实例) - /// - Parameter callback: 配置代码 - public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) { - customShared = callback - } - - /// 距离窗口左右的间距 - public var windowEdgeInset: CGFloat? - var windowEdgeInsetByDefault: CGFloat { - windowEdgeInset ?? 16 - } - - override var cardMaxWidthByDefault: CGFloat { - cardMaxWidth ?? 500 - } - - override var cardMaxHeightByDefault: CGFloat { - cardMaxHeight ?? (AppContext.appBounds.height / 3) - } - - override var animateDurationForBuildInByDefault: CGFloat { - animateDurationForBuildIn ?? 0.8 - } - - override var animateDurationForBuildOutByDefault: CGFloat { - animateDurationForBuildIn ?? 0.8 - } - + /// 元素与元素之间的距离 + public var margin = CGFloat(8) + + var customInfoStack: ((_ stack: StackView) -> Void)? + public func customInfoStack(handler: @escaping (_ stack: StackView) -> Void) { + customInfoStack = handler + } + /// 行间距 + public var lineSpace = CGFloat(4) + + static var customGlobalConfig: ((_ config: ToastConfiguration) -> Void)? + + /// 全局共享配置(只能设置一次,影响所有实例) + /// - Parameter callback: 配置代码 + public static func global(_ callback: @escaping (_ config: ToastConfiguration) -> Void) { + customGlobalConfig = callback + } + + /// 距离窗口左右的间距 + public var windowEdgeInset: CGFloat? + var windowEdgeInsetByDefault: CGFloat { + windowEdgeInset ?? 16 + } + + override var cardMaxWidthByDefault: CGFloat { + cardMaxWidth ?? 500 + } + + override var cardMaxHeightByDefault: CGFloat { + cardMaxHeight ?? (AppContext.appBounds.height / 3) + } + + override var animateDurationForBuildInByDefault: CGFloat { + animateDurationForBuildIn ?? 0.8 + } + + override var animateDurationForBuildOutByDefault: CGFloat { + animateDurationForBuildIn ?? 0.8 } } - diff --git a/Sources/ProHUD/Toast/ToastConvenienceLayout.swift b/Sources/ProHUD/Toast/ToastConvenienceLayout.swift index c6b98df..3e5c881 100644 --- a/Sources/ProHUD/Toast/ToastConvenienceLayout.swift +++ b/Sources/ProHUD/Toast/ToastConvenienceLayout.swift @@ -7,7 +7,7 @@ import UIKit -extension Toast: ConvenienceLayout { +extension ToastTarget: ConvenienceLayout { // MARK: 增加 @@ -18,10 +18,10 @@ extension Toast: ConvenienceLayout { /// - identifier: 唯一标识符 /// - handler: 点击事件 /// - Returns: 按钮实例 - @discardableResult public func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ toast: Toast) -> Void)? = nil) -> Button { + @discardableResult public func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ toast: ToastTarget) -> Void)? = nil) -> Button { if let handler = handler { let action = Action(identifier: identifier, style: style, title: title) { vc in - if let vc = vc as? Toast { + if let vc = vc as? ToastTarget { handler(vc) } } diff --git a/Sources/ProHUD/Toast/ToastDefaultLayout.swift b/Sources/ProHUD/Toast/ToastDefaultLayout.swift index b98e719..7c9f543 100644 --- a/Sources/ProHUD/Toast/ToastDefaultLayout.swift +++ b/Sources/ProHUD/Toast/ToastDefaultLayout.swift @@ -7,9 +7,9 @@ import UIKit -extension Toast: DefaultLayout { +extension ToastTarget: DefaultLayout { - var cfg: ProHUD.Configuration { + var cfg: CommonConfiguration { return config } @@ -17,6 +17,7 @@ extension Toast: DefaultLayout { if self.cfg.customReloadData?(self) == true { return } + view.tintColor = vm.tintColor ?? config.tintColor loadContentViewIfNeeded() loadContentMaskViewIfNeeded() guard customView == nil else { @@ -117,7 +118,7 @@ extension Toast: DefaultLayout { } -extension Toast { +extension ToastTarget { func loadActionStackIfNeeded() { guard actionStack.arrangedSubviews.count > 0 else { actionStack.removeFromSuperview() @@ -149,4 +150,5 @@ extension Toast { } } + } diff --git a/Sources/ProHUD/Toast/ToastManager.swift b/Sources/ProHUD/Toast/ToastManager.swift index c5eea55..631589e 100644 --- a/Sources/ProHUD/Toast/ToastManager.swift +++ b/Sources/ProHUD/Toast/ToastManager.swift @@ -7,7 +7,7 @@ import UIKit -extension Toast: HUD { +extension ToastTarget { private func calcHeight() -> CGFloat { var height = CGFloat(0) @@ -34,8 +34,9 @@ extension Toast: HUD { height += cardEdgeInsets.top + cardEdgeInsets.bottom return height } + @objc open func push() { - guard Configuration.isEnabled else { return } + guard ToastConfiguration.isEnabled else { return } let isNew: Bool let window: ToastWindow var windows = AppContext.current?.toastWindows ?? [] @@ -104,29 +105,9 @@ extension Toast: HUD { } } -} - -public extension Toast { - - /// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例 - /// - Parameters: - /// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符) - /// - handler: 实例创建代码 - static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ toast: Toast) -> Void, onExists: ((_ toast: Toast) -> Void)? = nil) { - let id = identifier ?? (file + "#\(line)") - if let vc = find(identifier: id).last { - vc.update(handler: onExists ?? handler) - } else { - Toast { toast in - toast.identifier = id - handler(toast) - } - } - } - /// 更新HUD实例 /// - Parameter handler: 实例更新代码 - func update(handler: @escaping (_ toast: Toast) -> Void) { + @objc open func update(handler: @escaping (_ toast: ToastTarget) -> Void) { handler(self) reloadData() UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) { @@ -134,17 +115,6 @@ public extension Toast { } } - /// 查找HUD实例 - /// - Parameter identifier: 唯一标识符 - /// - Returns: HUD实例 - @discardableResult static func find(identifier: String, update handler: ((_ toast: Toast) -> Void)? = nil) -> [Toast] { - let arr = AppContext.toastWindows.values.flatMap({ $0 }).compactMap({ $0.toast }).filter({ $0.identifier == identifier }) - if let handler = handler { - arr.forEach({ $0.update(handler: handler) }) - } - return arr - } - } // MARK: - layout diff --git a/Sources/ProHUD/Toast/ToastProvider.swift b/Sources/ProHUD/Toast/ToastProvider.swift new file mode 100644 index 0000000..b726549 --- /dev/null +++ b/Sources/ProHUD/Toast/ToastProvider.swift @@ -0,0 +1,48 @@ +// +// ToastProvider.swift +// +// +// Created by xaoxuu on 2023/8/18. +// + +import UIKit + +open class ToastProvider: HUDProvider { + @discardableResult public required init(_ vm: ViewModel?, initializer: ((_ toast: Target) -> Void)?) { + super.init(vm, initializer: initializer) + } + + @discardableResult public required convenience init(initializer: ((_ toast: Target) -> Void)?) { + self.init(nil, initializer: initializer) + } + + /// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例 + /// - Parameters: + /// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符) + /// - handler: 实例创建代码 + public static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ toast: ToastTarget) -> Void, onExists: ((_ toast: ToastTarget) -> Void)? = nil) { + let id = identifier ?? (file + "#\(line)") + if let vc = find(identifier: id).last { + vc.update(handler: onExists ?? handler) + } else { + Self.init { toast in + toast.identifier = id + handler(toast) + } + } + } + + /// 查找HUD实例 + /// - Parameter identifier: 唯一标识符 + /// - Returns: HUD实例 + @discardableResult public static func find(identifier: String, update handler: ((_ toast: ToastTarget) -> Void)? = nil) -> [ToastTarget] { + let arr = AppContext.toastWindows.values.flatMap({ $0 }).compactMap({ $0.toast }).filter({ $0.identifier == identifier }) + if let handler = handler { + arr.forEach({ $0.update(handler: handler) }) + } + return arr + } + +} + +public typealias Toast = ToastProvider diff --git a/Sources/ProHUD/Toast/Toast.swift b/Sources/ProHUD/Toast/ToastTarget.swift similarity index 81% rename from Sources/ProHUD/Toast/Toast.swift rename to Sources/ProHUD/Toast/ToastTarget.swift index 11e8152..b94dd70 100644 --- a/Sources/ProHUD/Toast/Toast.swift +++ b/Sources/ProHUD/Toast/ToastTarget.swift @@ -7,13 +7,13 @@ import UIKit -open class Toast: Controller { +open class ToastTarget: BaseController, HUDTargetType { weak var window: ToastWindow? - public lazy var config: Configuration = { - var cfg = Configuration() - Configuration.customShared?(cfg) + public lazy var config: ToastConfiguration = { + var cfg = ToastConfiguration() + ToastConfiguration.customGlobalConfig?(cfg) return cfg }() @@ -83,12 +83,10 @@ open class Toast: Controller { /// 是否可以通过手势移除(向上滑出屏幕) public var isRemovable = true - @objc(ToastViewModel) open class ViewModel: BaseViewModel {} - /// 视图模型 - @objc public var vm = ViewModel() + @objc public var vm = ToastViewModel() - private var tapActionCallback: ((_ toast: Toast) -> Void)? + private var tapActionCallback: ((_ toast: ToastTarget) -> Void)? public override var title: String? { @@ -97,20 +95,8 @@ open class Toast: Controller { } } - - @discardableResult @objc public init(_ vm: ViewModel, handler: ((_ toast: Toast) -> Void)? = nil) { + required public override init() { super.init() - self.vm = vm - handler?(self) - DispatchQueue.main.async { - if handler != nil { - self.push() - } - } - } - - @discardableResult @objc public convenience init(handler: ((_ toast: Toast) -> Void)?) { - self.init(.init(), handler: handler) } required public init?(coder: NSCoder) { @@ -119,7 +105,6 @@ open class Toast: Controller { public override func viewDidLoad() { super.viewDidLoad() - view.tintColor = config.tintColor // 点击 let tap = UITapGestureRecognizer(target: self, action: #selector(_onTappedGesture(_:))) @@ -133,13 +118,13 @@ open class Toast: Controller { } - public func onTapped(action: @escaping (_ toast: Toast) -> Void) { + public func onTapped(action: @escaping (_ toast: ToastTarget) -> Void) { self.tapActionCallback = action } } -fileprivate extension Toast { +fileprivate extension ToastTarget { /// 点击事件 /// - Parameter sender: 手势 @@ -173,4 +158,4 @@ fileprivate extension Toast { } -extension Toast: LoadingAnimation { } +extension ToastTarget: LoadingAnimation { } diff --git a/Sources/ProHUD/Toast/ToastViewModel.swift b/Sources/ProHUD/Toast/ToastViewModel.swift new file mode 100644 index 0000000..ef52a73 --- /dev/null +++ b/Sources/ProHUD/Toast/ToastViewModel.swift @@ -0,0 +1,12 @@ +// +// ToastViewModel.swift +// +// +// Created by xaoxuu on 2023/8/18. +// + +import UIKit + +@objc open class ToastViewModel: BaseViewModel { + +} diff --git a/Sources/ProHUD/Toast/ToastWindow.swift b/Sources/ProHUD/Toast/ToastWindow.swift index 68a336f..11ea671 100644 --- a/Sources/ProHUD/Toast/ToastWindow.swift +++ b/Sources/ProHUD/Toast/ToastWindow.swift @@ -9,11 +9,11 @@ import UIKit class ToastWindow: Window { - var toast: Toast + var toast: ToastTarget var maxY = CGFloat(0) - init(toast: Toast) { + init(toast: ToastTarget) { self.toast = toast super.init(frame: .zero) windowScene = AppContext.windowScene @@ -36,7 +36,7 @@ class ToastWindow: Window { } -extension Toast { +extension ToastTarget { func getContextWindows() -> [ToastWindow] { guard let windowScene = windowScene else { return []