diff --git a/PHDemo/PHDemo/DemoAlertVC.swift b/PHDemo/PHDemo/DemoAlertVC.swift index 5518e53..330af50 100644 --- a/PHDemo/PHDemo/DemoAlertVC.swift +++ b/PHDemo/PHDemo/DemoAlertVC.swift @@ -110,7 +110,7 @@ class DemoAlertVC: ListVC { alert.config.customButton { button in button.titleLabel?.font = .systemFont(ofSize: 15) } - alert.vm?.message = "为了维护社区氛围,上麦用户需进行主播认证" + alert.title = "为了维护社区氛围,上麦用户需进行主播认证" alert.onViewDidLoad { vc in guard let alert = vc as? AlertTarget else { return @@ -140,7 +140,7 @@ class DemoAlertVC: ListVC { alert.config.customButton { button in button.titleLabel?.font = .systemFont(ofSize: 15) } - alert.vm?.message = "本次消费需要你支付999软妹豆,确认支付吗?" + alert.title = "本次消费需要你支付999软妹豆,确认支付吗?" alert.config.customActionStack { stack in stack.spacing = 0 stack.axis = .vertical // 竖排按钮 @@ -193,8 +193,7 @@ class DemoAlertVC: ListVC { } section.add(title: "标题 + 正文 + 按钮") { Alert { alert in - alert.vm?.title = "标题" - alert.vm?.message = "这是一段正文,长度超出最大宽度时会自动换行" + alert.vm = .title("标题").message("这是一段正文,长度超出最大宽度时会自动换行") alert.add(action: "取消", style: .gray) alert.add(action: "删除", style: .destructive) { alert in // 自定义了按钮事件之后,需要手动pop弹窗 @@ -301,8 +300,7 @@ class DemoAlertVC: ListVC { section.add(title: "多层级弹窗") { func f(i: Int) { Alert { alert in - alert.vm?.title = "第\(i)次弹" - alert.vm?.message = "每次都是一个新的实例覆盖在上一个弹窗上面,而背景不会叠加变深。" + alert.vm = .title("第\(i)次弹").message("每次都是一个新的实例覆盖在上一个弹窗上面,而背景不会叠加变深。") alert.add(action: "取消", style: .gray) alert.add(action: "增加一个") { alert in f(i: i + 1) @@ -338,7 +336,7 @@ class DemoAlertVC: ListVC { let vc = UIViewController() vc.title = "页面" vc.view.backgroundColor = .systemYellow - let alert = Alert(.loading.title("正在加载").message("这个弹窗被放在指定容器中")).target + let alert = AlertTarget(.loading.title("正在加载").message("这个弹窗被放在指定容器中")) alert.add(action: "返回上一页") { alert in vc.dismiss(animated: true) } diff --git a/PHDemo/PHDemo/DemoCapsuleVC.swift b/PHDemo/PHDemo/DemoCapsuleVC.swift index c98fbdf..e8c8127 100644 --- a/PHDemo/PHDemo/DemoCapsuleVC.swift +++ b/PHDemo/PHDemo/DemoCapsuleVC.swift @@ -35,9 +35,8 @@ class DemoCapsuleVC: ListVC { } } section.add(title: "延迟显示") { - // 也可以创建一个空白实例,在需要的时候再push - let obj = Capsule().target - obj.vm = .message("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") + // 也可以手动创建一个Target实例,在需要的时候再push + let obj = CapsuleTarget(.message("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")) // ... 在需要的时候手动push DispatchQueue.main.asyncAfter(deadline: .now() + 1) { obj.push() @@ -55,7 +54,7 @@ class DemoCapsuleVC: ListVC { list.add(title: "默认布局:图文") { section in section.add(title: "下载进度") { - let capsule = Capsule().target + let capsule = CapsuleTarget() capsule.vm = .message("正在下载").icon(.init(systemName: "arrow.down.circle.fill")).duration(.infinity) capsule.update(progress: 0) capsule.push() diff --git a/PHDemo/PHDemo/DemoToastVC.swift b/PHDemo/PHDemo/DemoToastVC.swift index 72bf556..e84f7e9 100644 --- a/PHDemo/PHDemo/DemoToastVC.swift +++ b/PHDemo/PHDemo/DemoToastVC.swift @@ -79,7 +79,7 @@ class DemoToastVC: ListVC { section.add(title: "图标 + 标题 + 正文") { let s1 = "笑容正在加载" let s2 = "这通常不会太久" - let toast = Toast(.loading.title(s1).message(s2)).target + let toast = ToastTarget(.loading.title(s1).message(s2)) toast.push() toast.update(progress: 0) updateProgress(in: 4) { percent in @@ -186,9 +186,7 @@ class DemoToastVC: ListVC { } section.add(title: "不存在就创建,存在就更新") { i += 1 - Toast.lazyPush(identifier: "loading") { toast in - toast.vm = .loading.title("正在加载\(i)").message("这条消息不会重复显示多条") - } + Toast(.identifier("loading").title("正在加载\(i)").message("这条消息不会重复显示多条")) } section.add(title: "如果存在就更新,如果不存在就忽略") { i += 1 diff --git a/Sources/ProHUD/Alert/AlertProvider.swift b/Sources/ProHUD/Alert/AlertProvider.swift index f480f70..7837890 100644 --- a/Sources/ProHUD/Alert/AlertProvider.swift +++ b/Sources/ProHUD/Alert/AlertProvider.swift @@ -21,9 +21,17 @@ open class AlertProvider: HUDProvider { /// - vm: 数据模型 /// - initializer: 自定义的初始化代码 @discardableResult public convenience init(_ vm: ViewModel, initializer: ((_ alert: Target) -> Void)?) { - self.init { alert in - alert.vm = vm - initializer?(alert) + if let id = vm.identifier, id.count > 0 { + Self.lazyPush(identifier: id) { target in + target.vm = vm + initializer?(target) + } + self.init(initializer: nil) + } else { + self.init { target in + target.vm = vm + initializer?(target) + } } } /// 根据ViewModel创建一个Target并显示 diff --git a/Sources/ProHUD/Alert/AlertTarget.swift b/Sources/ProHUD/Alert/AlertTarget.swift index 57709ba..3149e53 100644 --- a/Sources/ProHUD/Alert/AlertTarget.swift +++ b/Sources/ProHUD/Alert/AlertTarget.swift @@ -9,6 +9,8 @@ import UIKit open class AlertTarget: BaseController, HUDTargetType { + public typealias ViewModel = AlertViewModel + public lazy var config: AlertConfiguration = { var cfg = AlertConfiguration() AlertConfiguration.customGlobalConfig?(cfg) @@ -74,7 +76,11 @@ open class AlertTarget: BaseController, HUDTargetType { }() /// 视图模型 - @objc public var vm: AlertViewModel? + @objc public var vm: AlertViewModel? { + didSet { + vm?.vc = self + } + } public override var title: String? { didSet { @@ -94,6 +100,11 @@ open class AlertTarget: BaseController, HUDTargetType { fatalError("init(coder:) has not been implemented") } + public convenience init(_ vm: ViewModel) { + self.init() + self.vm = vm + } + public override func viewDidLoad() { super.viewDidLoad() reloadData(animated: false) diff --git a/Sources/ProHUD/Capsule/CapsuleProvider.swift b/Sources/ProHUD/Capsule/CapsuleProvider.swift index 00b8d5a..161fc4a 100644 --- a/Sources/ProHUD/Capsule/CapsuleProvider.swift +++ b/Sources/ProHUD/Capsule/CapsuleProvider.swift @@ -21,9 +21,17 @@ open class CapsuleProvider: HUDProvider { /// - vm: 数据模型 /// - initializer: 初始化代码 @discardableResult public convenience init(_ vm: ViewModel, initializer: ((_ capsule: Target) -> Void)?) { - self.init { capsule in - capsule.vm = vm - initializer?(capsule) + if let id = vm.identifier, id.count > 0 { + Self.lazyPush(identifier: id) { target in + target.vm = vm + initializer?(target) + } + self.init(initializer: nil) + } else { + self.init { target in + target.vm = vm + initializer?(target) + } } } diff --git a/Sources/ProHUD/Capsule/CapsuleTarget.swift b/Sources/ProHUD/Capsule/CapsuleTarget.swift index dc56c33..85c8fc1 100644 --- a/Sources/ProHUD/Capsule/CapsuleTarget.swift +++ b/Sources/ProHUD/Capsule/CapsuleTarget.swift @@ -9,7 +9,9 @@ import UIKit open class CapsuleTarget: BaseController, HUDTargetType { - public lazy var config: CapsuleConfiguration = { + public typealias ViewModel = CapsuleViewModel + + @objc public lazy var config: CapsuleConfiguration = { var cfg = CapsuleConfiguration() CapsuleConfiguration.customGlobalConfig?(cfg) return cfg @@ -46,7 +48,11 @@ open class CapsuleTarget: BaseController, HUDTargetType { return lb }() - public var vm: CapsuleViewModel? + @objc public var vm: CapsuleViewModel? { + didSet { + vm?.vc = self + } + } public override var title: String? { didSet { @@ -68,6 +74,11 @@ open class CapsuleTarget: BaseController, HUDTargetType { fatalError("init(coder:) has not been implemented") } + public convenience init(_ vm: ViewModel) { + self.init() + self.vm = vm + } + public override func viewDidLoad() { super.viewDidLoad() view.layer.shadowRadius = 8 @@ -84,7 +95,7 @@ open class CapsuleTarget: BaseController, HUDTargetType { } - public func onTapped(action: @escaping (_ capsule: CapsuleTarget) -> Void) { + @objc public func onTapped(action: @escaping (_ capsule: CapsuleTarget) -> Void) { self.tapActionCallback = action } diff --git a/Sources/ProHUD/Core/Models/BaseViewModel.swift b/Sources/ProHUD/Core/Models/BaseViewModel.swift index 689e825..ab32a33 100644 --- a/Sources/ProHUD/Core/Models/BaseViewModel.swift +++ b/Sources/ProHUD/Core/Models/BaseViewModel.swift @@ -12,6 +12,26 @@ public protocol HUDViewModelType {} /// 数据模型 open class BaseViewModel: NSObject, HUDViewModelType { + /// 在创建target之前临时暂存的id + private var tmpStoredIdentifier: String? + + /// 创建target时将指定为此值 + @objc open var identifier: String? { + set { + tmpStoredIdentifier = newValue + if let id = tmpStoredIdentifier, id.count > 0 { + vc?.identifier = id + } + } + get { + if let vc = vc { + return vc.identifier + } else { + return tmpStoredIdentifier + } + } + } + /// 图标 @objc open var icon: UIImage? @objc var iconURL: URL? @@ -34,7 +54,15 @@ open class BaseViewModel: NSObject, HUDViewModelType { } } - @objc public required override init() { + weak var vc: BaseController? { + didSet { + if let id = tmpStoredIdentifier { + vc?.identifier = id + } + } + } + + @objc required public override init() { } @@ -70,6 +98,11 @@ open class BaseViewModel: NSObject, HUDViewModelType { // MARK: - convenience func public extension BaseViewModel { + func identifier(_ identifier: String?) -> Self { + self.identifier = identifier + return self + } + func icon(_ image: UIImage?) -> Self { self.icon = image return self @@ -110,6 +143,11 @@ public extension BaseViewModel { // MARK: - example scenes public extension BaseViewModel { + static func identifier(_ text: String?) -> Self { + .init() + .identifier(text) + } + // MARK: plain static func title(_ text: String?) -> Self { .init() diff --git a/Sources/ProHUD/Core/Protocols/Provider.swift b/Sources/ProHUD/Core/Protocols/Provider.swift index b25c7cb..134eb65 100644 --- a/Sources/ProHUD/Core/Protocols/Provider.swift +++ b/Sources/ProHUD/Core/Protocols/Provider.swift @@ -18,28 +18,23 @@ public protocol HUDProviderType { } -open class HUDProvider: HUDProviderType { - - /// HUD实例 - public var target: Target +open class HUDProvider: NSObject, HUDProviderType { /// 根据自定义的初始化代码创建一个Target并显示 - /// - Parameter initializer: 初始化代码 + /// - Parameter initializer: 初始化代码(传空值时不会做任何事) @discardableResult public required init(initializer: ((_ target: Target) -> Void)?) { + guard let initializer = initializer else { + // Provider的作用就是push一个target + // 如果没有任何初始化代码就没有target,就是个无意义的Provider + // 但为了支持lazyPush(找到已有实例并更新),所以就需要支持无意义的Provider + // 详见子类中的 self.init(initializer: nil) + return + } var t = Target() - initializer?(t) - self.target = t - if (t.vm == nil && initializer == nil) == false { - DispatchQueue.main.async { - t.push() - } + initializer(t) + DispatchQueue.main.async { + t.push() } } - /// 创建一个空白的实例,不立即显示,需要手动调用target.push()来显示 - @discardableResult public convenience init() { - self.init(initializer: nil) - } - - } diff --git a/Sources/ProHUD/Core/Views/ProgressView.swift b/Sources/ProHUD/Core/Views/ProgressView.swift index e2d555d..d1f5238 100644 --- a/Sources/ProHUD/Core/Views/ProgressView.swift +++ b/Sources/ProHUD/Core/Views/ProgressView.swift @@ -34,7 +34,7 @@ public class ProgressView: UIView { let bgLayer = CAShapeLayer() bgLayer.fillColor = UIColor.clear.cgColor bgLayer.path = path.cgPath - bgLayer.strokeColor = UIColor.white.cgColor + bgLayer.strokeColor = UIColor.clear.cgColor bgLayer.lineWidth = lineWidth bgLayer.lineCap = .round bgLayer.strokeStart = 0 diff --git a/Sources/ProHUD/Sheet/SheetTarget.swift b/Sources/ProHUD/Sheet/SheetTarget.swift index 5fc245f..b83287a 100644 --- a/Sources/ProHUD/Sheet/SheetTarget.swift +++ b/Sources/ProHUD/Sheet/SheetTarget.swift @@ -41,7 +41,11 @@ open class SheetTarget: BaseController, HUDTargetType { } } - public var vm: SheetViewModel? = nil + public var vm: SheetViewModel? = nil { + didSet { + vm?.vc = self + } + } required public override init() { super.init() diff --git a/Sources/ProHUD/Toast/ToastDefaultLayout.swift b/Sources/ProHUD/Toast/ToastDefaultLayout.swift index 4e6decb..fcc3d40 100644 --- a/Sources/ProHUD/Toast/ToastDefaultLayout.swift +++ b/Sources/ProHUD/Toast/ToastDefaultLayout.swift @@ -26,11 +26,17 @@ extension ToastTarget: DefaultLayout { } return } + let titleCount = vm?.title?.count ?? 0 + let bodyCount = vm?.message?.count ?? 0 if vm?.icon != nil || vm?.iconURL != nil { if imageView.superview == nil { infoStack.insertArrangedSubview(imageView, at: 0) imageView.snp.makeConstraints { make in - make.width.height.equalTo(config.iconSize) + if titleCount > 0 && bodyCount > 0 { + make.width.height.equalTo(config.iconSize) + } else { + make.width.equalTo(config.iconSize) + } } } } else { @@ -42,8 +48,6 @@ extension ToastTarget: DefaultLayout { if textStack.superview == nil { infoStack.addArrangedSubview(textStack) } - let titleCount = vm?.title?.count ?? 0 - let bodyCount = vm?.message?.count ?? 0 if titleCount > 0 { textStack.insertArrangedSubview(titleLabel, at: 0) if bodyCount > 0 { diff --git a/Sources/ProHUD/Toast/ToastProvider.swift b/Sources/ProHUD/Toast/ToastProvider.swift index a94f6fe..b65ce18 100644 --- a/Sources/ProHUD/Toast/ToastProvider.swift +++ b/Sources/ProHUD/Toast/ToastProvider.swift @@ -21,9 +21,17 @@ open class ToastProvider: HUDProvider { /// - vm: 数据模型 /// - initializer: 自定义的初始化代码 @discardableResult public convenience init(_ vm: ViewModel, initializer: ((_ toast: Target) -> Void)?) { - self.init { toast in - toast.vm = vm - initializer?(toast) + if let id = vm.identifier, id.count > 0 { + Self.lazyPush(identifier: id) { target in + target.vm = vm + initializer?(target) + } + self.init(initializer: nil) + } else { + self.init { target in + target.vm = vm + initializer?(target) + } } } diff --git a/Sources/ProHUD/Toast/ToastTarget.swift b/Sources/ProHUD/Toast/ToastTarget.swift index 9b875d9..2a230a3 100644 --- a/Sources/ProHUD/Toast/ToastTarget.swift +++ b/Sources/ProHUD/Toast/ToastTarget.swift @@ -9,6 +9,8 @@ import UIKit open class ToastTarget: BaseController, HUDTargetType { + public typealias ViewModel = ToastViewModel + weak var window: ToastWindow? public lazy var config: ToastConfiguration = { @@ -84,7 +86,11 @@ open class ToastTarget: BaseController, HUDTargetType { public var isRemovable = true /// 视图模型 - @objc public var vm: ToastViewModel? + @objc public var vm: ToastViewModel? { + didSet { + vm?.vc = self + } + } private var tapActionCallback: ((_ toast: ToastTarget) -> Void)? @@ -103,7 +109,12 @@ open class ToastTarget: BaseController, HUDTargetType { } required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: coder) + } + + public convenience init(_ vm: ViewModel) { + self.init() + self.vm = vm } public override func viewDidLoad() {