diff --git a/Example-Old/Example/AppDelegate.swift b/Example-Old/Example/AppDelegate.swift index c128af9..596d8f6 100644 --- a/Example-Old/Example/AppDelegate.swift +++ b/Example-Old/Example/AppDelegate.swift @@ -30,7 +30,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // a.bodyFont = .regular(17) // a.boldTextFont = .bold(18) // a.buttonFont = .bold(18) - a.forceQuitTimer = 3 + a.forceQuitTimer = 5 // 多少秒后可以强制关闭弹窗(为了方便测试,一般不要设置这么小) } // cfg.toast { (t) in // t.titleFont = .bold(18) @@ -78,7 +78,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { extension ProHUD.Scene { static var delete: ProHUD.Scene { - var scene = ProHUD.Scene(identifier: "delete") + var scene = ProHUD.Scene(identifier: "prohud.delete") scene.image = UIImage(named: "prohud.trash") scene.title = "确认删除" scene.message = "此操作不可撤销" diff --git a/Example-Old/Example/BaseListVC.swift b/Example-Old/Example/BaseListVC.swift index 6360fd6..d37958b 100644 --- a/Example-Old/Example/BaseListVC.swift +++ b/Example-Old/Example/BaseListVC.swift @@ -8,18 +8,46 @@ import UIKit +typealias Callback = () -> Void + +struct Table { + var sections = [Section]() + mutating func addSection(title: String, callback: @escaping (inout Section) -> Void) { + var sec = Section() + sec.header = title + callback(&sec) + sections.append(sec) + } +} +struct Section { + var header = "" + var footer = "" + var rows = [Row]() + mutating func addRow(title: String, subtitle: String? = nil, callback: @escaping Callback) { + rows.append(Row(title: title, subtitle: subtitle, callback: callback)) + } +} +struct Row { + var title: String + var subtitle: String? + var callback: Callback +} + class BaseListVC: UIViewController { + + var ts = [[String]]() + var cs = [[Callback]]() + + var vm = Table() + var secs = [Section]() + lazy var tableView: UITableView = { - let tv = UITableView() + let tv = UITableView(frame: .zero, style: .grouped) return tv }() - var titles: [String] { - return ["Toast", "Alert", "Guard"] - } - override func viewDidLoad() { super.viewDidLoad() @@ -27,29 +55,47 @@ class BaseListVC: UIViewController { view.addSubview(tableView) tableView.dataSource = self tableView.delegate = self - tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") tableView.snp.makeConstraints { (mk) in mk.edges.equalToSuperview() } - } - + } extension BaseListVC: UITableViewDataSource, UITableViewDelegate { + func numberOfSections(in tableView: UITableView) -> Int { + return vm.sections.count + } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return titles.count + return vm.sections[section].rows.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) - cell.textLabel?.text = titles[indexPath.row] + let cell: UITableViewCell + if let c = tableView.dequeueReusableCell(withIdentifier: "cell") { + cell = c + } else { + cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell") + cell.textLabel?.numberOfLines = 0 + cell.detailTextLabel?.textColor = .gray + cell.detailTextLabel?.numberOfLines = 0 + + } + cell.textLabel?.text = vm.sections[indexPath.section].rows[indexPath.row].title + cell.detailTextLabel?.text = vm.sections[indexPath.section].rows[indexPath.row].subtitle return cell } - + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return vm.sections[section].header + } + func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { + return vm.sections[section].footer + } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) + vm.sections[indexPath.section].rows[indexPath.row].callback() } } + diff --git a/Example-Old/Example/TestAlertVC.swift b/Example-Old/Example/TestAlertVC.swift index 68ce9a5..ebd407b 100644 --- a/Example-Old/Example/TestAlertVC.swift +++ b/Example-Old/Example/TestAlertVC.swift @@ -9,221 +9,254 @@ import UIKit import ProHUD +// 模拟2秒后同步成功 +func loadingSuccessAfter2Seconds() { + DispatchQueue.main.asyncAfter(deadline: .now()+2) { + Alert.find("loading", last: { (a) in + a.update { (vm) in + vm.scene = .success + vm.title = "同步成功" + vm.message = nil + } + }) + } +} + class TestAlertVC: BaseListVC { override func viewDidLoad() { super.viewDidLoad() - - // Do any additional setup after loading the view. - } - - override var titles: [String] { - return ["极端场景:正在同步(超时未处理)", - "场景:同步成功(写法1)", - "场景:同步成功(写法2)", - "场景:同步失败和重试", - "极端场景:短时间内调用了多次同一个弹窗", - "极端场景:多个不同的弹窗重叠", - "测试较长的标题和内容", - "测试特别长的标题和内容", - "场景:正在同步(更新进度)"] - } - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - tableView.deselectRow(at: indexPath, animated: true) - let row = indexPath.row - if row == 0 { - func f() { - Alert.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (a) in - a.identifier = "loading" - a.startRotate() - a.didForceQuit { - let t = Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻(点击展开为Alert)") { (vm) in - vm.identifier = "loading" - } - t.startRotate() - t.didTapped { [weak t] in - t?.pop() - f() - } - } - } + + vm.addSection(title: "最简单的写法") { (sec) in + sec.addRow(title: "Alert.push(scene: .loading)") { + Alert.push(scene: .loading) } - f() - } else if row == 1 { - Alert.push() { (a) in - a.identifier = "loading" - a.startRotate() - a.update { (vm) in - vm.scene = .loading - vm.title = "正在同步" - vm.message = "请稍等片刻" - } + sec.addRow(title: "Alert.push(scene: .success)") { + Alert.push(scene: .success) } - DispatchQueue.main.asyncAfter(deadline: .now()+2) { - Alert.find("loading", last: { (a) in - a.update { (vm) in - vm.scene = .success - vm.title = "同步成功" - vm.message = nil - } - }) + sec.addRow(title: "Alert.push(scene: .warning)") { + Alert.push(scene: .warning) } - } else if row == 2 { - let a = Alert.push() { (a) in - a.identifier = "loading" + sec.addRow(title: "Alert.push(scene: .error)") { + Alert.push(scene: .error) } - a.startRotate() - a.update { (vm) in - vm.scene = .loading - vm.title = "正在同步" - vm.message = "请稍等片刻" + sec.addRow(title: "Alert.push(scene: .failure)") { + Alert.push(scene: .failure) } - DispatchQueue.main.asyncAfter(deadline: .now()+2) { - Alert.find("loading", last: { (a) in - a.update { (vm) in - vm.scene = .success - vm.title = "同步成功" - vm.message = nil - } - }) - } - } else if row == 3 { - Alert.push() { (a) in - a.identifier = "loading" - } - func loading() { - Alert.find("loading", last: { (a) in + sec.footer = "这些是自带的场景,可以重写已有场景,或者扩展新的场景。" + } + + vm.addSection(title: "常用场景示例") { (sec) in + // MARK: 场景:同步成功(写法1) + sec.addRow(title: "场景:同步成功(写法1)") { + Alert.push("loading") { (a) in a.update { (vm) in vm.scene = .loading vm.title = "正在同步" vm.message = "请稍等片刻" - vm.remove(action: 0, 1) } a.startRotate() - DispatchQueue.main.asyncAfter(deadline: .now()+2) { - a.update { (vm) in - vm.scene = .error - vm.title = "同步失败" - vm.message = "请检查网络是否通畅" - vm.add(action: .default, title: "重试") { - loading() - } - vm.add(action: .cancel, title: "取消", handler: nil) - } - } - }) - } - loading() - } else if row == 4 { - func loading(_ index: Int = 1) { - if let _ = Alert.find("loading").last { - Toast.push("loading-tip") { (t) in - t.update { (vm) in - vm.title = "此时又调用了一次相同的弹窗 x\(index)" - } - t.pulse() - } - } else { - Alert.push("loading") { (a) in - a.update { (vm) in - vm.scene = .loading - vm.title = "正在加载" - } - a.startRotate() - } } + loadingSuccessAfter2Seconds() } - loading(1) - DispatchQueue.main.asyncAfter(deadline: .now()+1) { - loading(2) - } - DispatchQueue.main.asyncAfter(deadline: .now()+2) { - loading(3) - } - DispatchQueue.main.asyncAfter(deadline: .now()+3) { - loading(4) - } - DispatchQueue.main.asyncAfter(deadline: .now()+3.5) { - loading(5) - } - DispatchQueue.main.asyncAfter(deadline: .now()+4) { - loading(6) - } - } else if row == 5 { - func f(_ i: Int) { - Alert.push() { (a) in + // MARK: 场景:同步成功(写法2) + sec.addRow(title: "场景:同步成功(写法2)") { + let a = Alert.push("loading") { (a) in a.startRotate() - 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) - } - } else if row == 6 { - Alert.push() { (a) in - a.update { (vm) in - vm.scene = .confirm - vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" - vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" - vm.add(action: .default, title: "我知道了", handler: nil) - } - } - } else if row == 7 { - Alert.push() { (a) in - a.update { (vm) in - vm.scene = .warning - vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" - vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" - vm.add(action: .default, title: "我知道了", handler: nil) - } - } - } else if row == 8 { - Alert.push("progress") { (a) in a.update { (vm) in vm.scene = .loading vm.title = "正在同步" vm.message = "请稍等片刻" } - a.startRotate() - a.update(progress: 0) - let s = DispatchSemaphore(value: 1) - DispatchQueue.global().async { - for i in 0 ... 5 { - s.wait() - DispatchQueue.main.async { - Alert.find("progress", last: { (a) in - a.update(progress: CGFloat(i)/5) - print("\(CGFloat(i)/5)") - if i == 5 { - a.update { (vm) in - vm.scene = .success - vm.title = "同步成功" - vm.message = nil + loadingSuccessAfter2Seconds() + } + // MARK: 场景:正在同步(更新进度) + sec.addRow(title: "场景:正在同步(更新进度)") { + Alert.push("progress") { (a) in + a.update { (vm) in + vm.scene = .loading + vm.title = "正在同步" + vm.message = "请稍等片刻" + } + a.startRotate() + a.update(progress: 0) + let s = DispatchSemaphore(value: 1) + DispatchQueue.global().async { + for i in 0 ... 100 { + s.wait() + DispatchQueue.main.async { + Alert.find("progress", last: { (a) in + a.update(progress: CGFloat(i)/100) + print("\(CGFloat(i)/100)") + if i == 100 { + DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { + a.update { (vm) in + vm.scene = .success + vm.title = "同步成功" + vm.message = nil + } + } } + }) + DispatchQueue.main.asyncAfter(deadline: .now()+0.03) { + s.signal() } - }) - DispatchQueue.main.asyncAfter(deadline: .now()+1) { - s.signal() } } } } } + // MARK: 场景:同步失败和重试 + sec.addRow(title: "场景:同步失败和重试(布局变化)") { + Alert.push("loading", scene: .loading) + func loading() { + Alert.find("loading") { (a) in + a.update { (vm) in + vm.scene = .loading + vm.title = "正在同步" + vm.message = "请稍等片刻" + vm.remove(action: 0, 1) + } + a.startRotate() + DispatchQueue.main.asyncAfter(deadline: .now()+2) { + a.update { (vm) in + vm.scene = .error + vm.title = "同步失败" + vm.message = "请检查网络是否通畅" + vm.add(action: .default, title: "重试") { + loading() + } + vm.add(action: .cancel, title: "取消", handler: nil) + } + } + } + } + loading() + } + sec.footer = "两种写法1和写法2动画效果略微不同" } - } + + vm.addSection(title: "为了解决代码逻辑疏漏导致程序卡死、弹窗重叠等问题") { (sec) in + // MARK: 极端场景:正在同步(超时未处理) + sec.addRow(title: "极端场景:正在同步(超时未处理)", subtitle: "超时未处理的意外情况,弹窗可以手动关闭。(在config中配置所需时间)") { + func f() { + Alert.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (a) in + a.identifier = "loading" + a.startRotate() + a.didForceQuit { + let t = Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻(点击展开为Alert)") { (vm) in + vm.identifier = "loading" + } + t.startRotate() + t.didTapped { [weak t] in + t?.pop() + f() + } + } + } + } + f() + } + + + // MARK: 极端场景:短时间内调用了多次同一个弹窗 + sec.addRow(title: "极端场景:短时间内调用了多次同一个弹窗", subtitle: "多次调用,页面弹窗应该是毫无变化的。(本例中,4秒内调用了6次)") { + func loading(_ index: Int = 1) { + if Alert.find("loading").count == 0 { + Alert.push("loading", scene: .loading) { (a) in + a.update { (vm) in + vm.title = "正在加载" + } + a.startRotate() + } + } + } + loading(1) + DispatchQueue.main.asyncAfter(deadline: .now()+1) { + loading(2) + } + DispatchQueue.main.asyncAfter(deadline: .now()+2) { + loading(3) + } + DispatchQueue.main.asyncAfter(deadline: .now()+3) { + loading(4) + } + DispatchQueue.main.asyncAfter(deadline: .now()+3.5) { + loading(5) + } + DispatchQueue.main.asyncAfter(deadline: .now()+4) { + loading(6) + } + } + // MARK: 极端场景:多个不同的弹窗重叠 + sec.addRow(title: "极端场景:多个不同的弹窗重叠", subtitle: "多个弹窗不得不重叠的时候,ProHUD的景深处理可以使其看起来舒服一些。") { + func f(_ i: Int) { + Alert.push() { (a) in + a.startRotate() + 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) + } + } + + + } + + vm.addSection(title: "") { (sec) in + + // MARK: 测试较长的标题和内容 + sec.addRow(title: "测试较长的标题和内容") { + Alert.push() { (a) in + a.update { (vm) in + vm.scene = .confirm + vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" + vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" + vm.add(action: .default, title: "我知道了", handler: nil) + } + } + } + // MARK: 测试特别长的标题和内容 + sec.addRow(title: "测试特别长的标题和内容", subtitle: "这种情况编码阶段就可以避免,所以没有做特别处理,请控制内容长度不要过长。") { + Alert.push() { (a) in + a.update { (vm) in + vm.scene = .warning + vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" + vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" + vm.add(action: .default, title: "我知道了", handler: nil) + } + } + } + // MARK: 只有标题 + sec.addRow(title: "只有标题") { + Alert.push(scene: .default, title: "标题") + } + // MARK: 只有消息 + sec.addRow(title: "只有消息") { + Alert.push(scene: .default, message: "这是消息") + } + // MARK: 只有消息 + sec.addRow(title: "既没有标题也没有消息") { + Alert.push("a") + } + } + + } + func simulateSync() { DispatchQueue.main.asyncAfter(deadline: .now() + 15) { Alert.find("loading", last: { (a) in diff --git a/Example-Old/Example/TestGuardVC.swift b/Example-Old/Example/TestGuardVC.swift index ef86510..3045518 100644 --- a/Example-Old/Example/TestGuardVC.swift +++ b/Example-Old/Example/TestGuardVC.swift @@ -14,112 +14,117 @@ class TestGuardVC: BaseListVC { override func viewDidLoad() { super.viewDidLoad() - - // Do any additional setup after loading the view. - } - - override var titles: [String] { - return ["场景:删除菜单", "场景:升级至专业版", "场景:隐私协议页面", "对比:原生的ActionSheet", "对比:原生Present效果"] - } - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - tableView.deselectRow(at: indexPath, animated: true) - let row = indexPath.row - if row == 0 { - Guard.push("del") { (vc) in - vc.update { (vm) in - vm.add(action: .destructive, title: "删除") { [weak vc] in - Alert.push(scene: .delete) { (vc) in - vc.update { (vm) in - vm.add(action: .destructive, title: "删除") { [weak vc] in - vc?.pop() - } - vm.add(action: .cancel, title: "取消", handler: nil) - } - } - vc?.pop() - } - vm.add(action: .cancel, title: "取消", handler: nil) - } - } - } else if row == 1 { - // 可以通过id来避免重复 - Guard.push("pro") { (vc) in - vc.identifier = "pro" - vc.update { (vm) in - vm.add(title: "升级至专业版") - vm.add(subTitle: "解锁功能") - vm.add(message: "功能1功能2...") - vm.add(subTitle: "价格") - vm.add(message: "只需一次性付费$2999即可永久享用。") - vm.add(message: "只需一次性付费$2999即可永久享用。") - vm.add(action: .destructive, title: "购买") { [weak 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 = .loading - vm.title = "正在付款" - vm.message = "请稍等片刻" - vm.remove(action: 0, 1) - }) - vc?.startRotate() - 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.addSection(title: "") { (sec) in + // MARK: 场景:删除菜单 + sec.addRow(title: "场景:删除菜单") { + Guard.push("del") { (vc) in + vc.update { (vm) in + vm.add(action: .destructive, title: "删除") { [weak vc] in + Alert.push(scene: .delete) { (vc) in + vc.update { (vm) in + vm.add(action: .destructive, title: "删除") { [weak vc] in + vc?.pop() } + vm.add(action: .cancel, title: "取消", handler: nil) } - vm.add(action: .cancel, title: "取消", handler: nil) } + vc?.pop() } - vc?.pop() - } - vm.add(action: .cancel, title: "取消", handler: nil) - } - } - } else if row == 2 { - Guard.push("license") { (vc) in - vc.isFullScreen = true - vc.update { (vm) in - let titleLabel = vm.add(title: "隐私协议") - titleLabel.snp.makeConstraints { (mk) in - mk.height.equalTo(44) - } - let tv = UITextView() - tv.backgroundColor = .white - tv.isEditable = false - vc.textStack.addArrangedSubview(tv) - tv.text = "这里可以插入一个webView" - vm.add(message: "请认真阅读以上内容,当您阅读完毕并同意协议内容时点击接受按钮。") - - vm.add(action: .default, title: "接受") { [weak vc] in - vc?.pop() + vm.add(action: .cancel, title: "取消", handler: nil) } } } - } else if row == 3 { - let ac = UIAlertController(title: "Title", message: "message", preferredStyle: .actionSheet) - let ok = UIAlertAction(title: "OK", style: .default, handler: nil) - let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) - ac.addAction(ok) - ac.addAction(cancel) - ac.popoverPresentationController?.sourceView = tableView.cellForRow(at: indexPath) - self.present(ac, animated: true, completion: nil) - } else if row == 4 { - let vc = UIViewController() - vc.view.backgroundColor = .white - vc.title = "ceshi" - present(vc, animated: true, completion: nil) + + // MARK: 场景:升级至专业版 + sec.addRow(title: "场景:升级至专业版") { + // 可以通过id来避免重复 + Guard.push("pro") { (vc) in + vc.identifier = "pro" + vc.update { (vm) in + vm.add(title: "升级至专业版") + vm.add(subTitle: "解锁功能") + vm.add(message: "功能1功能2...") + vm.add(subTitle: "价格") + vm.add(message: "只需一次性付费$2999即可永久享用。") + vm.add(message: "只需一次性付费$2999即可永久享用。") + vm.add(action: .destructive, title: "购买") { [weak 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 = .loading + vm.title = "正在付款" + vm.message = "请稍等片刻" + vm.remove(action: 0, 1) + }) + vc?.startRotate() + 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) + } + } + vc?.pop() + } + vm.add(action: .cancel, title: "取消", handler: nil) + } + } + } + + // MARK: 场景:隐私协议页面 + sec.addRow(title: "场景:隐私协议页面") { + Guard.push("license") { (vc) in + vc.isFullScreen = true + vc.update { (vm) in + let titleLabel = vm.add(title: "隐私协议") + titleLabel.snp.makeConstraints { (mk) in + mk.height.equalTo(44) + } + let tv = UITextView() + tv.backgroundColor = .white + tv.isEditable = false + vc.textStack.addArrangedSubview(tv) + tv.text = "这里可以插入一个webView" + vm.add(message: "请认真阅读以上内容,当您阅读完毕并同意协议内容时点击接受按钮。") + + vm.add(action: .default, title: "接受") { [weak vc] in + vc?.pop() + } + } + } + } + + // MARK: 对比:原生的ActionSheet + sec.addRow(title: "对比:原生的ActionSheet") { + let ac = UIAlertController(title: "Title", message: "message", preferredStyle: .actionSheet) + let ok = UIAlertAction(title: "OK", style: .default, handler: nil) + let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) + ac.addAction(ok) + ac.addAction(cancel) + ac.popoverPresentationController?.sourceView = self.view + self.present(ac, animated: true, completion: nil) + } + // MARK: 对比:原生Present效果 + sec.addRow(title: "对比:原生Present效果") { + let vc = UIViewController() + vc.view.backgroundColor = .white + vc.title = "ceshi" + self.present(vc, animated: true, completion: nil) + } } - + + } - + } diff --git a/Example-Old/Example/TestToastVC.swift b/Example-Old/Example/TestToastVC.swift index 5a05644..a8705ab 100644 --- a/Example-Old/Example/TestToastVC.swift +++ b/Example-Old/Example/TestToastVC.swift @@ -9,208 +9,218 @@ import UIKit import ProHUD +typealias Callback2 = (String) -> Void class TestToastVC: BaseListVC { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. - } - - override var titles: [String] { - return ["场景:正在同步", - "场景:正在同步(更新进度)", - "场景:同步成功", - "场景:同步失败", - "场景:设备电量过低", - "传入指定图标", - "禁止手势移除", - "组合使用示例", - "避免重复发布同一条信息", - "根据id查找并修改实例", - "测试较长的标题和内容", - "测试特别长的标题和内容", - "测试只有title", - "测试只有message", - "自定义旋转的图片"] - } - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - tableView.deselectRow(at: indexPath, animated: true) - let row = indexPath.row - if row == 0 { - Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (vm) in - vm.identifier = "loading" - }.startRotate() - simulateSync() - } else if row == 1 { - if let _ = Toast.find("progress").last { - - } else { - Toast.push("progress") { (t) in - t.update { (vm) in - vm.scene = .loading - vm.title = "正在同步" - vm.message = "请稍等片刻" - } - t.startRotate() - t.update(progress: 0) - let s = DispatchSemaphore(value: 1) - DispatchQueue.global().async { - for i in 0 ... 5 { - s.wait() - DispatchQueue.main.async { - Toast.find("progress", last: { (t) in - t.update(progress: CGFloat(i)/5) - print("\(CGFloat(i)/5)") - if i == 5 { - t.update { (vm) in - vm.scene = .success - vm.title = "同步成功" - vm.message = "xxx" + + vm.addSection(title: "基本场景") { (sec) in + // MARK: 场景:正在同步 + sec.addRow(title: "场景:正在同步") { + Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (vm) in + vm.identifier = "loading" + }.startRotate() + self.simulateSync() + } + // MARK: 场景:正在同步(更新进度) + sec.addRow(title: "场景:正在同步(更新进度)") { + if Toast.find("progress").count == 0 { + Toast.push("progress") { (t) in + t.update { (vm) in + vm.scene = .loading + vm.title = "正在同步" + vm.message = "请稍等片刻" + } + t.startRotate() + t.update(progress: 0) + let s = DispatchSemaphore(value: 1) + DispatchQueue.global().async { + for i in 0 ... 5 { + s.wait() + DispatchQueue.main.async { + Toast.find("progress", last: { (t) in + t.update(progress: CGFloat(i)/5) + print("\(CGFloat(i)/5)") + if i == 5 { + t.update { (vm) in + vm.scene = .success + vm.title = "同步成功" + vm.message = "xxx" + } } + }) + DispatchQueue.main.asyncAfter(deadline: .now()+1) { + s.signal() } - }) - DispatchQueue.main.asyncAfter(deadline: .now()+1) { - s.signal() } } } } } } - - } else if row == 2 { - let t = Toast.push(scene: .success, title: "同步成功", message: "点击查看详情") - t.didTapped { [weak self, weak t] in - self?.presentEmptyVC(title: "详情") - t?.pop() - } - } else if row == 3 { - Toast.push(scene: .error, title: "同步失败", message: "请稍后重试。点击查看详情") { (vc) in - vc.update { (vm) in - vm.duration = 0 - } - vc.didTapped { [weak self, weak vc] in - self?.presentEmptyVC(title: "这是错误详情") - vc?.pop() + // MARK: 场景:同步成功 + sec.addRow(title: "场景:同步成功") { + let t = Toast.push(scene: .success, title: "同步成功", message: "点击查看详情") + t.didTapped { [weak self, weak t] in + self?.presentEmptyVC(title: "详情") + t?.pop() } } - } else if row == 4 { - Toast.push(scene: .warning, title: "设备电量过低", message: "请及时对设备进行充电,以免影响使用。") - - } else if row == 5 { - Toast.push(scene: .default, title: "传入指定图标测试", message: "这是消息内容") { (vc) in - vc.update { (vm) in - if #available(iOS 13.0, *) { - vc.imageView.tintColor = .brown - vm.icon = UIImage(systemName: "icloud.and.arrow.down") - } else { - vm.icon = UIImage(named: "icloud.and.arrow.down") + // MARK: 场景:同步失败 + sec.addRow(title: "场景:同步失败") { + Toast.push(scene: .error, title: "同步失败", message: "请稍后重试。点击查看详情") { (vc) in + vc.update { (vm) in + vm.duration = 0 + } + vc.didTapped { [weak self, weak vc] in + self?.presentEmptyVC(title: "这是错误详情") + vc?.pop() } } } - } else if row == 6 { - Toast.push(scene: .default, title: "禁止手势移除", message: "这条消息无法通过向上滑动移出屏幕。5秒后自动消失,每次拖拽都会刷新倒计时。") { (vc) in - vc.isRemovable = false - vc.update { (vm) in - vm.duration = 5 + // MARK: 场景:设备电量过低 + sec.addRow(title: "场景:设备电量过低") { + Toast.push(scene: .default, title: "传入指定图标测试", message: "这是消息内容") { (vc) in + vc.update { (vm) in + if #available(iOS 13.0, *) { + vc.imageView.tintColor = .brown + vm.icon = UIImage(systemName: "icloud.and.arrow.down") + } else { + vm.icon = UIImage(named: "icloud.and.arrow.down") + } + } } } - } else if row == 7 { - Toast.push(scene: .message, title: "好友邀请", message: "你收到一条好友邀请,点击查看详情。", duration: 10) { (vc) in - vc.identifier = "xxx" - vc.didTapped { [weak vc] in - vc?.pop() - Alert.push(scene: .confirm, title: "好友邀请", message: "用户xxx想要添加你为好友,是否同意?") { (vc) in - vc.update { (vm) in - vm.add(action: .default, title: "接受") { [weak vc] in - vc?.pop() - Toast.push(scene: .success, title: "好友添加成功", message: "这是消息内容") - } - vm.add(action: .cancel, title: "拒绝") { - + } + + vm.addSection(title: "") { (sec) in + // MARK: 传入指定图标 + sec.addRow(title: "传入指定图标") { + Toast.push(scene: .warning, title: "设备电量过低", message: "请及时对设备进行充电,以免影响使用。") + } + // MARK: 禁止手势移除 + sec.addRow(title: "禁止手势移除") { + Toast.push(scene: .default, title: "禁止手势移除", message: "这条消息无法通过向上滑动移出屏幕。5秒后自动消失,每次拖拽都会刷新倒计时。") { (vc) in + vc.isRemovable = false + vc.update { (vm) in + vm.duration = 5 + } + } + } + // MARK: 组合使用示例 + sec.addRow(title: "组合使用示例") { + Toast.push(scene: .message, title: "好友邀请", message: "你收到一条好友邀请,点击查看详情。", duration: 10) { (vc) in + vc.identifier = "xxx" + vc.didTapped { [weak vc] in + vc?.pop() + Alert.push(scene: .confirm, title: "好友邀请", message: "用户xxx想要添加你为好友,是否同意?") { (vc) in + vc.update { (vm) in + vm.add(action: .default, title: "接受") { [weak vc] in + vc?.pop() + Toast.push(scene: .success, title: "好友添加成功", message: "这是消息内容") + } + vm.add(action: .cancel, title: "拒绝") { + + } } } } } } - } else if row == 8 { - if let t = Toast.find("aaa").last { - t.pulse() - t.update() { (vm) in - vm.title = "已经存在了" - } - } else { - Toast.push(title: "这是一条id为aaa的横幅", message: "避免重复发布同一条信息") { (t) in - t.identifier = "aaa" - t.update { (vm) in - vm.scene = .warning - vm.duration = 0 + // MARK: 避免重复发布同一条信息 + sec.addRow(title: "避免重复发布同一条信息") { + if let t = Toast.find("aaa").last { + t.pulse() + t.update() { (vm) in + vm.title = "已经存在了" + } + } else { + Toast.push(title: "这是一条id为aaa的横幅", message: "避免重复发布同一条信息") { (t) in + t.identifier = "aaa" + t.update { (vm) in + vm.scene = .warning + vm.duration = 0 + } } } } - } else if row == 9 { - Toast.push("aaa") { (t) in - t.update { (vm) in - vm.scene = .success - vm.title = "找到了哈哈" - vm.message = "根据id查找并修改实例" - } - t.pulse() - } - } else if row == 10 { - Toast.push() { (a) in - a.update { (vm) in - vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" - vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" - + // MARK: 根据id查找并修改实例 + sec.addRow(title: "根据id查找并修改实例") { + Toast.push("aaa") { (t) in + t.update { (vm) in + vm.scene = .success + vm.title = "找到了哈哈" + vm.message = "根据id查找并修改实例" + } + t.pulse() } } - } else if row == 11 { - Toast.push() { (a) in - a.update { (vm) in - vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" - vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" - + // MARK: 测试较长的标题和内容 + sec.addRow(title: "测试较长的标题和内容") { + Toast.push() { (a) in + a.update { (vm) in + vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" + vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" + + } } } - } else if row == 12 { - Toast.push() { (a) in - a.update { (vm) in - vm.scene = .warning - vm.title = "正在同步看到了你撒地" - + // MARK: 测试特别长的标题和内容 + sec.addRow(title: "测试特别长的标题和内容") { + Toast.push() { (a) in + a.update { (vm) in + vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" + vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" + + } } } - } else if row == 13 { - Toast.push() { (a) in - a.update { (vm) in - vm.scene = .warning - vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" - + // MARK: 测试只有title + sec.addRow(title: "测试只有title") { + Toast.push() { (a) in + a.update { (vm) in + vm.scene = .warning + vm.title = "正在同步看到了你撒地" + + } } } - } else if row == 14 { - - Toast.push(scene: .privacy, title: "正在授权", message: "请稍等片刻") { (t) in - t.identifier = "loading" - let imgv = UIImageView(image: UIImage(named: "prohud.rainbow.circle")) - t.imageView.addSubview(imgv) - imgv.snp.makeConstraints { (mk) in - mk.center.equalToSuperview() - mk.width.height.equalTo(18) + // MARK: 测试只有message + sec.addRow(title: "测试只有message") { + Toast.push() { (a) in + a.update { (vm) in + vm.scene = .warning + vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" + + } } - t.startRotate(imgv.layer, speed: 4) } - simulateSync() + // MARK: 自定义旋转的图片 + sec.addRow(title: "自定义旋转的图片") { + Toast.push(scene: .privacy, title: "正在授权", message: "请稍等片刻") { (t) in + t.identifier = "loading" + let imgv = UIImageView(image: UIImage(named: "prohud.rainbow.circle")) + t.imageView.addSubview(imgv) + imgv.snp.makeConstraints { (mk) in + mk.center.equalToSuperview() + mk.width.height.equalTo(18) + } + t.startRotate(imgv.layer, speed: 4) + } + self.simulateSync() + } } + } + func simulateSync() { - DispatchQueue.main.asyncAfter(deadline: .now() + 10) { - Toast.find("loading", last: { (t) in - t.update { (vm) in + DispatchQueue.main.asyncAfter(deadline: .now() + 15) { + Toast.find("loading", last: { (a) in + a.update { (vm) in vm.scene = .success vm.title = "同步成功" vm.message = "啊哈哈哈哈哈哈哈哈" diff --git a/Example-Old/Example/ViewController.swift b/Example-Old/Example/ViewController.swift index 373d23f..3448d54 100644 --- a/Example-Old/Example/ViewController.swift +++ b/Example-Old/Example/ViewController.swift @@ -16,27 +16,25 @@ class ViewController: BaseListVC { super.viewDidLoad() // Do any additional setup after loading the view. title = "\(Bundle.main.infoDictionary?["CFBundleName"] ?? "ProHUD")" - } - - override var titles: [String] { - return ["Toast", "Alert", "Guard"] - } - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - tableView.deselectRow(at: indexPath, animated: true) - if indexPath.row == 0 { - let vc = TestToastVC() - vc.title = titles[indexPath.row] - navigationController?.pushViewController(vc, animated: true) - } else if indexPath.row == 1 { - let vc = TestAlertVC() - vc.title = titles[indexPath.row] - navigationController?.pushViewController(vc, animated: true) - } else { - let vc = TestGuardVC() - vc.title = titles[indexPath.row] - navigationController?.pushViewController(vc, animated: true) + + vm.addSection(title: "") { (sec) in + sec.addRow(title: "Toast") { + let vc = TestToastVC() + vc.title = "Toast" + self.navigationController?.pushViewController(vc, animated: true) + } + sec.addRow(title: "Alert") { + let vc = TestAlertVC() + vc.title = "Alert" + self.navigationController?.pushViewController(vc, animated: true) + } + sec.addRow(title: "Guard") { + let vc = TestGuardVC() + vc.title = "Guard" + self.navigationController?.pushViewController(vc, animated: true) + } } + } } diff --git a/Source/Alert/AlertController.swift b/Source/Alert/AlertController.swift index 466dbcf..c8728eb 100644 --- a/Source/Alert/AlertController.swift +++ b/Source/Alert/AlertController.swift @@ -63,10 +63,10 @@ public extension ProHUD { /// - Parameter title: 标题 /// - Parameter message: 内容 /// - Parameter icon: 图标 - public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, actions: ((Alert) -> Void)? = nil) { + public convenience init(scene: Scene?, title: String? = nil, message: String? = nil, actions: ((Alert) -> Void)? = nil) { self.init() vm.vc = self - vm.scene = scene + vm.scene = scene ?? .default vm.title = title vm.message = message actions?(self) @@ -148,7 +148,7 @@ public extension Alert { } - /// 最小化事件 + /// 强制关闭弹窗的事件 /// - Parameter callback: 事件回调 func didForceQuit(_ callback: (() -> Void)?) { vm.forceQuitCallback = callback @@ -177,14 +177,19 @@ public extension Alert { /// - identifier: 唯一标识 /// - toast: 实例对象 /// - Returns: 回调 - @discardableResult class func push(_ identifier: String, instance: @escaping (Alert) -> Void) -> Alert { + @discardableResult class func push(_ identifier: String, scene: ProHUD.Scene? = nil, instance: ((Alert) -> Void)? = nil) -> Alert { if let a = find(identifier).last { - instance(a) + if let s = scene, s != a.vm.scene { + a.update { (vm) in + vm.scene = s + } + } + instance?(a) return a } else { - return Alert() { (aa) in + return Alert(scene: scene) { (aa) in aa.identifier = identifier - instance(aa) + instance?(aa) }.push() } } diff --git a/Source/Toast/ToastController.swift b/Source/Toast/ToastController.swift index 68de5a3..ca9268c 100644 --- a/Source/Toast/ToastController.swift +++ b/Source/Toast/ToastController.swift @@ -75,11 +75,11 @@ public extension ProHUD { /// - Parameter title: 标题 /// - Parameter message: 内容 /// - Parameter icon: 图标 - public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, icon: UIImage? = nil, duration: TimeInterval? = nil, actions: ((Toast) -> Void)? = nil) { + public convenience init(scene: Scene?, title: String? = nil, message: String? = nil, icon: UIImage? = nil, duration: TimeInterval? = nil, actions: ((Toast) -> Void)? = nil) { self.init() vm.vc = self - - vm.scene = scene + + vm.scene = scene ?? .default vm.title = title vm.message = message vm.icon = icon @@ -227,14 +227,19 @@ public extension Toast { /// - identifier: 唯一标识 /// - toast: 实例对象 /// - Returns: 回调 - @discardableResult class func push(_ identifier: String, instance: @escaping (Toast) -> Void) -> Toast { + @discardableResult class func push(_ identifier: String, scene: ProHUD.Scene? = nil, instance: ((Toast) -> Void)? = nil) -> Toast { if let t = find(identifier).last { - instance(t) + if let s = scene, s != t.vm.scene { + t.update { (vm) in + vm.scene = s + } + } + instance?(t) return t } else { - return Toast() { (tt) in + return Toast(scene: scene) { (tt) in tt.identifier = identifier - instance(tt) + instance?(tt) }.push() } }