diff --git a/Example/Example/AppDelegate.swift b/Example/Example/AppDelegate.swift index 25ae9a1..e1991e1 100644 --- a/Example/Example/AppDelegate.swift +++ b/Example/Example/AppDelegate.swift @@ -73,3 +73,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } +extension ProHUD.Scene { + static var confirm: ProHUD.Scene { + var scene = ProHUD.Scene(identifier: "confirm") + scene.image = UIImage(named: "ProHUDMessage") + return scene + } + static var delete: ProHUD.Scene { + var scene = ProHUD.Scene(identifier: "delete") + scene.image = UIImage(named: "ProHUDTrash") + scene.title = "确认删除" + scene.message = "此操作不可撤销" + return scene + } + static var buy: ProHUD.Scene { + var scene = ProHUD.Scene(identifier: "buy") + scene.image = UIImage(named: "ProHUDBuy") + scene.title = "确认付款" + scene.message = "一旦购买拒不退款" + return scene + } +} diff --git a/Example/Example/Assets.xcassets/ProHUDBuy.imageset/Contents.json b/Example/Example/Assets.xcassets/ProHUDBuy.imageset/Contents.json new file mode 100644 index 0000000..50c90e6 --- /dev/null +++ b/Example/Example/Assets.xcassets/ProHUDBuy.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ProHUDBuy@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ProHUDBuy@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Example/Assets.xcassets/ProHUDBuy.imageset/ProHUDBuy@2x.png b/Example/Example/Assets.xcassets/ProHUDBuy.imageset/ProHUDBuy@2x.png new file mode 100644 index 0000000..2e97e5c Binary files /dev/null and b/Example/Example/Assets.xcassets/ProHUDBuy.imageset/ProHUDBuy@2x.png differ diff --git a/Example/Example/Assets.xcassets/ProHUDBuy.imageset/ProHUDBuy@3x.png b/Example/Example/Assets.xcassets/ProHUDBuy.imageset/ProHUDBuy@3x.png new file mode 100644 index 0000000..32e5d56 Binary files /dev/null and b/Example/Example/Assets.xcassets/ProHUDBuy.imageset/ProHUDBuy@3x.png differ diff --git a/Example/Example/Assets.xcassets/ProHUDMessage.imageset/Contents.json b/Example/Example/Assets.xcassets/ProHUDMessage.imageset/Contents.json new file mode 100644 index 0000000..d050e76 --- /dev/null +++ b/Example/Example/Assets.xcassets/ProHUDMessage.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ProHUDMessage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ProHUDMessage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Example/Assets.xcassets/ProHUDMessage.imageset/ProHUDMessage@2x.png b/Example/Example/Assets.xcassets/ProHUDMessage.imageset/ProHUDMessage@2x.png new file mode 100644 index 0000000..9b5aa6a Binary files /dev/null and b/Example/Example/Assets.xcassets/ProHUDMessage.imageset/ProHUDMessage@2x.png differ diff --git a/Example/Example/Assets.xcassets/ProHUDMessage.imageset/ProHUDMessage@3x.png b/Example/Example/Assets.xcassets/ProHUDMessage.imageset/ProHUDMessage@3x.png new file mode 100644 index 0000000..a7322dd Binary files /dev/null and b/Example/Example/Assets.xcassets/ProHUDMessage.imageset/ProHUDMessage@3x.png differ diff --git a/Example/Example/Assets.xcassets/ProHUDTrash.imageset/Contents.json b/Example/Example/Assets.xcassets/ProHUDTrash.imageset/Contents.json new file mode 100644 index 0000000..f6faed5 --- /dev/null +++ b/Example/Example/Assets.xcassets/ProHUDTrash.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ProHUDTrash@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ProHUDTrash@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Example/Assets.xcassets/ProHUDTrash.imageset/ProHUDTrash@2x.png b/Example/Example/Assets.xcassets/ProHUDTrash.imageset/ProHUDTrash@2x.png new file mode 100644 index 0000000..d136622 Binary files /dev/null and b/Example/Example/Assets.xcassets/ProHUDTrash.imageset/ProHUDTrash@2x.png differ diff --git a/Example/Example/Assets.xcassets/ProHUDTrash.imageset/ProHUDTrash@3x.png b/Example/Example/Assets.xcassets/ProHUDTrash.imageset/ProHUDTrash@3x.png new file mode 100644 index 0000000..d3f1cce Binary files /dev/null and b/Example/Example/Assets.xcassets/ProHUDTrash.imageset/ProHUDTrash@3x.png differ diff --git a/Example/Example/TestAlertVC.swift b/Example/Example/TestAlertVC.swift index a5122ab..5fea8ff 100644 --- a/Example/Example/TestAlertVC.swift +++ b/Example/Example/TestAlertVC.swift @@ -18,7 +18,11 @@ class TestAlertVC: BaseListVC { } override var titles: [String] { - return ["场景:正在同步(超时)", "场景:同步成功(写法1)", "场景:同步成功(写法2)", "场景:同步失败和重试"] + return ["场景:正在同步(超时)", + "场景:同步成功(写法1)", + "场景:同步成功(写法2)", + "场景:同步失败和重试", + "极限场景:多个弹窗重叠"] } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -28,12 +32,12 @@ class TestAlertVC: BaseListVC { func f() { Alert.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (a) in a.identifier = "loading" - a.animate(rotate: true) + a.rotate() a.didForceQuit { [weak self] in let t = Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻(点击展开为Alert)") { (vm) in vm.identifier = "loading" } - t.animate(rotate: true) + t.rotate() t.didTapped { [weak t] in t?.pop() f() @@ -47,7 +51,7 @@ class TestAlertVC: BaseListVC { } else if row == 1 { Alert.push() { (a) in a.identifier = "loading" - a.animate(rotate: true) + a.rotate() a.update { (vm) in vm.scene = .loading vm.title = "正在同步" @@ -67,7 +71,7 @@ class TestAlertVC: BaseListVC { let a = Alert.push() { (a) in a.identifier = "loading" } - a.animate(rotate: true) + a.rotate() a.update { (vm) in vm.scene = .loading vm.title = "正在同步" @@ -94,7 +98,7 @@ class TestAlertVC: BaseListVC { vm.message = "请稍等片刻" vm.remove(action: 0, 1) } - a.animate(rotate: true) + a.rotate() DispatchQueue.main.asyncAfter(deadline: .now()+2) { a.update { (vm) in vm.scene = .error @@ -109,6 +113,27 @@ class TestAlertVC: BaseListVC { }) } loading() + } else if row == 4 { + func f(_ i: Int) { + Alert.push() { (a) in + a.rotate() + a.update { (vm) in + vm.scene = .loading + vm.title = "正在同步" + String(i) + vm.message = "请稍等片刻" + } + } + } + f(1) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + f(2) + } + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + f(3) + } + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + f(4) + } } } diff --git a/Example/Example/TestGuardVC.swift b/Example/Example/TestGuardVC.swift index d80a6fc..2156478 100644 --- a/Example/Example/TestGuardVC.swift +++ b/Example/Example/TestGuardVC.swift @@ -54,19 +54,27 @@ class TestGuardVC: BaseListVC { vm.add(subTitle: "价格") vm.add(message: "只需一次性付费$2999即可永久享用。") vm.add(action: .destructive, title: "购买") { [weak vc] in - Alert.push(scene: .confirm, title: "确认购买", message: "一旦购买拒不退款") { (vc) in + Alert.push(scene: .buy) { (vc) in vc.identifier = "confirm" vc.update { (vm) in vm.add(action: .destructive, title: "购买") { [weak vc] in vc?.update({ (vm) in - vm.scene = .success - vm.title = "购买成功" - vm.message = "感谢您的支持" - vm.remove(action: 1) - vm.update(action: 0, style: .default, title: "我知道了") { - vc?.pop() - } + vm.scene = .loading + vm.title = "正在付款" + vm.message = "请稍等片刻" + vm.remove(action: 0, 1) }) + vc?.rotate() + DispatchQueue.main.asyncAfter(deadline: .now()+1) { + vc?.update({ (vm) in + vm.scene = .success + vm.title = "购买成功" + vm.message = "感谢您的支持" + vm.add(action: .default, title: "我知道了") { + vc?.pop() + } + }) + } } vm.add(action: .cancel, title: "取消", handler: nil) } diff --git a/Example/Example/TestToastVC.swift b/Example/Example/TestToastVC.swift index d8abb9c..9b4cdb9 100644 --- a/Example/Example/TestToastVC.swift +++ b/Example/Example/TestToastVC.swift @@ -35,7 +35,7 @@ class TestToastVC: BaseListVC { if row == 0 { Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (vm) in vm.identifier = "loading" - }.animate(rotate: true) + }.rotate() simulateSync() } else if row == 1 { let t = Toast.push(scene: .success, title: "同步成功", message: "点击查看详情") @@ -64,8 +64,8 @@ class TestToastVC: BaseListVC { } } else if row == 5 { Toast.push(scene: .default, title: "禁止手势移除", message: "这条消息无法通过向上滑动移出屏幕。5秒后自动消失,每次拖拽都会刷新倒计时。") { (vc) in + vc.isRemovable = false vc.update { (vm) in - vm.isRemovable = false vm.duration = 5 } } diff --git a/ProHUD/Alert/AlertConfig.swift b/ProHUD/Alert/AlertConfig.swift index 0e056a0..a10d9a9 100644 --- a/ProHUD/Alert/AlertConfig.swift +++ b/ProHUD/Alert/AlertConfig.swift @@ -28,11 +28,6 @@ public extension ProHUD.Configuration { // MARK: 图标样式 /// 图标尺寸 public var iconSize = CGSize(width: 48, height: 48) - /// 某个场景的默认图片 - /// - Parameter callback: 回调 - public func iconForScene(_ callback: @escaping (ProHUD.Alert.Scene) -> UIImage?) { - privIconForScene = callback - } // MARK: 文本样式 /// 标题字体 @@ -59,11 +54,6 @@ public extension ProHUD.Configuration { privReloadData = callback } - /// 默认持续时间(当viewmodel的duration为nil时,会从这里获取) - public func durationForScene(_ callback: @escaping (ProHUD.Alert.Scene) -> TimeInterval?) { - privDurationForScene = callback - } - /// 多少秒后显示强制退出的按钮(只有无按钮的弹窗才会出现) public var forceQuitTimer = TimeInterval(30) @@ -88,10 +78,6 @@ internal extension ProHUD.Configuration.Alert { return privReloadData } - var durationForScene: (ProHUD.Alert.Scene) -> TimeInterval? { - return privDurationForScene - } - } @@ -122,33 +108,10 @@ fileprivate var privLayoutContentView: (ProHUD.Alert) -> Void = { } }() -fileprivate var privIconForScene: (ProHUD.Alert.Scene) -> UIImage? = { - return { (scene) in - let imgStr: String - switch scene { - case .success: - imgStr = "ProHUDSuccess" - case .warning: - imgStr = "ProHUDWarning" - case .error: - imgStr = "ProHUDError" - case .loading: - imgStr = "ProHUDLoading" - case .confirm: - imgStr = "ProHUDMessage" - case .delete: - imgStr = "ProHUDTrash" - default: - imgStr = "ProHUDMessage" - } - return ProHUD.image(named: imgStr) - } -}() - fileprivate var privUpdateImage: (ProHUD.Alert) -> Void = { return { (vc) in let config = cfg.alert - let img = vc.vm.icon ?? privIconForScene(vc.vm.scene) + let img = vc.vm.icon ?? vc.vm.scene.image if let imgv = vc.imageView { imgv.image = img } else { @@ -172,6 +135,12 @@ fileprivate var privUpdateImage: (ProHUD.Alert) -> Void = { fileprivate var privUpdateTextStack: (ProHUD.Alert) -> Void = { return { (vc) in let config = cfg.alert + if vc.vm.title == nil { + vc.vm.title = vc.vm.scene.title + } + if vc.vm.message == nil { + vc.vm.message = vc.vm.scene.message + } if vc.vm.title?.count ?? 0 > 0 || vc.vm.message?.count ?? 0 > 0 { vc.contentStack.addArrangedSubview(vc.textStack) vc.textStack.snp.makeConstraints { (mk) in @@ -360,15 +329,3 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = { } }() - - -fileprivate var privDurationForScene: (ProHUD.Alert.Scene) -> TimeInterval? = { - return { (scene) in - switch scene { - case .loading: - return nil - default: - return 2 - } - } -}() diff --git a/ProHUD/Alert/AlertController.swift b/ProHUD/Alert/AlertController.swift index 1eedafc..37f771b 100644 --- a/ProHUD/Alert/AlertController.swift +++ b/ProHUD/Alert/AlertController.swift @@ -178,7 +178,7 @@ public extension Alert { /// - Parameter title: 标题 /// - Parameter message: 正文 /// - Parameter actions: 更多操作 - @discardableResult class func push(scene: Alert.Scene = .default, title: String? = nil, message: String? = nil, _ actions: ((Alert) -> Void)? = nil) -> Alert { + @discardableResult class func push(scene: ProHUD.Scene = .default, title: String? = nil, message: String? = nil, _ actions: ((Alert) -> Void)? = nil) -> Alert { return Alert(scene: scene, title: title, message: message, actions: actions).push() } diff --git a/ProHUD/Alert/AlertModel.swift b/ProHUD/Alert/AlertModel.swift index ebdc493..172c52b 100644 --- a/ProHUD/Alert/AlertModel.swift +++ b/ProHUD/Alert/AlertModel.swift @@ -9,34 +9,10 @@ import UIKit public extension Alert { - enum Scene { - /// 默认场景 - case `default` - - /// 加载中场景 - case loading - - /// 确认场景 - case confirm - - /// 删除场景 - case delete - - /// 成功场景 - case success - - /// 警告场景 - case warning - - /// 错误场景 - case error - - } - class ViewModel { /// 使用场景 - public var scene = Scene.default + public var scene = ProHUD.Scene.default /// 标题 public var title: String? { @@ -81,7 +57,7 @@ public extension Alert { internal func updateDuration() { durationBlock?.cancel() - if let t = duration ?? cfg.alert.durationForScene(scene), t > 0 { + if let t = duration ?? scene.alertDuration, t > 0 { durationBlock = DispatchWorkItem(block: { [weak self] in self?.vc?.pop() }) diff --git a/ProHUD/ProHUD.swift b/ProHUD/ProHUD.swift index 0a0b605..af4c7d3 100644 --- a/ProHUD/ProHUD.swift +++ b/ProHUD/ProHUD.swift @@ -16,8 +16,67 @@ public class ProHUD { return cfg } + public struct Scene { + private var id = "unknown" + public var identifier: String { + return id + } + public var image: UIImage? + public var alertDuration: TimeInterval? + public var toastDuration: TimeInterval? = 3 + public var title: String? + public var message: String? + init() { + + } + } + } +// 默认场景 +public extension ProHUD.Scene { + init(identifier: String) { + self.init() + id = identifier + } + + static var `default`: ProHUD.Scene { + var scene = ProHUD.Scene.init(identifier: "default") + scene.image = ProHUD.image(named: "ProHUDMessage") + return scene + } + static var loading: ProHUD.Scene { + var scene = ProHUD.Scene.init(identifier: "loading") + scene.alertDuration = 0 + scene.toastDuration = 0 + scene.image = ProHUD.image(named: "ProHUDLoading") + return scene + } + static var success: ProHUD.Scene { + var scene = ProHUD.Scene.init(identifier: "success") + scene.alertDuration = 2 + scene.image = ProHUD.image(named: "ProHUDSuccess") + return scene + } + static var warning: ProHUD.Scene { + var scene = ProHUD.Scene.init(identifier: "warning") + scene.alertDuration = 2 + scene.toastDuration = 5 + scene.image = ProHUD.image(named: "ProHUDWarning") + return scene + } + static var error: ProHUD.Scene { + var scene = ProHUD.Scene.init(identifier: "error") + scene.alertDuration = 2 + scene.toastDuration = 5 + scene.image = ProHUD.image(named: "ProHUDError") + return scene + } + + +} + + // MARK: - Utilities internal extension ProHUD { diff --git a/ProHUD/Toast/ToastConfig.swift b/ProHUD/Toast/ToastConfig.swift index cc858da..da3eda0 100644 --- a/ProHUD/Toast/ToastConfig.swift +++ b/ProHUD/Toast/ToastConfig.swift @@ -23,11 +23,6 @@ public extension ProHUD.Configuration { // MARK: 图标样式 /// 图标尺寸 public var iconSize = CGSize(width: 48, height: 48) - /// 某个场景的默认图片 - /// - Parameter callback: 回调 - public func iconForScene(_ callback: @escaping (ProHUD.Toast.Scene) -> UIImage?) { - privIconForScene = callback - } // MARK: 文本样式 /// 标题字体 @@ -46,10 +41,6 @@ public extension ProHUD.Configuration { privReloadData = callback } - /// 默认持续时间(当viewmodel的duration为nil时,会从这里获取) - public mutating func durationForScene(_ callback: @escaping (ProHUD.Toast.Scene) -> TimeInterval?) { - privDurationForScene = callback - } } } @@ -62,9 +53,6 @@ internal extension ProHUD.Configuration.Toast { return privReloadData } - var durationForScene: (ProHUD.Toast.Scene) -> TimeInterval? { - return privDurationForScene - } } @@ -84,12 +72,12 @@ fileprivate var privReloadData: (ProHUD.Toast) -> Void = { vc.view.addSubview(vc.imageView) } // 设置数据 - vc.imageView.image = vc.vm.icon ?? privIconForScene(vc.vm.scene) + vc.imageView.image = vc.vm.icon ?? vc.vm.scene.image vc.imageView.layer.removeAllAnimations() vc.titleLabel.textColor = cfg.primaryLabelColor - vc.titleLabel.text = vc.vm.title + vc.titleLabel.text = vc.vm.title ?? vc.vm.scene.title vc.bodyLabel.textColor = cfg.secondaryLabelColor - vc.bodyLabel.text = vc.vm.message + vc.bodyLabel.text = vc.vm.message ?? vc.vm.scene.message // 更新布局 vc.imageView.snp.makeConstraints { (mk) in @@ -118,38 +106,3 @@ fileprivate var privReloadData: (ProHUD.Toast) -> Void = { } }() - - - -fileprivate var privIconForScene: (ProHUD.Toast.Scene) -> UIImage? = { - return { (scene) in - let imgStr: String - switch scene { - case .success: - imgStr = "ProHUDSuccess" - case .warning: - imgStr = "ProHUDWarning" - case .error: - imgStr = "ProHUDError" - case .loading: - imgStr = "ProHUDLoading" - default: - imgStr = "ProHUDMessage" - } - return ProHUD.image(named: imgStr) - } -}() - - -fileprivate var privDurationForScene: (ProHUD.Toast.Scene) -> TimeInterval? = { - return { (scene) in - switch scene { - case .loading: - return nil - case .error, .warning: - return 5 - default: - return 3 - } - } -}() diff --git a/ProHUD/Toast/ToastController.swift b/ProHUD/Toast/ToastController.swift index c283383..84d3f9c 100644 --- a/ProHUD/Toast/ToastController.swift +++ b/ProHUD/Toast/ToastController.swift @@ -53,6 +53,9 @@ public extension ProHUD { return vev }() + /// 是否可以通过手势移除(向上滑出屏幕) + public var isRemovable = true + /// 视图模型 public var vm = ViewModel() @@ -215,7 +218,7 @@ public extension Toast { /// - Parameter title: 标题 /// - Parameter message: 内容 /// - Parameter actions: 更多操作 - @discardableResult class func push(scene: Toast.Scene = .default, title: String? = nil, message: String? = nil, duration: TimeInterval? = nil, _ actions: ((Toast) -> Void)? = nil) -> Toast { + @discardableResult class func push(scene: ProHUD.Scene = .default, title: String? = nil, message: String? = nil, duration: TimeInterval? = nil, _ actions: ((Toast) -> Void)? = nil) -> Toast { return Toast(scene: scene, title: title, message: message, duration: duration, actions: actions).push() } @@ -297,7 +300,7 @@ fileprivate extension Toast { window?.transform = .init(translationX: 0, y: point.y) if sender.state == .recognized { let v = sender.velocity(in: sender.view) - if vm.isRemovable == true && (((window?.frame.origin.y ?? 0) < 0 && v.y < 0) || v.y < -1200) { + if isRemovable == true && (((window?.frame.origin.y ?? 0) < 0 && v.y < 0) || v.y < -1200) { // 移除 self.pop() } else { diff --git a/ProHUD/Toast/ToastModel.swift b/ProHUD/Toast/ToastModel.swift index e46bb33..85c9f59 100644 --- a/ProHUD/Toast/ToastModel.swift +++ b/ProHUD/Toast/ToastModel.swift @@ -9,28 +9,10 @@ import UIKit public extension Toast { - enum Scene { - /// 默认场景 - case `default` - - /// 加载中场景 - case loading - - /// 成功场景 - case success - - /// 警告场景 - case warning - - /// 错误场景 - case error - - } - class ViewModel { /// 使用场景 - public var scene = Scene.default + public var scene = ProHUD.Scene.default /// 标题 public var title: String? { @@ -62,10 +44,6 @@ public extension Toast { public weak var vc: Toast? - /// 是否可以通过手势移除(向上滑出屏幕) - public var isRemovable = true - - // MARK: 私有 /// 持续时间 @@ -76,7 +54,7 @@ public extension Toast { internal func updateDuration() { durationBlock?.cancel() - if let t = duration ?? cfg.toast.durationForScene(scene), t > 0 { + if let t = duration ?? scene.toastDuration, t > 0 { durationBlock = DispatchWorkItem(block: { [weak self] in self?.vc?.pop() }) @@ -87,5 +65,4 @@ public extension Toast { } } - }