代码重构

This commit is contained in:
xaoxuu 2023-08-18 20:55:57 +08:00
parent 6eee00880b
commit 32f9af1ae6
52 changed files with 823 additions and 682 deletions

View File

@ -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,20 +82,21 @@ class AlertVC: ListVC {
button.titleLabel?.font = .systemFont(ofSize: 15)
}
alert.vm.title = "你正在使用移动网络观看"
} .onViewDidLoad { vc in
guard let alert = vc as? Alert else {
return
alert.onViewDidLoad { vc in
guard let alert = vc as? AlertTarget else {
return
}
alert.add(contentSpacing: 30)
let v = UIView()
v.backgroundColor = UIColor("#f2f2f2")
alert.add(subview: v).snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.height.equalTo(1)
}
alert.add(contentSpacing: 16)
alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc")))
}
alert.add(contentSpacing: 30)
let v = UIView()
v.backgroundColor = UIColor("#f2f2f2")
alert.add(subview: v).snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.height.equalTo(1)
}
alert.add(contentSpacing: 16)
alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc")))
}
}
section.add(title: "只有一段文字 + 无背景色按钮") {
@ -111,20 +112,21 @@ class AlertVC: ListVC {
button.titleLabel?.font = .systemFont(ofSize: 15)
}
alert.vm.message = "为了维护社区氛围,上麦用户需进行主播认证"
} .onViewDidLoad { vc in
guard let alert = vc as? Alert else {
return
alert.onViewDidLoad { vc in
guard let alert = vc as? AlertTarget else {
return
}
alert.add(contentSpacing: 30)
let v = UIView()
v.backgroundColor = UIColor("#f2f2f2")
alert.add(subview: v).snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.height.equalTo(1)
}
alert.add(contentSpacing: 16)
alert.add(action: "取消", style: .plain(textColor: UIColor("#939999")))
alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc")))
}
alert.add(contentSpacing: 30)
let v = UIView()
v.backgroundColor = UIColor("#f2f2f2")
alert.add(subview: v).snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.height.equalTo(1)
}
alert.add(contentSpacing: 16)
alert.add(action: "取消", style: .plain(textColor: UIColor("#939999")))
alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc")))
}
}
section.add(title: "只有一段文字 + 3个无背景色按钮") {
@ -144,41 +146,42 @@ class AlertVC: ListVC {
stack.spacing = 0
stack.axis = .vertical //
}
} .onViewDidLoad { vc in
guard let alert = vc as? Alert else {
return
alert.onViewDidLoad { vc in
guard let alert = vc as? AlertTarget else {
return
}
func createLine() -> UIView {
let v = UIView()
v.backgroundColor = UIColor("#f2f2f2")
return v
}
let btn1 = alert.add(action: "确认,以后不需要再提醒", style: .plain(textColor: UIColor("#14cccc")))
btn1.contentEdgeInsets.top = 16 + 1 // + 线
btn1.contentEdgeInsets.bottom = 16
let line1 = createLine()
btn1.insertSubview(line1, at: 0)
line1.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.height.equalTo(1)
}
let btn2 = alert.add(action: "确认,每次消费前提醒", style: .plain(textColor: UIColor("#14cccc")))
let line2 = createLine()
btn2.insertSubview(line2, at: 0)
line2.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.height.equalTo(1)
}
let btn3 = alert.add(action: "取消", style: .plain(textColor: UIColor("#939999")))
let line3 = createLine()
btn3.insertSubview(line3, at: 0)
line3.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.height.equalTo(1)
}
}
func createLine() -> UIView {
let v = UIView()
v.backgroundColor = UIColor("#f2f2f2")
return v
}
let btn1 = alert.add(action: "确认,以后不需要再提醒", style: .plain(textColor: UIColor("#14cccc")))
btn1.contentEdgeInsets.top = 16 + 1 // + 线
btn1.contentEdgeInsets.bottom = 16
let line1 = createLine()
btn1.insertSubview(line1, at: 0)
line1.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.height.equalTo(1)
}
let btn2 = alert.add(action: "确认,每次消费前提醒", style: .plain(textColor: UIColor("#14cccc")))
let line2 = createLine()
btn2.insertSubview(line2, at: 0)
line2.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.height.equalTo(1)
}
let btn3 = alert.add(action: "取消", style: .plain(textColor: UIColor("#939999")))
let line3 = createLine()
btn3.insertSubview(line3, at: 0)
line3.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.height.equalTo(1)
}
}
}
@ -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,12 +290,13 @@ 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 {
return
alert.onViewWillAppear { vc in
guard let alert = vc as? AlertTarget else {
return
}
alert.vm.message = "这是一段后增加的文字\n动画效果会有细微差别"
alert.reloadTextStack()
}
alert.vm.message = "这是一段后增加的文字\n动画效果会有细微差别"
alert.reloadTextStack()
}
}
}
@ -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)
}

