This commit is contained in:
xaoxuu 2019-08-09 18:02:41 +08:00
parent 6596e6c971
commit 26d7aa0049
12 changed files with 531 additions and 363 deletions

View File

@ -18,8 +18,15 @@ class ViewController: UIViewController {
ProHUD.config { (cfg) in
cfg.alert { (a) in
a.forceQuitTimer = 2
a.duration = 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")
}
}
cfg.toast { (t) in
// t.iconSize = .init(width: 300, height: 30)
@ -30,43 +37,134 @@ class ViewController: UIViewController {
@IBAction func test(_ sender: UIButton) {
// testAlert()
testToast()
// testUpdateAction()
testGuard()
// testGuard()
// fastGuard()
}
func testDelete() {
let a = ProHUD.push(alert: .delete, title: "确认删除", message: "此操作不可撤销")
a.add(action: .destructive, title: "确认", action: { [weak a] in
a?.remove(action: 0, 1)
a?.update(scene: .loading, title: "正在删除", message: "请稍后片刻")
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
a?.update(scene: .success, title: "删除成功", message: "啊哈哈哈哈").duration(2)
ProHUD.push(toast: .success, title: "删除成功", message: "aaa")
func testAlert() {
let a = Alert.push(scene: .loading) { (a) in
// a.update()
}
}).add(action: .cancel, title: "取消", action: nil)
a.update { (vm) in
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() {
let a = Alert.push(scene: .delete, title: "确认删除", message: "此操作不可撤销")
a.update { (vm) in
vm.add(action: .destructive, title: "确认", handler: { [weak a] in
a?.update { (vm) in
vm.scene = .loading
vm.title = "正在删除"
vm.message = "请稍后片刻"
vm.remove(action: 0, 1)
}
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
a?.update { (vm) in
vm.scene = .success
vm.title = "删除成功"
vm.message = "啊哈哈哈哈"
vm.duration = 2
}
Toast.push(scene: .success, title: "删除成功", message: "aaa")
}
})
vm.add(action: .cancel, title: "取消", handler: nil)
}
}
func testToast() {
let t = ProHUD.Toast(scene: .loading, title: "正在加载", message: "请稍候片刻")
let t = Toast(scene: .loading, title: "正在加载", message: "请稍候片刻")
let a = ProHUD.push(alert : .loading, title: "正在加载", message: "请稍候片刻")
let a = Alert.push(scene : .loading, title: "正在加载", message: "请稍候片刻")
a.didForceQuit {
hud.push(t)
t.push()
}
t.didTapped { [weak t] in
t?.pop()
let a2 = ProHUD.push(alert: .loading, title: "正在加载", message: "马上就要成功了")
Alert.push(scene: .loading, title: "正在加载", message: "马上就要成功了")
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
let a3 = ProHUD.push(alert: .error, title: "加载失败", message: "点击充实")
a3.add(action: .default, title: "重新加载") { [weak a3] in
a3?.update(scene: .success, title: "加载成功", message: "马上就要成功了")
a3?.update(action: 0, style: .default, title: "OK", action: { [weak a3] in
a3?.pop()
}).remove(action: 1, 2)
}.add(action: .destructive, title: "终止", action: nil).add(action: .cancel, title: "取消", action: nil)
Alert.push(scene: .error, title: "加载失败", message: "点击充实") { (vm) in
vm.duration = 0
vm.identifier = "hehe"
let a = vm.vc!
vm.add(action: .default, title: "重新加载") {
a.vm.scene = .success
a.vm.title = "加载成功"
a.vm.message = "马上就要成功了aaaa"
a.vm.remove(action: 1, 2)
a.vm.update(action: 0, style: .default, title: "OK") { [weak a] in
a?.pop()
}
}
vm.add(action: .destructive, title: "终止", handler: nil)
vm.add(action: .cancel, title: "取消", handler: nil)
}
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
if let a = Alert.alerts("hehe").last {
a.update { (vm) in
vm.add(action: .cancel, title: "CANCEL", handler: nil)
}
}
}
}
@ -80,37 +178,47 @@ class ViewController: UIViewController {
g.add(title: "呵呵")
g.add(message: "请打开相机权限开关,否则无法进行测量。请打开相机权限开关,否则无法进行测量。")
g.add(action: .default, title: "测试弹窗", action: { [weak self] in
g.add(action: .default, title: "测试弹窗", handler: { [weak self] in
self?.testToast()
})
g.add(action: .destructive, title: "测试删除弹窗", action: { [weak self] in
g.add(action: .destructive, title: "测试删除弹窗", handler: { [weak self] in
self?.testDelete()
})
g.add(action: .cancel, title: "我知道了", action: nil)
g.add(action: .cancel, title: "我知道了", handler: nil)
g.push(to: self)
debugPrint("test: ", g)
}
func testUpdateAction() {
let a = ProHUD.push(alert: .confirm, title: "确认删除", message: "此操作无法撤销")
a.add(action: .destructive, title: "删除") {
a.remove(action: 0, 1).update(scene: .loading, title: "正在删除", message: "请稍后片刻")
}.add(action: .cancel, title: "取消", action: nil)
let a = Alert.push(scene: .confirm, title: "确认删除", message: "此操作无法撤销")
a.update { (vm) in
vm.add(action: .destructive, title: "删除") {
a.update { (vm) in
vm.remove(action: 0, 1)
vm.scene = .loading
vm.title = "正在删除"
vm.message = "请稍后片刻"
}
}
vm.add(action: .cancel, title: "取消", handler: nil)
}
}
func fastGuard() {
let g = ProHUD.push(guard: self, title: "测试", message: "测试测试")
g.add(action: .default, title: "默认按钮", action: {
Guard.push(to: self) { (vm) in
vm.add(title: "测试")
vm.add(message: "测试测试")
vm.add(action: .default, title: "默认按钮", handler: {
})
g.add(action: .cancel, title: "取消", action: nil)
g.view.backgroundColor = .clear
vm.add(action: .cancel, title: "取消", handler: nil)
vm.vc?.view.backgroundColor = .clear
}
// g.contentView.backgroundColor = UIColor.white
}
@ -120,37 +228,6 @@ class ViewController: UIViewController {
//
// ProHUD.show(alert: .loading, title: "", message: "").timeout(nil)
//
// ProHUD.show(alert: .confirm, title: "", message: "").timeout(3)
//
// a.addAction(style: .destructive, title: "") { [weak a] in
// a?.updateContent(scene: .success, title: "", message: "xxx")
// a?.updateAction(index: 0, style: .default, title: "", action: {
// a?.remove()
// }).removeAction(index: 1)
//// a?.updateContent(scene: .success, title: "").removeAction(index: -1).timeout(2)
// }.addAction(style: .cancel, title: "", action: nil).didDisappear {
// debugPrint("didDisappear")
// }
//
// ProHUD.show(alert: .delete, title: "", message: "").addAction(style: .destructive, title: "") {
//
// }.addAction(style: .cancel, title: "", action: nil).didDisappear {
// debugPrint("didDisappear")
// }.addAction(style: .cancel, title: nil) {
//
// }
// ProHUD.show(toast: .loading, title: "", message: "")
// ProHUD.show(toast: .loading, title: "", message: "")
}
}

View File

@ -25,6 +25,7 @@
CD95D26B22E72DB3007559A3 /* ProHUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD95D26A22E72DB3007559A3 /* ProHUD.swift */; };
CDB6A07B22EEF06500AF6CF0 /* HUDController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDB6A07A22EEF06500AF6CF0 /* HUDController.swift */; };
CDB6A07D22EEF19D00AF6CF0 /* HUDConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDB6A07C22EEF19D00AF6CF0 /* HUDConfig.swift */; };
CDC39CFD22FD6DDF0070E914 /* GuardModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC39CFC22FD6DDF0070E914 /* GuardModel.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -50,6 +51,7 @@
CD95D26A22E72DB3007559A3 /* ProHUD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProHUD.swift; sourceTree = "<group>"; };
CDB6A07A22EEF06500AF6CF0 /* HUDController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDController.swift; sourceTree = "<group>"; };
CDB6A07C22EEF19D00AF6CF0 /* HUDConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDConfig.swift; sourceTree = "<group>"; };
CDC39CFC22FD6DDF0070E914 /* GuardModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuardModel.swift; sourceTree = "<group>"; };
DC31EBFAC56868D6096A233A /* Pods-ProHUD.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ProHUD.release.xcconfig"; path = "Pods/Target Support Files/Pods-ProHUD/Pods-ProHUD.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -110,6 +112,7 @@
CD6CD87422F185C200F4FD4A /* GuardController.swift */,
CD6CD87822F185D000F4FD4A /* GuardConfig.swift */,
CD6CD87A22F185D600F4FD4A /* GuardView.swift */,
CDC39CFC22FD6DDF0070E914 /* GuardModel.swift */,
);
path = Guard;
sourceTree = "<group>";
@ -292,6 +295,7 @@
CD16490B22EF09AB0077988C /* AlertModel.swift in Sources */,
CD16491222EF0D900077988C /* HUDView.swift in Sources */,
CDB6A07B22EEF06500AF6CF0 /* HUDController.swift in Sources */,
CDC39CFD22FD6DDF0070E914 /* GuardModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -11,7 +11,7 @@ import SnapKit
import Inspire
public extension ProHUD.Configuration {
struct Alert {
class Alert {
// MARK:
/// iPad
public var maxWidth = CGFloat(400)
@ -28,6 +28,10 @@ public extension ProHUD.Configuration {
///
public var iconSize = CGSize(width: 48, height: 48)
public func iconForScene(_ callback: @escaping (ProHUD.Alert.Scene) -> UIImage?) {
privIconForScene = callback
}
// MARK:
///
public var titleFont = UIFont.boldSystemFont(ofSize: 22)
@ -48,18 +52,14 @@ public extension ProHUD.Configuration {
// MARK:
///
/// - Parameter callback:
public mutating func loadSubviews(_ callback: @escaping (ProHUD.Alert) -> Void) {
privLoadSubviews = callback
}
///
/// - Parameter callback:
public mutating func reloadData(_ callback: @escaping (ProHUD.Alert) -> Void) {
///
public func reloadData(_ callback: @escaping (ProHUD.Alert) -> Void) {
privReloadData = callback
}
/// Loading
public var duration = TimeInterval(2)
/// 退
public var forceQuitTimer = TimeInterval(30)
@ -68,77 +68,41 @@ public extension ProHUD.Configuration {
/// 退
/// - Parameter callback:
public mutating func loadForceQuitButton(_ callback: @escaping (ProHUD.Alert) -> Void) {
public func loadForceQuitButton(_ callback: @escaping (ProHUD.Alert) -> Void) {
privLoadForceQuitButton = callback
}
}
}
// MARK: -
internal extension ProHUD.Configuration.Alert {
var reloadData: (ProHUD.Alert) -> Void {
return privReloadData
}
}
// MARK: -
internal extension ProHUD.Configuration.Alert {
var loadSubviews: (ProHUD.Alert) -> Void {
return privLoadSubviews
}
var reloadData: (ProHUD.Alert) -> Void {
return privReloadData
}
var loadForceQuitButton: (ProHUD.Alert) -> Void {
return privLoadForceQuitButton
}
var setupDefaultDuration: (ProHUD.Alert) -> Void {
fileprivate var privLayoutContentView: (ProHUD.Alert) -> Void = {
return { (vc) in
//
if let t = vc.model.duration, t > 0 {
if vc.buttonEvents.count > 0 {
vc.duration(nil)
}
} else if vc.model.duration == nil && vc.model.scene != .loading {
//
vc.duration(2)
}
}
}
var reloadStack: (ProHUD.Alert) -> Void {
return { (vc) in
if vc.textStack.arrangedSubviews.count > 0 {
vc.contentStack.addArrangedSubview(vc.textStack)
} else {
vc.textStack.removeFromSuperview()
}
if vc.actionStack.arrangedSubviews.count > 0 {
vc.contentStack.addArrangedSubview(vc.actionStack)
} else {
vc.actionStack.removeFromSuperview()
}
}
}
}
fileprivate var privLoadSubviews: (ProHUD.Alert) -> Void = {
return { (vc) in
debug(vc, "loadSubviews")
let config = cfg.alert
if vc.contentView.superview == nil {
vc.view.addSubview(vc.contentView)
vc.contentView.contentView.addSubview(vc.contentStack)
vc.contentStack.spacing = cfg.alert.margin + cfg.alert.padding
vc.contentView.layer.masksToBounds = true
vc.contentView.layer.cornerRadius = cfg.alert.cornerRadius
let maxWidth = CGFloat.maximum(CGFloat.minimum(UIScreen.main.bounds.width * 0.68, cfg.alert.maxWidth), 268)
vc.contentView.snp.makeConstraints { (mk) in
mk.center.equalToSuperview()
mk.width.lessThanOrEqualTo(maxWidth)
}
}
if vc.contentStack.superview == nil {
vc.contentView.contentView.addSubview(vc.contentStack)
vc.contentStack.spacing = cfg.alert.margin + cfg.alert.padding
vc.contentStack.snp.makeConstraints { (mk) in
mk.centerX.equalToSuperview()
mk.top.equalToSuperview().offset(cfg.alert.padding)
@ -147,23 +111,13 @@ fileprivate var privLoadSubviews: (ProHUD.Alert) -> Void = {
mk.trailing.equalToSuperview().offset(-cfg.alert.padding)
}
}
}
}()
fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
return { (vc) in
debug(vc, "reloadData")
let config = cfg.alert
let isFirstLayout: Bool
// layout
if vc.textStack.superview == nil && vc.imageView?.superview == nil {
isFirstLayout = true
} else {
isFirstLayout = false
}
fileprivate var privIconForScene: (ProHUD.Alert.Scene) -> UIImage? = {
return { (scene) in
let imgStr: String
switch vc.model.scene {
switch scene {
case .success:
imgStr = "ProHUDSuccess"
case .warning:
@ -179,7 +133,14 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
default:
imgStr = "ProHUDMessage"
}
let img = vc.model.icon ?? ProHUD.image(named: imgStr)
return ProHUD.image(named: imgStr)
}
}()
fileprivate var privUpdateImage: (ProHUD.Alert) -> Void = {
return { (vc) in
let config = cfg.alert
let img = vc.vm.icon ?? privIconForScene(vc.vm.scene)
if let imgv = vc.imageView {
imgv.image = img
} else {
@ -197,9 +158,13 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
vc.imageView = icon
}
vc.imageView?.layer.removeAllAnimations()
}
}()
// text
if vc.model.title?.count ?? 0 > 0 || vc.model.message?.count ?? 0 > 0 {
fileprivate var privUpdateTextStack: (ProHUD.Alert) -> Void = {
return { (vc) in
let config = cfg.alert
if vc.vm.title?.count ?? 0 > 0 || vc.vm.message?.count ?? 0 > 0 {
vc.contentStack.addArrangedSubview(vc.textStack)
vc.textStack.snp.makeConstraints { (mk) in
mk.top.greaterThanOrEqualTo(vc.contentView).offset(config.padding*1.75)
@ -212,19 +177,19 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
mk.trailing.lessThanOrEqualTo(vc.contentView).offset(-config.padding*2)
}
}
if vc.model.title?.count ?? 0 > 0 {
if vc.vm.title?.count ?? 0 > 0 {
if let lb = vc.titleLabel {
lb.text = vc.model.title
lb.text = vc.vm.title
} else {
let title = UILabel()
title.textAlignment = .center
title.numberOfLines = config.titleMaxLines
title.textColor = cfg.primaryLabelColor
title.text = vc.model.title
title.text = vc.vm.title
vc.textStack.addArrangedSubview(title)
vc.titleLabel = title
}
if vc.model.message?.count ?? 0 > 0 {
if vc.vm.message?.count ?? 0 > 0 {
// message
vc.titleLabel?.font = config.titleFont
} else {
@ -234,20 +199,20 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
} else {
vc.titleLabel?.removeFromSuperview()
}
if vc.model.message?.count ?? 0 > 0 {
if vc.vm.message?.count ?? 0 > 0 {
if let lb = vc.bodyLabel {
lb.text = vc.model.message
lb.text = vc.vm.message
} else {
let body = UILabel()
body.textAlignment = .center
body.font = config.bodyFont
body.numberOfLines = config.bodyMaxLines
body.textColor = cfg.secondaryLabelColor
body.text = vc.model.message
body.text = vc.vm.message
vc.textStack.addArrangedSubview(body)
vc.bodyLabel = body
}
if vc.model.title?.count ?? 0 > 0 {
if vc.vm.title?.count ?? 0 > 0 {
// title
vc.bodyLabel?.font = config.bodyFont
} else {
@ -260,18 +225,19 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
} else {
vc.textStack.removeFromSuperview()
}
if vc.actionStack.superview != nil {
if isFirstLayout {
vc.contentStack.addArrangedSubview(vc.actionStack)
} else {
vc.actionStack.transform = .init(scaleX: 1, y: 0.001)
UIView.animateForAlert {
vc.contentStack.addArrangedSubview(vc.actionStack)
vc.view.layoutIfNeeded()
}
vc.textStack.layoutIfNeeded()
}
}()
fileprivate var privUpdateActionStack: (ProHUD.Alert) -> Void = {
return { (vc) in
let config = cfg.alert
if vc.buttonEvents.count > 0 {
//
vc.contentStack.addArrangedSubview(vc.actionStack)
// iPad
if isPortrait == false {
if isPortrait == false && vc.buttonEvents.count < 4 {
vc.actionStack.axis = .horizontal
vc.actionStack.alignment = .fill
vc.actionStack.distribution = .fillEqually
@ -280,28 +246,14 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
mk.width.greaterThanOrEqualTo(200)
mk.leading.trailing.equalToSuperview()
}
if isFirstLayout == false {
UIView.animateForAlert {
vc.actionStack.transform = .identity
}
}
}
if isFirstLayout {
vc.view.layoutIfNeeded()
vc.imageView?.transform = .init(scaleX: 0.75, y: 0.75)
UIView.animateForAlert {
vc.imageView?.transform = .identity
vc.view.layoutIfNeeded()
}
} else {
UIView.animateForAlert {
vc.view.layoutIfNeeded()
//
for v in vc.actionStack.arrangedSubviews {
v.removeFromSuperview()
}
vc.actionStack.removeFromSuperview()
}
vc.actionStack.layoutIfNeeded()
}
}()
@ -335,12 +287,74 @@ fileprivate var privLoadForceQuitButton: (ProHUD.Alert) -> Void = {
bg.transform = .identity
}
vc.addTouchUpAction(for: btn) { [weak vc] in
debug("点击了隐藏")
vc?.model.forceQuitCallback?()
debug("点击了\(config.forceQuitTitle)")
vc?.vm.forceQuitCallback?()
vc?.pop()
}
}
}()
///
fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
return { (vc) in
debug(vc, "reloadData")
let config = cfg.alert
let isFirstLayout: Bool
if vc.contentView.superview == nil {
isFirstLayout = true
//
privLayoutContentView(vc)
} else {
isFirstLayout = false
}
//
privUpdateImage(vc)
//
privUpdateTextStack(vc)
//
privUpdateActionStack(vc)
vc.contentStack.layoutIfNeeded()
vc.contentView.layoutIfNeeded()
//
if isFirstLayout {
vc.view.layoutIfNeeded()
vc.imageView?.transform = .init(scaleX: 0.75, y: 0.75)
UIView.animateForAlert {
vc.view.layoutIfNeeded()
vc.imageView?.transform = .identity
}
} else {
UIView.animateForAlert {
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.forceQuitTimerBlock?.cancel()
if vc.buttonEvents.count == 0 {
vc.vm.forceQuitTimerBlock = DispatchWorkItem(block: { [weak vc] in
if let vc = vc {
if vc.buttonEvents.count == 0 {
privLoadForceQuitButton(vc)
}
}
})
DispatchQueue.main.asyncAfter(deadline: .now() + config.forceQuitTimer, execute: vc.vm.forceQuitTimerBlock!)
} else {
vc.vm.forceQuitTimerBlock = nil
}
}
}()

View File

@ -53,28 +53,32 @@ public extension ProHUD {
}()
///
public var model = ViewModel()
public var vm = ViewModel()
// MARK:
internal var isLoadFinished = false
///
/// - Parameter scene:
/// - Parameter title:
/// - Parameter message:
/// - Parameter icon:
public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, icon: UIImage? = nil, actions: ((Alert) -> Void)? = nil) {
public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, icon: UIImage? = nil, actions: ((inout ViewModel) -> Void)? = nil) {
self.init()
view.tintColor = cfg.alert.tintColor
model.scene = scene
model.title = title
model.message = message
model.icon = icon
actions?(self)
willLayoutSubviews()
vm.vc = self
vm.scene = scene
vm.title = title
vm.message = message
vm.icon = icon
actions?(&vm)
}
public override func viewDidLoad() {
super.viewDidLoad()
view.tintColor = cfg.alert.tintColor
cfg.alert.reloadData(self)
isLoadFinished = true
}
}
@ -88,7 +92,6 @@ public extension Alert {
///
@discardableResult func push() -> Alert {
let hud = ProHUD.shared
if Alert.alerts.contains(self) == false {
let window = Alert.getAlertWindow(self)
window.makeKeyAndVisible()
@ -104,11 +107,6 @@ public extension Alert {
Alert.alerts.append(self)
}
Alert.updateAlertsLayout()
// setup duration
if let _ = model.duration, model.durationBlock == nil {
duration(model.duration)
}
return self
}
@ -137,22 +135,11 @@ public extension Alert {
// MARK:
///
/// - Parameter style:
/// - Parameter text:
/// - Parameter handler:
@discardableResult func add(action style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
let btn = privAddButton(custom: Button.actionButton(title: title), action: handler)
if let b = btn as? Button {
b.update(style: style)
}
return btn
}
///
/// - Parameter callback:
@discardableResult func didForceQuit(_ callback: (() -> Void)?) -> Alert {
model.forceQuitCallback = callback
vm.forceQuitCallback = callback
return self
}
@ -163,77 +150,29 @@ public extension Alert {
return self
}
///
/// - Parameter duration:
@discardableResult func duration(_ duration: TimeInterval?) -> Alert {
model.setupDuration(duration: duration) { [weak self] in
self?.pop()
}
return self
}
///
/// - Parameter scene:
/// - Parameter title:
/// - Parameter message:
@discardableResult func update(scene: Scene, title: String?, message: String?) -> Alert {
model.scene = scene
model.title = title
model.message = message
willLayoutSubviews()
return self
}
///
/// - Parameter icon:
@discardableResult func update(icon: UIImage?) -> Alert {
model.icon = icon
/// - Parameter callback:
func update(_ callback: ((inout Alert.ViewModel) -> Void)? = nil) {
callback?(&vm)
cfg.alert.reloadData(self)
return self
}
@discardableResult func animate(rotate: Bool) -> Alert {
func animate(rotate: Bool) {
if rotate {
DispatchQueue.main.async {
let ani = CABasicAnimation(keyPath: "transform.rotation.z")
ani.toValue = M_PI*2.0
ani.duration = 2
ani.toValue = Double.pi * 2.0
ani.duration = 3
ani.repeatCount = 10000
self.imageView?.layer.add(ani, forKey: "rotationAnimation")
}
} else {
imageView?.layer.removeAllAnimations()
}
return self
}
///
/// - Parameter index:
/// - Parameter style:
/// - Parameter title:
/// - Parameter action:
@discardableResult func update(action index: Int, style: UIAlertAction.Style, title: String?, action: (() -> Void)?) -> Alert {
if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
btn.setTitle(title, for: .normal)
if let b = btn as? Button {
b.update(style: style)
}
btn.layoutIfNeeded()
if let ac = action {
addTouchUpAction(for: btn, action: ac)
}
}
return self
}
///
/// - Parameter index:
@discardableResult func remove(action index: Int...) -> Alert {
for (i, idx) in index.enumerated() {
privRemoveAction(index: idx-i)
}
return self
}
}
@ -247,7 +186,7 @@ public extension Alert {
/// - Parameter title:
/// - Parameter message:
/// - Parameter actions:
@discardableResult class func push(alert scene: Alert.Scene, title: String? = nil, message: String? = nil, actions: ((Alert) -> Void)? = nil) -> Alert {
@discardableResult class func push(scene: Alert.Scene, title: String? = nil, message: String? = nil, actions: ((inout Alert.ViewModel) -> Void)? = nil) -> Alert {
return Alert(scene: scene, title: title, message: message, actions: actions).push()
}
@ -256,7 +195,7 @@ public extension Alert {
class func alerts(_ identifier: String?) -> [Alert] {
var aa = [Alert]()
for a in Alert.alerts {
if a.identifier == identifier {
if a.vm.identifier == identifier {
aa.append(a)
}
}
@ -281,7 +220,7 @@ public extension Alert {
// MARK: -
fileprivate extension Alert {
internal extension Alert {
///
/// - Parameter index:
@ -298,52 +237,56 @@ fileprivate extension Alert {
if self.actionStack.arrangedSubviews.count == 0 {
self.actionStack.removeFromSuperview()
}
willLayoutSubviews()
UIView.animateForAlert {
self.view.layoutIfNeeded()
}
return self
}
func willLayoutSubviews() {
model.setupWillLayout(duration: 0.001) { [weak self] in
if let a = self {
//
cfg.alert.loadSubviews(a)
cfg.alert.reloadData(a)
cfg.alert.setupDefaultDuration(a)
// 退
a.model.setupForceQuit(duration: cfg.alert.forceQuitTimer) { [weak self] in
if let aa = self, aa.actionStack.superview == nil {
cfg.alert.loadForceQuitButton(aa)
}
}
}
}
}
@discardableResult func privAddButton(custom button: UIButton, action: (() -> Void)?) -> UIButton {
model.duration = nil
@discardableResult func privAddButton(custom button: UIButton, at index: Int? = nil, handler: (() -> Void)?) -> UIButton {
if actionStack.superview == nil {
contentStack.addArrangedSubview(actionStack)
contentStack.layoutIfNeeded()
}
self.view.layoutIfNeeded()
button.transform = .init(scaleX: 1, y: 0.001)
if let idx = index, idx < actionStack.arrangedSubviews.count {
actionStack.insertArrangedSubview(button, at: idx)
} else {
actionStack.addArrangedSubview(button)
UIView.animateForAlert {
button.transform = .identity
self.view.layoutIfNeeded()
}
addTouchUpAction(for: button) { [weak self] in
action?()
handler?()
if button.tag == UIAlertAction.Style.cancel.rawValue {
self?.pop()
}
}
willLayoutSubviews()
if isLoadFinished {
actionStack.layoutIfNeeded()
UIView.animateForAlert {
self.view.layoutIfNeeded()
}
}
return button
}
func privUpdateButton(action index: Int, style: UIAlertAction.Style, title: String?, _ handler: (() -> Void)?) {
if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
btn.setTitle(title, for: .normal)
if let b = btn as? Button {
b.update(style: style)
}
if let _ = buttonEvents[btn] {
buttonEvents.removeValue(forKey: btn)
}
addTouchUpAction(for: btn) { [weak self] in
handler?()
if btn.tag == UIAlertAction.Style.cancel.rawValue {
self?.pop()
}
}
}
}
}
fileprivate extension Alert {

View File

@ -35,20 +35,50 @@ public extension Alert {
struct ViewModel {
/// ID
public var identifier = String(Date().timeIntervalSince1970)
/// 使
public var scene = Scene.default
///
public var title: String?
public var title: String? {
didSet {
vc?.titleLabel?.text = title
}
}
///
public var message: String?
public var message: String? {
didSet {
vc?.bodyLabel?.text = message
}
}
///
public var icon: UIImage?
public var icon: UIImage? {
didSet {
vc?.imageView?.image = icon
}
}
///
internal var duration: TimeInterval?
/// 0
public var duration: TimeInterval? {
didSet {
durationBlock?.cancel()
if let t = duration, t > 0 {
let v = vc
durationBlock = DispatchWorkItem(block: {
v?.pop()
})
DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: durationBlock!)
} else {
durationBlock = nil
}
}
}
public weak var vc: Alert?
///
internal var durationBlock: DispatchWorkItem?
@ -59,40 +89,56 @@ public extension Alert {
/// 退
internal var forceQuitCallback: (() -> Void)?
internal var willLayoutBlock: DispatchWorkItem?
internal mutating func setupDuration(duration: TimeInterval?, callback: @escaping () -> Void) {
self.duration = duration
durationBlock?.cancel()
if let t = duration, t > 0 {
durationBlock = DispatchWorkItem(block: callback)
DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: durationBlock!)
} else {
durationBlock = nil
}
}
internal mutating func setupForceQuit(duration: TimeInterval?, callback: @escaping () -> Void) {
forceQuitTimerBlock?.cancel()
if let t = duration, t > 0 {
forceQuitTimerBlock = DispatchWorkItem(block: callback)
DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: forceQuitTimerBlock!)
} else {
forceQuitTimerBlock = nil
}
}
internal mutating func setupWillLayout(duration: TimeInterval?, callback: @escaping () -> Void) {
willLayoutBlock?.cancel()
if let t = duration, t > 0 {
willLayoutBlock = DispatchWorkItem(block: callback)
DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: willLayoutBlock!)
} else {
willLayoutBlock = nil
}
}
}
}
public extension Alert.ViewModel {
///
/// - Parameter style:
/// - Parameter text:
/// - Parameter handler:
@discardableResult mutating func add(action style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
duration = 0
let btn = vc?.privAddButton(custom: Alert.Button.actionButton(title: title), handler: handler)
if let b = btn as? Alert.Button {
b.update(style: style)
}
return btn!
}
///
/// - Parameter index:
/// - Parameter style:
/// - Parameter title:
/// - Parameter handler:
@discardableResult mutating func insert(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
duration = 0
let btn = vc?.privAddButton(custom: Alert.Button.actionButton(title: title), at: index, handler: handler)
if let b = btn as? Alert.Button {
b.update(style: style)
}
return btn!
}
///
/// - Parameter index:
/// - Parameter style:
/// - Parameter title:
/// - Parameter handler:
mutating func update(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) {
vc?.privUpdateButton(action: index, style: style, title: title, handler)
}
///
/// - Parameter index:
mutating func remove(action index: Int...) {
guard let alert = self.vc else { return }
for (i, idx) in index.enumerated() {
alert.privRemoveAction(index: idx-i)
}
}
}

View File

@ -24,6 +24,7 @@ internal extension Alert {
backgroundColor = cfg.dynamicColor.withAlphaComponent(0.04)
contentEdgeInsets = .init(top: pd*1.5, left: pd*1.5, bottom: pd*1.5, right: pd*1.5)
} else {
backgroundColor = .clear
contentEdgeInsets = .init(top: pd*0.5, left: pd*1.5, bottom: pd*0.5, right: pd*1.5)
}
switch style {

View File

@ -86,11 +86,7 @@ fileprivate var privLoadSubviews: (ProHUD.Guard) -> Void = {
return { (vc) in
debug(vc, "loadSubviews")
let config = cfg.guard
// background
vc.view.tintColor = config.tintColor
vc.view.backgroundColor = UIColor(white: 0, alpha: 0)
vc.view.addSubview(vc.contentView)
vc.contentView.contentView.addSubview(vc.contentStack)
}
}()
@ -98,6 +94,11 @@ fileprivate var privReloadData: (ProHUD.Guard) -> Void = {
return { (vc) in
debug(vc, "reloadData")
let config = cfg.guard
// background
vc.view.tintColor = config.tintColor
vc.view.backgroundColor = UIColor(white: 0, alpha: 0)
vc.view.addSubview(vc.contentView)
vc.contentView.contentView.addSubview(vc.contentStack)
//
var width = UIScreen.main.bounds.width
if width > config.cardMaxWidth {

View File

@ -50,23 +50,26 @@ public extension ProHUD {
///
public var backgroundColor: UIColor? = UIColor(white: 0, alpha: 0.5)
public var vm = ViewModel()
// MARK:
///
/// - Parameter title:
/// - Parameter message:
/// - Parameter actions:
public convenience init(title: String? = nil, message: String? = nil, actions: ((Guard) -> Void)? = nil) {
public convenience init(title: String? = nil, message: String? = nil, actions: ((inout ViewModel) -> Void)? = nil) {
self.init()
view.tintColor = cfg.guard.tintColor
vm.vc = self
if let _ = title {
add(title: title)
}
if let _ = message {
add(message: message)
}
actions?(self)
actions?(&vm)
view.tintColor = cfg.guard.tintColor
cfg.guard.loadSubviews(self)
cfg.guard.reloadData(self)
cfg.guard.reloadStack(self)
@ -91,7 +94,7 @@ public extension Guard {
///
/// - Parameter viewController:
func push(to viewController: UIViewController? = nil) -> Guard {
@discardableResult func push(to viewController: UIViewController? = nil) -> Guard {
func f(_ vc: UIViewController) {
view.layoutIfNeeded()
vc.addChild(self)
@ -226,7 +229,7 @@ public extension Guard {
/// - Parameter title:
/// - Parameter message:
/// - Parameter icon:
@discardableResult class func push(to viewController: UIViewController? = nil, actions: ((Guard) -> Void)? = nil) -> Guard {
@discardableResult class func push(to viewController: UIViewController? = nil, actions: ((inout ViewModel) -> Void)? = nil) -> Guard {
return Guard(actions: actions).push(to: viewController)
}
@ -239,7 +242,7 @@ public extension Guard {
if child.isKind(of: Guard.self) {
if let g = child as? Guard {
if let id = identifier {
if g.identifier == id {
if g.vm.identifier == id {
gg.append(g)
}
} else {
@ -272,7 +275,7 @@ public extension Guard {
// MARK: -
fileprivate extension Guard {
internal extension Guard {
///
/// - Parameter sender:
@ -317,6 +320,5 @@ fileprivate extension Guard {
}
}

View File

@ -0,0 +1,80 @@
//
// GuardModel.swift
// ProHUD
//
// Created by xaoxuu on 2019/8/9.
// Copyright © 2019 Titan Studio. All rights reserved.
//
import UIKit
public extension Guard {
struct ViewModel {
/// ID
public var identifier = String(Date().timeIntervalSince1970)
public weak var vc: Guard?
}
}
public extension Guard.ViewModel {
///
/// - Parameter text:
@discardableResult func add(title: String?) -> UILabel {
return vc!.add(title: title)
}
///
/// - Parameter text:
@discardableResult func add(subTitle: String?) -> UILabel {
return vc!.add(subTitle: subTitle)
}
///
/// - Parameter text:
@discardableResult func add(message: String?) -> UILabel {
return vc!.add(message: message)
}
///
/// - Parameter style:
/// - Parameter title:
/// - Parameter action:
@discardableResult func add(action style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
return vc!.add(action: style, title: title, handler: handler)
}
///
/// - Parameter index:
/// - Parameter style:
/// - Parameter title:
/// - Parameter handler:
@discardableResult mutating func insert(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
return UIButton()
}
///
/// - Parameter index:
/// - Parameter style:
/// - Parameter title:
/// - Parameter handler:
mutating func update(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) {
// vc?.privUpdateButton(action: index, style: style, title: title, handler)
}
///
/// - Parameter index:
func remove(action index: Int...) {
for (i, idx) in index.enumerated() {
vc!.privRemoveAction(index: idx-i)
}
}
}

View File

@ -10,9 +10,6 @@ import UIKit
public class HUDController: UIViewController {
/// ID
public var identifier = String(Date().timeIntervalSince1970)
///
internal var disappearCallback: (() -> Void)?

View File

@ -243,7 +243,7 @@ public extension Toast {
/// - Parameter title:
/// - Parameter message:
/// - Parameter actions:
@discardableResult class func push(toast scene: Toast.Scene, title: String? = nil, message: String? = nil, actions: ((Toast) -> Void)? = nil) -> Toast {
@discardableResult class func push(scene: Toast.Scene, title: String? = nil, message: String? = nil, actions: ((Toast) -> Void)? = nil) -> Toast {
return Toast(scene: scene, title: title, message: message, actions: actions).push()
}
@ -252,7 +252,7 @@ public extension Toast {
class func toasts(_ identifier: String?) -> [Toast] {
var tt = [Toast]()
for t in toasts {
if t.identifier == identifier {
if t.model.identifier == identifier {
tt.append(t)
}
}

View File

@ -35,6 +35,9 @@ public extension Toast {
struct ViewModel {
/// ID
public var identifier = String(Date().timeIntervalSince1970)
/// 使
public var scene = Scene.default