diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 2855b97..61798b7 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -8,8 +8,11 @@ /* Begin PBXBuildFile section */ 33E2B6CF0D9BD11D8C027DE6 /* Pods_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB925B38880FB296AF5D219F /* Pods_Example.framework */; }; - CD10F0DA211582580077CAFF /* header.gif in Resources */ = {isa = PBXBuildFile; fileRef = CD10F0D9211582580077CAFF /* header.gif */; }; - CD95D26F22E732CE007559A3 /* TestA.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD95D26E22E732CE007559A3 /* TestA.swift */; }; + CD8BFF1823014850001E08DD /* TestToastVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8BFF1723014850001E08DD /* TestToastVC.swift */; }; + CD8BFF1A2301485E001E08DD /* TestAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8BFF192301485E001E08DD /* TestAlertVC.swift */; }; + CD8BFF1C23014867001E08DD /* TestGuardVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8BFF1B23014867001E08DD /* TestGuardVC.swift */; }; + CD8BFF1E230148DD001E08DD /* BaseListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8BFF1D230148DD001E08DD /* BaseListVC.swift */; }; + CD8BFF2023014CB5001E08DD /* EmptyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8BFF1F23014CB5001E08DD /* EmptyVC.swift */; }; CDA4E03C20D3935B00CD2A0C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDA4E03B20D3935B00CD2A0C /* AppDelegate.swift */; }; CDA4E03E20D3935B00CD2A0C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDA4E03D20D3935B00CD2A0C /* ViewController.swift */; }; CDA4E04120D3935B00CD2A0C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CDA4E03F20D3935B00CD2A0C /* Main.storyboard */; }; @@ -21,9 +24,12 @@ AB925B38880FB296AF5D219F /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BA1A6035F1B9A658B3BB225C /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = ""; }; CA1298266BE89D7950DE99F2 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = ""; }; - CD10F0D9211582580077CAFF /* header.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = header.gif; sourceTree = ""; }; CD59584620E36DA8000F6427 /* Example-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Example-Bridging-Header.h"; sourceTree = ""; }; - CD95D26E22E732CE007559A3 /* TestA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestA.swift; sourceTree = ""; }; + CD8BFF1723014850001E08DD /* TestToastVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestToastVC.swift; sourceTree = ""; }; + CD8BFF192301485E001E08DD /* TestAlertVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestAlertVC.swift; sourceTree = ""; }; + CD8BFF1B23014867001E08DD /* TestGuardVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestGuardVC.swift; sourceTree = ""; }; + CD8BFF1D230148DD001E08DD /* BaseListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseListVC.swift; sourceTree = ""; }; + CD8BFF1F23014CB5001E08DD /* EmptyVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyVC.swift; sourceTree = ""; }; CDA4E03820D3935B00CD2A0C /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; CDA4E03B20D3935B00CD2A0C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; CDA4E03D20D3935B00CD2A0C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -84,11 +90,14 @@ isa = PBXGroup; children = ( CDA4E03B20D3935B00CD2A0C /* AppDelegate.swift */, + CD8BFF1D230148DD001E08DD /* BaseListVC.swift */, + CD8BFF1F23014CB5001E08DD /* EmptyVC.swift */, CDA4E03D20D3935B00CD2A0C /* ViewController.swift */, - CD95D26E22E732CE007559A3 /* TestA.swift */, + CD8BFF1723014850001E08DD /* TestToastVC.swift */, + CD8BFF192301485E001E08DD /* TestAlertVC.swift */, + CD8BFF1B23014867001E08DD /* TestGuardVC.swift */, CDA4E03F20D3935B00CD2A0C /* Main.storyboard */, CDA4E04220D3935C00CD2A0C /* Assets.xcassets */, - CD10F0D9211582580077CAFF /* header.gif */, CDA4E04420D3935C00CD2A0C /* LaunchScreen.storyboard */, CDA4E04720D3935C00CD2A0C /* Info.plist */, CD59584620E36DA8000F6427 /* Example-Bridging-Header.h */, @@ -161,7 +170,6 @@ CDA4E04620D3935C00CD2A0C /* LaunchScreen.storyboard in Resources */, CDA4E04320D3935C00CD2A0C /* Assets.xcassets in Resources */, CDA4E04120D3935B00CD2A0C /* Main.storyboard in Resources */, - CD10F0DA211582580077CAFF /* header.gif in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -230,9 +238,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CD8BFF1823014850001E08DD /* TestToastVC.swift in Sources */, CDA4E03E20D3935B00CD2A0C /* ViewController.swift in Sources */, + CD8BFF2023014CB5001E08DD /* EmptyVC.swift in Sources */, + CD8BFF1A2301485E001E08DD /* TestAlertVC.swift in Sources */, CDA4E03C20D3935B00CD2A0C /* AppDelegate.swift in Sources */, - CD95D26F22E732CE007559A3 /* TestA.swift in Sources */, + CD8BFF1C23014867001E08DD /* TestGuardVC.swift in Sources */, + CD8BFF1E230148DD001E08DD /* BaseListVC.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Example/Example/Assets.xcassets/alert-circle.imageset/Contents.json b/Example/Example/Assets.xcassets/alert-circle.imageset/Contents.json deleted file mode 100644 index 78cde9e..0000000 --- a/Example/Example/Assets.xcassets/alert-circle.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "alert-circle.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "alert-circle (1).png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/Example/Example/Assets.xcassets/alert-circle.imageset/alert-circle (1).png b/Example/Example/Assets.xcassets/alert-circle.imageset/alert-circle (1).png deleted file mode 100644 index 0088b5f..0000000 Binary files a/Example/Example/Assets.xcassets/alert-circle.imageset/alert-circle (1).png and /dev/null differ diff --git a/Example/Example/Assets.xcassets/alert-circle.imageset/alert-circle.png b/Example/Example/Assets.xcassets/alert-circle.imageset/alert-circle.png deleted file mode 100644 index edd5e44..0000000 Binary files a/Example/Example/Assets.xcassets/alert-circle.imageset/alert-circle.png and /dev/null differ diff --git a/Example/Example/Assets.xcassets/header_center.imageset/Contents.json b/Example/Example/Assets.xcassets/header_center.imageset/Contents.json deleted file mode 100644 index 028aba0..0000000 --- a/Example/Example/Assets.xcassets/header_center.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "header_center@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "header_center@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/Example/Assets.xcassets/header_center.imageset/header_center@2x.png b/Example/Example/Assets.xcassets/header_center.imageset/header_center@2x.png deleted file mode 100644 index e56bf3e..0000000 Binary files a/Example/Example/Assets.xcassets/header_center.imageset/header_center@2x.png and /dev/null differ diff --git a/Example/Example/Assets.xcassets/header_center.imageset/header_center@3x.png b/Example/Example/Assets.xcassets/header_center.imageset/header_center@3x.png deleted file mode 100644 index f550546..0000000 Binary files a/Example/Example/Assets.xcassets/header_center.imageset/header_center@3x.png and /dev/null differ diff --git a/Example/Example/Base.lproj/Main.storyboard b/Example/Example/Base.lproj/Main.storyboard index ded2e01..0d51b02 100644 --- a/Example/Example/Base.lproj/Main.storyboard +++ b/Example/Example/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -8,42 +8,39 @@ - + - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + - - - diff --git a/Example/Example/BaseListVC.swift b/Example/Example/BaseListVC.swift new file mode 100644 index 0000000..6360fd6 --- /dev/null +++ b/Example/Example/BaseListVC.swift @@ -0,0 +1,55 @@ +// +// BaseListVC.swift +// Example +// +// Created by xaoxuu on 2019/8/12. +// Copyright © 2019 Titan Studio. All rights reserved. +// + +import UIKit + +class BaseListVC: UIViewController { + + lazy var tableView: UITableView = { + let tv = UITableView() + + return tv + }() + + var titles: [String] { + return ["Toast", "Alert", "Guard"] + } + + override func viewDidLoad() { + super.viewDidLoad() + + + 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 tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return titles.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + cell.textLabel?.text = titles[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + } + +} diff --git a/Example/Example/EmptyVC.swift b/Example/Example/EmptyVC.swift new file mode 100644 index 0000000..e1afacb --- /dev/null +++ b/Example/Example/EmptyVC.swift @@ -0,0 +1,57 @@ +// +// EmptyVC.swift +// Example +// +// Created by xaoxuu on 2019/8/12. +// Copyright © 2019 Titan Studio. All rights reserved. +// + +import UIKit +import SnapKit +import Inspire + +class EmptyVC: UIViewController { + + + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = UIColor(white: 0.95, alpha: 1) + // Do any additional setup after loading the view. + + let lb = UILabel() + lb.numberOfLines = 0 + lb.text = title + lb.font = .regular(40) + view.addSubview(lb) + lb.snp.makeConstraints { (mk) in + mk.center.equalToSuperview() + mk.leading.greaterThanOrEqualToSuperview().offset(16) + mk.trailing.lessThanOrEqualToSuperview().offset(-16) + } + + let btn = UIButton(type: .system) + btn.titleLabel?.font = .bold(20) + btn.setTitle("Dismiss", for: .normal) + btn.addTarget(self, action: #selector(didTappedDismiss(_:)), for: .touchUpInside) + view.addSubview(btn) + btn.snp.makeConstraints { (mk) in + mk.top.equalToSuperview().offset(Inspire.current.layout.safeAreaInsets(for: self).top) + mk.trailing.equalToSuperview().offset(-16) + mk.height.equalTo(44) + } + } + + @objc func didTappedDismiss(_ sender: UIButton) { + dismiss(animated: true, completion: nil) + } + +} + +extension UIViewController { + func presentEmptyVC(title: String?) { + let vc = EmptyVC() + vc.title = title + present(vc, animated: true, completion: nil) + } +} diff --git a/Example/Example/TestA.swift b/Example/Example/TestA.swift deleted file mode 100644 index f159b82..0000000 --- a/Example/Example/TestA.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// TestA.swift -// ProHUDExample -// -// Created by xaoxuu on 2019/7/23. -// Copyright © 2019 Titan Studio. All rights reserved. -// - -import UIKit - -public class TestA: NSObject { - - class func test1() { - print(self, "test1") - } - class func test2() { - print(self, "test2") - } - -} - - -open class TestB: NSObject { - - - class func test1() { - print(self, "test1") - } - open class func test2() { - print(self, "test2") - } - - -} - -class TestAA: TestA { - - override class func test2() { - print(self, "test2", "override") - } - -} - -class TestBB: TestB { - - override class func test2() { - print(self, "test2", "override") - } -} diff --git a/Example/Example/TestAlertVC.swift b/Example/Example/TestAlertVC.swift new file mode 100644 index 0000000..9caba64 --- /dev/null +++ b/Example/Example/TestAlertVC.swift @@ -0,0 +1,102 @@ +// +// TestAlertVC.swift +// Example +// +// Created by xaoxuu on 2019/8/12. +// Copyright © 2019 Titan Studio. All rights reserved. +// + +import UIKit +import ProHUD + +class TestAlertVC: BaseListVC { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override var titles: [String] { + return ["场景:正在同步(超时)", "场景:同步成功", "场景:同步失败和重试"] + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + let row = indexPath.row + if row == 0 { + func f() { + let a = Alert.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (vm) in + vm.identifier = "loading" + } + a.animate(rotate: true) + a.didForceQuit { [weak self] in + let t = Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻(点击展开为Alert)") { (vm) in + vm.identifier = "loading" + } + t.animate(rotate: true) + t.didTapped { [weak t] in + t?.pop() + f() + } + self?.simulateSync() + } + simulateSync() + } + f() + } else if row == 1 { + Alert.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (vm) in + vm.identifier = "loading" + }.animate(rotate: true) + DispatchQueue.main.asyncAfter(deadline: .now()+2) { + if let a = Alert.get("loading").last { + a.update { (vm) in + vm.scene = .success + vm.title = "同步成功" + vm.message = nil + } + } + } + } else if row == 2 { + Alert.push() { (vm) in + vm.identifier = "loading" + } + func loading() { + if let a = Alert.get("loading").last { + a.update { (vm) in + vm.scene = .loading + vm.title = "正在同步" + vm.message = "请稍等片刻" + vm.remove(action: 0, 1) + } + a.animate(rotate: true) + 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() + } + } + + func simulateSync() { + DispatchQueue.main.asyncAfter(deadline: .now() + 15) { + if let t = Alert.get("loading").last { + t.update { (vm) in + vm.scene = .success + vm.title = "同步成功" + vm.message = "啊哈哈哈哈哈哈哈哈" + } + } + } + } + +} diff --git a/Example/Example/TestGuardVC.swift b/Example/Example/TestGuardVC.swift new file mode 100644 index 0000000..d5e6278 --- /dev/null +++ b/Example/Example/TestGuardVC.swift @@ -0,0 +1,100 @@ +// +// TestGuardVC.swift +// Example +// +// Created by xaoxuu on 2019/8/12. +// Copyright © 2019 Titan Studio. All rights reserved. +// + +import UIKit +import ProHUD +import Inspire + +class TestGuardVC: BaseListVC { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override var titles: [String] { + return ["场景:删除菜单", "场景:升级至专业版", "场景:隐私协议页面"] + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + let row = indexPath.row + if row == 0 { + Guard.push(to: self.navigationController) { (vm) in + let vc = vm.vc + vm.add(action: .destructive, title: "删除") { + Alert.push(scene: .delete, title: "确认删除", message: "此操作不可撤销") { (vm) in + let vc = vm.vc + vm.add(action: .destructive, title: "删除") { + vc?.pop() + } + vm.add(action: .cancel, title: "取消", handler: nil) + } + vc?.pop() + } + vm.add(action: .cancel, title: "取消", handler: nil) + } + } else if row == 1 { + // 可以通过id来避免重复 + if Guard.get("pro", from: self.navigationController).count == 0 { + Guard.push(to: self.navigationController) { (vm) in + let vc = vm.vc + vm.identifier = "pro" + vm.add(title: "升级至专业版") + vm.add(subTitle: "解锁功能") + vm.add(message: "功能1功能2...") + vm.add(subTitle: "价格") + vm.add(message: "只需一次性付费$2999即可永久享用。") + vm.add(action: .destructive, title: "购买") { [weak vc] in + Alert.push(scene: .confirm, title: "确认购买", message: "一旦购买拒不退款") { (vm) in + let vc = vm.vc + 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.add(action: .cancel, title: "取消", handler: nil) + } + vc?.pop() + } + vm.add(action: .cancel, title: "取消", handler: nil) + } + } + + } else if row == 2 { + let g = Guard.push(to: self.navigationController) { (vm) in + let vc = vm.vc + vc?.isFullScreen = true + let titleLabel = vm.add(title: "隐私协议") + titleLabel.snp.makeConstraints { (mk) in + mk.height.equalTo(44) + } + let tv = UITextView() + tv.backgroundColor = .white + vc?.textStack.addArrangedSubview(tv) + tv.text = "这里可以插入一个webView" + vm.add(message: "请认真阅读以上内容,当您阅读完毕并同意协议内容时点击接受按钮。") + + vm.add(action: .default, title: "接受") { [weak vc] in + vc?.pop() + } + + } + + } + } + + +} diff --git a/Example/Example/TestToastVC.swift b/Example/Example/TestToastVC.swift new file mode 100644 index 0000000..266b184 --- /dev/null +++ b/Example/Example/TestToastVC.swift @@ -0,0 +1,97 @@ +// +// TestToastVC.swift +// Example +// +// Created by xaoxuu on 2019/8/12. +// Copyright © 2019 Titan Studio. All rights reserved. +// + +import UIKit +import ProHUD + +class TestToastVC: BaseListVC { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override var titles: [String] { + return ["场景:正在同步", + "场景:同步成功", + "场景:同步失败", + "场景:设备电量过低", + "传入指定图标", + "禁止手势移除", + "组合使用示例"] + } + + 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" + }.animate(rotate: true) + simulateSync() + } else if row == 1 { + let t = Toast.push(scene: .success, title: "同步成功", message: "点击查看详情") + t.didTapped { [weak self, weak t] in + self?.presentEmptyVC(title: "详情") + t?.pop() + } + } else if row == 2 { + let t = Toast.push(scene: .error, title: "同步失败", message: "请稍后重试。点击查看详情") { (vm) in + vm.duration = 0 + } + t.didTapped { [weak self, weak t] in + self?.presentEmptyVC(title: "这是错误详情") + t?.pop() + } + + } else if row == 3 { + Toast.push(scene: .warning, title: "设备电量过低", message: "请及时对设备进行充电,以免影响使用。") + + } else if row == 4 { + Toast.push(scene: .default, title: "传入指定图标测试", message: "这是消息内容") { (vm) in + vm.icon = UIImage(named: "icon_download") + } + } else if row == 5 { + Toast.push(scene: .default, title: "禁止手势移除", message: "这条消息无法通过向上滑动移出屏幕。5秒后自动消失,每次拖拽都会刷新倒计时。") { (vm) in + vm.removable = false + vm.duration = 5 + } + } else if row == 6 { + let t = Toast.push(scene: .default, title: "好友邀请", message: "你收到一条好友邀请,点击查看详情。", duration: 10) + + t.didTapped { [weak t] in + t?.pop() + Alert.push(scene: .confirm, title: "好友邀请", message: "用户xxx想要添加你为好友,是否同意?") { (vm) in + let vc = vm.vc + vm.add(action: .default, title: "接受") { + vc?.pop() + Toast.push(scene: .success, title: "好友添加成功", message: "这是消息内容") + } + vm.add(action: .cancel, title: "拒绝") { + + } + } + } + } + + } + + func simulateSync() { + DispatchQueue.main.asyncAfter(deadline: .now() + 5) { + if let t = Toast.get("loading").last { + t.update { (vm) in + vm.scene = .success + vm.title = "同步成功" + vm.message = "啊哈哈哈哈哈哈哈哈" + } + } + } + } + +} diff --git a/Example/Example/ViewController.swift b/Example/Example/ViewController.swift index 3838190..c46bb40 100644 --- a/Example/Example/ViewController.swift +++ b/Example/Example/ViewController.swift @@ -8,8 +8,10 @@ import UIKit import ProHUD +import SnapKit -class ViewController: UIViewController { +class ViewController: BaseListVC { + override func viewDidLoad() { super.viewDidLoad() @@ -20,14 +22,17 @@ class ViewController: UIViewController { cfg.rootViewController = self cfg.alert { (a) in - a.duration = 1 + +// a.durationForScene { (s) -> TimeInterval? in +// return 1 +// } a.forceQuitTimer = 3 // a.iconSize = .init(width: 20, height: 80) // a.reloadData // a.iconSize = .init(width: 20, height: 80) - a.iconForScene { (s) -> UIImage? in - return UIImage(named: "icon_download") - } +// a.iconForScene { (s) -> UIImage? in +// return UIImage(named: "icon_download") +// } } cfg.toast { (t) in @@ -39,15 +44,21 @@ class ViewController: UIViewController { } } + } + override var titles: [String] { + return ["Toast", "Alert", "Guard"] + } - @IBAction func test(_ sender: UIButton) { -// testAlert() - testToast() -// testUpdateAction() -// testGuard() -// fastGuard() + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if indexPath.row == 0 { + navigationController?.pushViewController(TestToastVC(), animated: true) + } else if indexPath.row == 1 { + navigationController?.pushViewController(TestAlertVC(), animated: true) + } else { + navigationController?.pushViewController(TestGuardVC(), animated: true) + } } func testAlert() { @@ -59,53 +70,6 @@ class ViewController: UIViewController { vm.add(action: .default, title: "OK", handler: nil) } -// a.update() -// Alert.push(scene: .loading, title: "Loading") { (a) in -// a.animate(rotate: true) -// DispatchQueue.main.asyncAfter(deadline: .now()+1) { -// a.update { (vm) in -// vm.message = "请稍后片刻" -// } -// a.animate(rotate: true) -// } -// DispatchQueue.main.asyncAfter(deadline: .now()+2) { -// a.update { (vm) in -// vm.message = "请稍后片刻请稍后片刻" -// } -// a.animate(rotate: true) -// } -// DispatchQueue.main.asyncAfter(deadline: .now()+3) { -// a.update { (vm) in -// vm.scene = .success -// vm.add(action: .default, title: "OK") { [weak a] in -// a?.pop() -// } -// } -// } -// DispatchQueue.main.asyncAfter(deadline: .now()+4) { -// a.update { (vm) in -// vm.update(action: 0, style: .cancel, title: "Cancel", handler: nil) -// } -// } -// } -// Alert.push(scene: .delete, title: "确认删除", message: "此操作不可撤销!此操作不可撤销!此操作不可撤销!") { (a) in -// a.identifier = "" -// DispatchQueue.main.asyncAfter(deadline: .now()+1) { -// a.update { (vm) in -// vm.add(action: .destructive, title: "确认") { [weak a] in -// a?.update({ (vm) in -// vm.message = "但是饭撒 打算放过" -// vm.remove(action: 1) -// vm.update(action: 0, style: .destructive, title: "确认", handler: { -// a?.pop() -// }) -// }) -// } -// vm.add(action: .cancel, title: "取消", handler: nil) -// } -// } -// -// } } func testDelete() { diff --git a/Example/Example/header.gif b/Example/Example/header.gif deleted file mode 100644 index e62e8b1..0000000 Binary files a/Example/Example/header.gif and /dev/null differ diff --git a/ProHUD/Alert/AlertConfig.swift b/ProHUD/Alert/AlertConfig.swift index 0d075e6..0e056a0 100644 --- a/ProHUD/Alert/AlertConfig.swift +++ b/ProHUD/Alert/AlertConfig.swift @@ -11,7 +11,7 @@ import SnapKit import Inspire public extension ProHUD.Configuration { - class Alert { + struct Alert { // MARK: 卡片样式 /// 最大宽度(用于优化横屏或者iPad显示) public var maxWidth = CGFloat(400) @@ -59,8 +59,10 @@ public extension ProHUD.Configuration { privReloadData = callback } - /// 非Loading弹窗的默认持续时间 - public var duration = TimeInterval(2) + /// 默认持续时间(当viewmodel的duration为nil时,会从这里获取) + public func durationForScene(_ callback: @escaping (ProHUD.Alert.Scene) -> TimeInterval?) { + privDurationForScene = callback + } /// 多少秒后显示强制退出的按钮(只有无按钮的弹窗才会出现) public var forceQuitTimer = TimeInterval(30) @@ -81,9 +83,15 @@ public extension ProHUD.Configuration { // MARK: - 内部调用 internal extension ProHUD.Configuration.Alert { + var reloadData: (ProHUD.Alert) -> Void { return privReloadData } + + var durationForScene: (ProHUD.Alert.Scene) -> TimeInterval? { + return privDurationForScene + } + } @@ -233,11 +241,11 @@ fileprivate var privUpdateTextStack: (ProHUD.Alert) -> Void = { fileprivate var privUpdateActionStack: (ProHUD.Alert) -> Void = { return { (vc) in let config = cfg.alert - if vc.buttonEvents.count > 0 { + if vc.actionStack.arrangedSubviews.count > 0 { // 有按钮 vc.contentStack.addArrangedSubview(vc.actionStack) // 适配横竖屏和iPad - if isPortrait == false && vc.buttonEvents.count < 4 { + if isPortrait == false && vc.actionStack.arrangedSubviews.count < 4 { vc.actionStack.axis = .horizontal vc.actionStack.alignment = .fill vc.actionStack.distribution = .fillEqually @@ -332,14 +340,8 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = { } } - // 设置默认持续时间 - if vc.vm.duration == nil { - if vc.vm.scene == .loading { - vc.vm.duration = 0 - } else { - vc.vm.duration = config.duration - } - } + // 设置持续时间 + vc.vm.updateDuration() // 强制退出按钮 vc.vm.forceQuitTimerBlock?.cancel() @@ -358,3 +360,15 @@ 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 d93e879..3d89da4 100644 --- a/ProHUD/Alert/AlertController.swift +++ b/ProHUD/Alert/AlertController.swift @@ -125,7 +125,9 @@ public extension Alert { UIView.animateForAlertBuildOut(animations: { window.backgroundColor = window.backgroundColor?.withAlphaComponent(0) }) { (done) in - Alert.alertWindow = nil + if Alert.alerts.count == 0 { + Alert.alertWindow = nil + } } } } @@ -178,13 +180,13 @@ public extension Alert { /// - Parameter title: 标题 /// - Parameter message: 正文 /// - Parameter actions: 更多操作 - @discardableResult class func push(scene: Alert.Scene = .default, title: String? = nil, message: String? = nil, actions: ((inout ViewModel) -> Void)? = nil) -> Alert { + @discardableResult class func push(scene: Alert.Scene = .default, title: String? = nil, message: String? = nil, _ actions: ((inout ViewModel) -> Void)? = nil) -> Alert { return Alert(scene: scene, title: title, message: message, actions: actions).push() } /// 获取指定的实例 /// - Parameter identifier: 指定实例的标识 - class func alerts(_ identifier: String?) -> [Alert] { + class func get(_ identifier: String?) -> [Alert] { var aa = [Alert]() for a in Alert.alerts { if a.vm.identifier == identifier { @@ -203,7 +205,7 @@ public extension Alert { /// 弹出屏幕 /// - Parameter identifier: 指定实例的标识 class func pop(_ identifier: String?) { - for a in alerts(identifier) { + for a in get(identifier) { a.pop() } } @@ -271,10 +273,16 @@ internal extension Alert { for view in self.actionStack.arrangedSubviews { if let btn = view as? UIButton { btn.removeFromSuperview() + if let _ = buttonEvents[btn] { + buttonEvents.removeValue(forKey: btn) + } } } } else if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton { btn.removeFromSuperview() + if let _ = buttonEvents[btn] { + buttonEvents.removeValue(forKey: btn) + } } if self.actionStack.arrangedSubviews.count == 0 { self.actionStack.removeFromSuperview() diff --git a/ProHUD/Alert/AlertModel.swift b/ProHUD/Alert/AlertModel.swift index f38c438..b536271 100644 --- a/ProHUD/Alert/AlertModel.swift +++ b/ProHUD/Alert/AlertModel.swift @@ -65,15 +65,7 @@ public extension Alert { /// 持续时间(为空代表根据场景不同的默认配置,为0代表无穷大) public var duration: TimeInterval? { didSet { - durationBlock?.cancel() - if let t = duration, t > 0 { - durationBlock = DispatchWorkItem(block: { [weak self] in - self?.vc?.pop() - }) - DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: durationBlock!) - } else { - durationBlock = nil - } + updateDuration() } } @@ -90,6 +82,18 @@ public extension Alert { /// 强制退出代码 internal var forceQuitCallback: (() -> Void)? + internal func updateDuration() { + durationBlock?.cancel() + if let t = duration ?? cfg.alert.durationForScene(scene), t > 0 { + durationBlock = DispatchWorkItem(block: { [weak self] in + self?.vc?.pop() + }) + DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: durationBlock!) + } else { + durationBlock = nil + } + } + } } diff --git a/ProHUD/Guard/GuardConfig.swift b/ProHUD/Guard/GuardConfig.swift index 0e25973..153af26 100644 --- a/ProHUD/Guard/GuardConfig.swift +++ b/ProHUD/Guard/GuardConfig.swift @@ -97,6 +97,9 @@ fileprivate var privReloadData: (ProHUD.Guard) -> Void = { vc.contentView.layer.shadowOpacity = 0.12 } vc.contentView.snp.makeConstraints { (mk) in + if isPortrait && vc.isFullScreen { + mk.top.equalToSuperview() + } mk.centerX.equalToSuperview() if UIDevice.current.userInterfaceIdiom == .phone { if width < config.cardMaxWidth { @@ -111,7 +114,11 @@ fileprivate var privReloadData: (ProHUD.Guard) -> Void = { } // stack vc.contentStack.snp.makeConstraints { (mk) in - mk.top.equalToSuperview().offset(config.padding) + if isPortrait && vc.isFullScreen { + mk.top.equalToSuperview().offset(Inspire.shared.screen.safeAreaInsets.top) + } else { + mk.top.equalToSuperview().offset(config.padding) + } mk.centerX.equalToSuperview() if width < config.cardMaxWidth { let bottom = Inspire.shared.screen.safeAreaInsets.bottom diff --git a/ProHUD/Guard/GuardController.swift b/ProHUD/Guard/GuardController.swift index 3218285..9daf62b 100644 --- a/ProHUD/Guard/GuardController.swift +++ b/ProHUD/Guard/GuardController.swift @@ -7,6 +7,7 @@ // import SnapKit +import Inspire public typealias Guard = ProHUD.Guard @@ -44,11 +45,14 @@ public extension ProHUD { /// 是否是强制性的(点击空白处是否可以消失) public var force = false + /// 是否是全屏的(仅手机竖屏有效) + public var isFullScreen = false + /// 是否正在显示 private var displaying = false /// 背景颜色 - public var backgroundColor: UIColor? = UIColor(white: 0, alpha: 0.5) + public var backgroundColor: UIColor? = UIColor(white: 0, alpha: 0.4) public var vm = ViewModel() @@ -142,7 +146,9 @@ public extension Guard { cfg.guard.reloadData(self) } - + func willAppear(_ callback: (() -> Void)?) { + willAppearCallback = callback + } /// 消失事件 /// - Parameter callback: 事件回调 func didDisappear(_ callback: (() -> Void)?) { @@ -159,13 +165,13 @@ public extension Guard { /// - Parameter title: 标题 /// - Parameter message: 正文 /// - Parameter icon: 图标 - @discardableResult class func push(to viewController: UIViewController? = nil, actions: ((inout ViewModel) -> Void)? = nil) -> Guard { + @discardableResult class func push(to viewController: UIViewController? = nil, _ actions: ((inout ViewModel) -> Void)? = nil) -> Guard { return Guard(actions: actions).push(to: viewController) } /// 获取指定的实例 /// - Parameter identifier: 指定实例的标识 - class func guards(_ identifier: String? = nil, from viewController: UIViewController? = nil) -> [Guard] { + class func get(_ identifier: String? = nil, from viewController: UIViewController? = nil) -> [Guard] { var gg = [Guard]() if let vc = viewController ?? cfg.rootViewController { for child in vc.children { @@ -194,7 +200,7 @@ public extension Guard { /// 弹出所有实例 /// - Parameter identifier: 指定实例的标识 class func pop(from viewController: UIViewController?) { - for g in guards(from: viewController) { + for g in get(from: viewController) { g.pop() } } @@ -304,10 +310,16 @@ internal extension Guard { for view in self.actionStack.arrangedSubviews { if let btn = view as? UIButton { btn.removeFromSuperview() + if let _ = buttonEvents[btn] { + buttonEvents.removeValue(forKey: btn) + } } } } else if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton { btn.removeFromSuperview() + if let _ = buttonEvents[btn] { + buttonEvents.removeValue(forKey: btn) + } } cfg.guard.reloadStack(self) UIView.animateForAlert { diff --git a/ProHUD/HUDController.swift b/ProHUD/HUDController.swift index af7b0a5..327dda4 100644 --- a/ProHUD/HUDController.swift +++ b/ProHUD/HUDController.swift @@ -13,6 +13,8 @@ public class HUDController: UIViewController { /// 消失回调 internal var disappearCallback: (() -> Void)? + internal var willAppearCallback: (() -> Void)? + /// 按钮事件 internal var buttonEvents = [UIButton:() -> Void]() @@ -36,6 +38,11 @@ public class HUDController: UIViewController { // Do any additional setup after loading the view. } + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + willAppearCallback?() + } + public override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) disappearCallback?() diff --git a/ProHUD/Toast/ToastConfig.swift b/ProHUD/Toast/ToastConfig.swift index 0861868..cc858da 100644 --- a/ProHUD/Toast/ToastConfig.swift +++ b/ProHUD/Toast/ToastConfig.swift @@ -46,8 +46,10 @@ public extension ProHUD.Configuration { privReloadData = callback } - /// 非Loading弹窗的默认持续时间 - public var duration = TimeInterval(3) + /// 默认持续时间(当viewmodel的duration为nil时,会从这里获取) + public mutating func durationForScene(_ callback: @escaping (ProHUD.Toast.Scene) -> TimeInterval?) { + privDurationForScene = callback + } } } @@ -60,6 +62,10 @@ internal extension ProHUD.Configuration.Toast { return privReloadData } + var durationForScene: (ProHUD.Toast.Scene) -> TimeInterval? { + return privDurationForScene + } + } // MARK: - 默认实现 @@ -79,6 +85,7 @@ fileprivate var privReloadData: (ProHUD.Toast) -> Void = { } // 设置数据 vc.imageView.image = vc.vm.icon ?? privIconForScene(vc.vm.scene) + vc.imageView.layer.removeAllAnimations() vc.titleLabel.textColor = cfg.primaryLabelColor vc.titleLabel.text = vc.vm.title vc.bodyLabel.textColor = cfg.secondaryLabelColor @@ -104,14 +111,10 @@ fileprivate var privReloadData: (ProHUD.Toast) -> Void = { } vc.view.layoutIfNeeded() - // 设置默认持续时间 - if vc.vm.duration == nil { - if vc.vm.scene == .loading { - vc.vm.duration = 0 - } else { - vc.vm.duration = config.duration - } - } + + // 设置持续时间 + vc.vm.updateDuration() + } }() @@ -136,3 +139,17 @@ fileprivate var privIconForScene: (ProHUD.Toast.Scene) -> UIImage? = { 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 da19233..69bf920 100644 --- a/ProHUD/Toast/ToastController.swift +++ b/ProHUD/Toast/ToastController.swift @@ -65,7 +65,7 @@ public extension ProHUD { /// - Parameter title: 标题 /// - Parameter message: 内容 /// - Parameter icon: 图标 - public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, icon: UIImage? = nil, actions: ((inout ViewModel) -> Void)? = nil) { + public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, icon: UIImage? = nil, duration: TimeInterval? = nil, actions: ((inout ViewModel) -> Void)? = nil) { self.init() vm.vc = self @@ -73,6 +73,7 @@ public extension ProHUD { vm.title = title vm.message = message vm.icon = icon + vm.duration = duration actions?(&vm) // 点击 @@ -180,6 +181,20 @@ public extension Toast { return self } + func animate(rotate: Bool) { + if rotate { + DispatchQueue.main.async { + let ani = CABasicAnimation(keyPath: "transform.rotation.z") + ani.toValue = Double.pi * 2.0 + ani.duration = 3 + ani.repeatCount = 10000 + self.imageView.layer.add(ani, forKey: "rotationAnimation") + } + } else { + imageView.layer.removeAllAnimations() + } + } + } @@ -191,13 +206,13 @@ public extension Toast { /// - Parameter title: 标题 /// - Parameter message: 内容 /// - Parameter actions: 更多操作 - @discardableResult class func push(scene: Toast.Scene = .default, title: String? = nil, message: String? = nil, actions: ((inout ViewModel) -> Void)? = nil) -> Toast { - return Toast(scene: scene, title: title, message: message, actions: actions).push() + @discardableResult class func push(scene: Toast.Scene = .default, title: String? = nil, message: String? = nil, duration: TimeInterval? = nil, _ actions: ((inout ViewModel) -> Void)? = nil) -> Toast { + return Toast(scene: scene, title: title, message: message, duration: duration, actions: actions).push() } /// 获取指定的toast /// - Parameter identifier: 标识 - class func toasts(_ identifier: String?) -> [Toast] { + class func get(_ identifier: String?) -> [Toast] { var tt = [Toast]() for t in toasts { if t.vm.identifier == identifier { @@ -234,7 +249,7 @@ public extension Toast { /// 弹出屏幕 /// - Parameter identifier: 指定实例的标识 class func pop(_ identifier: String?) { - for t in toasts(identifier) { + for t in get(identifier) { t.pop() } } diff --git a/ProHUD/Toast/ToastModel.swift b/ProHUD/Toast/ToastModel.swift index fab95e8..639dd12 100644 --- a/ProHUD/Toast/ToastModel.swift +++ b/ProHUD/Toast/ToastModel.swift @@ -59,15 +59,7 @@ public extension Toast { /// 持续时间 public var duration: TimeInterval? { didSet { - durationBlock?.cancel() - if let t = duration, t > 0 { - durationBlock = DispatchWorkItem(block: { [weak self] in - self?.vc?.pop() - }) - DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: durationBlock!) - } else { - durationBlock = nil - } + updateDuration() } } @@ -85,6 +77,17 @@ public extension Toast { /// 点击事件回调 internal var tapCallback: (() -> Void)? + internal func updateDuration() { + durationBlock?.cancel() + if let t = duration ?? cfg.toast.durationForScene(scene), t > 0 { + durationBlock = DispatchWorkItem(block: { [weak self] in + self?.vc?.pop() + }) + DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: durationBlock!) + } else { + durationBlock = nil + } + } }