View File

@ -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()
// vmhandlerpushvm
Capsule(.message("一条简短消息"))
}
section.add(title: "一条稍微长一点的消息") {
Capsule(.message("一条稍微长一点的消息")).push()
// vmhandlerpushhandler
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行状态胶囊控件用于状态显示一个主程序窗口只有一个状态胶囊实例。") {
// vmhandler
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,9 +135,10 @@ class CapsuleVC: ListVC {
mask.layer.insertSublayer(gradientLayer, at: 0)
}
capsule.config.cardCornerRadius = .infinity
}.onTapped { capsule in
Alert(.message("收到点击事件").duration(1)).push()
capsule.pop()
capsule.onTapped { capsule in
Alert(.message("收到点击事件").duration(1))
capsule.pop()
}
}
}
}
@ -126,7 +146,7 @@ class CapsuleVC: ListVC {
}
extension Capsule.ViewModel {
extension CapsuleTarget.ViewModel {
static func info(_ text: String?) -> Self {
.init()

View File

@ -73,13 +73,14 @@ class SheetVC: ListVC {
sheet.add(spacing: 24)
sheet.add(action: "确认")
sheet.add(action: "取消", style: .gray)
} onTappedBackground: { sheet in
print("点击了背景")
Toast.lazyPush(identifier: "alert") { toast in
toast.vm = .error
toast.vm.title = "点击了背景"
toast.vm.message = "点击背景将不会dismiss必须在下方做出选择才能关掉"
toast.vm.duration = 2
sheet.onTappedBackground { sheet in
print("点击了背景")
Toast.lazyPush(identifier: "alert") { toast in
toast.vm = .error
toast.vm.title = "点击了背景"
toast.vm.message = "点击背景将不会dismiss必须在下方做出选择才能关掉"
toast.vm.duration = 2
}
}
}
}
@ -180,16 +181,17 @@ class SheetVC: ListVC {
mask.effect = .none
mask.backgroundColor = .clear
}
} .onViewWillAppear { vc in
guard let sheet = vc as? Sheet else { return }
let imgv = UIImageView(image: UIImage(named: "landscape"))
imgv.contentMode = .scaleAspectFill
imgv.clipsToBounds = true
imgv.layer.cornerRadiusWithContinuous = 16
sheet.contentView.insertSubview(imgv, at: 0)
imgv.snp.makeConstraints { make in
make.edges.equalToSuperview()
make.height.equalTo(300)
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
imgv.layer.cornerRadiusWithContinuous = 16
sheet.contentView.insertSubview(imgv, at: 0)
imgv.snp.makeConstraints { make in
make.edges.equalToSuperview()
make.height.equalTo(300)
}
}
}
}

View File

@ -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)
}

View File

@ -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)

View File

