This commit is contained in:
xaoxuu 2020-06-23 20:16:12 +08:00
parent 539ad04623
commit ad3576cf6f
8 changed files with 589 additions and 487 deletions

View File

@ -30,7 +30,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// a.bodyFont = .regular(17) // a.bodyFont = .regular(17)
// a.boldTextFont = .bold(18) // a.boldTextFont = .bold(18)
// a.buttonFont = .bold(18) // a.buttonFont = .bold(18)
a.forceQuitTimer = 3 a.forceQuitTimer = 5 // 便
} }
// cfg.toast { (t) in // cfg.toast { (t) in
// t.titleFont = .bold(18) // t.titleFont = .bold(18)
@ -78,7 +78,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
extension ProHUD.Scene { extension ProHUD.Scene {
static var delete: 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.image = UIImage(named: "prohud.trash")
scene.title = "确认删除" scene.title = "确认删除"
scene.message = "此操作不可撤销" scene.message = "此操作不可撤销"

View File

@ -8,18 +8,46 @@
import UIKit 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 { class BaseListVC: UIViewController {
var ts = [[String]]()
var cs = [[Callback]]()
var vm = Table()
var secs = [Section]()
lazy var tableView: UITableView = { lazy var tableView: UITableView = {
let tv = UITableView() let tv = UITableView(frame: .zero, style: .grouped)
return tv return tv
}() }()
var titles: [String] {
return ["Toast", "Alert", "Guard"]
}
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -27,29 +55,47 @@ class BaseListVC: UIViewController {
view.addSubview(tableView) view.addSubview(tableView)
tableView.dataSource = self tableView.dataSource = self
tableView.delegate = self tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.snp.makeConstraints { (mk) in tableView.snp.makeConstraints { (mk) in
mk.edges.equalToSuperview() mk.edges.equalToSuperview()
} }
} }
} }
extension BaseListVC: UITableViewDataSource, UITableViewDelegate { extension BaseListVC: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return vm.sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 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 { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) let cell: UITableViewCell
cell.textLabel?.text = titles[indexPath.row] 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 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) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
vm.sections[indexPath.section].rows[indexPath.row].callback()
} }
} }

View File

