mirror of https://github.com/xaoxuu/ProHUD
代码重构
This commit is contained in:
parent
6eee00880b
commit
32f9af1ae6
|
@ -18,7 +18,7 @@ class AlertVC: ListVC {
|
|||
title = "Alert"
|
||||
header.detailLabel.text = "弹窗控件,用于强阻塞性交互,用户必须做出选择或者等待结果才能进入下一步,当多个实例出现时,会以堆叠的形式显示,新的实例会在覆盖旧的实例上层。"
|
||||
|
||||
Alert.Configuration.shared { config in
|
||||
AlertConfiguration.global { config in
|
||||
config.reloadData { vc in
|
||||
if vc.identifier == "custom" {
|
||||
return true
|
||||
|
@ -29,7 +29,7 @@ class AlertVC: ListVC {
|
|||
|
||||
list.add(title: "纯文字") { section in
|
||||
section.add(title: "只有一句话") {
|
||||
Alert(.message("只有一句话").duration(2)).push()
|
||||
Alert(.message("只有一句话").duration(2))
|
||||
}
|
||||
section.add(title: "标题 + 正文") {
|
||||
let title = "这是标题"
|
||||
|
@ -43,7 +43,7 @@ class AlertVC: ListVC {
|
|||
|
||||
list.add(title: "图文弹窗") { section in
|
||||
section.add(title: "纯图标") {
|
||||
Alert(.loading(3)).push()
|
||||
Alert(.loading(3))
|
||||
}
|
||||
section.add(title: "图标 + 文字") {
|
||||
Alert(.loading.message("正在加载")) { alert in
|
||||
|
@ -82,8 +82,8 @@ class AlertVC: ListVC {
|
|||
button.titleLabel?.font = .systemFont(ofSize: 15)
|
||||
}
|
||||
alert.vm.title = "你正在使用移动网络观看"
|
||||
} .onViewDidLoad { vc in
|
||||
guard let alert = vc as? Alert else {
|
||||
alert.onViewDidLoad { vc in
|
||||
guard let alert = vc as? AlertTarget else {
|
||||
return
|
||||
}
|
||||
alert.add(contentSpacing: 30)
|
||||
|
@ -98,6 +98,7 @@ class AlertVC: ListVC {
|
|||
|
||||
}
|
||||
}
|
||||
}
|
||||
section.add(title: "只有一段文字 + 无背景色按钮") {
|
||||
Alert { alert in
|
||||
alert.config.cardMinWidth = 270
|
||||
|
@ -111,8 +112,8 @@ class AlertVC: ListVC {
|
|||
button.titleLabel?.font = .systemFont(ofSize: 15)
|
||||
}
|
||||
alert.vm.message = "为了维护社区氛围,上麦用户需进行主播认证"
|
||||
} .onViewDidLoad { vc in
|
||||
guard let alert = vc as? Alert else {
|
||||
alert.onViewDidLoad { vc in
|
||||
guard let alert = vc as? AlertTarget else {
|
||||
return
|
||||
}
|
||||
alert.add(contentSpacing: 30)
|
||||
|
@ -127,6 +128,7 @@ class AlertVC: ListVC {
|
|||
alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc")))
|
||||
}
|
||||
}
|
||||
}
|
||||
section.add(title: "只有一段文字 + 3个无背景色按钮") {
|
||||
Alert { alert in
|
||||
alert.config.cardMinWidth = 270
|
||||
|
@ -144,8 +146,8 @@ class AlertVC: ListVC {
|
|||
stack.spacing = 0
|
||||
stack.axis = .vertical // 竖排按钮
|
||||
}
|
||||
} .onViewDidLoad { vc in
|
||||
guard let alert = vc as? Alert else {
|
||||
alert.onViewDidLoad { vc in
|
||||
guard let alert = vc as? AlertTarget else {
|
||||
return
|
||||
}
|
||||
func createLine() -> UIView {
|
||||
|
@ -181,6 +183,7 @@ class AlertVC: ListVC {
|
|||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section.add(title: "只有一段文字 + 按钮") {
|
||||
Alert { alert in
|
||||
|
@ -203,7 +206,7 @@ class AlertVC: ListVC {
|
|||
}
|
||||
list.add(title: "图标 + 文字 + 按钮") { section in
|
||||
section.add(title: "操作成功") {
|
||||
Alert(.success(3).title("操作成功").message("这条消息将在3s后消失")).push()
|
||||
Alert(.success(3).title("操作成功").message("这条消息将在3s后消失"))
|
||||
}
|
||||
section.add(title: "操作失败") {
|
||||
Alert { alert in
|
||||
|
@ -287,8 +290,8 @@ class AlertVC: ListVC {
|
|||
Alert(.loading) { alert in
|
||||
alert.vm.title = "在弹出过程中增加元素"
|
||||
alert.add(action: "OK", style: .gray)
|
||||
} .onViewWillAppear { vc in
|
||||
guard let alert = vc as? Alert else {
|
||||
alert.onViewWillAppear { vc in
|
||||
guard let alert = vc as? AlertTarget else {
|
||||
return
|
||||
}
|
||||
alert.vm.message = "这是一段后增加的文字\n动画效果会有细微差别"
|
||||
|
@ -296,6 +299,7 @@ class AlertVC: ListVC {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
list.add(title: "实例管理") { section in
|
||||
section.add(title: "多层级弹窗") {
|
||||
func f(i: Int) {
|
||||
|
@ -337,7 +341,7 @@ class AlertVC: ListVC {
|
|||
let vc = UIViewController()
|
||||
vc.title = "页面"
|
||||
vc.view.backgroundColor = .systemYellow
|
||||
let alert = Alert(.loading.title("正在加载").message("这个弹窗被放在指定容器中"))
|
||||
let alert = Alert(.loading.title("正在加载").message("这个弹窗被放在指定容器中")).target
|
||||
alert.add(action: "返回上一页") { alert in
|
||||
vc.dismiss(animated: true)
|
||||
}
|
||||
|
|
|
@ -16,20 +16,30 @@ class CapsuleVC: ListVC {
|
|||
title = "Capsule"
|
||||
header.detailLabel.text = "状态胶囊控件,用于状态显示,一个主程序窗口每个位置(上中下)各自最多只有一个状态胶囊实例。"
|
||||
|
||||
Capsule.Configuration.shared { config in
|
||||
CapsuleConfiguration.global { config in
|
||||
// config.cardCornerRadius = .infinity // 设置一个较大的数字就会变成胶囊形状
|
||||
}
|
||||
list.add(title: "默认布局:纯文字") { section in
|
||||
section.add(title: "一条简短的消息") {
|
||||
Capsule(.message("一条简短的消息")).push()
|
||||
// 设置vm或者handler都会自动push,这里测试传入vm:
|
||||
Capsule(.message("一条简短消息"))
|
||||
}
|
||||
section.add(title: "一条稍微长一点的消息") {
|
||||
Capsule(.message("一条稍微长一点的消息")).push()
|
||||
// 设置vm或者handler都会自动push,这里测试传入handler:
|
||||
Capsule { capsule in
|
||||
// handler中可以进行复杂设置(详见下面的其它例子)
|
||||
capsule.vm = .message("一条稍微长一点的消息")
|
||||
}
|
||||
}
|
||||
section.add(title: "(默认)状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") {
|
||||
Capsule(.message("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")).push()
|
||||
// 也可以创建一个空白实例,在需要的时候再push
|
||||
let obj = Capsule().target
|
||||
obj.vm = .message("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")
|
||||
// ... 在需要的时候手动push
|
||||
obj.push()
|
||||
}
|
||||
section.add(title: "(限制1行)状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") {
|
||||
// 同时设置vm和handler也可以
|
||||
Capsule(.message("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")) { capsule in
|
||||
capsule.config.customTextLabel { label in
|
||||
label.numberOfLines = 1
|
||||
|
@ -39,14 +49,27 @@ class CapsuleVC: ListVC {
|
|||
}
|
||||
|
||||
list.add(title: "默认布局:图文") { section in
|
||||
section.add(title: "一条简短的消息") {
|
||||
Capsule(.info("一条简短的消息")).push()
|
||||
section.add(title: "下载进度") {
|
||||
let capsule = Capsule().target
|
||||
capsule.vm = .message("正在下载").icon(.init(systemName: "arrow.down.circle.fill")).duration(.infinity)
|
||||
capsule.update(progress: 0)
|
||||
capsule.push()
|
||||
updateProgress(in: 4) { percent in
|
||||
capsule.update(progress: percent)
|
||||
} completion: {
|
||||
capsule.update { toast in
|
||||
toast.vm = .message("下载成功")
|
||||
.icon(.init(systemName: "checkmark.circle.fill"))
|
||||
.duration(5)
|
||||
.tintColor(.systemGreen)
|
||||
}
|
||||
section.add(title: "一条稍微长一点的消息") {
|
||||
Capsule(.systemError.title("500").message("一条稍微长一点的消息")).push()
|
||||
}
|
||||
}
|
||||
section.add(title: "接口报错提示") {
|
||||
Capsule(.systemError.title("[500]").message("服务端错误"))
|
||||
}
|
||||
section.add(title: "(默认)状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") {
|
||||
Capsule(.info("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")).push()
|
||||
Capsule(.info("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。"))
|
||||
}
|
||||
section.add(title: "(限制1行)状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") {
|
||||
Capsule(.info("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")) { capsule in
|
||||
|
@ -59,14 +82,10 @@ class CapsuleVC: ListVC {
|
|||
|
||||
list.add(title: "不同位置、不同动画") { section in
|
||||
section.add(title: "顶部,默认滑入") {
|
||||
Capsule(.info("一条简短的消息")) { capsule in
|
||||
|
||||
}
|
||||
Capsule(.info("一条简短的消息"))
|
||||
}
|
||||
section.add(title: "中间,默认缩放") {
|
||||
Capsule(.middle.info("一条简短的消息")) { capsule in
|
||||
|
||||
}
|
||||
Capsule(.middle.info("一条简短的消息"))
|
||||
}
|
||||
section.add(title: "中间,黑底白字,透明渐变") {
|
||||
Capsule(.middle.info("一条简短的消息")) { capsule in
|
||||
|
@ -98,7 +117,7 @@ class CapsuleVC: ListVC {
|
|||
section.add(title: "底部,渐变背景,默认回弹滑入") {
|
||||
Capsule(.bottom.enter("点击进入")) { capsule in
|
||||
capsule.config.tintColor = .white
|
||||
capsule.config.cardEdgeInsets = .init(top: 16, left: 24, bottom: 16, right: 24)
|
||||
capsule.config.cardEdgeInsets = .init(top: 12, left: 20, bottom: 12, right: 20)
|
||||
capsule.config.customTextLabel { label in
|
||||
label.textColor = .white
|
||||
label.font = .boldSystemFont(ofSize: 16)
|
||||
|
@ -116,17 +135,18 @@ class CapsuleVC: ListVC {
|
|||
mask.layer.insertSublayer(gradientLayer, at: 0)
|
||||
}
|
||||
capsule.config.cardCornerRadius = .infinity
|
||||
}.onTapped { capsule in
|
||||
Alert(.message("收到点击事件").duration(1)).push()
|
||||
capsule.onTapped { capsule in
|
||||
Alert(.message("收到点击事件").duration(1))
|
||||
capsule.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Capsule.ViewModel {
|
||||
extension CapsuleTarget.ViewModel {
|
||||
|
||||
static func info(_ text: String?) -> Self {
|
||||
.init()
|
||||
|
|
|
@ -73,7 +73,7 @@ class SheetVC: ListVC {
|
|||
sheet.add(spacing: 24)
|
||||
sheet.add(action: "确认")
|
||||
sheet.add(action: "取消", style: .gray)
|
||||
} onTappedBackground: { sheet in
|
||||
sheet.onTappedBackground { sheet in
|
||||
print("点击了背景")
|
||||
Toast.lazyPush(identifier: "alert") { toast in
|
||||
toast.vm = .error
|
||||
|
@ -84,6 +84,7 @@ class SheetVC: ListVC {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.add(title: "自定义样式") { section in
|
||||
section.add(title: "堆叠样式") {
|
||||
|
@ -180,8 +181,8 @@ class SheetVC: ListVC {
|
|||
mask.effect = .none
|
||||
mask.backgroundColor = .clear
|
||||
}
|
||||
} .onViewWillAppear { vc in
|
||||
guard let sheet = vc as? Sheet else { return }
|
||||
sheet.onViewWillAppear { vc in
|
||||
guard let sheet = vc as? SheetTarget else { return }
|
||||
let imgv = UIImageView(image: UIImage(named: "landscape"))
|
||||
imgv.contentMode = .scaleAspectFill
|
||||
imgv.clipsToBounds = true
|
||||
|
@ -193,6 +194,7 @@ class SheetVC: ListVC {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
section.add(title: "透明背景") {
|
||||
|
|
|
@ -32,15 +32,14 @@ func updateProgress(in duration: TimeInterval, callback: @escaping (_ percent: C
|
|||
|
||||
let isTesting: Bool = true
|
||||
|
||||
class TestToast: Toast {
|
||||
class TestToastTarget: ToastTarget {
|
||||
override func push() {
|
||||
guard isTesting else {
|
||||
return
|
||||
}
|
||||
guard isTesting else { return }
|
||||
super.push()
|
||||
}
|
||||
}
|
||||
|
||||
typealias TestToast = HUDProvider<ToastViewModel, TestToastTarget>
|
||||
|
||||
class ToastVC: ListVC {
|
||||
|
||||
|
@ -54,7 +53,7 @@ class ToastVC: ListVC {
|
|||
|
||||
header.detailLabel.text = message
|
||||
|
||||
Toast.Configuration.shared { config in
|
||||
ToastConfiguration.global { config in
|
||||
config.contentViewMask { mask in
|
||||
mask.backgroundColor = .clear
|
||||
mask.effect = UIBlurEffect(style: .systemChromeMaterial)
|
||||
|
@ -66,15 +65,15 @@ class ToastVC: ListVC {
|
|||
|
||||
list.add(title: "默认布局") { section in
|
||||
section.add(title: "标题 + 正文") {
|
||||
TestToast(.title(title).message(message)).push()
|
||||
TestToast(.title(title).message(message))
|
||||
}
|
||||
section.add(title: "一段长文本") {
|
||||
Toast(.message(message)).push()
|
||||
Toast(.message(message))
|
||||
}
|
||||
section.add(title: "图标 + 标题 + 正文") {
|
||||
let s1 = "笑容正在加载"
|
||||
let s2 = "这通常不会太久"
|
||||
let toast = Toast(.loading.title(s1).message(s2))
|
||||
let toast = Toast(.loading.title(s1).message(s2)).target
|
||||
toast.push()
|
||||
toast.update(progress: 0)
|
||||
updateProgress(in: 4) { percent in
|
||||
|
@ -99,10 +98,10 @@ class ToastVC: ListVC {
|
|||
}
|
||||
}
|
||||
section.add(title: "图标 + 一段长文本") {
|
||||
Toast(.note.message(message)).push()
|
||||
Toast(.note.message(message))
|
||||
}
|
||||
section.add(title: "网络图标 + 一段文本") {
|
||||
Toast(.message("这是网络图标").icon(.init(string: "https://xaoxuu.com/assets/xaoxuu/avatar/rect-256@2x.png"))).push()
|
||||
Toast(.message("这是网络图标").icon(.init(string: "https://xaoxuu.com/assets/xaoxuu/avatar/rect-256@2x.png")))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,7 +112,7 @@ class ToastVC: ListVC {
|
|||
Toast(.msg.title(title).message(message)) { toast in
|
||||
toast.onTapped { toast in
|
||||
toast.pop()
|
||||
Alert(.success(1).message("操作成功")).push()
|
||||
Alert(.success(1).message("操作成功"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +149,7 @@ class ToastVC: ListVC {
|
|||
alert.pop()
|
||||
})
|
||||
toast.pop()
|
||||
Alert(.success(1).message("Good choice!")).push()
|
||||
Alert(.success(1).message("Good choice!"))
|
||||
}
|
||||
Toast.find(identifier: "loading") { toast in
|
||||
toast.vm = .success(2).message("加载成功")
|
||||
|
@ -227,28 +226,28 @@ class ToastVC: ListVC {
|
|||
Toast { toast in
|
||||
toast.vm = .title("圆角半径").message("可以在共享配置中设置,则全局生效")
|
||||
toast.add(action: "8", style: .gray) { toast in
|
||||
Toast.Configuration.shared { config in
|
||||
ToastConfiguration.global { config in
|
||||
config.cardCornerRadius = 8
|
||||
}
|
||||
toast.pop()
|
||||
foo()
|
||||
}
|
||||
toast.add(action: "16", style: .gray) { toast in
|
||||
Toast.Configuration.shared { config in
|
||||
ToastConfiguration.global { config in
|
||||
config.cardCornerRadius = 16
|
||||
}
|
||||
toast.pop()
|
||||
foo()
|
||||
}
|
||||
toast.add(action: "24", style: .gray) { toast in
|
||||
Toast.Configuration.shared { config in
|
||||
ToastConfiguration.global { config in
|
||||
config.cardCornerRadius = 24
|
||||
}
|
||||
toast.pop()
|
||||
foo()
|
||||
}
|
||||
toast.add(action: "32", style: .gray) { toast in
|
||||
Toast.Configuration.shared { config in
|
||||
ToastConfiguration.global { config in
|
||||
config.cardCornerRadius = 32
|
||||
}
|
||||
toast.pop()
|
||||
|
@ -330,7 +329,7 @@ class ToastVC: ListVC {
|
|||
toast.title = "共享配置"
|
||||
toast.vm.message = "建议在App启动后进行通用配置设置,所有实例都会先拉取通用配置为默认值,修改这些配置会影响到所有实例。"
|
||||
toast.add(action: "默认", style: .gray) { toast in
|
||||
Toast.Configuration.shared { config in
|
||||
ToastConfiguration.global { config in
|
||||
config.customTitleLabel { titleLabel in
|
||||
titleLabel.font = .systemFont(ofSize: 19, weight: .bold)
|
||||
}
|
||||
|
@ -339,7 +338,7 @@ class ToastVC: ListVC {
|
|||
foo()
|
||||
}
|
||||
toast.add(action: "大号标题") { toast in
|
||||
Toast.Configuration.shared { config in
|
||||
ToastConfiguration.global { config in
|
||||
config.customTitleLabel { titleLabel in
|
||||
titleLabel.font = .systemFont(ofSize: 28, weight: .medium)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ public class AlertButton: Button {
|
|||
.init(top: 12, left: 24, bottom: 12, right: 24)
|
||||
}
|
||||
|
||||
public override func update(config: ProHUD.Configuration, action: Action) {
|
||||
public override func update(config: CommonConfiguration, action: Action) {
|
||||
titleLabel?.font = .boldSystemFont(ofSize: 15)
|
||||
layer.cornerRadiusWithContinuous = 8
|
||||
super.update(config: config, action: action)
|
||||
|
|
|
@ -7,21 +7,19 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public extension Alert {
|
||||
|
||||
class Configuration: ProHUD.Configuration {
|
||||
public class AlertConfiguration: CommonConfiguration {
|
||||
|
||||
/// 堆叠深度
|
||||
public var stackDepth: CGFloat = 30
|
||||
|
||||
public var enableShadow: Bool = true
|
||||
|
||||
static var customShared: ((_ config: Configuration) -> Void)?
|
||||
static var customGlobalConfig: ((_ config: AlertConfiguration) -> Void)?
|
||||
|
||||
/// 共享配置(只能设置一次,影响所有实例)
|
||||
/// 全局共享配置(只能设置一次,影响所有实例)
|
||||
/// - Parameter callback: 配置代码
|
||||
public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) {
|
||||
customShared = callback
|
||||
public static func global(_ callback: @escaping (_ config: AlertConfiguration) -> Void) {
|
||||
customGlobalConfig = callback
|
||||
}
|
||||
|
||||
var customBackgroundViewMask: ((_ mask: UIView) -> Void)?
|
||||
|
@ -36,6 +34,4 @@ public extension Alert {
|
|||
animateDurationForBuildIn ?? 0.6
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Alert: InternalConvenienceLayout {
|
||||
extension AlertTarget: InternalConvenienceLayout {
|
||||
|
||||
// MARK: 增加
|
||||
@discardableResult public func add(action: Action) -> Button {
|
||||
|
@ -155,7 +155,7 @@ extension Alert: InternalConvenienceLayout {
|
|||
}
|
||||
|
||||
// MARK: more
|
||||
public extension Alert {
|
||||
public extension AlertTarget {
|
||||
|
||||
/// 增加一个按钮
|
||||
/// - Parameters:
|
||||
|
@ -164,10 +164,10 @@ public extension Alert {
|
|||
/// - identifier: 唯一标识符
|
||||
/// - handler: 点击事件
|
||||
/// - Returns: 按钮实例
|
||||
@discardableResult func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ alert: Alert) -> Void)? = nil) -> Button {
|
||||
@discardableResult func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ alert: AlertTarget) -> Void)? = nil) -> Button {
|
||||
if let handler = handler {
|
||||
let action = Action(identifier: identifier, style: style, title: title) { vc in
|
||||
if let vc = vc as? Alert {
|
||||
if let vc = vc as? AlertTarget {
|
||||
handler(vc)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Alert: DefaultLayout {
|
||||
extension AlertTarget: DefaultLayout {
|
||||
|
||||
var cfg: ProHUD.Configuration {
|
||||
var cfg: CommonConfiguration {
|
||||
return config
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ extension Alert: DefaultLayout {
|
|||
if self.cfg.customReloadData?(self) == true {
|
||||
return
|
||||
}
|
||||
view.tintColor = vm.tintColor ?? config.tintColor
|
||||
let isFirstLayout: Bool
|
||||
if contentView.superview == nil {
|
||||
isFirstLayout = animated
|
||||
|
@ -132,7 +133,7 @@ extension Alert: DefaultLayout {
|
|||
|
||||
}
|
||||
|
||||
extension Alert {
|
||||
extension AlertTarget {
|
||||
|
||||
func setupImageView() {
|
||||
// 移除动画
|
||||
|
|
|
@ -7,14 +7,15 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Alert: HUD {
|
||||
extension AlertTarget {
|
||||
|
||||
@objc open func push() {
|
||||
guard Configuration.isEnabled else { return }
|
||||
guard AlertConfiguration.isEnabled else { return }
|
||||
let window = createAttachedWindowIfNotExists()
|
||||
guard window.alerts.contains(self) == false else {
|
||||
return
|
||||
}
|
||||
setDefaultAxis()
|
||||
view.transform = .init(scaleX: 1.2, y: 1.2)
|
||||
view.alpha = 0
|
||||
navEvents[.onViewWillAppear]?(self)
|
||||
|
@ -34,12 +35,12 @@ extension Alert: HUD {
|
|||
self.navEvents[.onViewDidAppear]?(self)
|
||||
}
|
||||
window.alerts.append(self)
|
||||
Alert.updateAlertsLayout(alerts: window.alerts)
|
||||
AlertTarget.updateAlertsLayout(alerts: window.alerts)
|
||||
}
|
||||
|
||||
@objc open func pop() {
|
||||
navEvents[.onViewWillDisappear]?(self)
|
||||
Alert.removeAlert(alert: self)
|
||||
AlertTarget.removeAlert(alert: self)
|
||||
let duration = config.animateDurationForBuildOut ?? config.animateDurationForBuildOutByDefault
|
||||
UIView.animateEaseOut(duration: duration) {
|
||||
self.view.alpha = 0
|
||||
|
@ -63,29 +64,9 @@ extension Alert: HUD {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension Alert {
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
/// - Parameters:
|
||||
/// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符)
|
||||
/// - handler: 实例创建代码
|
||||
static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ alert: Alert) -> Void, onExists: ((_ alert: Alert) -> Void)? = nil) {
|
||||
let id = identifier ?? (file + "#\(line)")
|
||||
if let vc = find(identifier: id).last {
|
||||
vc.update(handler: onExists ?? handler)
|
||||
} else {
|
||||
Alert { alert in
|
||||
alert.identifier = id
|
||||
handler(alert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新HUD实例
|
||||
/// - Parameter callback: 实例更新代码
|
||||
func update(handler: @escaping (_ alert: Alert) -> Void) {
|
||||
@objc open func update(handler: @escaping (_ alert: AlertTarget) -> Void) {
|
||||
handler(self)
|
||||
reloadData()
|
||||
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
|
||||
|
@ -93,23 +74,12 @@ public extension Alert {
|
|||
}
|
||||
}
|
||||
|
||||
/// 查找HUD实例
|
||||
/// - Parameter identifier: 唯一标识符
|
||||
/// - Returns: HUD实例
|
||||
@discardableResult static func find(identifier: String, update handler: ((_ alert: Alert) -> Void)? = nil) -> [Alert] {
|
||||
let arr = AppContext.alertWindow.values.flatMap({ $0.alerts }).filter({ $0.identifier == identifier })
|
||||
if let handler = handler {
|
||||
arr.forEach({ $0.update(handler: handler) })
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - layout
|
||||
|
||||
fileprivate extension Alert {
|
||||
static func updateAlertsLayout(alerts: [Alert]) {
|
||||
fileprivate extension AlertTarget {
|
||||
static func updateAlertsLayout(alerts: [AlertTarget]) {
|
||||
for (i, a) in alerts.reversed().enumerated() {
|
||||
let scale = CGFloat(pow(0.9, CGFloat(i)))
|
||||
UIView.animate(withDuration: 1.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5, options: [.allowUserInteraction, .curveEaseInOut], animations: {
|
||||
|
@ -125,7 +95,7 @@ fileprivate extension Alert {
|
|||
AlertWindow.createAttachedWindowIfNotExists(config: config)
|
||||
}
|
||||
|
||||
static func removeAlert(alert: Alert) {
|
||||
static func removeAlert(alert: AlertTarget) {
|
||||
guard var alerts = alert.attachedWindow?.alerts else {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// AlertProvider.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2023/8/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class AlertProvider: HUDProvider<AlertViewModel, AlertTarget> {
|
||||
@discardableResult public required init(_ vm: ViewModel?, initializer: ((_ alert: Target) -> Void)?) {
|
||||
super.init(vm, initializer: initializer)
|
||||
}
|
||||
|
||||
@discardableResult public required convenience init(initializer: ((_ alert: Target) -> Void)?) {
|
||||
self.init(nil, initializer: initializer)
|
||||
}
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
/// - Parameters:
|
||||
/// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符)
|
||||
/// - handler: 实例创建代码
|
||||
public static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ alert: AlertTarget) -> Void, onExists: ((_ alert: AlertTarget) -> Void)? = nil) {
|
||||
let id = identifier ?? (file + "#\(line)")
|
||||
if let vc = find(identifier: id).last {
|
||||
vc.update(handler: onExists ?? handler)
|
||||
} else {
|
||||
Self.init { alert in
|
||||
alert.identifier = id
|
||||
handler(alert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 查找HUD实例
|
||||
/// - Parameter identifier: 唯一标识符
|
||||
/// - Returns: HUD实例
|
||||
@discardableResult public static func find(identifier: String, update handler: ((_ alert: AlertTarget) -> Void)? = nil) -> [AlertTarget] {
|
||||
let arr = AppContext.alertWindow.values.flatMap({ $0.alerts }).filter({ $0.identifier == identifier })
|
||||
if let handler = handler {
|
||||
arr.forEach({ $0.update(handler: handler) })
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public typealias Alert = AlertProvider
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
open class Alert: ProHUD.Controller {
|
||||
open class AlertTarget: BaseController, HUDTargetType {
|
||||
|
||||
public lazy var config: Configuration = {
|
||||
var cfg = Configuration()
|
||||
Configuration.customShared?(cfg)
|
||||
public lazy var config: AlertConfiguration = {
|
||||
var cfg = AlertConfiguration()
|
||||
AlertConfiguration.customGlobalConfig?(cfg)
|
||||
return cfg
|
||||
}()
|
||||
|
||||
|
@ -73,10 +73,8 @@ open class Alert: ProHUD.Controller {
|
|||
return stack
|
||||
}()
|
||||
|
||||
@objc(AlertViewModel) open class ViewModel: BaseViewModel {}
|
||||
|
||||
/// 视图模型
|
||||
@objc public var vm = ViewModel()
|
||||
@objc public var vm: AlertViewModel = .init()
|
||||
|
||||
public override var title: String? {
|
||||
didSet {
|
||||
|
@ -84,35 +82,22 @@ open class Alert: ProHUD.Controller {
|
|||
}
|
||||
}
|
||||
|
||||
@discardableResult @objc public init(_ vm: ViewModel, handler: ((_ alert: Alert) -> Void)? = nil) {
|
||||
required public override init() {
|
||||
super.init()
|
||||
self.vm = vm
|
||||
handler?(self)
|
||||
DispatchQueue.main.async {
|
||||
if handler != nil {
|
||||
self.setDefaultAxis()
|
||||
self.push()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult @objc public convenience init(handler: ((_ alert: Alert) -> Void)?) {
|
||||
self.init(.init(), handler: handler)
|
||||
}
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.tintColor = config.tintColor
|
||||
reloadData(animated: false)
|
||||
navEvents[.onViewDidLoad]?(self)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
reloadData(animated: false)
|
||||
navEvents[.onViewDidLoad]?(self)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: 动画扩展
|
||||
extension Alert: LoadingAnimation {}
|
||||
extension AlertTarget: LoadingAnimation {}
|
|
@ -0,0 +1,10 @@
|
|||
//
|
||||
// AlertViewModel.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2023/8/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc open class AlertViewModel: BaseViewModel {}
|
|
@ -9,11 +9,11 @@ import UIKit
|
|||
|
||||
class AlertWindow: Window {
|
||||
|
||||
var alerts: [Alert] = []
|
||||
var alerts: [AlertTarget] = []
|
||||
|
||||
override var usingBackground: Bool { true }
|
||||
|
||||
static func createAttachedWindowIfNotExists(config: Configuration) -> AlertWindow {
|
||||
static func createAttachedWindowIfNotExists(config: AlertConfiguration) -> AlertWindow {
|
||||
let windowScene = AppContext.windowScene
|
||||
if let windowScene = windowScene, let w = AppContext.alertWindow[windowScene] {
|
||||
return w
|
||||
|
@ -34,7 +34,7 @@ class AlertWindow: Window {
|
|||
|
||||
}
|
||||
|
||||
extension Alert {
|
||||
extension AlertTarget {
|
||||
var attachedWindow: AlertWindow? {
|
||||
view.window as? AlertWindow
|
||||
}
|
||||
|
|
|
@ -7,18 +7,16 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public extension Capsule {
|
||||
public class CapsuleConfiguration: CommonConfiguration {
|
||||
|
||||
typealias CustomAnimateHandler = ((_ window: UIWindow, _ completion: @escaping () -> Void) -> Void)
|
||||
public typealias CustomAnimateHandler = ((_ window: UIWindow, _ completion: @escaping () -> Void) -> Void)
|
||||
|
||||
class Configuration: ProHUD.Configuration {
|
||||
static var customGlobalConfig: ((_ config: CapsuleConfiguration) -> Void)?
|
||||
|
||||
static var customShared: ((_ config: Configuration) -> Void)?
|
||||
|
||||
/// 共享配置(只能设置一次,影响所有实例)
|
||||
/// 全局共享配置(只能设置一次,影响所有实例)
|
||||
/// - Parameter callback: 配置代码
|
||||
public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) {
|
||||
customShared = callback
|
||||
public static func global(_ callback: @escaping (_ config: CapsuleConfiguration) -> Void) {
|
||||
customGlobalConfig = callback
|
||||
}
|
||||
|
||||
override var cardCornerRadiusByDefault: CGFloat {
|
||||
|
@ -52,6 +50,4 @@ public extension Capsule {
|
|||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Capsule: DefaultLayout {
|
||||
extension CapsuleTarget: DefaultLayout {
|
||||
|
||||
var cfg: ProHUD.Configuration {
|
||||
var cfg: CommonConfiguration {
|
||||
return config
|
||||
}
|
||||
|
||||
|
@ -18,18 +18,14 @@ extension Capsule: DefaultLayout {
|
|||
return
|
||||
}
|
||||
|
||||
view.tintColor = vm.tintColor ?? config.tintColor
|
||||
|
||||
// content
|
||||
loadContentViewIfNeeded()
|
||||
loadContentMaskViewIfNeeded()
|
||||
|
||||
// image
|
||||
imageView.removeFromSuperview()
|
||||
if let icon = vm.icon {
|
||||
contentStack.insertArrangedSubview(imageView, at: 0)
|
||||
imageView.image = icon
|
||||
} else {
|
||||
contentStack.removeArrangedSubview(imageView)
|
||||
}
|
||||
setupImageView()
|
||||
|
||||
// text
|
||||
textLabel.removeFromSuperview()
|
||||
|
@ -94,4 +90,27 @@ extension Capsule: DefaultLayout {
|
|||
})
|
||||
}
|
||||
|
||||
private func setupImageView() {
|
||||
// 移除动画
|
||||
stopRotate(animateLayer)
|
||||
animateLayer = nil
|
||||
animation = nil
|
||||
|
||||
// 移除进度
|
||||
progressView?.removeFromSuperview()
|
||||
|
||||
if vm.icon == nil && vm.iconURL == nil {
|
||||
contentStack.removeArrangedSubview(imageView)
|
||||
} else {
|
||||
contentStack.insertArrangedSubview(imageView, at: 0)
|
||||
}
|
||||
imageView.image = vm.icon
|
||||
if let iconURL = vm.iconURL {
|
||||
config.customWebImage?(imageView, iconURL)
|
||||
}
|
||||
if let rotation = vm.rotation {
|
||||
startRotate(rotation)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Capsule: HUD {
|
||||
extension CapsuleTarget {
|
||||
|
||||
@objc open func push() {
|
||||
guard Configuration.isEnabled else { return }
|
||||
guard CapsuleConfiguration.isEnabled else { return }
|
||||
let isNew: Bool
|
||||
let window: CapsuleWindow
|
||||
let position = vm.position
|
||||
|
@ -156,78 +156,15 @@ extension Capsule: HUD {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public extension Capsule {
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
/// - Parameters:
|
||||
/// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符)
|
||||
/// - handler: 实例创建代码
|
||||
static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ capsule: Capsule) -> Void, onExists: ((_ capsule: Capsule) -> Void)? = nil) {
|
||||
let id = identifier ?? (file + "#\(line)")
|
||||
if let vc = find(identifier: id).last {
|
||||
vc.update(handler: onExists ?? handler)
|
||||
} else {
|
||||
Capsule { capsule in
|
||||
capsule.identifier = id
|
||||
handler(capsule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新HUD实例
|
||||
/// - Parameter handler: 实例更新代码
|
||||
func update(handler: @escaping (_ capsule: Capsule) -> Void) {
|
||||
@objc open func update(handler: @escaping (_ capsule: CapsuleTarget) -> Void) {
|
||||
handler(self)
|
||||
|
||||
reloadData()
|
||||
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
/// 查找HUD实例
|
||||
/// - Parameter identifier: 唯一标识符
|
||||
/// - Returns: HUD实例
|
||||
@discardableResult static func find(identifier: String, update handler: ((_ capsule: Capsule) -> Void)? = nil) -> [Capsule] {
|
||||
let arr = AppContext.capsuleWindows.values.flatMap({ $0.values }).compactMap({ $0.capsule }).filter({ $0.identifier == identifier })
|
||||
if let handler = handler {
|
||||
arr.forEach({ $0.update(handler: handler) })
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//extension Capsule {
|
||||
//
|
||||
// func translateIn(completion: (() -> Void)?) {
|
||||
// UIView.animateEaseOut(duration: config.animateDurationForBuildInByDefault) {
|
||||
// if self.config.stackDepthEffect {
|
||||
// if isPhonePortrait {
|
||||
// AppContext.appWindow?.transform = .init(translationX: 0, y: 8).scaledBy(x: 0.9, y: 0.9)
|
||||
// } else {
|
||||
// AppContext.appWindow?.transform = .init(scaleX: 0.92, y: 0.92)
|
||||
// }
|
||||
// AppContext.appWindow?.layer.cornerRadiusWithContinuous = 16
|
||||
// AppContext.appWindow?.layer.masksToBounds = true
|
||||
// }
|
||||
// } completion: { done in
|
||||
// completion?()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func translateOut(completion: (() -> Void)?) {
|
||||
// UIView.animateEaseOut(duration: config.animateDurationForBuildOutByDefault) {
|
||||
// if self.config.stackDepthEffect {
|
||||
// AppContext.appWindow?.transform = .identity
|
||||
// AppContext.appWindow?.layer.cornerRadius = 0
|
||||
// }
|
||||
// } completion: { done in
|
||||
// completion?()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// CapsuleProvider.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2023/8/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class CapsuleProvider: HUDProvider<CapsuleTarget.ViewModel, CapsuleTarget> {
|
||||
|
||||
/// 根据ViewModel和自定义的初始化代码创建一个Target并显示
|
||||
/// - Parameters:
|
||||
/// - vm: 数据模型
|
||||
/// - initializer: 初始化代码
|
||||
@discardableResult public required init(_ vm: ViewModel?, initializer: ((_ capsule: Target) -> Void)?) {
|
||||
super.init(vm, initializer: initializer)
|
||||
}
|
||||
|
||||
@discardableResult public required convenience init(initializer: ((_ capsule: Target) -> Void)?) {
|
||||
self.init(nil, initializer: initializer)
|
||||
}
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
/// - Parameters:
|
||||
/// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符)
|
||||
/// - handler: 实例创建代码
|
||||
public static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ capsule: CapsuleTarget) -> Void, onExists: ((_ capsule: CapsuleTarget) -> Void)? = nil) {
|
||||
let id = identifier ?? (file + "#\(line)")
|
||||
if let vc = find(identifier: id).last {
|
||||
vc.update(handler: onExists ?? handler)
|
||||
} else {
|
||||
Self.init { capsule in
|
||||
capsule.identifier = id
|
||||
handler(capsule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 查找HUD实例
|
||||
/// - Parameter identifier: 唯一标识符
|
||||
/// - Returns: HUD实例
|
||||
@discardableResult public static func find(identifier: String, update handler: ((_ capsule: CapsuleTarget) -> Void)? = nil) -> [CapsuleTarget] {
|
||||
let arr = AppContext.capsuleWindows.values.flatMap({ $0.values }).compactMap({ $0.capsule }).filter({ $0.identifier == identifier })
|
||||
if let handler = handler {
|
||||
arr.forEach({ $0.update(handler: handler) })
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public typealias Capsule = CapsuleProvider
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
open class Capsule: Controller {
|
||||
open class CapsuleTarget: BaseController, HUDTargetType {
|
||||
|
||||
public lazy var config: Configuration = {
|
||||
var cfg = Configuration()
|
||||
Configuration.customShared?(cfg)
|
||||
public lazy var config: CapsuleConfiguration = {
|
||||
var cfg = CapsuleConfiguration()
|
||||
CapsuleConfiguration.customGlobalConfig?(cfg)
|
||||
return cfg
|
||||
}()
|
||||
|
||||
|
@ -33,6 +33,8 @@ open class Capsule: Controller {
|
|||
return imgv
|
||||
}()
|
||||
|
||||
public var progressView: ProgressView?
|
||||
|
||||
/// 文本
|
||||
public lazy var textLabel: UILabel = {
|
||||
let lb = UILabel()
|
||||
|
@ -44,56 +46,12 @@ open class Capsule: Controller {
|
|||
return lb
|
||||
}()
|
||||
|
||||
@objc(CapsuleViewModel) open class ViewModel: BaseViewModel {
|
||||
public var vm: CapsuleViewModel = .init()
|
||||
|
||||
@objc public enum Position: Int {
|
||||
case top
|
||||
case middle
|
||||
case bottom
|
||||
}
|
||||
private var tapActionCallback: ((_ capsule: CapsuleTarget) -> Void)?
|
||||
|
||||
@objc public var position: Position = .top
|
||||
|
||||
public func position(position: Position) -> Self {
|
||||
self.position = position
|
||||
return self
|
||||
}
|
||||
public static var top: Self {
|
||||
let obj = Self.init()
|
||||
obj.position = .top
|
||||
return obj
|
||||
}
|
||||
public static var middle: Self {
|
||||
let obj = Self.init()
|
||||
obj.position = .middle
|
||||
return obj
|
||||
}
|
||||
public static var bottom: Self {
|
||||
let obj = Self.init()
|
||||
obj.position = .bottom
|
||||
return obj
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// 视图模型
|
||||
@objc public var vm = ViewModel()
|
||||
|
||||
private var tapActionCallback: ((_ capsule: Capsule) -> Void)?
|
||||
|
||||
@discardableResult @objc public init(_ vm: ViewModel, handler: ((_ capsule: Capsule) -> Void)? = nil) {
|
||||
required public override init() {
|
||||
super.init()
|
||||
self.vm = vm
|
||||
handler?(self)
|
||||
DispatchQueue.main.async {
|
||||
if handler != nil {
|
||||
self.push()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult @objc public convenience init(handler: ((_ capsule: Capsule) -> Void)?) {
|
||||
self.init(.init(), handler: handler)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
|
@ -102,7 +60,6 @@ open class Capsule: Controller {
|
|||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.tintColor = vm.tintColor ?? config.tintColor
|
||||
view.layer.shadowRadius = 8
|
||||
view.layer.shadowOffset = .init(width: 0, height: 5)
|
||||
view.layer.shadowOpacity = 0.1
|
||||
|
@ -117,13 +74,13 @@ open class Capsule: Controller {
|
|||
|
||||
}
|
||||
|
||||
public func onTapped(action: @escaping (_ capsule: Capsule) -> Void) {
|
||||
public func onTapped(action: @escaping (_ capsule: CapsuleTarget) -> Void) {
|
||||
self.tapActionCallback = action
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate extension Capsule {
|
||||
fileprivate extension CapsuleTarget {
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter sender: 手势
|
||||
|
@ -132,3 +89,6 @@ fileprivate extension Capsule {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension CapsuleTarget: LoadingAnimation { }
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// CapsuleViewModel.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2023/8/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc open class CapsuleViewModel: BaseViewModel {
|
||||
|
||||
@objc public enum Position: Int {
|
||||
case top
|
||||
case middle
|
||||
case bottom
|
||||
}
|
||||
|
||||
@objc public var position: Position = .top
|
||||
|
||||
public func position(position: Position) -> Self {
|
||||
self.position = position
|
||||
return self
|
||||
}
|
||||
public static var top: Self {
|
||||
let obj = Self.init()
|
||||
obj.position = .top
|
||||
return obj
|
||||
}
|
||||
public static var middle: Self {
|
||||
let obj = Self.init()
|
||||
obj.position = .middle
|
||||
return obj
|
||||
}
|
||||
public static var bottom: Self {
|
||||
let obj = Self.init()
|
||||
obj.position = .bottom
|
||||
return obj
|
||||
}
|
||||
|
||||
}
|
|
@ -9,9 +9,9 @@ import UIKit
|
|||
|
||||
class CapsuleWindow: Window {
|
||||
|
||||
var capsule: Capsule
|
||||
var capsule: CapsuleTarget
|
||||
|
||||
init(capsule: Capsule) {
|
||||
init(capsule: CapsuleTarget) {
|
||||
self.capsule = capsule
|
||||
super.init(frame: .zero)
|
||||
windowScene = AppContext.windowScene
|
||||
|
@ -36,7 +36,7 @@ class CapsuleWindow: Window {
|
|||
|
||||
}
|
||||
|
||||
extension Capsule {
|
||||
extension CapsuleTarget {
|
||||
var attachedWindow: CapsuleWindow? {
|
||||
view.window as? CapsuleWindow
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Controller.swift
|
||||
// BaseController.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2022/8/29.
|
||||
|
@ -7,7 +7,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
open class Controller: UIViewController {
|
||||
open class BaseController: UIViewController {
|
||||
|
||||
/// ID标识
|
||||
public var identifier = String(Date().timeIntervalSince1970)
|
||||
|
@ -56,29 +56,29 @@ open class Controller: UIViewController {
|
|||
case onTappedBackground = "onTappedBackground"
|
||||
}
|
||||
|
||||
var navEvents = [NavEvent: ((Controller) -> Void)]()
|
||||
var navEvents = [NavEvent: ((BaseController) -> Void)]()
|
||||
|
||||
@discardableResult public func onViewDidLoad(_ callback: ((_ vc: Controller) -> Void)?) -> Controller {
|
||||
@discardableResult public func onViewDidLoad(_ callback: ((_ vc: BaseController) -> Void)?) -> BaseController {
|
||||
navEvents[.onViewDidLoad] = callback
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult public func onViewWillAppear(_ callback: ((_ vc: Controller) -> Void)?) -> Controller {
|
||||
@discardableResult public func onViewWillAppear(_ callback: ((_ vc: BaseController) -> Void)?) -> BaseController {
|
||||
navEvents[.onViewWillAppear] = callback
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult public func onViewDidAppear(_ callback: ((_ vc: Controller) -> Void)?) -> Controller {
|
||||
@discardableResult public func onViewDidAppear(_ callback: ((_ vc: BaseController) -> Void)?) -> BaseController {
|
||||
navEvents[.onViewDidAppear] = callback
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult public func onViewWillDisappear(_ callback: ((_ vc: Controller) -> Void)?) -> Controller {
|
||||
@discardableResult public func onViewWillDisappear(_ callback: ((_ vc: BaseController) -> Void)?) -> BaseController {
|
||||
navEvents[.onViewWillDisappear] = callback
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult public func onViewDidDisappear(_ callback: ((_ vc: Controller) -> Void)?) -> Controller {
|
||||
@discardableResult public func onViewDidDisappear(_ callback: ((_ vc: BaseController) -> Void)?) -> BaseController {
|
||||
navEvents[.onViewDidDisappear] = callback
|
||||
return self
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ open class Controller: UIViewController {
|
|||
}
|
||||
|
||||
// MARK: - 事件
|
||||
extension Controller {
|
||||
extension BaseController {
|
||||
|
||||
func addTouchUpAction(for button: UIButton, action: @escaping () -> Void) {
|
||||
button.addTarget(self, action: #selector(didTappedButton(_:)), for: .touchUpInside)
|
|
@ -37,9 +37,9 @@ open class Action: NSObject {
|
|||
|
||||
open var title: String?
|
||||
|
||||
open var handler: ((_ vc: HUD) -> Void)?
|
||||
open var handler: ((_ vc: HUDControllerType) -> Void)?
|
||||
|
||||
public init(identifier: String? = nil, style: Style = .tinted, title: String?, handler: ((_ vc: HUD) -> Void)? = nil) {
|
||||
public init(identifier: String? = nil, style: Style = .tinted, title: String?, handler: ((_ vc: HUDControllerType) -> Void)? = nil) {
|
||||
self.identifier = identifier
|
||||
self.style = style
|
||||
self.title = title
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public protocol HUDViewModelType {}
|
||||
|
||||
/// 数据模型
|
||||
open class BaseViewModel: NSObject {
|
||||
open class BaseViewModel: NSObject, HUDViewModelType {
|
||||
|
||||
/// 图标
|
||||
@objc open var icon: UIImage?
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public class Configuration: NSObject {
|
||||
open class CommonConfiguration: NSObject {
|
||||
|
||||
/// 全局功能开关
|
||||
public static var isEnabled: Bool = true
|
||||
|
@ -171,11 +171,11 @@ public class Configuration: NSObject {
|
|||
customWebImage = handler
|
||||
}
|
||||
|
||||
var customReloadData: ((_ vc: Controller) -> Bool)?
|
||||
var customReloadData: ((_ vc: BaseController) -> Bool)?
|
||||
|
||||
/// 自定义刷新规则( ⚠️ 自定义此函数之后,整个容器将不再走默认布局规则,可实现完全自定义)
|
||||
/// - Parameter callback: 自定义刷新规则代码
|
||||
public func reloadData(_ callback: @escaping (_ vc: Controller) -> Bool) {
|
||||
public func reloadData(_ callback: @escaping (_ vc: BaseController) -> Bool) {
|
||||
customReloadData = callback
|
||||
}
|
||||
|
||||
|
@ -188,5 +188,9 @@ public class Configuration: NSObject {
|
|||
customContentViewMask = callback
|
||||
}
|
||||
|
||||
override init() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public protocol CommonLayout: Controller {
|
||||
public protocol CommonLayout: BaseController {
|
||||
func reloadData()
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import UIKit
|
|||
|
||||
protocol DefaultLayout: CommonLayout {
|
||||
|
||||
var cfg: Configuration { get }
|
||||
var cfg: CommonConfiguration { get }
|
||||
|
||||
func reloadData(animated: Bool)
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
//
|
||||
// ProHUD.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2022/8/29.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc public protocol HUD {
|
||||
@objc func push()
|
||||
@objc func pop()
|
||||
}
|
||||
|
||||
//public extension HUD {
|
||||
// func push(workspace: Workspace?) {
|
||||
// AppContext.workspace = workspace
|
||||
// push()
|
||||
// }
|
||||
//}
|
|
@ -8,7 +8,7 @@
|
|||
import UIKit
|
||||
|
||||
/// 加载动画
|
||||
public protocol LoadingAnimation: Controller {
|
||||
public protocol LoadingAnimation: BaseController {
|
||||
|
||||
var imageView: UIImageView { get }
|
||||
var progressView: ProgressView? { get set }
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// Provider.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2023/8/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public protocol HUDProviderType {
|
||||
|
||||
associatedtype ViewModel = HUDViewModelType
|
||||
associatedtype Target = HUDTargetType
|
||||
|
||||
/// 根据ViewModel和自定义的初始化代码创建一个Target并显示
|
||||
/// - Parameters:
|
||||
/// - vm: 数据模型
|
||||
/// - initializer: 初始化代码
|
||||
@discardableResult init(_ vm: ViewModel?, initializer: ((_ target: Target) -> Void)?)
|
||||
|
||||
}
|
||||
|
||||
open class HUDProvider<ViewModel: HUDViewModelType, Target: HUDTargetType>: HUDProviderType {
|
||||
|
||||
/// HUD实例
|
||||
public var target: Target
|
||||
|
||||
/// 根据ViewModel和自定义的初始化代码创建一个Target并显示
|
||||
/// - Parameters:
|
||||
/// - vm: 数据模型
|
||||
/// - initializer: 初始化代码
|
||||
@discardableResult public required init(_ vm: ViewModel?, initializer: ((_ target: Target) -> Void)?) {
|
||||
var t = Target()
|
||||
if let vm = vm as? Target.ViewModel {
|
||||
t.vm = vm
|
||||
}
|
||||
initializer?(t)
|
||||
self.target = t
|
||||
if (vm == nil && initializer == nil) == false {
|
||||
DispatchQueue.main.async {
|
||||
t.push()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据自定义的初始化代码创建一个Target并显示
|
||||
/// - Parameter initializer: 初始化代码
|
||||
@discardableResult public convenience init(initializer: ((_ target: Target) -> Void)?) {
|
||||
self.init(nil, initializer: initializer)
|
||||
}
|
||||
|
||||
/// 根据ViewModel创建一个Target并显示
|
||||
/// - Parameter vm: 数据模型
|
||||
@discardableResult public convenience init(_ vm: ViewModel?) {
|
||||
self.init(vm, initializer: nil)
|
||||
}
|
||||
|
||||
/// 创建一个空白的实例,不立即显示,需要手动调用target.push()来显示
|
||||
@discardableResult public convenience init() {
|
||||
self.init(nil, initializer: nil)
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Target.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2023/8/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc public protocol HUDControllerType {
|
||||
@objc func push()
|
||||
@objc func pop()
|
||||
}
|
||||
|
||||
public protocol HUDTargetType: HUDControllerType {
|
||||
associatedtype ViewModel = HUDViewModelType
|
||||
var vm: ViewModel { get set }
|
||||
init()
|
||||
}
|
|
@ -36,7 +36,7 @@ public struct AppContext {
|
|||
static var toastWindows: [UIWindowScene: [ToastWindow]] = [:]
|
||||
static var alertWindow: [UIWindowScene: AlertWindow] = [:]
|
||||
static var sheetWindows: [UIWindowScene: [SheetWindow]] = [:]
|
||||
static var capsuleWindows: [UIWindowScene: [Capsule.ViewModel.Position: CapsuleWindow]] = [:]
|
||||
static var capsuleWindows: [UIWindowScene: [CapsuleTarget.ViewModel.Position: CapsuleWindow]] = [:]
|
||||
|
||||
static var current: AppContext? {
|
||||
guard let windowScene = windowScene else { return nil }
|
||||
|
@ -120,7 +120,7 @@ extension AppContext {
|
|||
var toastWindows: [ToastWindow] {
|
||||
Self.toastWindows[windowScene] ?? []
|
||||
}
|
||||
var capsuleWindows: [Capsule.ViewModel.Position: CapsuleWindow] {
|
||||
var capsuleWindows: [CapsuleTarget.ViewModel.Position: CapsuleWindow] {
|
||||
Self.capsuleWindows[windowScene] ?? [:]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import Foundation
|
|||
|
||||
func consolePrint(_ items: Any...) {
|
||||
#if DEBUG
|
||||
guard Configuration.enablePrint else { return }
|
||||
guard CommonConfiguration.enablePrint else { return }
|
||||
print(">> ProHUD:", items)
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ extension LoadingAnimation {
|
|||
}
|
||||
|
||||
/// 动画扩展
|
||||
extension Controller {
|
||||
extension BaseController {
|
||||
@objc func pauseLoadingAnimation() {
|
||||
if let layer = animateLayer {
|
||||
animation = layer.animation(forKey: .rotateKey)
|
||||
|
|
|
@ -29,7 +29,7 @@ open class Button: UIButton {
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public convenience init(config: ProHUD.Configuration, action: Action) {
|
||||
public convenience init(config: CommonConfiguration, action: Action) {
|
||||
self.init(frame: .zero)
|
||||
layer.cornerRadiusWithContinuous = 8
|
||||
self.update(config: config, action: action)
|
||||
|
@ -37,7 +37,7 @@ open class Button: UIButton {
|
|||
|
||||
/// 更新按钮
|
||||
/// - Parameter style: 样式
|
||||
open func update(config: ProHUD.Configuration, action: Action) {
|
||||
open func update(config: CommonConfiguration, action: Action) {
|
||||
self.action = action
|
||||
setTitle(action.title, for: .normal)
|
||||
switch action.style {
|
||||
|
|
|
@ -13,7 +13,7 @@ public class SheetButton: Button {
|
|||
.init(top: 14, left: 28, bottom: 14, right: 28)
|
||||
}
|
||||
|
||||
public override func update(config: ProHUD.Configuration, action: Action) {
|
||||
public override func update(config: CommonConfiguration, action: Action) {
|
||||
titleLabel?.font = .boldSystemFont(ofSize: 18)
|
||||
layer.cornerRadiusWithContinuous = 12
|
||||
super.update(config: config, action: action)
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public extension Sheet {
|
||||
|
||||
class Configuration: ProHUD.Configuration {
|
||||
public class SheetConfiguration: CommonConfiguration {
|
||||
|
||||
/// 堆叠效果
|
||||
public var stackDepthEffect: Bool = false
|
||||
|
@ -28,12 +26,12 @@ public extension Sheet {
|
|||
customSubtitleLabel = handler
|
||||
}
|
||||
|
||||
static var customShared: ((_ config: Configuration) -> Void)?
|
||||
static var customGlobalConfig: ((_ config: SheetConfiguration) -> Void)?
|
||||
|
||||
/// 共享配置(只能设置一次,影响所有实例)
|
||||
/// 全局共享配置(只能设置一次,影响所有实例)
|
||||
/// - Parameter callback: 配置代码
|
||||
public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) {
|
||||
customShared = callback
|
||||
public static func global(_ callback: @escaping (_ config: SheetConfiguration) -> Void) {
|
||||
customGlobalConfig = callback
|
||||
}
|
||||
|
||||
var customBackgroundViewMask: ((_ mask: UIView) -> Void)?
|
||||
|
@ -62,6 +60,4 @@ public extension Sheet {
|
|||
|
||||
override var cardCornerRadiusByDefault: CGFloat { cardCornerRadius ?? 32 }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Sheet: ConvenienceLayout {
|
||||
extension SheetTarget: ConvenienceLayout {
|
||||
|
||||
// MARK: 增加
|
||||
@discardableResult public func add(action: Action) -> Button {
|
||||
|
@ -126,7 +126,7 @@ extension Sheet: ConvenienceLayout {
|
|||
}
|
||||
|
||||
// MARK: more
|
||||
public extension Sheet {
|
||||
public extension SheetTarget {
|
||||
|
||||
/// 增加一个标题
|
||||
/// - Parameter text: 文本
|
||||
|
@ -185,10 +185,10 @@ public extension Sheet {
|
|||
/// - identifier: 唯一标识符
|
||||
/// - handler: 点击事件
|
||||
/// - Returns: 按钮实例
|
||||
@discardableResult func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ sheet: Sheet) -> Void)? = nil) -> Button {
|
||||
@discardableResult func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ sheet: SheetTarget) -> Void)? = nil) -> Button {
|
||||
if let handler = handler {
|
||||
let action = Action(identifier: identifier, style: style, title: title) { vc in
|
||||
if let vc = vc as? Sheet {
|
||||
if let vc = vc as? SheetTarget {
|
||||
handler(vc)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Sheet: DefaultLayout {
|
||||
extension SheetTarget: DefaultLayout {
|
||||
|
||||
var cfg: ProHUD.Configuration {
|
||||
var cfg: CommonConfiguration {
|
||||
return config
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ extension Sheet: DefaultLayout {
|
|||
if self.cfg.customReloadData?(self) == true {
|
||||
return
|
||||
}
|
||||
view.tintColor = vm.tintColor ?? config.tintColor
|
||||
// background
|
||||
if backgroundView.superview == nil {
|
||||
view.insertSubview(backgroundView, at: 0)
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Sheet: HUD {
|
||||
extension SheetTarget {
|
||||
|
||||
@objc open func push() {
|
||||
guard Configuration.isEnabled else { return }
|
||||
guard SheetConfiguration.isEnabled else { return }
|
||||
let isNew: Bool
|
||||
let window: SheetWindow
|
||||
var windows = AppContext.current?.sheetWindows ?? []
|
||||
|
@ -57,29 +57,9 @@ extension Sheet: HUD {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension Sheet {
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
/// - Parameters:
|
||||
/// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符)
|
||||
/// - handler: 实例创建代码
|
||||
static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ sheet: Sheet) -> Void, onExists: ((_ sheet: Sheet) -> Void)? = nil) {
|
||||
let id = identifier ?? (file + "#\(line)")
|
||||
if let vc = find(identifier: id).last {
|
||||
vc.update(handler: onExists ?? handler)
|
||||
} else {
|
||||
Sheet { sheet in
|
||||
sheet.identifier = id
|
||||
handler(sheet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新HUD实例
|
||||
/// - Parameter handler: 实例更新代码
|
||||
func update(handler: @escaping (_ sheet: Sheet) -> Void) {
|
||||
@objc open func update(handler: @escaping (_ sheet: SheetTarget) -> Void) {
|
||||
handler(self)
|
||||
reloadData()
|
||||
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
|
||||
|
@ -87,21 +67,9 @@ public extension Sheet {
|
|||
}
|
||||
}
|
||||
|
||||
/// 查找HUD实例
|
||||
/// - Parameter identifier: 唯一标识符
|
||||
/// - Returns: HUD实例
|
||||
@discardableResult static func find(identifier: String, update handler: ((_ sheet: Sheet) -> Void)? = nil) -> [Sheet] {
|
||||
let arr = AppContext.sheetWindows.values.flatMap({ $0 }).compactMap({ $0.sheet }).filter({ $0.identifier == identifier })
|
||||
if let handler = handler {
|
||||
arr.forEach({ $0.update(handler: handler) })
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension Sheet {
|
||||
extension SheetTarget {
|
||||
|
||||
func translateIn(completion: (() -> Void)?) {
|
||||
UIView.animateEaseOut(duration: config.animateDurationForBuildInByDefault) {
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// SheetProvider.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2023/8/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class SheetProvider: HUDProvider<SheetViewModel, SheetTarget> {
|
||||
@discardableResult public required init(_ vm: ViewModel?, initializer: ((_ sheet: Target) -> Void)?) {
|
||||
super.init(vm, initializer: initializer)
|
||||
}
|
||||
|
||||
@discardableResult public required convenience init(initializer: ((_ sheet: Target) -> Void)?) {
|
||||
self.init(nil, initializer: initializer)
|
||||
}
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
/// - Parameters:
|
||||
/// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符)
|
||||
/// - handler: 实例创建代码
|
||||
public static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ sheet: SheetTarget) -> Void, onExists: ((_ sheet: SheetTarget) -> Void)? = nil) {
|
||||
let id = identifier ?? (file + "#\(line)")
|
||||
if let vc = find(identifier: id).last {
|
||||
vc.update(handler: onExists ?? handler)
|
||||
} else {
|
||||
Self.init { sheet in
|
||||
sheet.identifier = id
|
||||
handler(sheet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 查找HUD实例
|
||||
/// - Parameter identifier: 唯一标识符
|
||||
/// - Returns: HUD实例
|
||||
@discardableResult public static func find(identifier: String, update handler: ((_ sheet: SheetTarget) -> Void)? = nil) -> [SheetTarget] {
|
||||
let arr = AppContext.sheetWindows.values.flatMap({ $0 }).compactMap({ $0.sheet }).filter({ $0.identifier == identifier })
|
||||
if let handler = handler {
|
||||
arr.forEach({ $0.update(handler: handler) })
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public typealias Sheet = SheetProvider
|
|
@ -7,13 +7,13 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
open class Sheet: Controller {
|
||||
open class SheetTarget: BaseController, HUDTargetType {
|
||||
|
||||
weak var window: SheetWindow?
|
||||
|
||||
public lazy var config: Configuration = {
|
||||
var cfg = Configuration()
|
||||
Configuration.customShared?(cfg)
|
||||
public lazy var config: SheetConfiguration = {
|
||||
var cfg = SheetConfiguration()
|
||||
SheetConfiguration.customGlobalConfig?(cfg)
|
||||
return cfg
|
||||
}()
|
||||
|
||||
|
@ -33,7 +33,7 @@ open class Sheet: Controller {
|
|||
return stack
|
||||
}()
|
||||
|
||||
private var onTappedBackground: ((_ sheet: Sheet) -> Void)?
|
||||
private var tapActionCallback: ((_ sheet: SheetTarget) -> Void)?
|
||||
|
||||
public override var title: String? {
|
||||
didSet {
|
||||
|
@ -41,16 +41,10 @@ open class Sheet: Controller {
|
|||
}
|
||||
}
|
||||
|
||||
@discardableResult @objc public init(handler: @escaping (_ sheet: Sheet) -> Void, onTappedBackground action: ((_ sheet: Sheet) -> Void)? = nil) {
|
||||
public var vm: SheetViewModel = .init()
|
||||
|
||||
required public override init() {
|
||||
super.init()
|
||||
|
||||
onTappedBackground = action
|
||||
handler(self)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.push()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
|
@ -59,7 +53,6 @@ open class Sheet: Controller {
|
|||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.tintColor = config.tintColor
|
||||
|
||||
// 点击
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(_onTappedBackground(_:)))
|
||||
|
@ -73,11 +66,15 @@ open class Sheet: Controller {
|
|||
|
||||
}
|
||||
|
||||
public func onTappedBackground(action: @escaping (_ sheet: SheetTarget) -> Void) {
|
||||
self.tapActionCallback = action
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Sheet {
|
||||
extension SheetTarget {
|
||||
@objc func _onTappedBackground(_ sender: UITapGestureRecognizer) {
|
||||
if let act = onTappedBackground {
|
||||
if let act = tapActionCallback {
|
||||
act(self)
|
||||
} else {
|
||||
self.pop()
|
|
@ -0,0 +1,10 @@
|
|||
//
|
||||
// SheetViewModel.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2023/8/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc open class SheetViewModel: BaseViewModel {}
|
|
@ -9,9 +9,9 @@ import UIKit
|
|||
|
||||
class SheetWindow: Window {
|
||||
|
||||
var sheet: Sheet
|
||||
var sheet: SheetTarget
|
||||
|
||||
init(sheet: Sheet) {
|
||||
init(sheet: SheetTarget) {
|
||||
self.sheet = sheet
|
||||
if let scene = AppContext.windowScene {
|
||||
super.init(windowScene: scene)
|
||||
|
@ -29,7 +29,7 @@ class SheetWindow: Window {
|
|||
|
||||
}
|
||||
|
||||
extension Sheet {
|
||||
extension SheetTarget {
|
||||
func getContextWindows() -> [SheetWindow] {
|
||||
guard let windowScene = windowScene else {
|
||||
return []
|
||||
|
|
|
@ -13,7 +13,7 @@ public class ToastButton: Button {
|
|||
.init(top: 10, left: 24, bottom: 10, right: 24)
|
||||
}
|
||||
|
||||
public override func update(config: ProHUD.Configuration, action: Action) {
|
||||
public override func update(config: CommonConfiguration, action: Action) {
|
||||
titleLabel?.font = .boldSystemFont(ofSize: 15)
|
||||
layer.cornerRadiusWithContinuous = 8
|
||||
super.update(config: config, action: action)
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public extension Toast {
|
||||
|
||||
class Configuration: ProHUD.Configuration {
|
||||
public class ToastConfiguration: CommonConfiguration {
|
||||
|
||||
/// 元素与元素之间的距离
|
||||
public var margin = CGFloat(8)
|
||||
|
@ -21,12 +19,12 @@ public extension Toast {
|
|||
/// 行间距
|
||||
public var lineSpace = CGFloat(4)
|
||||
|
||||
static var customShared: ((_ config: Configuration) -> Void)?
|
||||
static var customGlobalConfig: ((_ config: ToastConfiguration) -> Void)?
|
||||
|
||||
/// 共享配置(只能设置一次,影响所有实例)
|
||||
/// 全局共享配置(只能设置一次,影响所有实例)
|
||||
/// - Parameter callback: 配置代码
|
||||
public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) {
|
||||
customShared = callback
|
||||
public static func global(_ callback: @escaping (_ config: ToastConfiguration) -> Void) {
|
||||
customGlobalConfig = callback
|
||||
}
|
||||
|
||||
/// 距离窗口左右的间距
|
||||
|
@ -51,7 +49,4 @@ public extension Toast {
|
|||
animateDurationForBuildIn ?? 0.8
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Toast: ConvenienceLayout {
|
||||
extension ToastTarget: ConvenienceLayout {
|
||||
|
||||
// MARK: 增加
|
||||
|
||||
|
@ -18,10 +18,10 @@ extension Toast: ConvenienceLayout {
|
|||
/// - identifier: 唯一标识符
|
||||
/// - handler: 点击事件
|
||||
/// - Returns: 按钮实例
|
||||
@discardableResult public func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ toast: Toast) -> Void)? = nil) -> Button {
|
||||
@discardableResult public func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ toast: ToastTarget) -> Void)? = nil) -> Button {
|
||||
if let handler = handler {
|
||||
let action = Action(identifier: identifier, style: style, title: title) { vc in
|
||||
if let vc = vc as? Toast {
|
||||
if let vc = vc as? ToastTarget {
|
||||
handler(vc)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Toast: DefaultLayout {
|
||||
extension ToastTarget: DefaultLayout {
|
||||
|
||||
var cfg: ProHUD.Configuration {
|
||||
var cfg: CommonConfiguration {
|
||||
return config
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ extension Toast: DefaultLayout {
|
|||
if self.cfg.customReloadData?(self) == true {
|
||||
return
|
||||
}
|
||||
view.tintColor = vm.tintColor ?? config.tintColor
|
||||
loadContentViewIfNeeded()
|
||||
loadContentMaskViewIfNeeded()
|
||||
guard customView == nil else {
|
||||
|
@ -117,7 +118,7 @@ extension Toast: DefaultLayout {
|
|||
|
||||
}
|
||||
|
||||
extension Toast {
|
||||
extension ToastTarget {
|
||||
func loadActionStackIfNeeded() {
|
||||
guard actionStack.arrangedSubviews.count > 0 else {
|
||||
actionStack.removeFromSuperview()
|
||||
|
@ -149,4 +150,5 @@ extension Toast {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
extension Toast: HUD {
|
||||
extension ToastTarget {
|
||||
|
||||
private func calcHeight() -> CGFloat {
|
||||
var height = CGFloat(0)
|
||||
|
@ -34,8 +34,9 @@ extension Toast: HUD {
|
|||
height += cardEdgeInsets.top + cardEdgeInsets.bottom
|
||||
return height
|
||||
}
|
||||
|
||||
@objc open func push() {
|
||||
guard Configuration.isEnabled else { return }
|
||||
guard ToastConfiguration.isEnabled else { return }
|
||||
let isNew: Bool
|
||||
let window: ToastWindow
|
||||
var windows = AppContext.current?.toastWindows ?? []
|
||||
|
@ -104,29 +105,9 @@ extension Toast: HUD {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension Toast {
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
/// - Parameters:
|
||||
/// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符)
|
||||
/// - handler: 实例创建代码
|
||||
static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ toast: Toast) -> Void, onExists: ((_ toast: Toast) -> Void)? = nil) {
|
||||
let id = identifier ?? (file + "#\(line)")
|
||||
if let vc = find(identifier: id).last {
|
||||
vc.update(handler: onExists ?? handler)
|
||||
} else {
|
||||
Toast { toast in
|
||||
toast.identifier = id
|
||||
handler(toast)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新HUD实例
|
||||
/// - Parameter handler: 实例更新代码
|
||||
func update(handler: @escaping (_ toast: Toast) -> Void) {
|
||||
@objc open func update(handler: @escaping (_ toast: ToastTarget) -> Void) {
|
||||
handler(self)
|
||||
reloadData()
|
||||
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
|
||||
|
@ -134,17 +115,6 @@ public extension Toast {
|
|||
}
|
||||
}
|
||||
|
||||
/// 查找HUD实例
|
||||
/// - Parameter identifier: 唯一标识符
|
||||
/// - Returns: HUD实例
|
||||
@discardableResult static func find(identifier: String, update handler: ((_ toast: Toast) -> Void)? = nil) -> [Toast] {
|
||||
let arr = AppContext.toastWindows.values.flatMap({ $0 }).compactMap({ $0.toast }).filter({ $0.identifier == identifier })
|
||||
if let handler = handler {
|
||||
arr.forEach({ $0.update(handler: handler) })
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - layout
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// ToastProvider.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2023/8/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class ToastProvider: HUDProvider<ToastViewModel, ToastTarget> {
|
||||
@discardableResult public required init(_ vm: ViewModel?, initializer: ((_ toast: Target) -> Void)?) {
|
||||
super.init(vm, initializer: initializer)
|
||||
}
|
||||
|
||||
@discardableResult public required convenience init(initializer: ((_ toast: Target) -> Void)?) {
|
||||
self.init(nil, initializer: initializer)
|
||||
}
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
/// - Parameters:
|
||||
/// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符)
|
||||
/// - handler: 实例创建代码
|
||||
public static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ toast: ToastTarget) -> Void, onExists: ((_ toast: ToastTarget) -> Void)? = nil) {
|
||||
let id = identifier ?? (file + "#\(line)")
|
||||
if let vc = find(identifier: id).last {
|
||||
vc.update(handler: onExists ?? handler)
|
||||
} else {
|
||||
Self.init { toast in
|
||||
toast.identifier = id
|
||||
handler(toast)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 查找HUD实例
|
||||
/// - Parameter identifier: 唯一标识符
|
||||
/// - Returns: HUD实例
|
||||
@discardableResult public static func find(identifier: String, update handler: ((_ toast: ToastTarget) -> Void)? = nil) -> [ToastTarget] {
|
||||
let arr = AppContext.toastWindows.values.flatMap({ $0 }).compactMap({ $0.toast }).filter({ $0.identifier == identifier })
|
||||
if let handler = handler {
|
||||
arr.forEach({ $0.update(handler: handler) })
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public typealias Toast = ToastProvider
|
|
@ -7,13 +7,13 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
open class Toast: Controller {
|
||||
open class ToastTarget: BaseController, HUDTargetType {
|
||||
|
||||
weak var window: ToastWindow?
|
||||
|
||||
public lazy var config: Configuration = {
|
||||
var cfg = Configuration()
|
||||
Configuration.customShared?(cfg)
|
||||
public lazy var config: ToastConfiguration = {
|
||||
var cfg = ToastConfiguration()
|
||||
ToastConfiguration.customGlobalConfig?(cfg)
|
||||
return cfg
|
||||
}()
|
||||
|
||||
|
@ -83,12 +83,10 @@ open class Toast: Controller {
|
|||
/// 是否可以通过手势移除(向上滑出屏幕)
|
||||
public var isRemovable = true
|
||||
|
||||
@objc(ToastViewModel) open class ViewModel: BaseViewModel {}
|
||||
|
||||
/// 视图模型
|
||||
@objc public var vm = ViewModel()
|
||||
@objc public var vm = ToastViewModel()
|
||||
|
||||
private var tapActionCallback: ((_ toast: Toast) -> Void)?
|
||||
private var tapActionCallback: ((_ toast: ToastTarget) -> Void)?
|
||||
|
||||
|
||||
public override var title: String? {
|
||||
|
@ -97,20 +95,8 @@ open class Toast: Controller {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@discardableResult @objc public init(_ vm: ViewModel, handler: ((_ toast: Toast) -> Void)? = nil) {
|
||||
required public override init() {
|
||||
super.init()
|
||||
self.vm = vm
|
||||
handler?(self)
|
||||
DispatchQueue.main.async {
|
||||
if handler != nil {
|
||||
self.push()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult @objc public convenience init(handler: ((_ toast: Toast) -> Void)?) {
|
||||
self.init(.init(), handler: handler)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
|
@ -119,7 +105,6 @@ open class Toast: Controller {
|
|||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.tintColor = config.tintColor
|
||||
|
||||
// 点击
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(_onTappedGesture(_:)))
|
||||
|
@ -133,13 +118,13 @@ open class Toast: Controller {
|
|||
|
||||
}
|
||||
|
||||
public func onTapped(action: @escaping (_ toast: Toast) -> Void) {
|
||||
public func onTapped(action: @escaping (_ toast: ToastTarget) -> Void) {
|
||||
self.tapActionCallback = action
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate extension Toast {
|
||||
fileprivate extension ToastTarget {
|
||||
|
||||
/// 点击事件
|
||||
/// - Parameter sender: 手势
|
||||
|
@ -173,4 +158,4 @@ fileprivate extension Toast {
|
|||
|
||||
}
|
||||
|
||||
extension Toast: LoadingAnimation { }
|
||||
extension ToastTarget: LoadingAnimation { }
|
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// ToastViewModel.swift
|
||||
//
|
||||
//
|
||||
// Created by xaoxuu on 2023/8/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc open class ToastViewModel: BaseViewModel {
|
||||
|
||||
}
|
|
@ -9,11 +9,11 @@ import UIKit
|
|||
|
||||
class ToastWindow: Window {
|
||||
|
||||
var toast: Toast
|
||||
var toast: ToastTarget
|
||||
|
||||
var maxY = CGFloat(0)
|
||||
|
||||
init(toast: Toast) {
|
||||
init(toast: ToastTarget) {
|
||||
self.toast = toast
|
||||
super.init(frame: .zero)
|
||||
windowScene = AppContext.windowScene
|
||||
|
@ -36,7 +36,7 @@ class ToastWindow: Window {
|
|||
|
||||
}
|
||||
|
||||
extension Toast {
|
||||
extension ToastTarget {
|
||||
func getContextWindows() -> [ToastWindow] {
|
||||
guard let windowScene = windowScene else {
|
||||
return []
|
||||
|
|
Loading…
Reference in New Issue