@ -7,35 +7,31 @@
import UIKit
public extension Alert {
public class AlertConfiguration: CommonConfiguration {
class Configuration: ProHUD.Configuration {
///
public var stackDepth: CGFloat = 30
public var enableShadow: Bool = true
static var customShared: ((_ config: Configuration) -> Void)?
///
/// - Parameter callback:
public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) {
customShared = callback
}
var customBackgroundViewMask: ((_ mask: UIView) -> Void)?
///
/// - Parameter callback:
public func backgroundViewMask(_ callback: @escaping (_ mask: UIView) -> Void) {
customBackgroundViewMask = callback
}
override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.6
}
///
public var stackDepth: CGFloat = 30
public var enableShadow: Bool = true
static var customGlobalConfig: ((_ config: AlertConfiguration) -> Void)?
///
/// - Parameter callback:
public static func global(_ callback: @escaping (_ config: AlertConfiguration) -> Void) {
customGlobalConfig = callback
}
var customBackgroundViewMask: ((_ mask: UIView) -> Void)?
///
/// - Parameter callback:
public func backgroundViewMask(_ callback: @escaping (_ mask: UIView) -> Void) {
customBackgroundViewMask = callback
}
override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.6
}
}

View File

@ -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)
}
}

View File

@ -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() {
//

View File

@ -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
}

View File

@ -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

View File

@ -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 {}

View File

@ -0,0 +1,10 @@
//
// AlertViewModel.swift
//
//
// Created by xaoxuu on 2023/8/18.
//
import UIKit
@objc open class AlertViewModel: BaseViewModel {}

View File

@ -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
}

View File

@ -7,51 +7,47 @@
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 customShared: ((_ config: Configuration) -> Void)?
///
/// - Parameter callback:
public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) {
customShared = callback
}
override var cardCornerRadiusByDefault: CGFloat {
cardCornerRadius ?? 16
}
override var cardEdgeInsetsByDefault: UIEdgeInsets {
cardEdgeInsets ?? .init(top: 12, left: 16, bottom: 12, right: 16)
}
override var cardMaxWidthByDefault: CGFloat { cardMaxWidth ?? 320 }
override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? 120 }
override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.8
}
override var animateDurationForBuildOutByDefault: CGFloat {
animateDurationForBuildOut ?? 0.8
}
var animateBuildIn: CustomAnimateHandler?
public func animateBuildIn(_ handler: CustomAnimateHandler?) {
animateBuildIn = handler
}
var animateBuildOut: CustomAnimateHandler?
public func animateBuildOut(_ handler: CustomAnimateHandler?) {
animateBuildOut = handler
}
static var customGlobalConfig: ((_ config: CapsuleConfiguration) -> Void)?
///
/// - Parameter callback:
public static func global(_ callback: @escaping (_ config: CapsuleConfiguration) -> Void) {
customGlobalConfig = callback
}
override var cardCornerRadiusByDefault: CGFloat {
cardCornerRadius ?? 16
}
override var cardEdgeInsetsByDefault: UIEdgeInsets {
cardEdgeInsets ?? .init(top: 12, left: 16, bottom: 12, right: 16)
}
override var cardMaxWidthByDefault: CGFloat { cardMaxWidth ?? 320 }
override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? 120 }
override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.8
}
override var animateDurationForBuildOutByDefault: CGFloat {
animateDurationForBuildOut ?? 0.8
}
var animateBuildIn: CustomAnimateHandler?
public func animateBuildIn(_ handler: CustomAnimateHandler?) {
animateBuildIn = handler
}
var animateBuildOut: CustomAnimateHandler?
public func animateBuildOut(_ handler: CustomAnimateHandler?) {
animateBuildOut = handler
}
}

View File

@ -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)
}
}
}

View File

@ -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?()
// }
// }
//
//}

View File

@ -0,0 +1,53 @@
//
// CapsuleProvider.swift
//
//
// Created by xaoxuu on 2023/8/18.
//
import UIKit
open class CapsuleProvider: HUDProvider<CapsuleTarget.ViewModel, CapsuleTarget> {
/// ViewModelTarget
/// - 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

View File

@ -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 {
@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
}
}
public var vm: CapsuleViewModel = .init()
///
@objc public var vm = ViewModel()
private var tapActionCallback: ((_ capsule: CapsuleTarget) -> Void)?
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 { }

View File

@ -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
}
}

View File

@ -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
}

View File

