mirror of https://github.com/xaoxuu/ProHUD
update
This commit is contained in:
parent
26d7aa0049
commit
ca6c36650b
|
@ -22,8 +22,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ class ViewController: UIViewController {
|
|||
|
||||
|
||||
ProHUD.config { (cfg) in
|
||||
cfg.rootViewController = self
|
||||
|
||||
cfg.alert { (a) in
|
||||
a.duration = 1
|
||||
a.forceQuitTimer = 3
|
||||
|
@ -29,7 +31,11 @@ class ViewController: UIViewController {
|
|||
|
||||
}
|
||||
cfg.toast { (t) in
|
||||
// t.iconSize = .init(width: 300, height: 30)
|
||||
// t.iconSize = .init(width: 30, height: 30)
|
||||
// t.iconForScene { (scene) -> UIImage? in
|
||||
// return UIImage(named: "icon_download")
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,6 +57,7 @@ class ViewController: UIViewController {
|
|||
}
|
||||
a.update { (vm) in
|
||||
vm.add(action: .default, title: "OK", handler: nil)
|
||||
|
||||
}
|
||||
// a.update()
|
||||
// Alert.push(scene: .loading, title: "Loading") { (a) in
|
||||
|
@ -127,67 +134,107 @@ class ViewController: UIViewController {
|
|||
|
||||
}
|
||||
func testToast() {
|
||||
let t = Toast(scene: .loading, title: "正在加载", message: "请稍候片刻")
|
||||
|
||||
let a = Alert.push(scene : .loading, title: "正在加载", message: "请稍候片刻")
|
||||
a.didForceQuit {
|
||||
t.push()
|
||||
}
|
||||
|
||||
t.didTapped { [weak t] in
|
||||
t?.pop()
|
||||
Alert.push(scene: .loading, title: "正在加载", message: "马上就要成功了")
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
|
||||
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)
|
||||
}
|
||||
func f() {
|
||||
Toast.push { (vm) in
|
||||
vm.scene = .error
|
||||
vm.title = "正在加载"
|
||||
// vm.duration = 1
|
||||
vm.message = "请稍候片刻"
|
||||
|
||||
}.didTapped {
|
||||
debugPrint("didTapped")
|
||||
}.didDisappear {
|
||||
debugPrint("didDisappear")
|
||||
}
|
||||
}
|
||||
|
||||
f()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// let t = Toast(scene: .loading, title: "正在加载", message: "请稍候片刻")
|
||||
|
||||
// let a = Alert.push(scene : .loading, title: "正在加载", message: "请稍候片刻")
|
||||
// a.didForceQuit {
|
||||
// t.push()
|
||||
// }
|
||||
//
|
||||
// t.didTapped { [weak t] in
|
||||
// t?.pop()
|
||||
// Alert.push(scene: .loading, title: "正在加载", message: "马上就要成功了")
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now()+1) {
|
||||
// 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)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
func testGuard() {
|
||||
let g = ProHUD.Guard(title: "请求权限", message: "请打开相机权限开关,否则无法进行测量。")
|
||||
Guard.push { (vm) in
|
||||
vm.add(title: "大标题")
|
||||
vm.add(subTitle: "副标题")
|
||||
vm.add(message: "请打开相机权限开关,否则无法进行测量。请打开相机权限开关,否则无法进行测量。")
|
||||
vm.add(action: .default, title: "OK") { [weak vm] in
|
||||
vm?.insert(action: 0, style: .destructive, title: "Del") { [weak vm] in
|
||||
vm?.update(action: 0, style: .destructive, title: "Delete") {
|
||||
vm?.remove(action: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
vm.insert(action: 0, style: .destructive, title: "Del") { [weak vm] in
|
||||
|
||||
g.add(title: "呵呵")
|
||||
g.add(message: "请打开相机权限开关,否则无法进行测量。请打开相机权限开关,否则无法进行测量。")
|
||||
g.add(action: .default, title: "测试弹窗", handler: { [weak self] in
|
||||
self?.testToast()
|
||||
})
|
||||
g.add(action: .destructive, title: "测试删除弹窗", handler: { [weak self] in
|
||||
self?.testDelete()
|
||||
})
|
||||
g.add(action: .cancel, title: "我知道了", handler: nil)
|
||||
vm?.update(action: 0, style: .destructive, title: "Delete") {
|
||||
vm?.remove(action: 0)
|
||||
}
|
||||
}
|
||||
vm.add(action: .cancel, title: "Cancel") {
|
||||
|
||||
g.push(to: self)
|
||||
debugPrint("test: ", g)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// let g = ProHUD.Guard(title: "请求权限", message: "请打开相机权限开关,否则无法进行测量。")
|
||||
//
|
||||
// g.add(title: "呵呵")
|
||||
// g.add(message: "请打开相机权限开关,否则无法进行测量。请打开相机权限开关,否则无法进行测量。")
|
||||
// g.add(action: .default, title: "测试弹窗", handler: { [weak self] in
|
||||
// self?.testToast()
|
||||
// })
|
||||
// g.add(action: .destructive, title: "测试删除弹窗", handler: { [weak self] in
|
||||
// self?.testDelete()
|
||||
// })
|
||||
// g.add(action: .cancel, title: "我知道了", handler: nil)
|
||||
//
|
||||
// g.push(to: self)
|
||||
// debugPrint("test: ", g)
|
||||
}
|
||||
|
||||
func testUpdateAction() {
|
||||
|
|
|
@ -10,16 +10,13 @@
|
|||
1AE9C44ABAF3F797A5518CE8 /* Pods_ProHUD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2011798511AD590A613E54E /* Pods_ProHUD.framework */; };
|
||||
CD16490B22EF09AB0077988C /* AlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD16490A22EF09AB0077988C /* AlertModel.swift */; };
|
||||
CD16490D22EF09B40077988C /* AlertConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD16490C22EF09B40077988C /* AlertConfig.swift */; };
|
||||
CD16490F22EF09D50077988C /* AlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD16490E22EF09D50077988C /* AlertView.swift */; };
|
||||
CD16491222EF0D900077988C /* HUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD16491122EF0D900077988C /* HUDView.swift */; };
|
||||
CD16491422EF12220077988C /* ProHUD.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD16491322EF12220077988C /* ProHUD.xcassets */; };
|
||||
CD6CD86C22F1858F00F4FD4A /* ToastModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD86B22F1858F00F4FD4A /* ToastModel.swift */; };
|
||||
CD6CD86E22F185A000F4FD4A /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD86D22F185A000F4FD4A /* ToastView.swift */; };
|
||||
CD6CD87022F185A700F4FD4A /* ToastController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD86F22F185A700F4FD4A /* ToastController.swift */; };
|
||||
CD6CD87222F185AF00F4FD4A /* ToastConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD87122F185AF00F4FD4A /* ToastConfig.swift */; };
|
||||
CD6CD87522F185C200F4FD4A /* GuardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD87422F185C200F4FD4A /* GuardController.swift */; };
|
||||
CD6CD87922F185D000F4FD4A /* GuardConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD87822F185D000F4FD4A /* GuardConfig.swift */; };
|
||||
CD6CD87B22F185D600F4FD4A /* GuardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6CD87A22F185D600F4FD4A /* GuardView.swift */; };
|
||||
CD95D22122E72C4C007559A3 /* ProHUD.h in Headers */ = {isa = PBXBuildFile; fileRef = CD95D21F22E72C4C007559A3 /* ProHUD.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CD95D26922E72DA1007559A3 /* AlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD95D26822E72DA1007559A3 /* AlertController.swift */; };
|
||||
CD95D26B22E72DB3007559A3 /* ProHUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD95D26A22E72DB3007559A3 /* ProHUD.swift */; };
|
||||
|
@ -33,16 +30,13 @@
|
|||
C2011798511AD590A613E54E /* Pods_ProHUD.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ProHUD.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CD16490A22EF09AB0077988C /* AlertModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertModel.swift; sourceTree = "<group>"; };
|
||||
CD16490C22EF09B40077988C /* AlertConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertConfig.swift; sourceTree = "<group>"; };
|
||||
CD16490E22EF09D50077988C /* AlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertView.swift; sourceTree = "<group>"; };
|
||||
CD16491122EF0D900077988C /* HUDView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDView.swift; sourceTree = "<group>"; };
|
||||
CD16491322EF12220077988C /* ProHUD.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = ProHUD.xcassets; sourceTree = "<group>"; };
|
||||
CD6CD86B22F1858F00F4FD4A /* ToastModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastModel.swift; sourceTree = "<group>"; };
|
||||
CD6CD86D22F185A000F4FD4A /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
|
||||
CD6CD86F22F185A700F4FD4A /* ToastController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastController.swift; sourceTree = "<group>"; };
|
||||
CD6CD87122F185AF00F4FD4A /* ToastConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastConfig.swift; sourceTree = "<group>"; };
|
||||
CD6CD87422F185C200F4FD4A /* GuardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuardController.swift; sourceTree = "<group>"; };
|
||||
CD6CD87822F185D000F4FD4A /* GuardConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuardConfig.swift; sourceTree = "<group>"; };
|
||||
CD6CD87A22F185D600F4FD4A /* GuardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuardView.swift; sourceTree = "<group>"; };
|
||||
CD95D21C22E72C4C007559A3 /* ProHUD.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ProHUD.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CD95D21F22E72C4C007559A3 /* ProHUD.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProHUD.h; sourceTree = "<group>"; };
|
||||
CD95D22022E72C4C007559A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
@ -90,7 +84,6 @@
|
|||
CD95D26822E72DA1007559A3 /* AlertController.swift */,
|
||||
CD16490A22EF09AB0077988C /* AlertModel.swift */,
|
||||
CD16490C22EF09B40077988C /* AlertConfig.swift */,
|
||||
CD16490E22EF09D50077988C /* AlertView.swift */,
|
||||
);
|
||||
path = Alert;
|
||||
sourceTree = "<group>";
|
||||
|
@ -99,7 +92,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
CD6CD86B22F1858F00F4FD4A /* ToastModel.swift */,
|
||||
CD6CD86D22F185A000F4FD4A /* ToastView.swift */,
|
||||
CD6CD86F22F185A700F4FD4A /* ToastController.swift */,
|
||||
CD6CD87122F185AF00F4FD4A /* ToastConfig.swift */,
|
||||
);
|
||||
|
@ -111,7 +103,6 @@
|
|||
children = (
|
||||
CD6CD87422F185C200F4FD4A /* GuardController.swift */,
|
||||
CD6CD87822F185D000F4FD4A /* GuardConfig.swift */,
|
||||
CD6CD87A22F185D600F4FD4A /* GuardView.swift */,
|
||||
CDC39CFC22FD6DDF0070E914 /* GuardModel.swift */,
|
||||
);
|
||||
path = Guard;
|
||||
|
@ -280,12 +271,9 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CD6CD87B22F185D600F4FD4A /* GuardView.swift in Sources */,
|
||||
CD95D26922E72DA1007559A3 /* AlertController.swift in Sources */,
|
||||
CD6CD87922F185D000F4FD4A /* GuardConfig.swift in Sources */,
|
||||
CDB6A07D22EEF19D00AF6CF0 /* HUDConfig.swift in Sources */,
|
||||
CD16490F22EF09D50077988C /* AlertView.swift in Sources */,
|
||||
CD6CD86E22F185A000F4FD4A /* ToastView.swift in Sources */,
|
||||
CD6CD87222F185AF00F4FD4A /* ToastConfig.swift in Sources */,
|
||||
CD6CD87522F185C200F4FD4A /* GuardController.swift in Sources */,
|
||||
CD6CD87022F185A700F4FD4A /* ToastController.swift in Sources */,
|
||||
|
|
|
@ -22,12 +22,14 @@ public extension ProHUD.Configuration {
|
|||
/// 填充:元素内部控件距离元素边界的距离
|
||||
public var padding = CGFloat(16)
|
||||
|
||||
// MARK: 图标样式
|
||||
/// 图标、default按钮的颜色
|
||||
/// 颜色
|
||||
public var tintColor: UIColor?
|
||||
|
||||
// MARK: 图标样式
|
||||
/// 图标尺寸
|
||||
public var iconSize = CGSize(width: 48, height: 48)
|
||||
|
||||
/// 某个场景的默认图片
|
||||
/// - Parameter callback: 回调
|
||||
public func iconForScene(_ callback: @escaping (ProHUD.Alert.Scene) -> UIImage?) {
|
||||
privIconForScene = callback
|
||||
}
|
||||
|
@ -38,7 +40,7 @@ public extension ProHUD.Configuration {
|
|||
/// 标题最多行数
|
||||
public var titleMaxLines = Int(1)
|
||||
|
||||
/// 加粗正文字体(如果只有标题或者只有正文,则显示这种字体)
|
||||
/// 加粗字体(如果只有标题或者只有正文,则显示这种字体)
|
||||
public var boldTextFont = UIFont.boldSystemFont(ofSize: 18)
|
||||
|
||||
/// 正文字体
|
||||
|
@ -78,7 +80,6 @@ public extension ProHUD.Configuration {
|
|||
|
||||
|
||||
// MARK: - 内部调用
|
||||
|
||||
internal extension ProHUD.Configuration.Alert {
|
||||
var reloadData: (ProHUD.Alert) -> Void {
|
||||
return privReloadData
|
||||
|
@ -87,7 +88,6 @@ internal extension ProHUD.Configuration.Alert {
|
|||
|
||||
|
||||
// MARK: - 默认实现
|
||||
|
||||
fileprivate var privLayoutContentView: (ProHUD.Alert) -> Void = {
|
||||
return { (vc) in
|
||||
if vc.contentView.superview == nil {
|
||||
|
@ -263,7 +263,7 @@ fileprivate var privLoadForceQuitButton: (ProHUD.Alert) -> Void = {
|
|||
let config = cfg.alert
|
||||
let btn = ProHUD.Alert.Button.forceQuitButton()
|
||||
btn.setTitle(cfg.alert.forceQuitTitle, for: .normal)
|
||||
let bg = ProHUD.BlurView()
|
||||
let bg = createBlurView()
|
||||
bg.layer.masksToBounds = true
|
||||
bg.layer.cornerRadius = config.cornerRadius
|
||||
if let last = vc.view.subviews.last {
|
||||
|
|
|
@ -19,7 +19,7 @@ public extension ProHUD {
|
|||
internal static var alertWindow: UIWindow?
|
||||
|
||||
/// 内容视图
|
||||
public var contentView = BlurView()
|
||||
public var contentView = createBlurView()
|
||||
|
||||
/// 内容容器(包括icon、textStack、actionStack)
|
||||
public var contentStack: StackContainer = {
|
||||
|
@ -88,12 +88,10 @@ public extension ProHUD {
|
|||
|
||||
public extension Alert {
|
||||
|
||||
// MARK: 生命周期函数
|
||||
|
||||
/// 推入屏幕
|
||||
@discardableResult func push() -> Alert {
|
||||
if Alert.alerts.contains(self) == false {
|
||||
let window = Alert.getAlertWindow(self)
|
||||
let window = Alert.privGetAlertWindow(self)
|
||||
window.makeKeyAndVisible()
|
||||
window.resignKey()
|
||||
window.addSubview(view)
|
||||
|
@ -106,14 +104,14 @@ public extension Alert {
|
|||
}
|
||||
Alert.alerts.append(self)
|
||||
}
|
||||
Alert.updateAlertsLayout()
|
||||
Alert.privUpdateAlertsLayout()
|
||||
return self
|
||||
}
|
||||
|
||||
/// 弹出屏幕
|
||||
func pop() {
|
||||
let window = Alert.getAlertWindow(self)
|
||||
Alert.removeItemFromArray(alert: self)
|
||||
let window = Alert.privGetAlertWindow(self)
|
||||
Alert.privRemoveItemFromArray(alert: self)
|
||||
UIView.animateForAlertBuildOut(animations: {
|
||||
self.view.alpha = 0
|
||||
self.view.transform = .init(scaleX: 1.08, y: 1.08)
|
||||
|
@ -132,29 +130,24 @@ public extension Alert {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: 设置函数
|
||||
/// 更新
|
||||
/// - Parameter callback: 回调
|
||||
func update(_ callback: ((inout ViewModel) -> Void)? = nil) {
|
||||
callback?(&vm)
|
||||
cfg.alert.reloadData(self)
|
||||
}
|
||||
|
||||
|
||||
/// 最小化事件
|
||||
/// - Parameter callback: 事件回调
|
||||
@discardableResult func didForceQuit(_ callback: (() -> Void)?) -> Alert {
|
||||
func didForceQuit(_ callback: (() -> Void)?) {
|
||||
vm.forceQuitCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
/// 消失事件
|
||||
/// - Parameter callback: 事件回调
|
||||
@discardableResult func didDisappear(_ callback: (() -> Void)?) -> Alert {
|
||||
func didDisappear(_ callback: (() -> Void)?) {
|
||||
disappearCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
/// 更新
|
||||
/// - Parameter callback: 回调
|
||||
func update(_ callback: ((inout Alert.ViewModel) -> Void)? = nil) {
|
||||
callback?(&vm)
|
||||
cfg.alert.reloadData(self)
|
||||
}
|
||||
|
||||
func animate(rotate: Bool) {
|
||||
|
@ -177,8 +170,7 @@ public extension Alert {
|
|||
}
|
||||
|
||||
|
||||
// MARK: 类函数
|
||||
|
||||
// MARK: - 实例管理器
|
||||
public extension Alert {
|
||||
|
||||
/// 推入屏幕
|
||||
|
@ -186,7 +178,7 @@ public extension Alert {
|
|||
/// - Parameter title: 标题
|
||||
/// - Parameter message: 正文
|
||||
/// - Parameter actions: 更多操作
|
||||
@discardableResult class func push(scene: Alert.Scene, title: String? = nil, message: String? = nil, actions: ((inout Alert.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()
|
||||
}
|
||||
|
||||
|
@ -218,45 +210,30 @@ public extension Alert {
|
|||
|
||||
}
|
||||
|
||||
// MARK: - 私有
|
||||
|
||||
// MARK: - 创建和设置
|
||||
internal extension Alert {
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
@discardableResult func privRemoveAction(index: Int) -> Alert {
|
||||
if index < 0 {
|
||||
for view in self.actionStack.arrangedSubviews {
|
||||
if let btn = view as? UIButton {
|
||||
btn.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
} else if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
|
||||
btn.removeFromSuperview()
|
||||
}
|
||||
if self.actionStack.arrangedSubviews.count == 0 {
|
||||
self.actionStack.removeFromSuperview()
|
||||
}
|
||||
UIView.animateForAlert {
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult func privAddButton(custom button: UIButton, at index: Int? = nil, handler: (() -> Void)?) -> UIButton {
|
||||
/// 加载一个按钮
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter action: 事件
|
||||
@discardableResult func insert(action index: Int?, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
let btn = Button.actionButton(title: title)
|
||||
if let idx = index, idx < actionStack.arrangedSubviews.count {
|
||||
actionStack.insertArrangedSubview(btn, at: idx)
|
||||
} else {
|
||||
actionStack.addArrangedSubview(btn)
|
||||
}
|
||||
btn.update(style: style)
|
||||
if actionStack.superview == nil {
|
||||
contentStack.addArrangedSubview(actionStack)
|
||||
contentStack.layoutIfNeeded()
|
||||
}
|
||||
if let idx = index, idx < actionStack.arrangedSubviews.count {
|
||||
actionStack.insertArrangedSubview(button, at: idx)
|
||||
} else {
|
||||
actionStack.addArrangedSubview(button)
|
||||
}
|
||||
|
||||
addTouchUpAction(for: button) { [weak self] in
|
||||
addTouchUpAction(for: btn) { [weak self] in
|
||||
handler?()
|
||||
if button.tag == UIAlertAction.Style.cancel.rawValue {
|
||||
if btn.tag == UIAlertAction.Style.cancel.rawValue {
|
||||
self?.pop()
|
||||
}
|
||||
}
|
||||
|
@ -266,10 +243,10 @@ internal extension Alert {
|
|||
self.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
return button
|
||||
return btn
|
||||
}
|
||||
|
||||
func privUpdateButton(action index: Int, style: UIAlertAction.Style, title: String?, _ handler: (() -> Void)?) {
|
||||
func update(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 {
|
||||
|
@ -287,10 +264,30 @@ internal extension Alert {
|
|||
}
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
@discardableResult func remove(action index: Int) -> Alert {
|
||||
if index < 0 {
|
||||
for view in self.actionStack.arrangedSubviews {
|
||||
if let btn = view as? UIButton {
|
||||
btn.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
} else if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
|
||||
btn.removeFromSuperview()
|
||||
}
|
||||
if self.actionStack.arrangedSubviews.count == 0 {
|
||||
self.actionStack.removeFromSuperview()
|
||||
}
|
||||
UIView.animateForAlert {
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension Alert {
|
||||
class func updateAlertsLayout() {
|
||||
class func privUpdateAlertsLayout() {
|
||||
for (i, a) in alerts.reversed().enumerated() {
|
||||
let scale = CGFloat(pow(0.7, CGFloat(i)))
|
||||
UIView.animate(withDuration: 1.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5, options: [.allowUserInteraction, .curveEaseInOut], animations: {
|
||||
|
@ -301,7 +298,7 @@ fileprivate extension Alert {
|
|||
}
|
||||
}
|
||||
}
|
||||
class func getAlertWindow(_ vc: UIViewController) -> UIWindow {
|
||||
class func privGetAlertWindow(_ vc: UIViewController) -> UIWindow {
|
||||
if let w = alertWindow {
|
||||
return w
|
||||
}
|
||||
|
@ -313,7 +310,7 @@ fileprivate extension Alert {
|
|||
return w
|
||||
}
|
||||
|
||||
class func removeItemFromArray(alert: Alert) {
|
||||
class func privRemoveItemFromArray(alert: Alert) {
|
||||
if alerts.count > 1 {
|
||||
for (i, a) in alerts.enumerated() {
|
||||
if a == alert {
|
||||
|
@ -322,7 +319,7 @@ fileprivate extension Alert {
|
|||
}
|
||||
}
|
||||
}
|
||||
updateAlertsLayout()
|
||||
privUpdateAlertsLayout()
|
||||
} else if alerts.count == 1 {
|
||||
alerts.removeAll()
|
||||
} else {
|
||||
|
|
|
@ -33,7 +33,7 @@ public extension Alert {
|
|||
|
||||
}
|
||||
|
||||
struct ViewModel {
|
||||
class ViewModel {
|
||||
|
||||
/// ID标识
|
||||
public var identifier = String(Date().timeIntervalSince1970)
|
||||
|
@ -67,9 +67,8 @@ public extension Alert {
|
|||
didSet {
|
||||
durationBlock?.cancel()
|
||||
if let t = duration, t > 0 {
|
||||
let v = vc
|
||||
durationBlock = DispatchWorkItem(block: {
|
||||
v?.pop()
|
||||
durationBlock = DispatchWorkItem(block: { [weak self] in
|
||||
self?.vc?.pop()
|
||||
})
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: durationBlock!)
|
||||
} else {
|
||||
|
@ -80,6 +79,8 @@ public extension Alert {
|
|||
|
||||
public weak var vc: Alert?
|
||||
|
||||
// MARK: 私有
|
||||
|
||||
/// 持续时间
|
||||
internal var durationBlock: DispatchWorkItem?
|
||||
|
||||
|
@ -99,13 +100,9 @@ public extension Alert.ViewModel {
|
|||
/// - Parameter style: 样式
|
||||
/// - Parameter text: 标题
|
||||
/// - Parameter handler: 事件处理
|
||||
@discardableResult mutating func add(action style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
@discardableResult 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!
|
||||
return vc!.insert(action: nil, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 插入按钮
|
||||
|
@ -113,13 +110,9 @@ public extension Alert.ViewModel {
|
|||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter handler: 事件处理
|
||||
@discardableResult mutating func insert(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
@discardableResult 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!
|
||||
return vc!.insert(action: index, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 更新按钮
|
||||
|
@ -127,16 +120,15 @@ public extension Alert.ViewModel {
|
|||
/// - 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)
|
||||
func update(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) {
|
||||
vc?.update(action: index, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
mutating func remove(action index: Int...) {
|
||||
guard let alert = self.vc else { return }
|
||||
func remove(action index: Int...) {
|
||||
for (i, idx) in index.enumerated() {
|
||||
alert.privRemoveAction(index: idx-i)
|
||||
vc?.remove(action: idx-i)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
//
|
||||
// AlertView.swift
|
||||
// ProHUD
|
||||
//
|
||||
// Created by xaoxuu on 2019/7/29.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
internal extension Alert {
|
||||
class Button: UIButton {
|
||||
class func actionButton(title: String?) -> UIButton {
|
||||
let btn = Button(type: .system)
|
||||
btn.setTitle(title, for: .normal)
|
||||
btn.layer.cornerRadius = cfg.alert.cornerRadius / 2
|
||||
btn.titleLabel?.font = cfg.alert.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
func update(style: UIAlertAction.Style) {
|
||||
let pd = CGFloat(8)
|
||||
if style != .cancel {
|
||||
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 {
|
||||
case .default:
|
||||
setTitleColor(tintColor, for: .normal)
|
||||
case .destructive:
|
||||
setTitleColor(.init(red: 244/255, green: 67/255, blue: 54/255, alpha: 1), for: .normal)
|
||||
case .cancel:
|
||||
setTitleColor(cfg.secondaryLabelColor, for: .normal)
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
tag = style.rawValue
|
||||
}
|
||||
|
||||
class func forceQuitButton() -> UIButton {
|
||||
let btn = Button(type: .system)
|
||||
let pd = cfg.alert.padding/2
|
||||
btn.contentEdgeInsets = .init(top: pd*1.5, left: pd*1.5, bottom: pd*1.5, right: pd*1.5)
|
||||
btn.imageEdgeInsets.right = pd*1.5
|
||||
btn.setTitleColor(UIColor(red:1.00, green:0.55, blue:0.21, alpha:1.00), for: .normal)
|
||||
btn.titleLabel?.font = cfg.alert.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -22,7 +22,6 @@ public extension ProHUD.Configuration {
|
|||
/// 填充:元素内部控件距离元素边界的距离
|
||||
public var padding = CGFloat(16)
|
||||
|
||||
// MARK: 图标样式
|
||||
/// 颜色
|
||||
public var tintColor: UIColor?
|
||||
|
||||
|
@ -41,12 +40,6 @@ public extension ProHUD.Configuration {
|
|||
/// 按钮圆角半径
|
||||
public var buttonCornerRadius = CGFloat(12)
|
||||
|
||||
/// 加载视图
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func loadSubviews(_ callback: @escaping (ProHUD.Guard) -> Void) {
|
||||
privLoadSubviews = callback
|
||||
}
|
||||
|
||||
/// 更新视图
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func reloadData(_ callback: @escaping (ProHUD.Guard) -> Void) {
|
||||
|
@ -56,15 +49,14 @@ public extension ProHUD.Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - 默认实现
|
||||
|
||||
// MARK: - 内部调用
|
||||
internal extension ProHUD.Configuration.Guard {
|
||||
var loadSubviews: (ProHUD.Guard) -> Void {
|
||||
return privLoadSubviews
|
||||
}
|
||||
|
||||
var reloadData: (ProHUD.Guard) -> Void {
|
||||
return privReloadData
|
||||
}
|
||||
|
||||
var reloadStack: (ProHUD.Guard) -> Void {
|
||||
return { (vc) in
|
||||
if vc.textStack.arrangedSubviews.count > 0 {
|
||||
|
@ -82,14 +74,7 @@ internal extension ProHUD.Configuration.Guard {
|
|||
|
||||
}
|
||||
|
||||
fileprivate var privLoadSubviews: (ProHUD.Guard) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "loadSubviews")
|
||||
let config = cfg.guard
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
// MARK: - 默认实现
|
||||
fileprivate var privReloadData: (ProHUD.Guard) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "reloadData")
|
||||
|
@ -114,10 +99,10 @@ fileprivate var privReloadData: (ProHUD.Guard) -> Void = {
|
|||
vc.contentView.snp.makeConstraints { (mk) in
|
||||
mk.centerX.equalToSuperview()
|
||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||
if width == config.cardMaxWidth {
|
||||
mk.bottom.equalToSuperview().offset(-Inspire.shared.screen.safeAreaInsets.bottom)
|
||||
} else {
|
||||
if width < config.cardMaxWidth {
|
||||
mk.bottom.equalToSuperview()
|
||||
} else {
|
||||
mk.bottom.equalToSuperview().offset(-Inspire.shared.screen.safeAreaInsets.bottom)
|
||||
}
|
||||
} else if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
mk.centerY.equalToSuperview()
|
||||
|
@ -126,12 +111,17 @@ fileprivate var privReloadData: (ProHUD.Guard) -> Void = {
|
|||
}
|
||||
// stack
|
||||
vc.contentStack.snp.makeConstraints { (mk) in
|
||||
mk.top.equalToSuperview().offset(config.padding + config.margin)
|
||||
mk.top.equalToSuperview().offset(config.padding)
|
||||
mk.centerX.equalToSuperview()
|
||||
if width == config.cardMaxWidth {
|
||||
if width < config.cardMaxWidth {
|
||||
let bottom = Inspire.shared.screen.safeAreaInsets.bottom
|
||||
if bottom == 0 {
|
||||
mk.bottom.equalToSuperview().offset(-config.padding)
|
||||
} else {
|
||||
mk.bottom.equalToSuperview().offset(-config.padding-Inspire.shared.screen.safeAreaInsets.bottom)
|
||||
mk.bottom.equalToSuperview().offset(-config.padding/2 - bottom)
|
||||
}
|
||||
} else {
|
||||
mk.bottom.equalToSuperview().offset(-config.padding)
|
||||
}
|
||||
if isPortrait {
|
||||
mk.width.equalToSuperview().offset(-config.padding * 2)
|
||||
|
@ -139,6 +129,6 @@ fileprivate var privReloadData: (ProHUD.Guard) -> Void = {
|
|||
mk.width.equalToSuperview().offset(-config.padding * 4)
|
||||
}
|
||||
}
|
||||
|
||||
config.reloadStack(vc)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -15,7 +15,7 @@ public extension ProHUD {
|
|||
class Guard: HUDController {
|
||||
|
||||
/// 内容视图
|
||||
public var contentView = BlurView()
|
||||
public var contentView = createBlurView()
|
||||
|
||||
/// 内容容器(包括textStack、actionStack,可以自己插入需要的控件)
|
||||
public var contentStack: StackContainer = {
|
||||
|
@ -53,6 +53,7 @@ public extension ProHUD {
|
|||
public var vm = ViewModel()
|
||||
|
||||
// MARK: 生命周期
|
||||
internal var isLoadFinished = false
|
||||
|
||||
/// 实例化
|
||||
/// - Parameter title: 标题
|
||||
|
@ -69,29 +70,27 @@ public extension ProHUD {
|
|||
}
|
||||
actions?(&vm)
|
||||
|
||||
view.tintColor = cfg.guard.tintColor
|
||||
cfg.guard.loadSubviews(self)
|
||||
cfg.guard.reloadData(self)
|
||||
cfg.guard.reloadStack(self)
|
||||
|
||||
// 点击空白处
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(privDidTapped(_:)))
|
||||
view.addGestureRecognizer(tap)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.tintColor = cfg.guard.tintColor
|
||||
cfg.guard.reloadData(self)
|
||||
isLoadFinished = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - 实例函数
|
||||
|
||||
public extension Guard {
|
||||
|
||||
// MARK: 生命周期函数
|
||||
|
||||
/// 推入某个视图控制器
|
||||
/// - Parameter viewController: 视图控制器
|
||||
@discardableResult func push(to viewController: UIViewController? = nil) -> Guard {
|
||||
|
@ -104,11 +103,11 @@ public extension Guard {
|
|||
mk.edges.equalToSuperview()
|
||||
}
|
||||
if displaying == false {
|
||||
translateOut()
|
||||
privTranslateOut()
|
||||
}
|
||||
displaying = true
|
||||
UIView.animateForGuard {
|
||||
self.translateIn()
|
||||
self.privTranslateIn()
|
||||
}
|
||||
}
|
||||
if let vc = viewController ?? cfg.rootViewController {
|
||||
|
@ -127,7 +126,7 @@ public extension Guard {
|
|||
view.isUserInteractionEnabled = false
|
||||
self.removeFromParent()
|
||||
UIView.animateForGuard(animations: {
|
||||
self.translateOut()
|
||||
self.privTranslateOut()
|
||||
}) { (done) in
|
||||
if self.displaying == false {
|
||||
self.view.removeFromSuperview()
|
||||
|
@ -136,92 +135,23 @@ public extension Guard {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: 设置函数
|
||||
|
||||
/// 加载一个标题
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(title: String?) -> UILabel {
|
||||
let lb = UILabel()
|
||||
lb.font = cfg.guard.titleFont
|
||||
lb.textColor = cfg.primaryLabelColor
|
||||
lb.numberOfLines = 0
|
||||
lb.textAlignment = .justified
|
||||
lb.text = title
|
||||
textStack.addArrangedSubview(lb)
|
||||
if #available(iOS 11.0, *) {
|
||||
let count = textStack.arrangedSubviews.count
|
||||
if count > 1 {
|
||||
textStack.setCustomSpacing(cfg.guard.margin * 2, after: textStack.arrangedSubviews[count-2])
|
||||
}
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
cfg.guard.reloadStack(self)
|
||||
return lb
|
||||
/// 更新
|
||||
/// - Parameter callback: 回调
|
||||
func update(_ callback: ((inout ViewModel) -> Void)? = nil) {
|
||||
callback?(&vm)
|
||||
cfg.guard.reloadData(self)
|
||||
}
|
||||
|
||||
/// 加载一个副标题
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(subTitle: String?) -> UILabel {
|
||||
let lb = add(title: subTitle)
|
||||
lb.font = cfg.guard.subTitleFont
|
||||
return lb
|
||||
}
|
||||
|
||||
/// 加载一段正文
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(message: String?) -> UILabel {
|
||||
let lb = UILabel()
|
||||
lb.font = cfg.guard.bodyFont
|
||||
lb.textColor = cfg.secondaryLabelColor
|
||||
lb.numberOfLines = 0
|
||||
lb.textAlignment = .justified
|
||||
lb.text = message
|
||||
textStack.addArrangedSubview(lb)
|
||||
cfg.guard.reloadStack(self)
|
||||
return lb
|
||||
}
|
||||
|
||||
/// 加载一个按钮
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter action: 事件
|
||||
@discardableResult func add(action style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
let btn = Button.actionButton(title: title)
|
||||
btn.titleLabel?.font = cfg.guard.buttonFont
|
||||
actionStack.addArrangedSubview(btn)
|
||||
cfg.guard.reloadStack(self)
|
||||
btn.update(style: style)
|
||||
addTouchUpAction(for: btn) { [weak self] in
|
||||
handler?()
|
||||
if btn.tag == UIAlertAction.Style.cancel.rawValue {
|
||||
self?.pop()
|
||||
}
|
||||
}
|
||||
return btn
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
@discardableResult func remove(action index: Int...) -> Guard {
|
||||
for (i, idx) in index.enumerated() {
|
||||
privRemoveAction(index: idx-i)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
/// 消失事件
|
||||
/// - Parameter callback: 事件回调
|
||||
@discardableResult func didDisappear(_ callback: (() -> Void)?) -> Guard {
|
||||
func didDisappear(_ callback: (() -> Void)?) {
|
||||
disappearCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: 类函数
|
||||
|
||||
// MARK: - 实例管理器
|
||||
public extension Guard {
|
||||
|
||||
/// 推入屏幕
|
||||
|
@ -261,7 +191,7 @@ public extension Guard {
|
|||
`guard`.pop()
|
||||
}
|
||||
|
||||
/// 弹出屏幕
|
||||
/// 弹出所有实例
|
||||
/// - Parameter identifier: 指定实例的标识
|
||||
class func pop(from viewController: UIViewController?) {
|
||||
for g in guards(from: viewController) {
|
||||
|
@ -273,36 +203,103 @@ public extension Guard {
|
|||
|
||||
|
||||
|
||||
// MARK: - 私有
|
||||
|
||||
// MARK: - 创建和设置
|
||||
internal extension Guard {
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func privDidTapped(_ sender: UITapGestureRecognizer) {
|
||||
let point = sender.location(in: contentView)
|
||||
if point.x < 0 || point.y < 0 {
|
||||
if force == false {
|
||||
// 点击到操作区域外部
|
||||
pop()
|
||||
/// 加载一个标题
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(title: String?) -> UILabel {
|
||||
let lb = UILabel()
|
||||
lb.font = cfg.guard.titleFont
|
||||
lb.textColor = cfg.primaryLabelColor
|
||||
lb.numberOfLines = 0
|
||||
lb.textAlignment = .center
|
||||
lb.text = title
|
||||
textStack.addArrangedSubview(lb)
|
||||
if #available(iOS 11.0, *) {
|
||||
let count = textStack.arrangedSubviews.count
|
||||
if count > 1 {
|
||||
textStack.setCustomSpacing(cfg.guard.margin * 2, after: textStack.arrangedSubviews[count-2])
|
||||
}
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
cfg.guard.reloadStack(self)
|
||||
return lb
|
||||
}
|
||||
|
||||
/// 加载一个副标题
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(subTitle: String?) -> UILabel {
|
||||
let lb = add(title: subTitle)
|
||||
lb.font = cfg.guard.subTitleFont
|
||||
lb.textAlignment = .justified
|
||||
return lb
|
||||
}
|
||||
|
||||
func translateIn() {
|
||||
view.backgroundColor = backgroundColor
|
||||
contentView.transform = .identity
|
||||
/// 加载一段正文
|
||||
/// - Parameter text: 文本
|
||||
@discardableResult func add(message: String?) -> UILabel {
|
||||
let lb = UILabel()
|
||||
lb.font = cfg.guard.bodyFont
|
||||
lb.textColor = cfg.secondaryLabelColor
|
||||
lb.numberOfLines = 0
|
||||
lb.textAlignment = .justified
|
||||
lb.text = message
|
||||
textStack.addArrangedSubview(lb)
|
||||
cfg.guard.reloadStack(self)
|
||||
return lb
|
||||
}
|
||||
|
||||
func translateOut() {
|
||||
view.backgroundColor = UIColor(white: 0, alpha: 0)
|
||||
contentView.transform = .init(translationX: 0, y: view.frame.size.height - contentView.frame.minY + cfg.guard.margin)
|
||||
/// 加载一个按钮
|
||||
/// - Parameter index: 索引
|
||||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter action: 事件
|
||||
@discardableResult func insert(action index: Int?, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
let btn = Button.actionButton(title: title)
|
||||
btn.titleLabel?.font = cfg.guard.buttonFont
|
||||
if let idx = index, idx < actionStack.arrangedSubviews.count {
|
||||
actionStack.insertArrangedSubview(btn, at: idx)
|
||||
} else {
|
||||
actionStack.addArrangedSubview(btn)
|
||||
}
|
||||
btn.update(style: style)
|
||||
cfg.guard.reloadStack(self)
|
||||
addTouchUpAction(for: btn) { [weak self] in
|
||||
handler?()
|
||||
if btn.tag == UIAlertAction.Style.cancel.rawValue {
|
||||
self?.pop()
|
||||
}
|
||||
}
|
||||
if isLoadFinished {
|
||||
UIView.animateForGuard {
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
return btn
|
||||
}
|
||||
func update(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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
@discardableResult func privRemoveAction(index: Int) -> Guard {
|
||||
@discardableResult func remove(index: Int) -> Guard {
|
||||
if index < 0 {
|
||||
for view in self.actionStack.arrangedSubviews {
|
||||
if let btn = view as? UIButton {
|
||||
|
@ -322,3 +319,29 @@ internal extension Guard {
|
|||
|
||||
}
|
||||
|
||||
fileprivate extension Guard {
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func privDidTapped(_ sender: UITapGestureRecognizer) {
|
||||
let point = sender.location(in: contentView)
|
||||
if point.x < 0 || point.y < 0 {
|
||||
if force == false {
|
||||
// 点击到操作区域外部
|
||||
pop()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func privTranslateIn() {
|
||||
view.backgroundColor = backgroundColor
|
||||
contentView.transform = .identity
|
||||
}
|
||||
|
||||
func privTranslateOut() {
|
||||
view.backgroundColor = UIColor(white: 0, alpha: 0)
|
||||
contentView.transform = .init(translationX: 0, y: view.frame.size.height - contentView.frame.minY + cfg.guard.margin)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import UIKit
|
|||
|
||||
public extension Guard {
|
||||
|
||||
struct ViewModel {
|
||||
class ViewModel {
|
||||
|
||||
/// ID标识
|
||||
public var identifier = String(Date().timeIntervalSince1970)
|
||||
|
@ -46,7 +46,7 @@ public extension Guard.ViewModel {
|
|||
/// - 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)
|
||||
return vc!.insert(action: nil, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 插入按钮
|
||||
|
@ -54,9 +54,8 @@ public extension Guard.ViewModel {
|
|||
/// - Parameter style: 样式
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter handler: 事件处理
|
||||
@discardableResult mutating func insert(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
|
||||
return UIButton()
|
||||
@discardableResult func insert(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
|
||||
return vc!.insert(action: index, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 更新按钮
|
||||
|
@ -64,15 +63,15 @@ public extension Guard.ViewModel {
|
|||
/// - 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)
|
||||
func update(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) {
|
||||
vc?.update(action: index, style: style, title: title, handler: handler)
|
||||
}
|
||||
|
||||
/// 移除按钮
|
||||
/// - Parameter index: 索引
|
||||
func remove(action index: Int...) {
|
||||
for (i, idx) in index.enumerated() {
|
||||
vc!.privRemoveAction(index: idx-i)
|
||||
vc?.remove(index: idx-i)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// GuardView.swift
|
||||
// ProHUD
|
||||
//
|
||||
// Created by xaoxuu on 2019/7/31.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
internal extension Guard {
|
||||
|
||||
class Button: UIButton {
|
||||
class func actionButton(title: String?) -> Button {
|
||||
let btn = Button(type: .system)
|
||||
btn.setTitle(title, for: .normal)
|
||||
btn.layer.cornerRadius = cfg.guard.buttonCornerRadius
|
||||
btn.titleLabel?.font = cfg.guard.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
func update(style: UIAlertAction.Style) {
|
||||
let pd = CGFloat(8)
|
||||
if style != .cancel {
|
||||
contentEdgeInsets = .init(top: pd*1.5+2, left: pd*1.5, bottom: pd*1.5+2, right: pd*1.5)
|
||||
} else {
|
||||
contentEdgeInsets = .init(top: pd*1+2, left: pd*1.5, bottom: pd*1+2, right: pd*1.5)
|
||||
}
|
||||
switch style {
|
||||
case .default:
|
||||
backgroundColor = tintColor
|
||||
setTitleColor(.white, for: .normal)
|
||||
case .destructive:
|
||||
backgroundColor = .init(red: 244/255, green: 67/255, blue: 54/255, alpha: 1)
|
||||
setTitleColor(.white, for: .normal)
|
||||
case .cancel:
|
||||
setTitleColor(cfg.secondaryLabelColor, for: .normal)
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
tag = style.rawValue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -19,19 +19,19 @@ public extension ProHUD {
|
|||
|
||||
/// 动态颜色(适配iOS13)
|
||||
public lazy var dynamicColor: UIColor = {
|
||||
// if #available(iOS 13.0, *) {
|
||||
// let color = UIColor { (traitCollection: UITraitCollection) -> UIColor in
|
||||
// if traitCollection.userInterfaceStyle == .dark {
|
||||
// return .white
|
||||
// } else {
|
||||
// return .black
|
||||
// }
|
||||
// }
|
||||
// return color
|
||||
// } else {
|
||||
// // Fallback on earlier versions
|
||||
// }
|
||||
return .init(white: 0.15, alpha: 1)
|
||||
if #available(iOS 13.0, *) {
|
||||
let color = UIColor { (traitCollection: UITraitCollection) -> UIColor in
|
||||
if traitCollection.userInterfaceStyle == .dark {
|
||||
return .init(white: 1, alpha: 1)
|
||||
} else {
|
||||
return .init(white: 0.1, alpha: 1)
|
||||
}
|
||||
}
|
||||
return color
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
return .init(white: 0.1, alpha: 1)
|
||||
}()
|
||||
|
||||
/// 主标签文本颜色
|
||||
|
@ -41,9 +41,13 @@ public extension ProHUD {
|
|||
|
||||
/// 次级标签文本颜色
|
||||
public lazy var secondaryLabelColor: UIColor = {
|
||||
return dynamicColor.withAlphaComponent(0.6)
|
||||
return dynamicColor.withAlphaComponent(0.66)
|
||||
}()
|
||||
|
||||
public func blurView(_ callback: @escaping () -> UIVisualEffectView) {
|
||||
createBlurView = callback
|
||||
}
|
||||
|
||||
|
||||
public var toast = Toast()
|
||||
public var alert = Alert()
|
||||
|
@ -78,3 +82,19 @@ public extension ProHUD {
|
|||
config(&cfg)
|
||||
}
|
||||
}
|
||||
|
||||
internal var createBlurView: () -> UIVisualEffectView = {
|
||||
return {
|
||||
let vev = UIVisualEffectView()
|
||||
if #available(iOS 13.0, *) {
|
||||
vev.effect = UIBlurEffect(style: .systemMaterial)
|
||||
// vev.effect = UIBlurEffect(style: .extraLight)
|
||||
} else if #available(iOS 11.0, *) {
|
||||
vev.effect = UIBlurEffect(style: .extraLight)
|
||||
} else {
|
||||
vev.effect = .none
|
||||
vev.backgroundColor = .white
|
||||
}
|
||||
return vev
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -27,34 +27,94 @@ public extension ProHUD {
|
|||
|
||||
}
|
||||
|
||||
class BlurView: UIVisualEffectView {
|
||||
}
|
||||
|
||||
init() {
|
||||
internal extension Alert {
|
||||
class Button: UIButton {
|
||||
class func actionButton(title: String?) -> Button {
|
||||
let btn = Button(type: .system)
|
||||
btn.setTitle(title, for: .normal)
|
||||
btn.layer.cornerRadius = cfg.alert.cornerRadius / 2
|
||||
btn.titleLabel?.font = cfg.alert.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
// super.init(effect: UIBlurEffect(style: .systemMaterial))
|
||||
super.init(effect: UIBlurEffect(style: .extraLight))
|
||||
} else if #available(iOS 11.0, *) {
|
||||
super.init(effect: UIBlurEffect(style: .extraLight))
|
||||
func update(style: UIAlertAction.Style) {
|
||||
let pd = CGFloat(8)
|
||||
if style != .cancel {
|
||||
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 {
|
||||
super.init(effect: .none)
|
||||
backgroundColor = .white
|
||||
backgroundColor = .clear
|
||||
contentEdgeInsets = .init(top: pd*0.5, left: pd*1.5, bottom: pd*0.5, right: pd*1.5)
|
||||
}
|
||||
switch style {
|
||||
case .default:
|
||||
setTitleColor(tintColor, for: .normal)
|
||||
case .destructive:
|
||||
setTitleColor(.init(red: 244/255, green: 67/255, blue: 54/255, alpha: 1), for: .normal)
|
||||
case .cancel:
|
||||
setTitleColor(cfg.secondaryLabelColor, for: .normal)
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
tag = style.rawValue
|
||||
}
|
||||
|
||||
class func forceQuitButton() -> UIButton {
|
||||
let btn = Button(type: .system)
|
||||
let pd = cfg.alert.padding/2
|
||||
btn.contentEdgeInsets = .init(top: pd*1.5, left: pd*1.5, bottom: pd*1.5, right: pd*1.5)
|
||||
btn.imageEdgeInsets.right = pd*1.5
|
||||
btn.setTitleColor(UIColor(red:1.00, green:0.55, blue:0.21, alpha:1.00), for: .normal)
|
||||
btn.titleLabel?.font = cfg.alert.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
internal extension Guard {
|
||||
|
||||
class Button: UIButton {
|
||||
class func actionButton(title: String?) -> Button {
|
||||
let btn = Button(type: .system)
|
||||
btn.setTitle(title, for: .normal)
|
||||
btn.layer.cornerRadius = cfg.guard.buttonCornerRadius
|
||||
btn.titleLabel?.font = cfg.guard.buttonFont
|
||||
return btn
|
||||
}
|
||||
|
||||
func update(style: UIAlertAction.Style) {
|
||||
let pd = CGFloat(8)
|
||||
if style != .cancel {
|
||||
contentEdgeInsets = .init(top: pd*1.5+2, left: pd*1.5, bottom: pd*1.5+2, right: pd*1.5)
|
||||
} else {
|
||||
contentEdgeInsets = .init(top: pd*1+2, left: pd*1.5, bottom: pd*1+2, right: pd*1.5)
|
||||
}
|
||||
switch style {
|
||||
case .default:
|
||||
backgroundColor = tintColor
|
||||
setTitleColor(.white, for: .normal)
|
||||
case .destructive:
|
||||
backgroundColor = .init(red: 244/255, green: 67/255, blue: 54/255, alpha: 1)
|
||||
setTitleColor(.white, for: .normal)
|
||||
case .cancel:
|
||||
backgroundColor = .clear
|
||||
setTitleColor(cfg.secondaryLabelColor, for: .normal)
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
tag = style.rawValue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal extension UIView {
|
||||
|
||||
class func animateEaseOut(duration: TimeInterval = 1, delay: TimeInterval = 0, animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
private class func animateEaseOut(duration: TimeInterval = 1, delay: TimeInterval = 0, animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
animate(withDuration: duration, delay: delay, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.allowUserInteraction, .curveEaseOut], animations: animations, completion: completion)
|
||||
}
|
||||
|
||||
|
@ -64,27 +124,26 @@ internal extension UIView {
|
|||
class func animateForAlertBuildOut(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
animateEaseOut(duration: 0.38, delay: 0, animations: animations, completion: completion)
|
||||
}
|
||||
class func animateForAlert(animations: @escaping () -> Void) {
|
||||
animateEaseOut(duration: 1, delay: 0, animations: animations, completion: nil)
|
||||
}
|
||||
|
||||
class func animateForAlert(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
animateEaseOut(duration: 1, delay: 0, animations: animations, completion: completion)
|
||||
}
|
||||
|
||||
class func animateForToast(animations: @escaping () -> Void) {
|
||||
animateEaseOut(duration: 1, delay: 0, animations: animations, completion: nil)
|
||||
class func animateForAlert(animations: @escaping () -> Void) {
|
||||
animateForAlert(animations: animations, completion: nil)
|
||||
}
|
||||
|
||||
class func animateForToast(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
animateEaseOut(duration: 1, delay: 0, animations: animations, completion: completion)
|
||||
animateEaseOut(duration: 1.2, delay: 0, animations: animations, completion: completion)
|
||||
}
|
||||
|
||||
class func animateForGuard(animations: @escaping () -> Void) {
|
||||
animateForGuard(animations: animations, completion: nil)
|
||||
class func animateForToast(animations: @escaping () -> Void) {
|
||||
animateForToast(animations: animations, completion: nil)
|
||||
}
|
||||
|
||||
class func animateForGuard(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
||||
animateEaseOut(duration: 0.6, delay: 0, animations: animations, completion: completion)
|
||||
}
|
||||
class func animateForGuard(animations: @escaping () -> Void) {
|
||||
animateForGuard(animations: animations, completion: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,10 +21,13 @@ public extension ProHUD.Configuration {
|
|||
public var padding = CGFloat(16)
|
||||
|
||||
// MARK: 图标样式
|
||||
/// 图标、default按钮的颜色
|
||||
public var tintColor: UIColor?
|
||||
/// 图标尺寸
|
||||
public var iconSize = CGSize(width: 48, height: 48)
|
||||
/// 某个场景的默认图片
|
||||
/// - Parameter callback: 回调
|
||||
public func iconForScene(_ callback: @escaping (ProHUD.Toast.Scene) -> UIImage?) {
|
||||
privIconForScene = callback
|
||||
}
|
||||
|
||||
// MARK: 文本样式
|
||||
/// 标题字体
|
||||
|
@ -37,71 +40,49 @@ public extension ProHUD.Configuration {
|
|||
/// 正文最多行数
|
||||
public var bodyMaxLines = Int(10)
|
||||
|
||||
/// 加载视图(如果需要完全自定义整个View,可以重写这个)
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func loadSubviews(_ callback: @escaping (ProHUD.Toast) -> Void) {
|
||||
privLoadSubviews = callback
|
||||
}
|
||||
|
||||
/// 更新视图
|
||||
/// - Parameter callback: 回调代码
|
||||
public mutating func reloadData(_ callback: @escaping (ProHUD.Toast) -> Void) {
|
||||
privReloadData = callback
|
||||
}
|
||||
|
||||
/// 非Loading弹窗的默认持续时间
|
||||
public var duration = TimeInterval(3)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 默认实现
|
||||
|
||||
// MARK: - 内部调用
|
||||
internal extension ProHUD.Configuration.Toast {
|
||||
var loadSubviews: (ProHUD.Toast) -> Void {
|
||||
return privLoadSubviews
|
||||
}
|
||||
|
||||
var reloadData: (ProHUD.Toast) -> Void {
|
||||
return privReloadData
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate var privLoadSubviews: (ProHUD.Toast) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "loadSubviews")
|
||||
vc.view.tintColor = cfg.toast.tintColor
|
||||
vc.view.addSubview(vc.titleLabel)
|
||||
vc.view.addSubview(vc.bodyLabel)
|
||||
vc.view.addSubview(vc.imageView)
|
||||
}
|
||||
}()
|
||||
|
||||
// MARK: - 默认实现
|
||||
fileprivate var privReloadData: (ProHUD.Toast) -> Void = {
|
||||
return { (vc) in
|
||||
debug(vc, "reloadData")
|
||||
let config = cfg.toast
|
||||
let scene = vc.model.scene
|
||||
// 设置数据
|
||||
let imgStr: String
|
||||
switch vc.model.scene {
|
||||
case .success:
|
||||
imgStr = "ProHUDSuccess"
|
||||
case .warning:
|
||||
imgStr = "ProHUDWarning"
|
||||
case .error:
|
||||
imgStr = "ProHUDError"
|
||||
case .loading:
|
||||
imgStr = "ProHUDLoading"
|
||||
case .confirm:
|
||||
imgStr = "ProHUDMessage"
|
||||
case .delete:
|
||||
imgStr = "ProHUDTrash"
|
||||
default:
|
||||
imgStr = "ProHUDMessage"
|
||||
let scene = vc.vm.scene
|
||||
if vc.titleLabel.superview == nil {
|
||||
vc.view.addSubview(vc.titleLabel)
|
||||
}
|
||||
let img = vc.model.icon ?? ProHUD.image(named: imgStr)
|
||||
vc.imageView.image = img
|
||||
if vc.bodyLabel.superview == nil {
|
||||
vc.view.addSubview(vc.bodyLabel)
|
||||
}
|
||||
if vc.imageView.superview == nil {
|
||||
vc.view.addSubview(vc.imageView)
|
||||
}
|
||||
// 设置数据
|
||||
vc.imageView.image = vc.vm.icon ?? privIconForScene(vc.vm.scene)
|
||||
vc.titleLabel.textColor = cfg.primaryLabelColor
|
||||
vc.titleLabel.text = vc.model.title
|
||||
vc.titleLabel.text = vc.vm.title
|
||||
vc.bodyLabel.textColor = cfg.secondaryLabelColor
|
||||
vc.bodyLabel.text = vc.model.message
|
||||
vc.bodyLabel.text = vc.vm.message
|
||||
|
||||
// 更新布局
|
||||
vc.imageView.snp.makeConstraints { (mk) in
|
||||
|
@ -123,13 +104,35 @@ fileprivate var privReloadData: (ProHUD.Toast) -> Void = {
|
|||
}
|
||||
|
||||
vc.view.layoutIfNeeded()
|
||||
switch vc.model.scene {
|
||||
case .loading:
|
||||
vc.duration(nil)
|
||||
default:
|
||||
vc.duration(3)
|
||||
// 设置默认持续时间
|
||||
if vc.vm.duration == nil {
|
||||
if vc.vm.scene == .loading {
|
||||
vc.vm.duration = 0
|
||||
} else {
|
||||
vc.vm.duration = config.duration
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
|
||||
|
||||
fileprivate var privIconForScene: (ProHUD.Toast.Scene) -> UIImage? = {
|
||||
return { (scene) in
|
||||
let imgStr: String
|
||||
switch scene {
|
||||
case .success:
|
||||
imgStr = "ProHUDSuccess"
|
||||
case .warning:
|
||||
imgStr = "ProHUDWarning"
|
||||
case .error:
|
||||
imgStr = "ProHUDError"
|
||||
case .loading:
|
||||
imgStr = "ProHUDLoading"
|
||||
default:
|
||||
imgStr = "ProHUDMessage"
|
||||
}
|
||||
return ProHUD.image(named: imgStr)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -47,23 +47,16 @@ public extension ProHUD {
|
|||
|
||||
/// 背景层
|
||||
public var backgroundView: UIVisualEffectView = {
|
||||
let vev = UIVisualEffectView()
|
||||
if #available(iOS 13.0, *) {
|
||||
// vev.effect = UIBlurEffect.init(style: .systemMaterial))
|
||||
vev.effect = UIBlurEffect.init(style: .extraLight)
|
||||
} else if #available(iOS 11.0, *) {
|
||||
vev.effect = UIBlurEffect.init(style: .extraLight)
|
||||
} else {
|
||||
vev.effect = .none
|
||||
vev.backgroundColor = .white
|
||||
}
|
||||
let vev = createBlurView()
|
||||
vev.layer.masksToBounds = true
|
||||
vev.layer.cornerRadius = cfg.toast.cornerRadius
|
||||
return vev
|
||||
}()
|
||||
|
||||
/// 视图模型
|
||||
public var model = ViewModel()
|
||||
public var vm = ViewModel()
|
||||
|
||||
internal var maxY = CGFloat(0)
|
||||
|
||||
// MARK: 生命周期
|
||||
|
||||
|
@ -72,17 +65,15 @@ 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: ((Toast) -> Void)? = nil) {
|
||||
public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, icon: UIImage? = nil, actions: ((inout ViewModel) -> Void)? = nil) {
|
||||
self.init()
|
||||
vm.vc = self
|
||||
|
||||
model.scene = scene
|
||||
model.title = title
|
||||
model.message = message
|
||||
model.icon = icon
|
||||
actions?(self)
|
||||
// 布局
|
||||
cfg.toast.loadSubviews(self)
|
||||
cfg.toast.reloadData(self)
|
||||
vm.scene = scene
|
||||
vm.title = title
|
||||
vm.message = message
|
||||
vm.icon = icon
|
||||
actions?(&vm)
|
||||
|
||||
// 点击
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(privDidTapped(_:)))
|
||||
|
@ -94,16 +85,21 @@ public extension ProHUD {
|
|||
}
|
||||
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
cfg.toast.reloadData(self)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - 实例函数
|
||||
|
||||
public extension Toast {
|
||||
|
||||
// MARK: 生命周期函数
|
||||
|
||||
/// 推入屏幕
|
||||
@discardableResult func push() -> Toast {
|
||||
let config = cfg.toast
|
||||
|
@ -145,7 +141,7 @@ public extension Toast {
|
|||
if Toast.toasts.contains(self) == false {
|
||||
Toast.toasts.append(self)
|
||||
}
|
||||
Toast.updateToastsLayout()
|
||||
Toast.privUpdateToastsLayout()
|
||||
if isNew {
|
||||
window.transform = .init(translationX: 0, y: -window.frame.maxY)
|
||||
UIView.animateForToast {
|
||||
|
@ -159,32 +155,21 @@ public extension Toast {
|
|||
|
||||
/// 弹出屏幕
|
||||
func pop() {
|
||||
Toast.removeItemFromArray(toast: self)
|
||||
UIView.animateForToast(animations: {
|
||||
let frame = self.window?.frame ?? .zero
|
||||
self.window?.transform = .init(translationX: 0, y: -200-frame.maxY)
|
||||
}) { (done) in
|
||||
self.view.removeFromSuperview()
|
||||
self.removeFromParent()
|
||||
self.window = nil
|
||||
}
|
||||
Toast.pop(self)
|
||||
}
|
||||
|
||||
// MARK: 设置函数
|
||||
|
||||
/// 设置持续时间
|
||||
/// - Parameter duration: 持续时间
|
||||
@discardableResult func duration(_ duration: TimeInterval?) -> Toast {
|
||||
model.setupDuration(duration: duration) { [weak self] in
|
||||
self?.pop()
|
||||
}
|
||||
return self
|
||||
/// 更新
|
||||
/// - Parameter callback: 回调
|
||||
func update(_ callback: ((inout ViewModel) -> Void)? = nil) {
|
||||
callback?(&vm)
|
||||
cfg.toast.reloadData(self)
|
||||
}
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter callback: 事件回调
|
||||
@discardableResult func didTapped(_ callback: (() -> Void)?) -> Toast {
|
||||
model.tapCallback = callback
|
||||
vm.tapCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
|
@ -195,47 +180,10 @@ public extension Toast {
|
|||
return self
|
||||
}
|
||||
|
||||
/// 更新
|
||||
/// - Parameter scene: 场景
|
||||
/// - Parameter title: 标题
|
||||
/// - Parameter message: 内容
|
||||
@discardableResult func update(scene: Scene, title: String?, message: String?) -> Toast {
|
||||
model.scene = scene
|
||||
model.title = title
|
||||
model.message = message
|
||||
cfg.toast.reloadData(self)
|
||||
return self
|
||||
}
|
||||
|
||||
/// 更新标题
|
||||
/// - Parameter title: 标题
|
||||
@discardableResult func update(title: String?) -> Toast {
|
||||
model.title = title
|
||||
cfg.toast.reloadData(self)
|
||||
return self
|
||||
}
|
||||
|
||||
/// 更新文本
|
||||
/// - Parameter message: 消息
|
||||
@discardableResult func update(message: String?) -> Toast {
|
||||
model.message = message
|
||||
cfg.toast.reloadData(self)
|
||||
return self
|
||||
}
|
||||
|
||||
/// 更新图标
|
||||
/// - Parameter icon: 图标
|
||||
@discardableResult func update(icon: UIImage?) -> Toast {
|
||||
model.icon = icon
|
||||
cfg.toast.reloadData(self)
|
||||
return self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: 类函数
|
||||
|
||||
// MARK: - 实例管理器
|
||||
public extension Toast {
|
||||
|
||||
/// 推入屏幕
|
||||
|
@ -243,7 +191,7 @@ public extension Toast {
|
|||
/// - Parameter title: 标题
|
||||
/// - Parameter message: 内容
|
||||
/// - Parameter actions: 更多操作
|
||||
@discardableResult class func push(scene: Toast.Scene, title: String? = nil, message: String? = nil, actions: ((Toast) -> Void)? = nil) -> Toast {
|
||||
@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()
|
||||
}
|
||||
|
||||
|
@ -252,7 +200,7 @@ public extension Toast {
|
|||
class func toasts(_ identifier: String?) -> [Toast] {
|
||||
var tt = [Toast]()
|
||||
for t in toasts {
|
||||
if t.model.identifier == identifier {
|
||||
if t.vm.identifier == identifier {
|
||||
tt.append(t)
|
||||
}
|
||||
}
|
||||
|
@ -262,7 +210,25 @@ public extension Toast {
|
|||
/// 弹出屏幕
|
||||
/// - Parameter toast: 实例
|
||||
class func pop(_ toast: Toast) {
|
||||
toast.pop()
|
||||
if toasts.count > 1 {
|
||||
for (i, t) in toasts.enumerated() {
|
||||
if t == toast {
|
||||
toasts.remove(at: i)
|
||||
}
|
||||
}
|
||||
privUpdateToastsLayout()
|
||||
} else if toasts.count == 1 {
|
||||
toasts.removeAll()
|
||||
} else {
|
||||
debug("漏洞:已经没有toast了")
|
||||
}
|
||||
UIView.animateForToast(animations: {
|
||||
toast.window?.transform = .init(translationX: 0, y: -20-toast.maxY)
|
||||
}) { (done) in
|
||||
toast.view.removeFromSuperview()
|
||||
toast.removeFromParent()
|
||||
toast.window = nil
|
||||
}
|
||||
}
|
||||
|
||||
/// 弹出屏幕
|
||||
|
@ -275,57 +241,40 @@ public extension Toast {
|
|||
|
||||
}
|
||||
|
||||
// MARK: 私有
|
||||
|
||||
fileprivate var willUpdateToastsLayout: DispatchWorkItem?
|
||||
// MARK: - 创建和设置
|
||||
fileprivate var willprivUpdateToastsLayout: DispatchWorkItem?
|
||||
|
||||
fileprivate extension Toast {
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func privDidTapped(_ sender: UITapGestureRecognizer) {
|
||||
model.tapCallback?()
|
||||
vm.tapCallback?()
|
||||
}
|
||||
|
||||
/// 拖拽事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func privDidPan(_ sender: UIPanGestureRecognizer) {
|
||||
model.durationBlock?.cancel()
|
||||
vm.durationBlock?.cancel()
|
||||
let point = sender.translation(in: sender.view)
|
||||
window?.transform = .init(translationX: 0, y: point.y)
|
||||
if sender.state == .recognized {
|
||||
let v = sender.velocity(in: sender.view)
|
||||
if model.removable == true && (((window?.frame.origin.y ?? 0) < 0 && v.y < 0) || v.y < -1200) {
|
||||
if vm.removable == true && (((window?.frame.origin.y ?? 0) < 0 && v.y < 0) || v.y < -1200) {
|
||||
// 移除
|
||||
self.pop()
|
||||
} else {
|
||||
UIView.animateForToast(animations: {
|
||||
self.window?.transform = .identity
|
||||
}) { (done) in
|
||||
// FIXME: 重置计时器
|
||||
|
||||
let d = self.vm.duration
|
||||
self.vm.duration = d
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 从数组中移除
|
||||
/// - Parameter toast: 实例
|
||||
class func removeItemFromArray(toast: Toast) {
|
||||
if toasts.count > 1 {
|
||||
for (i, t) in toasts.enumerated() {
|
||||
if t == toast {
|
||||
toasts.remove(at: i)
|
||||
}
|
||||
}
|
||||
updateToastsLayout()
|
||||
} else if toasts.count == 1 {
|
||||
toasts.removeAll()
|
||||
} else {
|
||||
debug("漏洞:已经没有toast了")
|
||||
}
|
||||
}
|
||||
class func updateToastsLayout() {
|
||||
class func privUpdateToastsLayout() {
|
||||
func f() {
|
||||
let top = Inspire.shared.screen.updatedSafeAreaInsets.top
|
||||
for (i, e) in toasts.enumerated() {
|
||||
|
@ -342,18 +291,20 @@ fileprivate extension Toast {
|
|||
let lastY = toasts[i-1].window?.frame.maxY ?? .zero
|
||||
y = lastY + config.margin
|
||||
}
|
||||
e.maxY = y + window.frame.size.height
|
||||
UIView.animateForToast {
|
||||
e.window?.frame.origin.y = y
|
||||
window.frame.origin.y = y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
willUpdateToastsLayout?.cancel()
|
||||
willUpdateToastsLayout = DispatchWorkItem(block: {
|
||||
willprivUpdateToastsLayout?.cancel()
|
||||
willprivUpdateToastsLayout = DispatchWorkItem(block: {
|
||||
f()
|
||||
})
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+0.001, execute: willUpdateToastsLayout!)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+0.001, execute: willprivUpdateToastsLayout!)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,12 +16,6 @@ public extension Toast {
|
|||
/// 加载中场景
|
||||
case loading
|
||||
|
||||
/// 确认场景
|
||||
case confirm
|
||||
|
||||
/// 删除场景
|
||||
case delete
|
||||
|
||||
/// 成功场景
|
||||
case success
|
||||
|
||||
|
@ -33,7 +27,7 @@ public extension Toast {
|
|||
|
||||
}
|
||||
|
||||
struct ViewModel {
|
||||
class ViewModel {
|
||||
|
||||
/// ID标识
|
||||
public var identifier = String(Date().timeIntervalSince1970)
|
||||
|
@ -42,40 +36,56 @@ public extension Toast {
|
|||
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?
|
||||
|
||||
/// 持续时间
|
||||
internal var durationBlock: DispatchWorkItem?
|
||||
|
||||
/// 是否可以通过手势移除(向上划)
|
||||
public var removable = true
|
||||
|
||||
/// 点击事件回调
|
||||
internal var tapCallback: (() -> Void)?
|
||||
|
||||
internal mutating func setupDuration(duration: TimeInterval?, callback: @escaping () -> Void) {
|
||||
self.duration = duration
|
||||
public var duration: TimeInterval? {
|
||||
didSet {
|
||||
durationBlock?.cancel()
|
||||
if let t = duration, t > 0 {
|
||||
durationBlock = DispatchWorkItem(block: callback)
|
||||
durationBlock = DispatchWorkItem(block: { [weak self] in
|
||||
self?.vc?.pop()
|
||||
})
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: durationBlock!)
|
||||
} else {
|
||||
durationBlock = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public weak var vc: Toast?
|
||||
|
||||
/// 是否可以通过手势移除(向上滑出屏幕)
|
||||
public var removable = true
|
||||
|
||||
|
||||
// MARK: 私有
|
||||
|
||||
/// 持续时间
|
||||
internal var durationBlock: DispatchWorkItem?
|
||||
|
||||
/// 点击事件回调
|
||||
internal var tapCallback: (() -> Void)?
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
//
|
||||
// ToastView.swift
|
||||
// ProHUD
|
||||
//
|
||||
// Created by xaoxuu on 2019/7/31.
|
||||
// Copyright © 2019 Titan Studio. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
Loading…
Reference in New Issue