@ -9,219 +9,252 @@
import UIKit import UIKit
import ProHUD 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 { class TestAlertVC: BaseListVC {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// Do any additional setup after loading the view. vm.addSection(title: "最简单的写法") { (sec) in
} sec.addRow(title: "Alert.push(scene: .loading)") {
Alert.push(scene: .loading)
}
sec.addRow(title: "Alert.push(scene: .success)") {
Alert.push(scene: .success)
}
sec.addRow(title: "Alert.push(scene: .warning)") {
Alert.push(scene: .warning)
}
sec.addRow(title: "Alert.push(scene: .error)") {
Alert.push(scene: .error)
}
sec.addRow(title: "Alert.push(scene: .failure)") {
Alert.push(scene: .failure)
}
sec.footer = "这些是自带的场景,可以重写已有场景,或者扩展新的场景。"
}
override var titles: [String] { vm.addSection(title: "常用场景示例") { (sec) in
return ["极端场景:正在同步(超时未处理)", // MARK: 1
"场景同步成功写法1", sec.addRow(title: "场景同步成功写法1") {
"场景同步成功写法2", Alert.push("loading") { (a) in
"场景:同步失败和重试",
"极端场景:短时间内调用了多次同一个弹窗",
"极端场景:多个不同的弹窗重叠",
"测试较长的标题和内容",
"测试特别长的标题和内容",
"场景:正在同步(更新进度)"]
}
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()
}
}
}
}
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 = "请稍等片刻"
}
}
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 == 2 {
let a = Alert.push() { (a) in
a.identifier = "loading"
}
a.startRotate()
a.update { (vm) in
vm.scene = .loading
vm.title = "正在同步"
vm.message = "请稍等片刻"
}
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
a.update { (vm) in a.update { (vm) in
vm.scene = .loading vm.scene = .loading
vm.title = "正在同步" vm.title = "正在同步"
vm.message = "请稍等片刻" vm.message = "请稍等片刻"
vm.remove(action: 0, 1)
} }
a.startRotate() 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) // MARK: 2
DispatchQueue.main.asyncAfter(deadline: .now()+1) { sec.addRow(title: "场景同步成功写法2") {
loading(2) let a = Alert.push("loading") { (a) in
}
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
a.startRotate() 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 a.update { (vm) in
vm.scene = .loading vm.scene = .loading
vm.title = "正在同步" vm.title = "正在同步"
vm.message = "请稍等片刻" vm.message = "请稍等片刻"
} }
a.startRotate() loadingSuccessAfter2Seconds()
a.update(progress: 0) }
let s = DispatchSemaphore(value: 1) // MARK:
DispatchQueue.global().async { sec.addRow(title: "场景:正在同步(更新进度)") {
for i in 0 ... 5 { Alert.push("progress") { (a) in
s.wait() a.update { (vm) in
DispatchQueue.main.async { vm.scene = .loading
Alert.find("progress", last: { (a) in vm.title = "正在同步"
a.update(progress: CGFloat(i)/5) vm.message = "请稍等片刻"
print("\(CGFloat(i)/5)") }
if i == 5 { a.startRotate()
a.update { (vm) in a.update(progress: 0)
vm.scene = .success let s = DispatchSemaphore(value: 1)
vm.title = "同步成功" DispatchQueue.global().async {
vm.message = nil 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() { func simulateSync() {

View File

@ -15,110 +15,115 @@ class TestGuardVC: BaseListVC {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// Do any additional setup after loading the view. vm.addSection(title: "") { (sec) in
} // MARK:
sec.addRow(title: "场景:删除菜单") {
override var titles: [String] { Guard.push("del") { (vc) in
return ["场景:删除菜单", "场景:升级至专业版", "场景:隐私协议页面", "对比原生的ActionSheet", "对比原生Present效果"] vc.update { (vm) in
} vm.add(action: .destructive, title: "删除") { [weak vc] in
Alert.push(scene: .delete) { (vc) in
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { vc.update { (vm) in
tableView.deselectRow(at: indexPath, animated: true) vm.add(action: .destructive, title: "删除") { [weak vc] in
let row = indexPath.row vc?.pop()
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.add(action: .cancel, title: "取消", handler: nil)
} }
vm.add(action: .cancel, title: "取消", handler: nil)
} }
vc?.pop()
} }
vc?.pop() vm.add(action: .cancel, title: "取消", handler: nil)
} }
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 // MARK:
vc?.pop() 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)
} }
} }
} }
} else if row == 3 {
let ac = UIAlertController(title: "Title", message: "message", preferredStyle: .actionSheet) // MARK:
let ok = UIAlertAction(title: "OK", style: .default, handler: nil) sec.addRow(title: "场景:隐私协议页面") {
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) Guard.push("license") { (vc) in
ac.addAction(ok) vc.isFullScreen = true
ac.addAction(cancel) vc.update { (vm) in
ac.popoverPresentationController?.sourceView = tableView.cellForRow(at: indexPath) let titleLabel = vm.add(title: "隐私协议")
self.present(ac, animated: true, completion: nil) titleLabel.snp.makeConstraints { (mk) in
} else if row == 4 { mk.height.equalTo(44)
let vc = UIViewController() }
vc.view.backgroundColor = .white let tv = UITextView()
vc.title = "ceshi" tv.backgroundColor = .white
present(vc, animated: true, completion: nil) 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)
}
} }
} }

View File

@ -9,208 +9,218 @@
import UIKit import UIKit
import ProHUD import ProHUD
typealias Callback2 = (String) -> Void
class TestToastVC: BaseListVC { class TestToastVC: BaseListVC {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// Do any additional setup after loading the view. // Do any additional setup after loading the view.
}
override var titles: [String] { vm.addSection(title: "基本场景") { (sec) in
return ["场景:正在同步", // MARK:
"场景:正在同步(更新进度)", sec.addRow(title: "场景:正在同步") {
"场景:同步成功", Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (vm) in
"场景:同步失败", vm.identifier = "loading"
"场景:设备电量过低", }.startRotate()
"传入指定图标", self.simulateSync()
"禁止手势移除", }
"组合使用示例", // MARK:
"避免重复发布同一条信息", sec.addRow(title: "场景:正在同步(更新进度)") {
"根据id查找并修改实例", if Toast.find("progress").count == 0 {
"测试较长的标题和内容", Toast.push("progress") { (t) in
"测试特别长的标题和内容", t.update { (vm) in
"测试只有title", vm.scene = .loading
"测试只有message", vm.title = "正在同步"
"自定义旋转的图片"] vm.message = "请稍等片刻"
} }
t.startRotate()
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { t.update(progress: 0)
tableView.deselectRow(at: indexPath, animated: true) let s = DispatchSemaphore(value: 1)
let row = indexPath.row DispatchQueue.global().async {
if row == 0 { for i in 0 ... 5 {
Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (vm) in s.wait()
vm.identifier = "loading" DispatchQueue.main.async {
}.startRotate() Toast.find("progress", last: { (t) in
simulateSync() t.update(progress: CGFloat(i)/5)
} else if row == 1 { print("\(CGFloat(i)/5)")
if let _ = Toast.find("progress").last { if i == 5 {
t.update { (vm) in
} else { vm.scene = .success
Toast.push("progress") { (t) in vm.title = "同步成功"
t.update { (vm) in vm.message = "xxx"
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()
} }
} }
} }
} }
} }
} }
// MARK:
} else if row == 2 { sec.addRow(title: "场景:同步成功") {
let t = Toast.push(scene: .success, title: "同步成功", message: "点击查看详情") let t = Toast.push(scene: .success, title: "同步成功", message: "点击查看详情")
t.didTapped { [weak self, weak t] in t.didTapped { [weak self, weak t] in
self?.presentEmptyVC(title: "详情") self?.presentEmptyVC(title: "详情")
t?.pop() 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()
} }
} }
} else if row == 4 { // MARK:
Toast.push(scene: .warning, title: "设备电量过低", message: "请及时对设备进行充电,以免影响使用。") sec.addRow(title: "场景:同步失败") {
Toast.push(scene: .error, title: "同步失败", message: "请稍后重试。点击查看详情") { (vc) in
} else if row == 5 { vc.update { (vm) in
Toast.push(scene: .default, title: "传入指定图标测试", message: "这是消息内容") { (vc) in vm.duration = 0
vc.update { (vm) in }
if #available(iOS 13.0, *) { vc.didTapped { [weak self, weak vc] in
vc.imageView.tintColor = .brown self?.presentEmptyVC(title: "这是错误详情")
vm.icon = UIImage(systemName: "icloud.and.arrow.down") vc?.pop()
} else {
vm.icon = UIImage(named: "icloud.and.arrow.down")
} }
} }
} }
} else if row == 6 { // MARK:
Toast.push(scene: .default, title: "禁止手势移除", message: "这条消息无法通过向上滑动移出屏幕。5秒后自动消失每次拖拽都会刷新倒计时。") { (vc) in sec.addRow(title: "场景:设备电量过低") {
vc.isRemovable = false Toast.push(scene: .default, title: "传入指定图标测试", message: "这是消息内容") { (vc) in
vc.update { (vm) in vc.update { (vm) in
vm.duration = 5 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 { // MARK:
if let t = Toast.find("aaa").last { sec.addRow(title: "避免重复发布同一条信息") {
t.pulse() if let t = Toast.find("aaa").last {
t.update() { (vm) in t.pulse()
vm.title = "已经存在了" t.update() { (vm) in
} vm.title = "已经存在了"
} else { }
Toast.push(title: "这是一条id为aaa的横幅", message: "避免重复发布同一条信息") { (t) in } else {
t.identifier = "aaa" Toast.push(title: "这是一条id为aaa的横幅", message: "避免重复发布同一条信息") { (t) in
t.update { (vm) in t.identifier = "aaa"
vm.scene = .warning t.update { (vm) in
vm.duration = 0 vm.scene = .warning
vm.duration = 0
}
} }
} }
} }
} else if row == 9 { // MARK: id
Toast.push("aaa") { (t) in sec.addRow(title: "根据id查找并修改实例") {
t.update { (vm) in Toast.push("aaa") { (t) in
vm.scene = .success t.update { (vm) in
vm.title = "找到了哈哈" vm.scene = .success
vm.message = "根据id查找并修改实例" vm.title = "找到了哈哈"
vm.message = "根据id查找并修改实例"
}
t.pulse()
} }
t.pulse()
} }
} else if row == 10 { // MARK:
Toast.push() { (a) in sec.addRow(title: "测试较长的标题和内容") {
a.update { (vm) in Toast.push() { (a) in
vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" a.update { (vm) in
vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过"
vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过"
}
} }
} }
} else if row == 11 { // MARK:
Toast.push() { (a) in sec.addRow(title: "测试特别长的标题和内容") {
a.update { (vm) in Toast.push() { (a) in
vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" a.update { (vm) in
vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" vm.title = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过"
vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过"
}
} }
} }
} else if row == 12 { // MARK: title
Toast.push() { (a) in sec.addRow(title: "测试只有title") {
a.update { (vm) in Toast.push() { (a) in
vm.scene = .warning a.update { (vm) in
vm.title = "正在同步看到了你撒地" vm.scene = .warning
vm.title = "正在同步看到了你撒地"
}
} }
} }
} else if row == 13 { // MARK: message
Toast.push() { (a) in sec.addRow(title: "测试只有message") {
a.update { (vm) in Toast.push() { (a) in
vm.scene = .warning a.update { (vm) in
vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过" vm.scene = .warning
vm.message = "正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过正在同步看到了你撒地方快乐撒的肌肤轮廓啊就是;来的跨省的人格人格离开那地方离开过"
}
} }
} }
} else if row == 14 { // MARK:
sec.addRow(title: "自定义旋转的图片") {
Toast.push(scene: .privacy, title: "正在授权", message: "请稍等片刻") { (t) in Toast.push(scene: .privacy, title: "正在授权", message: "请稍等片刻") { (t) in
t.identifier = "loading" t.identifier = "loading"
let imgv = UIImageView(image: UIImage(named: "prohud.rainbow.circle")) let imgv = UIImageView(image: UIImage(named: "prohud.rainbow.circle"))
t.imageView.addSubview(imgv) t.imageView.addSubview(imgv)
imgv.snp.makeConstraints { (mk) in imgv.snp.makeConstraints { (mk) in
mk.center.equalToSuperview() mk.center.equalToSuperview()
mk.width.height.equalTo(18) mk.width.height.equalTo(18)
}
t.startRotate(imgv.layer, speed: 4)
} }
t.startRotate(imgv.layer, speed: 4) self.simulateSync()
} }
simulateSync()
} }
} }
func simulateSync() { func simulateSync() {
DispatchQueue.main.asyncAfter(deadline: .now() + 10) { DispatchQueue.main.asyncAfter(deadline: .now() + 15) {
Toast.find("loading", last: { (t) in Toast.find("loading", last: { (a) in
t.update { (vm) in a.update { (vm) in
vm.scene = .success vm.scene = .success
vm.title = "同步成功" vm.title = "同步成功"
vm.message = "啊哈哈哈哈哈哈哈哈" vm.message = "啊哈哈哈哈哈哈哈哈"

View File

@ -16,27 +16,25 @@ class ViewController: BaseListVC {
super.viewDidLoad() super.viewDidLoad()
// Do any additional setup after loading the view. // Do any additional setup after loading the view.
title = "\(Bundle.main.infoDictionary?["CFBundleName"] ?? "ProHUD")" title = "\(Bundle.main.infoDictionary?["CFBundleName"] ?? "ProHUD")"
}
override var titles: [String] { vm.addSection(title: "") { (sec) in
return ["Toast", "Alert", "Guard"] sec.addRow(title: "Toast") {
} let vc = TestToastVC()
vc.title = "Toast"
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { self.navigationController?.pushViewController(vc, animated: true)
tableView.deselectRow(at: indexPath, animated: true) }
if indexPath.row == 0 { sec.addRow(title: "Alert") {
let vc = TestToastVC() let vc = TestAlertVC()
vc.title = titles[indexPath.row] vc.title = "Alert"
navigationController?.pushViewController(vc, animated: true) self.navigationController?.pushViewController(vc, animated: true)
} else if indexPath.row == 1 { }
let vc = TestAlertVC() sec.addRow(title: "Guard") {
vc.title = titles[indexPath.row] let vc = TestGuardVC()
navigationController?.pushViewController(vc, animated: true) vc.title = "Guard"
} else { self.navigationController?.pushViewController(vc, animated: true)
let vc = TestGuardVC() }
vc.title = titles[indexPath.row]
navigationController?.pushViewController(vc, animated: true)
} }
} }
} }

View File

@ -63,10 +63,10 @@ public extension ProHUD {
/// - Parameter title: /// - Parameter title:
/// - Parameter message: /// - Parameter message:
/// - Parameter icon: /// - 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() self.init()
vm.vc = self vm.vc = self
vm.scene = scene vm.scene = scene ?? .default
vm.title = title vm.title = title
vm.message = message vm.message = message
actions?(self) actions?(self)
@ -148,7 +148,7 @@ public extension Alert {
} }
/// ///
/// - Parameter callback: /// - Parameter callback:
func didForceQuit(_ callback: (() -> Void)?) { func didForceQuit(_ callback: (() -> Void)?) {
vm.forceQuitCallback = callback vm.forceQuitCallback = callback
@ -177,14 +177,19 @@ public extension Alert {
/// - identifier: /// - identifier:
/// - toast: /// - toast:
/// - Returns: /// - 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 { 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 return a
} else { } else {
return Alert() { (aa) in return Alert(scene: scene) { (aa) in
aa.identifier = identifier aa.identifier = identifier
instance(aa) instance?(aa)
}.push() }.push()
} }
} }

View File

@ -75,11 +75,11 @@ public extension ProHUD {
/// - Parameter title: /// - Parameter title:
/// - Parameter message: /// - Parameter message:
/// - Parameter icon: /// - 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() self.init()
vm.vc = self vm.vc = self
vm.scene = scene vm.scene = scene ?? .default
vm.title = title vm.title = title
vm.message = message vm.message = message
vm.icon = icon vm.icon = icon
@ -227,14 +227,19 @@ public extension Toast {
/// - identifier: /// - identifier:
/// - toast: /// - toast:
/// - Returns: /// - 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 { 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 return t
} else { } else {
return Toast() { (tt) in return Toast(scene: scene) { (tt) in
tt.identifier = identifier tt.identifier = identifier
instance(tt) instance?(tt)
}.push() }.push()
} }
} }