@ -1,13 +1,13 @@
//
// Controller.swift
//
// BaseController.swift
//
//
// Created by xaoxuu on 2022/8/29.
//
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)

View File

@ -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

View File

@ -7,8 +7,10 @@
import UIKit
public protocol HUDViewModelType {}
///
open class BaseViewModel: NSObject {
open class BaseViewModel: NSObject, HUDViewModelType {
///
@objc open var icon: UIImage?

View File

@ -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() {
}
}

View File

@ -7,7 +7,7 @@
import Foundation
public protocol CommonLayout: Controller {
public protocol CommonLayout: BaseController {
func reloadData()
}

View File

@ -9,7 +9,7 @@ import UIKit
protocol DefaultLayout: CommonLayout {
var cfg: Configuration { get }
var cfg: CommonConfiguration { get }
func reloadData(animated: Bool)

View File

@ -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()
// }
//}

View File

@ -8,7 +8,7 @@
import UIKit
///
public protocol LoadingAnimation: Controller {
public protocol LoadingAnimation: BaseController {
var imageView: UIImageView { get }
var progressView: ProgressView? { get set }

View File

@ -0,0 +1,64 @@
//
// Provider.swift
//
//
// Created by xaoxuu on 2023/8/18.
//
import UIKit
public protocol HUDProviderType {
associatedtype ViewModel = HUDViewModelType
associatedtype Target = HUDTargetType
/// ViewModelTarget
/// - Parameters:
/// - vm:
/// - initializer:
@discardableResult init(_ vm: ViewModel?, initializer: ((_ target: Target) -> Void)?)
}
open class HUDProvider<ViewModel: HUDViewModelType, Target: HUDTargetType>: HUDProviderType {
/// HUD
public var target: Target
/// ViewModelTarget
/// - 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)
}
/// ViewModelTarget
/// - Parameter vm:
@discardableResult public convenience init(_ vm: ViewModel?) {
self.init(vm, initializer: nil)
}
/// target.push()
@discardableResult public convenience init() {
self.init(nil, initializer: nil)
}
}

View File

@ -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()
}

View File

@ -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] ?? [:]
}
}

View File

@ -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
}

View File

@ -59,7 +59,7 @@ extension LoadingAnimation {
}
///
extension Controller {
extension BaseController {
@objc func pauseLoadingAnimation() {
if let layer = animateLayer {
animation = layer.animation(forKey: .rotateKey)

View File

@ -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 {

View File

@ -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)

View File

@ -7,61 +7,57 @@
import UIKit
public extension Sheet {
public class SheetConfiguration: CommonConfiguration {
class Configuration: ProHUD.Configuration {
///
public var stackDepthEffect: Bool = false
///
public var windowEdgeInset: CGFloat = 16
///
public var isFullScreen = false
///
var customSubtitleLabel: ((_ label: UILabel) -> Void)?
public func customSubtitleLabel(handler: @escaping (_ label: UILabel) -> Void) {
customSubtitleLabel = handler
}
static var customShared: ((_ config: Configuration) -> Void)?
///
/// - Parameter callback:
public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) {
customShared = callback
}
var customBackgroundViewMask: ((_ mask: UIView) -> Void)?
///
/// - Parameter callback:
public func backgroundViewMask(_ callback: @escaping (_ mask: UIView) -> Void) {
customBackgroundViewMask = callback
}
override var cardEdgeInsetsByDefault: UIEdgeInsets {
cardEdgeInsets ?? .init(top: 24, left: 24, bottom: 24, right: 24)
}
override var cardMaxWidthByDefault: CGFloat { cardMaxWidth ?? 500 }
override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? (AppContext.appBounds.height - 50) }
override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.5
}
override var animateDurationForBuildOutByDefault: CGFloat {
animateDurationForBuildOut ?? 0.5
}
override var cardCornerRadiusByDefault: CGFloat { cardCornerRadius ?? 32 }
///
public var stackDepthEffect: Bool = false
///
public var windowEdgeInset: CGFloat = 16
///
public var isFullScreen = false
///
var customSubtitleLabel: ((_ label: UILabel) -> Void)?
public func customSubtitleLabel(handler: @escaping (_ label: UILabel) -> Void) {
customSubtitleLabel = handler
}
static var customGlobalConfig: ((_ config: SheetConfiguration) -> Void)?
///
/// - Parameter callback:
public static func global(_ callback: @escaping (_ config: SheetConfiguration) -> Void) {
customGlobalConfig = callback
}
var customBackgroundViewMask: ((_ mask: UIView) -> Void)?
///
/// - Parameter callback:
public func backgroundViewMask(_ callback: @escaping (_ mask: UIView) -> Void) {
customBackgroundViewMask = callback
}
override var cardEdgeInsetsByDefault: UIEdgeInsets {
cardEdgeInsets ?? .init(top: 24, left: 24, bottom: 24, right: 24)
}
override var cardMaxWidthByDefault: CGFloat { cardMaxWidth ?? 500 }
override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? (AppContext.appBounds.height - 50) }
override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.5
}
override var animateDurationForBuildOutByDefault: CGFloat {
animateDurationForBuildOut ?? 0.5
}
override var cardCornerRadiusByDefault: CGFloat { cardCornerRadius ?? 32 }
}

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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) {

View File

@ -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

View File

@ -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()

View File

@ -0,0 +1,10 @@
//
// SheetViewModel.swift
//
//
// Created by xaoxuu on 2023/8/18.
//
import UIKit
@objc open class SheetViewModel: BaseViewModel {}

View File

@ -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 []

View File

@ -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)

