This commit is contained in:
xaoxuu 2019-08-13 11:32:27 +08:00
parent 42e36b8f8e
commit 6ac80334f6
20 changed files with 215 additions and 170 deletions

View File

@ -73,3 +73,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
} }
extension ProHUD.Scene {
static var confirm: ProHUD.Scene {
var scene = ProHUD.Scene(identifier: "confirm")
scene.image = UIImage(named: "ProHUDMessage")
return scene
}
static var delete: ProHUD.Scene {
var scene = ProHUD.Scene(identifier: "delete")
scene.image = UIImage(named: "ProHUDTrash")
scene.title = "确认删除"
scene.message = "此操作不可撤销"
return scene
}
static var buy: ProHUD.Scene {
var scene = ProHUD.Scene(identifier: "buy")
scene.image = UIImage(named: "ProHUDBuy")
scene.title = "确认付款"
scene.message = "一旦购买拒不退款"
return scene
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ProHUDBuy@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ProHUDBuy@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ProHUDMessage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ProHUDMessage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ProHUDTrash@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ProHUDTrash@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -18,7 +18,11 @@ class TestAlertVC: BaseListVC {
} }
override var titles: [String] { override var titles: [String] {
return ["场景:正在同步(超时)", "场景同步成功写法1", "场景同步成功写法2", "场景:同步失败和重试"] return ["场景:正在同步(超时)",
"场景同步成功写法1",
"场景同步成功写法2",
"场景:同步失败和重试",
"极限场景:多个弹窗重叠"]
} }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
@ -28,12 +32,12 @@ class TestAlertVC: BaseListVC {
func f() { func f() {
Alert.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (a) in Alert.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (a) in
a.identifier = "loading" a.identifier = "loading"
a.animate(rotate: true) a.rotate()
a.didForceQuit { [weak self] in a.didForceQuit { [weak self] in
let t = Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻点击展开为Alert") { (vm) in let t = Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻点击展开为Alert") { (vm) in
vm.identifier = "loading" vm.identifier = "loading"
} }
t.animate(rotate: true) t.rotate()
t.didTapped { [weak t] in t.didTapped { [weak t] in
t?.pop() t?.pop()
f() f()
@ -47,7 +51,7 @@ class TestAlertVC: BaseListVC {
} else if row == 1 { } else if row == 1 {
Alert.push() { (a) in Alert.push() { (a) in
a.identifier = "loading" a.identifier = "loading"
a.animate(rotate: true) a.rotate()
a.update { (vm) in a.update { (vm) in
vm.scene = .loading vm.scene = .loading
vm.title = "正在同步" vm.title = "正在同步"
@ -67,7 +71,7 @@ class TestAlertVC: BaseListVC {
let a = Alert.push() { (a) in let a = Alert.push() { (a) in
a.identifier = "loading" a.identifier = "loading"
} }
a.animate(rotate: true) a.rotate()
a.update { (vm) in a.update { (vm) in
vm.scene = .loading vm.scene = .loading
vm.title = "正在同步" vm.title = "正在同步"
@ -94,7 +98,7 @@ class TestAlertVC: BaseListVC {
vm.message = "请稍等片刻" vm.message = "请稍等片刻"
vm.remove(action: 0, 1) vm.remove(action: 0, 1)
} }
a.animate(rotate: true) a.rotate()
DispatchQueue.main.asyncAfter(deadline: .now()+2) { DispatchQueue.main.asyncAfter(deadline: .now()+2) {
a.update { (vm) in a.update { (vm) in
vm.scene = .error vm.scene = .error
@ -109,6 +113,27 @@ class TestAlertVC: BaseListVC {
}) })
} }
loading() loading()
} else if row == 4 {
func f(_ i: Int) {
Alert.push() { (a) in
a.rotate()
a.update { (vm) in
vm.scene = .loading
vm.title = "正在同步" + String(i)
vm.message = "请稍等片刻"
}
}
}
f(1)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
f(2)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
f(3)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
f(4)
}
} }
} }

View File

@ -54,20 +54,28 @@ class TestGuardVC: BaseListVC {
vm.add(subTitle: "价格") vm.add(subTitle: "价格")
vm.add(message: "只需一次性付费$2999即可永久享用。") vm.add(message: "只需一次性付费$2999即可永久享用。")
vm.add(action: .destructive, title: "购买") { [weak vc] in vm.add(action: .destructive, title: "购买") { [weak vc] in
Alert.push(scene: .confirm, title: "确认购买", message: "一旦购买拒不退款") { (vc) in Alert.push(scene: .buy) { (vc) in
vc.identifier = "confirm" vc.identifier = "confirm"
vc.update { (vm) in vc.update { (vm) in
vm.add(action: .destructive, title: "购买") { [weak vc] in vm.add(action: .destructive, title: "购买") { [weak vc] in
vc?.update({ (vm) in
vm.scene = .loading
vm.title = "正在付款"
vm.message = "请稍等片刻"
vm.remove(action: 0, 1)
})
vc?.rotate()
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
vc?.update({ (vm) in vc?.update({ (vm) in
vm.scene = .success vm.scene = .success
vm.title = "购买成功" vm.title = "购买成功"
vm.message = "感谢您的支持" vm.message = "感谢您的支持"
vm.remove(action: 1) vm.add(action: .default, title: "我知道了") {
vm.update(action: 0, style: .default, title: "我知道了") {
vc?.pop() vc?.pop()
} }
}) })
} }
}
vm.add(action: .cancel, title: "取消", handler: nil) vm.add(action: .cancel, title: "取消", handler: nil)
} }
} }

View File

@ -35,7 +35,7 @@ class TestToastVC: BaseListVC {
if row == 0 { if row == 0 {
Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (vm) in Toast.push(scene: .loading, title: "正在同步", message: "请稍等片刻") { (vm) in
vm.identifier = "loading" vm.identifier = "loading"
}.animate(rotate: true) }.rotate()
simulateSync() simulateSync()
} else if row == 1 { } else if row == 1 {
let t = Toast.push(scene: .success, title: "同步成功", message: "点击查看详情") let t = Toast.push(scene: .success, title: "同步成功", message: "点击查看详情")
@ -64,8 +64,8 @@ class TestToastVC: BaseListVC {
} }
} else if row == 5 { } else if row == 5 {
Toast.push(scene: .default, title: "禁止手势移除", message: "这条消息无法通过向上滑动移出屏幕。5秒后自动消失每次拖拽都会刷新倒计时。") { (vc) in Toast.push(scene: .default, title: "禁止手势移除", message: "这条消息无法通过向上滑动移出屏幕。5秒后自动消失每次拖拽都会刷新倒计时。") { (vc) in
vc.isRemovable = false
vc.update { (vm) in vc.update { (vm) in
vm.isRemovable = false
vm.duration = 5 vm.duration = 5
} }
} }

View File

@ -28,11 +28,6 @@ public extension ProHUD.Configuration {
// MARK: // MARK:
/// ///
public var iconSize = CGSize(width: 48, height: 48) public var iconSize = CGSize(width: 48, height: 48)
///
/// - Parameter callback:
public func iconForScene(_ callback: @escaping (ProHUD.Alert.Scene) -> UIImage?) {
privIconForScene = callback
}
// MARK: // MARK:
/// ///
@ -59,11 +54,6 @@ public extension ProHUD.Configuration {
privReloadData = callback privReloadData = callback
} }
/// viewmodeldurationnil
public func durationForScene(_ callback: @escaping (ProHUD.Alert.Scene) -> TimeInterval?) {
privDurationForScene = callback
}
/// 退 /// 退
public var forceQuitTimer = TimeInterval(30) public var forceQuitTimer = TimeInterval(30)
@ -88,10 +78,6 @@ internal extension ProHUD.Configuration.Alert {
return privReloadData return privReloadData
} }
var durationForScene: (ProHUD.Alert.Scene) -> TimeInterval? {
return privDurationForScene
}
} }
@ -122,33 +108,10 @@ fileprivate var privLayoutContentView: (ProHUD.Alert) -> Void = {
} }
}() }()
fileprivate var privIconForScene: (ProHUD.Alert.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"
case .confirm:
imgStr = "ProHUDMessage"
case .delete:
imgStr = "ProHUDTrash"
default:
imgStr = "ProHUDMessage"
}
return ProHUD.image(named: imgStr)
}
}()
fileprivate var privUpdateImage: (ProHUD.Alert) -> Void = { fileprivate var privUpdateImage: (ProHUD.Alert) -> Void = {
return { (vc) in return { (vc) in
let config = cfg.alert let config = cfg.alert
let img = vc.vm.icon ?? privIconForScene(vc.vm.scene) let img = vc.vm.icon ?? vc.vm.scene.image
if let imgv = vc.imageView { if let imgv = vc.imageView {
imgv.image = img imgv.image = img
} else { } else {
@ -172,6 +135,12 @@ fileprivate var privUpdateImage: (ProHUD.Alert) -> Void = {
fileprivate var privUpdateTextStack: (ProHUD.Alert) -> Void = { fileprivate var privUpdateTextStack: (ProHUD.Alert) -> Void = {
return { (vc) in return { (vc) in
let config = cfg.alert let config = cfg.alert
if vc.vm.title == nil {
vc.vm.title = vc.vm.scene.title
}
if vc.vm.message == nil {
vc.vm.message = vc.vm.scene.message
}
if vc.vm.title?.count ?? 0 > 0 || vc.vm.message?.count ?? 0 > 0 { if vc.vm.title?.count ?? 0 > 0 || vc.vm.message?.count ?? 0 > 0 {
vc.contentStack.addArrangedSubview(vc.textStack) vc.contentStack.addArrangedSubview(vc.textStack)
vc.textStack.snp.makeConstraints { (mk) in vc.textStack.snp.makeConstraints { (mk) in
@ -360,15 +329,3 @@ fileprivate var privReloadData: (ProHUD.Alert) -> Void = {
} }
}() }()
fileprivate var privDurationForScene: (ProHUD.Alert.Scene) -> TimeInterval? = {
return { (scene) in
switch scene {
case .loading:
return nil
default:
return 2
}
}
}()

View File

@ -178,7 +178,7 @@ public extension Alert {
/// - Parameter title: /// - Parameter title:
/// - Parameter message: /// - Parameter message:
/// - Parameter actions: /// - Parameter actions:
@discardableResult class func push(scene: Alert.Scene = .default, title: String? = nil, message: String? = nil, _ actions: ((Alert) -> Void)? = nil) -> Alert { @discardableResult class func push(scene: ProHUD.Scene = .default, title: String? = nil, message: String? = nil, _ actions: ((Alert) -> Void)? = nil) -> Alert {
return Alert(scene: scene, title: title, message: message, actions: actions).push() return Alert(scene: scene, title: title, message: message, actions: actions).push()
} }

View File

@ -9,34 +9,10 @@
import UIKit import UIKit
public extension Alert { public extension Alert {
enum Scene {
///
case `default`
///
case loading
///
case confirm
///
case delete
///
case success
///
case warning
///
case error
}
class ViewModel { class ViewModel {
/// 使 /// 使
public var scene = Scene.default public var scene = ProHUD.Scene.default
/// ///
public var title: String? { public var title: String? {
@ -81,7 +57,7 @@ public extension Alert {
internal func updateDuration() { internal func updateDuration() {
durationBlock?.cancel() durationBlock?.cancel()
if let t = duration ?? cfg.alert.durationForScene(scene), t > 0 { if let t = duration ?? scene.alertDuration, t > 0 {
durationBlock = DispatchWorkItem(block: { [weak self] in durationBlock = DispatchWorkItem(block: { [weak self] in
self?.vc?.pop() self?.vc?.pop()
}) })

View File

@ -16,7 +16,66 @@ public class ProHUD {
return cfg return cfg
} }
public struct Scene {
private var id = "unknown"
public var identifier: String {
return id
} }
public var image: UIImage?
public var alertDuration: TimeInterval?
public var toastDuration: TimeInterval? = 3
public var title: String?
public var message: String?
init() {
}
}
}
//
public extension ProHUD.Scene {
init(identifier: String) {
self.init()
id = identifier
}
static var `default`: ProHUD.Scene {
var scene = ProHUD.Scene.init(identifier: "default")
scene.image = ProHUD.image(named: "ProHUDMessage")
return scene
}
static var loading: ProHUD.Scene {
var scene = ProHUD.Scene.init(identifier: "loading")
scene.alertDuration = 0
scene.toastDuration = 0
scene.image = ProHUD.image(named: "ProHUDLoading")
return scene
}
static var success: ProHUD.Scene {
var scene = ProHUD.Scene.init(identifier: "success")
scene.alertDuration = 2
scene.image = ProHUD.image(named: "ProHUDSuccess")
return scene
}
static var warning: ProHUD.Scene {
var scene = ProHUD.Scene.init(identifier: "warning")
scene.alertDuration = 2
scene.toastDuration = 5
scene.image = ProHUD.image(named: "ProHUDWarning")
return scene
}
static var error: ProHUD.Scene {
var scene = ProHUD.Scene.init(identifier: "error")
scene.alertDuration = 2
scene.toastDuration = 5
scene.image = ProHUD.image(named: "ProHUDError")
return scene
}
}
// MARK: - Utilities // MARK: - Utilities

View File

@ -23,11 +23,6 @@ public extension ProHUD.Configuration {
// MARK: // MARK:
/// ///
public var iconSize = CGSize(width: 48, height: 48) public var iconSize = CGSize(width: 48, height: 48)
///
/// - Parameter callback:
public func iconForScene(_ callback: @escaping (ProHUD.Toast.Scene) -> UIImage?) {
privIconForScene = callback
}
// MARK: // MARK:
/// ///
@ -46,10 +41,6 @@ public extension ProHUD.Configuration {
privReloadData = callback privReloadData = callback
} }
/// viewmodeldurationnil
public mutating func durationForScene(_ callback: @escaping (ProHUD.Toast.Scene) -> TimeInterval?) {
privDurationForScene = callback
}
} }
} }
@ -62,9 +53,6 @@ internal extension ProHUD.Configuration.Toast {
return privReloadData return privReloadData
} }
var durationForScene: (ProHUD.Toast.Scene) -> TimeInterval? {
return privDurationForScene
}
} }
@ -84,12 +72,12 @@ fileprivate var privReloadData: (ProHUD.Toast) -> Void = {
vc.view.addSubview(vc.imageView) vc.view.addSubview(vc.imageView)
} }
// //
vc.imageView.image = vc.vm.icon ?? privIconForScene(vc.vm.scene) vc.imageView.image = vc.vm.icon ?? vc.vm.scene.image
vc.imageView.layer.removeAllAnimations() vc.imageView.layer.removeAllAnimations()
vc.titleLabel.textColor = cfg.primaryLabelColor vc.titleLabel.textColor = cfg.primaryLabelColor
vc.titleLabel.text = vc.vm.title vc.titleLabel.text = vc.vm.title ?? vc.vm.scene.title
vc.bodyLabel.textColor = cfg.secondaryLabelColor vc.bodyLabel.textColor = cfg.secondaryLabelColor
vc.bodyLabel.text = vc.vm.message vc.bodyLabel.text = vc.vm.message ?? vc.vm.scene.message
// //
vc.imageView.snp.makeConstraints { (mk) in vc.imageView.snp.makeConstraints { (mk) in
@ -118,38 +106,3 @@ fileprivate var privReloadData: (ProHUD.Toast) -> Void = {
} }
}() }()
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)
}
}()
fileprivate var privDurationForScene: (ProHUD.Toast.Scene) -> TimeInterval? = {
return { (scene) in
switch scene {
case .loading:
return nil
case .error, .warning:
return 5
default:
return 3
}
}
}()

View File

@ -53,6 +53,9 @@ public extension ProHUD {
return vev return vev
}() }()
///
public var isRemovable = true
/// ///
public var vm = ViewModel() public var vm = ViewModel()
@ -215,7 +218,7 @@ public extension Toast {
/// - Parameter title: /// - Parameter title:
/// - Parameter message: /// - Parameter message:
/// - Parameter actions: /// - Parameter actions:
@discardableResult class func push(scene: Toast.Scene = .default, title: String? = nil, message: String? = nil, duration: TimeInterval? = nil, _ actions: ((Toast) -> Void)? = nil) -> Toast { @discardableResult class func push(scene: ProHUD.Scene = .default, title: String? = nil, message: String? = nil, duration: TimeInterval? = nil, _ actions: ((Toast) -> Void)? = nil) -> Toast {
return Toast(scene: scene, title: title, message: message, duration: duration, actions: actions).push() return Toast(scene: scene, title: title, message: message, duration: duration, actions: actions).push()
} }
@ -297,7 +300,7 @@ fileprivate extension Toast {
window?.transform = .init(translationX: 0, y: point.y) window?.transform = .init(translationX: 0, y: point.y)
if sender.state == .recognized { if sender.state == .recognized {
let v = sender.velocity(in: sender.view) let v = sender.velocity(in: sender.view)
if vm.isRemovable == true && (((window?.frame.origin.y ?? 0) < 0 && v.y < 0) || v.y < -1200) { if isRemovable == true && (((window?.frame.origin.y ?? 0) < 0 && v.y < 0) || v.y < -1200) {
// //
self.pop() self.pop()
} else { } else {

View File

@ -9,28 +9,10 @@
import UIKit import UIKit
public extension Toast { public extension Toast {
enum Scene {
///
case `default`
///
case loading
///
case success
///
case warning
///
case error
}
class ViewModel { class ViewModel {
/// 使 /// 使
public var scene = Scene.default public var scene = ProHUD.Scene.default
/// ///
public var title: String? { public var title: String? {
@ -62,10 +44,6 @@ public extension Toast {
public weak var vc: Toast? public weak var vc: Toast?
///
public var isRemovable = true
// MARK: // MARK:
/// ///
@ -76,7 +54,7 @@ public extension Toast {
internal func updateDuration() { internal func updateDuration() {
durationBlock?.cancel() durationBlock?.cancel()
if let t = duration ?? cfg.toast.durationForScene(scene), t > 0 { if let t = duration ?? scene.toastDuration, t > 0 {
durationBlock = DispatchWorkItem(block: { [weak self] in durationBlock = DispatchWorkItem(block: { [weak self] in
self?.vc?.pop() self?.vc?.pop()
}) })
@ -87,5 +65,4 @@ public extension Toast {
} }
} }
} }