View File

@ -7,51 +7,46 @@
import UIKit
public extension Toast {
public class ToastConfiguration: CommonConfiguration {
class Configuration: ProHUD.Configuration {
///
public var margin = CGFloat(8)
var customInfoStack: ((_ stack: StackView) -> Void)?
public func customInfoStack(handler: @escaping (_ stack: StackView) -> Void) {
customInfoStack = handler
}
///
public var lineSpace = CGFloat(4)
static var customShared: ((_ config: Configuration) -> Void)?
///
/// - Parameter callback:
public static func shared(_ callback: @escaping (_ config: Configuration) -> Void) {
customShared = callback
}
///
public var windowEdgeInset: CGFloat?
var windowEdgeInsetByDefault: CGFloat {
windowEdgeInset ?? 16
}
override var cardMaxWidthByDefault: CGFloat {
cardMaxWidth ?? 500
}
override var cardMaxHeightByDefault: CGFloat {
cardMaxHeight ?? (AppContext.appBounds.height / 3)
}
override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.8
}
override var animateDurationForBuildOutByDefault: CGFloat {
animateDurationForBuildIn ?? 0.8
}
///
public var margin = CGFloat(8)
var customInfoStack: ((_ stack: StackView) -> Void)?
public func customInfoStack(handler: @escaping (_ stack: StackView) -> Void) {
customInfoStack = handler
}
///
public var lineSpace = CGFloat(4)
static var customGlobalConfig: ((_ config: ToastConfiguration) -> Void)?
///
/// - Parameter callback:
public static func global(_ callback: @escaping (_ config: ToastConfiguration) -> Void) {
customGlobalConfig = callback
}
///
public var windowEdgeInset: CGFloat?
var windowEdgeInsetByDefault: CGFloat {
windowEdgeInset ?? 16
}
override var cardMaxWidthByDefault: CGFloat {
cardMaxWidth ?? 500
}
override var cardMaxHeightByDefault: CGFloat {
cardMaxHeight ?? (AppContext.appBounds.height / 3)
}
override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.8
}
override var animateDurationForBuildOutByDefault: CGFloat {
animateDurationForBuildIn ?? 0.8
}
}

View File

@ -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)
}
}

View File

@ -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 {
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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 { }

View File

@ -0,0 +1,12 @@
//
// ToastViewModel.swift
//
//
// Created by xaoxuu on 2023/8/18.
//
import UIKit
@objc open class ToastViewModel: BaseViewModel {
}

View File

@ -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 []