mirror of https://github.com/xaoxuu/ProHUD
代码重构
This commit is contained in:
parent
32f9af1ae6
commit
0574844a7f
|
@ -29,14 +29,15 @@ class AlertVC: ListVC {
|
|||
|
||||
list.add(title: "纯文字") { section in
|
||||
section.add(title: "只有一句话") {
|
||||
Alert(.message("只有一句话").duration(2))
|
||||
// Alert(.message("只有一句话").duration(2))
|
||||
// 可以简写成这样:
|
||||
Alert("只有一句话")
|
||||
}
|
||||
section.add(title: "标题 + 正文") {
|
||||
let title = "这是标题"
|
||||
let message = "这是正文,文字支持自动换行,可设置最小宽度和最大宽度。这个弹窗将会持续4秒。"
|
||||
Alert { alert in
|
||||
alert.vm = .title(title).message(message)
|
||||
alert.vm.duration = 4
|
||||
alert.vm = .title(title).message(message).duration(4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,9 +63,9 @@ class AlertVC: ListVC {
|
|||
}
|
||||
section.add(title: "图标 + 标题 + 正文") {
|
||||
Alert(.error) { alert in
|
||||
alert.vm.title = "加载失败"
|
||||
alert.vm.message = "请稍后重试"
|
||||
alert.vm.duration = 3
|
||||
alert.vm?.title = "加载失败"
|
||||
alert.vm?.message = "请稍后重试"
|
||||
alert.vm?.duration = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ class AlertVC: ListVC {
|
|||
alert.config.customButton { button in
|
||||
button.titleLabel?.font = .systemFont(ofSize: 15)
|
||||
}
|
||||
alert.vm.title = "你正在使用移动网络观看"
|
||||
alert.title = "你正在使用移动网络观看"
|
||||
alert.onViewDidLoad { vc in
|
||||
guard let alert = vc as? AlertTarget else {
|
||||
return
|
||||
|
@ -111,7 +112,7 @@ class AlertVC: ListVC {
|
|||
alert.config.customButton { button in
|
||||
button.titleLabel?.font = .systemFont(ofSize: 15)
|
||||
}
|
||||
alert.vm.message = "为了维护社区氛围,上麦用户需进行主播认证"
|
||||
alert.vm?.message = "为了维护社区氛围,上麦用户需进行主播认证"
|
||||
alert.onViewDidLoad { vc in
|
||||
guard let alert = vc as? AlertTarget else {
|
||||
return
|
||||
|
@ -141,7 +142,7 @@ class AlertVC: ListVC {
|
|||
alert.config.customButton { button in
|
||||
button.titleLabel?.font = .systemFont(ofSize: 15)
|
||||
}
|
||||
alert.vm.message = "本次消费需要你支付999软妹豆,确认支付吗?"
|
||||
alert.vm?.message = "本次消费需要你支付999软妹豆,确认支付吗?"
|
||||
alert.config.customActionStack { stack in
|
||||
stack.spacing = 0
|
||||
stack.axis = .vertical // 竖排按钮
|
||||
|
@ -187,15 +188,15 @@ class AlertVC: ListVC {
|
|||
|
||||
section.add(title: "只有一段文字 + 按钮") {
|
||||
Alert { alert in
|
||||
alert.vm.title = "只有一段文字"
|
||||
alert.title = "只有一段文字"
|
||||
alert.add(action: "取消", style: .gray)
|
||||
alert.add(action: "默认按钮")
|
||||
}
|
||||
}
|
||||
section.add(title: "标题 + 正文 + 按钮") {
|
||||
Alert { alert in
|
||||
alert.vm.title = "标题"
|
||||
alert.vm.message = "这是一段正文,长度超出最大宽度时会自动换行"
|
||||
alert.vm?.title = "标题"
|
||||
alert.vm?.message = "这是一段正文,长度超出最大宽度时会自动换行"
|
||||
alert.add(action: "取消", style: .gray)
|
||||
alert.add(action: "删除", style: .destructive) { alert in
|
||||
// 自定义了按钮事件之后,需要手动pop弹窗
|
||||
|
@ -229,8 +230,8 @@ class AlertVC: ListVC {
|
|||
}
|
||||
section.add(title: "确认删除") {
|
||||
Alert(.delete) { alert in
|
||||
alert.vm.title = "确认删除"
|
||||
alert.vm.message = "此操作无法撤销"
|
||||
alert.vm?.title = "确认删除"
|
||||
alert.vm?.message = "此操作无法撤销"
|
||||
alert.add(action: "取消", style: .gray)
|
||||
alert.add(action: "删除", style: .destructive)
|
||||
}
|
||||
|
@ -239,7 +240,7 @@ class AlertVC: ListVC {
|
|||
list.add(title: "控件管理") { section in
|
||||
section.add(title: "按钮增删改查") {
|
||||
Alert(.note) { alert in
|
||||
alert.vm.message = "可以动态增加、删除按钮"
|
||||
alert.vm?.message = "可以动态增加、删除按钮"
|
||||
alert.add(action: "在底部增加按钮", style: .filled(color: .systemGreen)) { alert in
|
||||
alert.add(action: "哈哈1", identifier: "haha1")
|
||||
}
|
||||
|
@ -265,36 +266,34 @@ class AlertVC: ListVC {
|
|||
}
|
||||
}
|
||||
section.add(title: "更新文字") {
|
||||
Alert(.note) { alert in
|
||||
alert.vm.message = "可以动态增加、删除、更新文字"
|
||||
Alert(.note.message("可以动态增加、删除、更新文字")) { alert in
|
||||
alert.add(action: "增加标题") { alert in
|
||||
alert.vm.title = "这是标题"
|
||||
alert.vm?.title = "这是标题"
|
||||
alert.reloadTextStack()
|
||||
}
|
||||
alert.add(action: "增加正文") { alert in
|
||||
alert.vm.message = "可以动态增加、删除、更新文字"
|
||||
alert.vm?.message = "可以动态增加、删除、更新文字"
|
||||
alert.reloadTextStack()
|
||||
}
|
||||
alert.add(action: "删除标题", style: .destructive) { alert in
|
||||
alert.vm.title = nil
|
||||
alert.vm?.title = nil
|
||||
alert.reloadTextStack()
|
||||
}
|
||||
alert.add(action: "删除正文", style: .destructive) { alert in
|
||||
alert.vm.message = nil
|
||||
alert.vm?.message = nil
|
||||
alert.reloadTextStack()
|
||||
}
|
||||
alert.add(action: "取消", style: .gray)
|
||||
}
|
||||
}
|
||||
section.add(title: "在弹出过程中增加元素") {
|
||||
Alert(.loading) { alert in
|
||||
alert.vm.title = "在弹出过程中增加元素"
|
||||
Alert(.loading.title("在弹出过程中增加元素")) { alert in
|
||||
alert.add(action: "OK", style: .gray)
|
||||
alert.onViewWillAppear { vc in
|
||||
guard let alert = vc as? AlertTarget else {
|
||||
return
|
||||
}
|
||||
alert.vm.message = "这是一段后增加的文字\n动画效果会有细微差别"
|
||||
alert.vm?.message = "这是一段后增加的文字\n动画效果会有细微差别"
|
||||
alert.reloadTextStack()
|
||||
}
|
||||
}
|
||||
|
@ -304,8 +303,8 @@ class AlertVC: ListVC {
|
|||
section.add(title: "多层级弹窗") {
|
||||
func f(i: Int) {
|
||||
Alert { alert in
|
||||
alert.vm.title = "第\(i)次弹"
|
||||
alert.vm.message = "每次都是一个新的实例覆盖在上一个弹窗上面,而背景不会叠加变深。"
|
||||
alert.vm?.title = "第\(i)次弹"
|
||||
alert.vm?.message = "每次都是一个新的实例覆盖在上一个弹窗上面,而背景不会叠加变深。"
|
||||
alert.add(action: "取消", style: .gray)
|
||||
alert.add(action: "增加一个") { alert in
|
||||
f(i: i + 1)
|
||||
|
@ -357,7 +356,7 @@ class AlertVC: ListVC {
|
|||
list.add(title: "自定义视图") { section in
|
||||
section.add(title: "自定义控件") {
|
||||
Alert { alert in
|
||||
alert.vm.title = "自定义控件"
|
||||
alert.title = "自定义控件"
|
||||
// 图片
|
||||
let imgv = UIImageView(image: UIImage(named: "landscape"))
|
||||
imgv.contentMode = .scaleAspectFill
|
||||
|
|
|
@ -17,12 +17,15 @@ class CapsuleVC: ListVC {
|
|||
header.detailLabel.text = "状态胶囊控件,用于状态显示,一个主程序窗口每个位置(上中下)各自最多只有一个状态胶囊实例。"
|
||||
|
||||
CapsuleConfiguration.global { config in
|
||||
config.defaultDuration = 3 // 默认的持续时间
|
||||
// config.cardCornerRadius = .infinity // 设置一个较大的数字就会变成胶囊形状
|
||||
}
|
||||
list.add(title: "默认布局:纯文字") { section in
|
||||
section.add(title: "一条简短的消息") {
|
||||
// 设置vm或者handler都会自动push,这里测试传入vm:
|
||||
Capsule(.message("一条简短消息"))
|
||||
// Capsule(.message("一条简短消息"))
|
||||
// 如果只有一条文字信息,可以直接传字符串:
|
||||
Capsule("一条简短消息")
|
||||
}
|
||||
section.add(title: "一条稍微长一点的消息") {
|
||||
// 设置vm或者handler都会自动push,这里测试传入handler:
|
||||
|
@ -31,12 +34,14 @@ class CapsuleVC: ListVC {
|
|||
capsule.vm = .message("一条稍微长一点的消息")
|
||||
}
|
||||
}
|
||||
section.add(title: "(默认)状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") {
|
||||
section.add(title: "延迟显示") {
|
||||
// 也可以创建一个空白实例,在需要的时候再push
|
||||
let obj = Capsule().target
|
||||
obj.vm = .message("状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。")
|
||||
// ... 在需要的时候手动push
|
||||
obj.push()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
obj.push()
|
||||
}
|
||||
}
|
||||
section.add(title: "(限制1行)状态胶囊控件,用于状态显示,一个主程序窗口只有一个状态胶囊实例。") {
|
||||
// 同时设置vm和handler也可以
|
||||
|
@ -146,7 +151,7 @@ class CapsuleVC: ListVC {
|
|||
|
||||
}
|
||||
|
||||
extension CapsuleTarget.ViewModel {
|
||||
extension CapsuleViewModel {
|
||||
|
||||
static func info(_ text: String?) -> Self {
|
||||
.init()
|
||||
|
|
|
@ -25,7 +25,7 @@ class SheetVC: ListVC {
|
|||
sheet.add(spacing: 24)
|
||||
sheet.add(action: "确认", style: .destructive) { sheet in
|
||||
Alert(.confirm) { alert in
|
||||
alert.vm.title = "处理点击事件"
|
||||
alert.title = "处理点击事件"
|
||||
alert.add(action: "我知道了")
|
||||
}
|
||||
}
|
||||
|
@ -74,12 +74,11 @@ class SheetVC: ListVC {
|
|||
sheet.add(action: "确认")
|
||||
sheet.add(action: "取消", style: .gray)
|
||||
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
|
||||
.title("点击了背景")
|
||||
.message("点击背景将不会dismiss,必须在下方做出选择才能关掉")
|
||||
.duration(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,10 @@ class TestToastTarget: ToastTarget {
|
|||
}
|
||||
}
|
||||
|
||||
typealias TestToast = HUDProvider<ToastViewModel, TestToastTarget>
|
||||
//typealias TestToast = HUDProvider<ToastViewModel, TestToastTarget>
|
||||
class TestToast: ToastProvider {
|
||||
typealias Target = TestToastTarget
|
||||
}
|
||||
|
||||
class ToastVC: ListVC {
|
||||
|
||||
|
@ -54,6 +57,7 @@ class ToastVC: ListVC {
|
|||
header.detailLabel.text = message
|
||||
|
||||
ToastConfiguration.global { config in
|
||||
config.defaultDuration = 5
|
||||
config.contentViewMask { mask in
|
||||
mask.backgroundColor = .clear
|
||||
mask.effect = UIBlurEffect(style: .systemChromeMaterial)
|
||||
|
@ -68,7 +72,9 @@ class ToastVC: ListVC {
|
|||
TestToast(.title(title).message(message))
|
||||
}
|
||||
section.add(title: "一段长文本") {
|
||||
Toast(.message(message))
|
||||
// Toast(.message(message))
|
||||
// 可以简写成这样:
|
||||
Toast(message)
|
||||
}
|
||||
section.add(title: "图标 + 标题 + 正文") {
|
||||
let s1 = "笑容正在加载"
|
||||
|
@ -80,8 +86,10 @@ class ToastVC: ListVC {
|
|||
toast.update(progress: percent)
|
||||
} completion: {
|
||||
toast.update { toast in
|
||||
toast.vm = .success(5).title("加载成功").message("这条通知5s后消失")
|
||||
toast.vm.icon = UIImage(named: "twemoji")
|
||||
toast.vm = .success(5)
|
||||
.title("加载成功")
|
||||
.message("这条通知5s后消失")
|
||||
.icon(.init(named: "twemoji"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,16 +127,15 @@ class ToastVC: ListVC {
|
|||
section.add(title: "增加按钮") {
|
||||
let title = "您收到了一条好友申请"
|
||||
let message = "丹妮莉丝·坦格利安申请添加您为好友,是否同意?"
|
||||
Toast(.title(title).message(message)) { toast in
|
||||
Toast(.title(title).message(message).icon(.init(named: "avatar"))) { toast in
|
||||
toast.isRemovable = false
|
||||
toast.vm.icon = UIImage(named: "avatar")
|
||||
toast.imageView.layer.masksToBounds = true
|
||||
toast.imageView.layer.cornerRadius = toast.config.iconSize.width / 2
|
||||
toast.add(action: "拒绝", style: .destructive) { toast in
|
||||
Alert.lazyPush(identifier: "Dracarys") { alert in
|
||||
alert.vm = .message("Dracarys")
|
||||
alert.vm.icon = UIImage(inProHUD: "prohud.windmill")
|
||||
alert.vm.rotation = .init(repeatCount: .infinity)
|
||||
.icon(UIImage(inProHUD: "prohud.windmill"))
|
||||
.rotation(.init(repeatCount: .infinity))
|
||||
alert.config.enableShadow = false
|
||||
alert.config.contentViewMask { mask in
|
||||
mask.effect = .none
|
||||
|
@ -217,7 +224,7 @@ class ToastVC: ListVC {
|
|||
}
|
||||
section.add(title: "修改左右外边距") {
|
||||
Toast(.message("这条toast的左右外边距经过自定义设置,与其它的有所不同。")) { toast in
|
||||
toast.config.windowEdgeInset = 8
|
||||
toast.config.marginX = 32
|
||||
toast.config.cardCornerRadius = 24
|
||||
}
|
||||
}
|
||||
|
@ -306,7 +313,7 @@ class ToastVC: ListVC {
|
|||
|
||||
section.add(title: "卡片背景样式") {
|
||||
Toast { toast in
|
||||
toast.vm.title = "卡片背景样式"
|
||||
toast.title = "卡片背景样式"
|
||||
toast.add(action: "浅色毛玻璃") { toast in
|
||||
toast.contentMaskView.effect = UIBlurEffect(style: .light)
|
||||
toast.contentMaskView.backgroundColor = .clear
|
||||
|
@ -327,7 +334,7 @@ class ToastVC: ListVC {
|
|||
func foo() {
|
||||
Toast { toast in
|
||||
toast.title = "共享配置"
|
||||
toast.vm.message = "建议在App启动后进行通用配置设置,所有实例都会先拉取通用配置为默认值,修改这些配置会影响到所有实例。"
|
||||
toast.vm?.message = "建议在App启动后进行通用配置设置,所有实例都会先拉取通用配置为默认值,修改这些配置会影响到所有实例。"
|
||||
toast.add(action: "默认", style: .gray) { toast in
|
||||
ToastConfiguration.global { config in
|
||||
config.customTitleLabel { titleLabel in
|
||||
|
@ -359,7 +366,7 @@ class ToastVC: ListVC {
|
|||
|
||||
fileprivate func testAlert() {
|
||||
Alert { alert in
|
||||
alert.vm.title = "处理点击事件"
|
||||
alert.title = "处理点击事件"
|
||||
alert.add(action: "我知道了", style: .destructive)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ extension AlertTarget: DefaultLayout {
|
|||
if self.cfg.customReloadData?(self) == true {
|
||||
return
|
||||
}
|
||||
view.tintColor = vm.tintColor ?? config.tintColor
|
||||
view.tintColor = vm?.tintColor ?? config.tintColor
|
||||
let isFirstLayout: Bool
|
||||
if contentView.superview == nil {
|
||||
isFirstLayout = animated
|
||||
|
@ -126,7 +126,7 @@ extension AlertTarget: DefaultLayout {
|
|||
|
||||
func updateTimeoutDuration() {
|
||||
// 设置持续时间
|
||||
vm.timeoutHandler = DispatchWorkItem(block: { [weak self] in
|
||||
vm?.timeoutHandler = DispatchWorkItem(block: { [weak self] in
|
||||
self?.pop()
|
||||
})
|
||||
}
|
||||
|
@ -144,9 +144,9 @@ extension AlertTarget {
|
|||
// 移除进度
|
||||
progressView?.removeFromSuperview()
|
||||
|
||||
if vm.icon != nil || vm.iconURL != nil {
|
||||
imageView.image = vm.icon
|
||||
if let iconURL = vm.iconURL {
|
||||
if vm?.icon != nil || vm?.iconURL != nil {
|
||||
imageView.image = vm?.icon
|
||||
if let iconURL = vm?.iconURL {
|
||||
config.customWebImage?(imageView, iconURL)
|
||||
}
|
||||
if imageView.superview == nil {
|
||||
|
@ -159,7 +159,7 @@ extension AlertTarget {
|
|||
mk.height.equalTo(config.iconSize.height)
|
||||
}
|
||||
}
|
||||
if let rotation = vm.rotation {
|
||||
if let rotation = vm?.rotation {
|
||||
startRotate(rotation)
|
||||
}
|
||||
} else {
|
||||
|
@ -171,8 +171,8 @@ extension AlertTarget {
|
|||
|
||||
}
|
||||
func setupTextStack() {
|
||||
let titleCount = vm.title?.count ?? 0
|
||||
let bodyCount = vm.message?.count ?? 0
|
||||
let titleCount = vm?.title?.count ?? 0
|
||||
let bodyCount = vm?.message?.count ?? 0
|
||||
if titleCount > 0 || bodyCount > 0 {
|
||||
if textStack.superview != contentStack {
|
||||
if let index = contentStack.arrangedSubviews.firstIndex(of: imageView) {
|
||||
|
@ -189,7 +189,7 @@ extension AlertTarget {
|
|||
}
|
||||
}
|
||||
if titleCount > 0 {
|
||||
titleLabel.text = vm.title
|
||||
titleLabel.text = vm?.title
|
||||
if titleLabel.superview != textStack {
|
||||
textStack.insertArrangedSubview(titleLabel, at: 0)
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ extension AlertTarget {
|
|||
titleLabel.removeFromSuperview()
|
||||
}
|
||||
if bodyCount > 0 {
|
||||
bodyLabel.text = vm.message
|
||||
bodyLabel.text = vm?.message
|
||||
if bodyLabel.superview != textStack {
|
||||
textStack.addArrangedSubview(bodyLabel)
|
||||
}
|
||||
|
|
|
@ -8,13 +8,30 @@
|
|||
import UIKit
|
||||
|
||||
open class AlertProvider: HUDProvider<AlertViewModel, AlertTarget> {
|
||||
@discardableResult public required init(_ vm: ViewModel?, initializer: ((_ alert: Target) -> Void)?) {
|
||||
super.init(vm, initializer: initializer)
|
||||
|
||||
public typealias ViewModel = AlertViewModel
|
||||
public typealias Target = AlertTarget
|
||||
|
||||
@discardableResult @objc public required init(initializer: ((_ alert: Target) -> Void)?) {
|
||||
super.init(initializer: initializer)
|
||||
}
|
||||
|
||||
@discardableResult public required convenience init(initializer: ((_ alert: Target) -> Void)?) {
|
||||
self.init(nil, initializer: initializer)
|
||||
@discardableResult public convenience init(_ vm: ViewModel, initializer: ((_ alert: Target) -> Void)?) {
|
||||
self.init { alert in
|
||||
alert.vm = vm
|
||||
initializer?(alert)
|
||||
}
|
||||
}
|
||||
/// 根据ViewModel创建一个Target并显示
|
||||
/// - Parameter vm: 数据模型
|
||||
@discardableResult public convenience init(_ vm: ViewModel) {
|
||||
self.init(vm, initializer: nil)
|
||||
}
|
||||
|
||||
@discardableResult @objc public convenience init(_ text: String, duration: TimeInterval = 3) {
|
||||
self.init(.message(text).duration(duration), initializer: nil)
|
||||
}
|
||||
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
/// - Parameters:
|
||||
|
|
|
@ -74,11 +74,15 @@ open class AlertTarget: BaseController, HUDTargetType {
|
|||
}()
|
||||
|
||||
/// 视图模型
|
||||
@objc public var vm: AlertViewModel = .init()
|
||||
@objc public var vm: AlertViewModel?
|
||||
|
||||
public override var title: String? {
|
||||
didSet {
|
||||
vm.title = title
|
||||
if let vm = vm {
|
||||
vm.title = title
|
||||
} else {
|
||||
vm = .title(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@ public class CapsuleConfiguration: CommonConfiguration {
|
|||
customGlobalConfig = callback
|
||||
}
|
||||
|
||||
/// 默认的持续时间
|
||||
public var defaultDuration: TimeInterval = 3
|
||||
|
||||
override var cardCornerRadiusByDefault: CGFloat {
|
||||
cardCornerRadius ?? 16
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ extension CapsuleTarget: DefaultLayout {
|
|||
return
|
||||
}
|
||||
|
||||
view.tintColor = vm.tintColor ?? config.tintColor
|
||||
view.tintColor = vm?.tintColor ?? config.tintColor
|
||||
|
||||
// content
|
||||
loadContentViewIfNeeded()
|
||||
|
@ -29,7 +29,7 @@ extension CapsuleTarget: DefaultLayout {
|
|||
|
||||
// text
|
||||
textLabel.removeFromSuperview()
|
||||
var text = [vm.title ?? "", vm.message ?? ""].filter({ $0.count > 0 }).joined(separator: " ")
|
||||
var text = [vm?.title ?? "", vm?.message ?? ""].filter({ $0.count > 0 }).joined(separator: " ")
|
||||
if text.count > 0 {
|
||||
contentStack.addArrangedSubview(textLabel)
|
||||
textLabel.snp.makeConstraints { make in
|
||||
|
@ -81,11 +81,11 @@ extension CapsuleTarget: DefaultLayout {
|
|||
|
||||
private func updateTimeoutDuration() {
|
||||
// 为空时使用默认规则
|
||||
if vm.duration == nil {
|
||||
vm.duration = 3
|
||||
if vm?.duration == nil {
|
||||
vm?.duration = config.defaultDuration
|
||||
}
|
||||
// 设置持续时间
|
||||
vm.timeoutHandler = DispatchWorkItem(block: { [weak self] in
|
||||
vm?.timeoutHandler = DispatchWorkItem(block: { [weak self] in
|
||||
self?.pop()
|
||||
})
|
||||
}
|
||||
|
@ -99,16 +99,16 @@ extension CapsuleTarget: DefaultLayout {
|
|||
// 移除进度
|
||||
progressView?.removeFromSuperview()
|
||||
|
||||
if vm.icon == nil && vm.iconURL == nil {
|
||||
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 {
|
||||
imageView.image = vm?.icon
|
||||
if let iconURL = vm?.iconURL {
|
||||
config.customWebImage?(imageView, iconURL)
|
||||
}
|
||||
if let rotation = vm.rotation {
|
||||
if let rotation = vm?.rotation {
|
||||
startRotate(rotation)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ extension CapsuleTarget {
|
|||
guard CapsuleConfiguration.isEnabled else { return }
|
||||
let isNew: Bool
|
||||
let window: CapsuleWindow
|
||||
let position = vm.position
|
||||
let position = vm?.position ?? .top
|
||||
|
||||
if let w = AppContext.current?.capsuleWindows[position] {
|
||||
isNew = false
|
||||
|
@ -30,10 +30,10 @@ extension CapsuleTarget {
|
|||
|
||||
// 应用到frame
|
||||
let newFrame: CGRect
|
||||
switch vm.position {
|
||||
case .top:
|
||||
let topLayoutMargins = AppContext.appWindow?.layoutMargins.top ?? 8
|
||||
let y = max(topLayoutMargins - 8, 8)
|
||||
switch vm?.position {
|
||||
case .top, .none:
|
||||
let topLayoutMargins = AppContext.appWindow?.safeAreaInsets.top ?? 8
|
||||
let y = max(topLayoutMargins, 8)
|
||||
newFrame = .init(x: (AppContext.appBounds.width - size.width) / 2, y: y, width: size.width, height: size.height)
|
||||
case .middle:
|
||||
newFrame = .init(x: (AppContext.appBounds.width - size.width) / 2, y: (AppContext.appBounds.height - size.height) / 2 - 20, width: size.width, height: size.height)
|
||||
|
@ -60,6 +60,10 @@ extension CapsuleTarget {
|
|||
AppContext.capsuleWindows[s]?[position] = window
|
||||
}
|
||||
navEvents[.onViewWillAppear]?(self)
|
||||
|
||||
// 更新toast防止重叠
|
||||
ToastWindow.updateToastWindowsLayout()
|
||||
|
||||
if isNew {
|
||||
window.isHidden = false
|
||||
func completion() {
|
||||
|
@ -116,8 +120,11 @@ extension CapsuleTarget {
|
|||
|
||||
@objc open func pop() {
|
||||
guard let window = attachedWindow, let windowScene = windowScene else { return }
|
||||
AppContext.capsuleWindows[windowScene]?[vm.position] = nil
|
||||
AppContext.capsuleWindows[windowScene]?[vm?.position ?? .top] = nil
|
||||
navEvents[.onViewWillDisappear]?(self)
|
||||
// 更新toast防止重叠
|
||||
ToastWindow.updateToastWindowsLayout()
|
||||
|
||||
func completion() {
|
||||
window.isHidden = true
|
||||
window.transform = .identity
|
||||
|
@ -128,8 +135,8 @@ extension CapsuleTarget {
|
|||
} else {
|
||||
let duration = config.animateDurationForBuildOutByDefault
|
||||
let oldFrame = window.frame
|
||||
switch vm.position {
|
||||
case .top:
|
||||
switch vm?.position {
|
||||
case .top, .none:
|
||||
UIView.animateEaseOut(duration: duration) {
|
||||
window.transform = .init(translationX: 0, y: -oldFrame.maxY - 20)
|
||||
} completion: { done in
|
||||
|
|
|
@ -7,20 +7,37 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
open class CapsuleProvider: HUDProvider<CapsuleTarget.ViewModel, CapsuleTarget> {
|
||||
open class CapsuleProvider: HUDProvider<CapsuleViewModel, CapsuleTarget> {
|
||||
|
||||
public typealias ViewModel = CapsuleViewModel
|
||||
public typealias Target = CapsuleTarget
|
||||
|
||||
@discardableResult @objc public required init(initializer: ((_ capsule: Target) -> Void)?) {
|
||||
super.init(initializer: initializer)
|
||||
}
|
||||
|
||||
/// 根据ViewModel和自定义的初始化代码创建一个Target并显示
|
||||
/// - Parameters:
|
||||
/// - vm: 数据模型
|
||||
/// - initializer: 初始化代码
|
||||
@discardableResult public required init(_ vm: ViewModel?, initializer: ((_ capsule: Target) -> Void)?) {
|
||||
super.init(vm, initializer: initializer)
|
||||
@discardableResult public convenience init(_ vm: ViewModel, initializer: ((_ capsule: Target) -> Void)?) {
|
||||
self.init { capsule in
|
||||
capsule.vm = vm
|
||||
initializer?(capsule)
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult public required convenience init(initializer: ((_ capsule: Target) -> Void)?) {
|
||||
self.init(nil, initializer: initializer)
|
||||
/// 根据ViewModel创建一个Target并显示
|
||||
/// - Parameter vm: 数据模型
|
||||
@discardableResult public convenience init(_ vm: ViewModel) {
|
||||
self.init(vm, initializer: nil)
|
||||
}
|
||||
|
||||
@discardableResult public convenience init(_ text: String) {
|
||||
self.init(.message(text), initializer: nil)
|
||||
}
|
||||
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
/// - Parameters:
|
||||
/// - identifier: 实例唯一标识符(如果为空,则以代码位置为唯一标识符)
|
||||
|
|
|
@ -46,7 +46,17 @@ open class CapsuleTarget: BaseController, HUDTargetType {
|
|||
return lb
|
||||
}()
|
||||
|
||||
public var vm: CapsuleViewModel = .init()
|
||||
public var vm: CapsuleViewModel?
|
||||
|
||||
public override var title: String? {
|
||||
didSet {
|
||||
if let vm = vm {
|
||||
vm.title = title
|
||||
} else {
|
||||
vm = .title(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var tapActionCallback: ((_ capsule: CapsuleTarget) -> Void)?
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ class CapsuleWindow: Window {
|
|||
self.capsule = capsule
|
||||
super.init(frame: .zero)
|
||||
windowScene = AppContext.windowScene
|
||||
switch capsule.vm.position {
|
||||
case .top:
|
||||
switch capsule.vm?.position {
|
||||
case .top, .none:
|
||||
// 略高于toast
|
||||
windowLevel = .phCapsuleTop
|
||||
case .middle:
|
||||
|
|
|
@ -12,11 +12,9 @@ public protocol HUDProviderType {
|
|||
associatedtype ViewModel = HUDViewModelType
|
||||
associatedtype Target = HUDTargetType
|
||||
|
||||
/// 根据ViewModel和自定义的初始化代码创建一个Target并显示
|
||||
/// - Parameters:
|
||||
/// - vm: 数据模型
|
||||
/// - initializer: 初始化代码
|
||||
@discardableResult init(_ vm: ViewModel?, initializer: ((_ target: Target) -> Void)?)
|
||||
/// 根据自定义的初始化代码创建一个Target并显示
|
||||
/// - Parameter initializer: 初始化代码
|
||||
@discardableResult init(initializer: ((_ target: Target) -> Void)?)
|
||||
|
||||
}
|
||||
|
||||
|
@ -25,39 +23,22 @@ open class HUDProvider<ViewModel: HUDViewModelType, Target: HUDTargetType>: HUDP
|
|||
/// HUD实例
|
||||
public var target: Target
|
||||
|
||||
/// 根据ViewModel和自定义的初始化代码创建一个Target并显示
|
||||
/// - Parameters:
|
||||
/// - vm: 数据模型
|
||||
/// - initializer: 初始化代码
|
||||
@discardableResult public required init(_ vm: ViewModel?, initializer: ((_ target: Target) -> Void)?) {
|
||||
/// 根据自定义的初始化代码创建一个Target并显示
|
||||
/// - Parameter initializer: 初始化代码
|
||||
@discardableResult public required init(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 {
|
||||
if (t.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)
|
||||
self.init(initializer: nil)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,6 @@ import UIKit
|
|||
|
||||
public protocol HUDTargetType: HUDControllerType {
|
||||
associatedtype ViewModel = HUDViewModelType
|
||||
var vm: ViewModel { get set }
|
||||
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: [CapsuleTarget.ViewModel.Position: CapsuleWindow]] = [:]
|
||||
static var capsuleWindows: [UIWindowScene: [CapsuleViewModel.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: [CapsuleTarget.ViewModel.Position: CapsuleWindow] {
|
||||
var capsuleWindows: [CapsuleViewModel.Position: CapsuleWindow] {
|
||||
Self.capsuleWindows[windowScene] ?? [:]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ extension SheetTarget: DefaultLayout {
|
|||
if self.cfg.customReloadData?(self) == true {
|
||||
return
|
||||
}
|
||||
view.tintColor = vm.tintColor ?? config.tintColor
|
||||
view.tintColor = vm?.tintColor ?? config.tintColor
|
||||
// background
|
||||
if backgroundView.superview == nil {
|
||||
view.insertSubview(backgroundView, at: 0)
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
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)
|
||||
public typealias ViewModel = SheetViewModel
|
||||
public typealias Target = SheetTarget
|
||||
|
||||
@discardableResult @objc public required init(initializer: ((_ sheet: Target) -> Void)?) {
|
||||
super.init(initializer: initializer)
|
||||
}
|
||||
|
||||
/// 如果不存在就创建并弹出一个HUD实例,如果存在就更新实例
|
||||
|
|
|
@ -41,7 +41,7 @@ open class SheetTarget: BaseController, HUDTargetType {
|
|||
}
|
||||
}
|
||||
|
||||
public var vm: SheetViewModel = .init()
|
||||
public var vm: SheetViewModel? = nil
|
||||
|
||||
required public override init() {
|
||||
super.init()
|
||||
|
|
|
@ -9,16 +9,6 @@ import UIKit
|
|||
|
||||
public class ToastConfiguration: CommonConfiguration {
|
||||
|
||||
/// 元素与元素之间的距离
|
||||
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)?
|
||||
|
||||
/// 全局共享配置(只能设置一次,影响所有实例)
|
||||
|
@ -27,11 +17,21 @@ public class ToastConfiguration: CommonConfiguration {
|
|||
customGlobalConfig = callback
|
||||
}
|
||||
|
||||
/// 距离窗口左右的间距
|
||||
public var windowEdgeInset: CGFloat?
|
||||
var windowEdgeInsetByDefault: CGFloat {
|
||||
windowEdgeInset ?? 16
|
||||
/// 默认的持续时间
|
||||
public var defaultDuration: TimeInterval = 10
|
||||
|
||||
/// 元素与左右屏幕之间的距离(在没有达到最大宽度的情况下)
|
||||
public var marginX = CGFloat(8)
|
||||
|
||||
/// 元素与元素之间的纵向距离
|
||||
public var marginY = CGFloat(8)
|
||||
|
||||
var customInfoStack: ((_ stack: StackView) -> Void)?
|
||||
public func customInfoStack(handler: @escaping (_ stack: StackView) -> Void) {
|
||||
customInfoStack = handler
|
||||
}
|
||||
/// 行间距
|
||||
public var lineSpace = CGFloat(4)
|
||||
|
||||
override var cardMaxWidthByDefault: CGFloat {
|
||||
cardMaxWidth ?? 500
|
||||
|
|
|
@ -17,7 +17,7 @@ extension ToastTarget: DefaultLayout {
|
|||
if self.cfg.customReloadData?(self) == true {
|
||||
return
|
||||
}
|
||||
view.tintColor = vm.tintColor ?? config.tintColor
|
||||
view.tintColor = vm?.tintColor ?? config.tintColor
|
||||
loadContentViewIfNeeded()
|
||||
loadContentMaskViewIfNeeded()
|
||||
guard customView == nil else {
|
||||
|
@ -26,7 +26,7 @@ extension ToastTarget: DefaultLayout {
|
|||
}
|
||||
return
|
||||
}
|
||||
if vm.icon != nil || vm.iconURL != nil {
|
||||
if vm?.icon != nil || vm?.iconURL != nil {
|
||||
if imageView.superview == nil {
|
||||
infoStack.insertArrangedSubview(imageView, at: 0)
|
||||
imageView.snp.makeConstraints { make in
|
||||
|
@ -42,8 +42,8 @@ extension ToastTarget: DefaultLayout {
|
|||
if textStack.superview == nil {
|
||||
infoStack.addArrangedSubview(textStack)
|
||||
}
|
||||
let titleCount = vm.title?.count ?? 0
|
||||
let bodyCount = vm.message?.count ?? 0
|
||||
let titleCount = vm?.title?.count ?? 0
|
||||
let bodyCount = vm?.message?.count ?? 0
|
||||
if titleCount > 0 {
|
||||
textStack.insertArrangedSubview(titleLabel, at: 0)
|
||||
if bodyCount > 0 {
|
||||
|
@ -79,14 +79,12 @@ extension ToastTarget: DefaultLayout {
|
|||
bodyLabel.removeFromSuperview()
|
||||
}
|
||||
// 设置数据
|
||||
titleLabel.text = vm.title
|
||||
bodyLabel.text = vm.message
|
||||
titleLabel.text = vm?.title
|
||||
bodyLabel.text = vm?.message
|
||||
view.layoutIfNeeded()
|
||||
|
||||
// 设置持续时间
|
||||
vm.timeoutHandler = DispatchWorkItem(block: { [weak self] in
|
||||
self?.pop()
|
||||
})
|
||||
updateTimeoutDuration()
|
||||
|
||||
setupImageView()
|
||||
|
||||
|
@ -132,6 +130,17 @@ extension ToastTarget {
|
|||
}
|
||||
}
|
||||
|
||||
private func updateTimeoutDuration() {
|
||||
// 为空时使用默认规则
|
||||
if vm?.duration == nil {
|
||||
vm?.duration = config.defaultDuration
|
||||
}
|
||||
// 设置持续时间
|
||||
vm?.timeoutHandler = DispatchWorkItem(block: { [weak self] in
|
||||
self?.pop()
|
||||
})
|
||||
}
|
||||
|
||||
func setupImageView() {
|
||||
// 移除动画
|
||||
stopRotate(animateLayer)
|
||||
|
@ -141,11 +150,11 @@ extension ToastTarget {
|
|||
// 移除进度
|
||||
progressView?.removeFromSuperview()
|
||||
|
||||
imageView.image = vm.icon
|
||||
if let iconURL = vm.iconURL {
|
||||
imageView.image = vm?.icon
|
||||
if let iconURL = vm?.iconURL {
|
||||
config.customWebImage?(imageView, iconURL)
|
||||
}
|
||||
if let rotation = vm.rotation {
|
||||
if let rotation = vm?.rotation {
|
||||
startRotate(rotation)
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ extension ToastTarget {
|
|||
|
||||
// frame
|
||||
let cardEdgeInsets = config.cardEdgeInsetsByDefault
|
||||
let width = CGFloat.minimum(AppContext.appBounds.width - config.windowEdgeInsetByDefault - config.windowEdgeInsetByDefault, config.cardMaxWidthByDefault)
|
||||
let width = CGFloat.minimum(AppContext.appBounds.width - config.marginX - config.marginX, config.cardMaxWidthByDefault)
|
||||
view.frame.size = CGSize(width: width, height: config.cardMaxHeightByDefault)
|
||||
titleLabel.sizeToFit()
|
||||
bodyLabel.sizeToFit()
|
||||
|
@ -92,7 +92,7 @@ extension ToastTarget {
|
|||
} else {
|
||||
consolePrint("‼️代码漏洞:已经没有toast了")
|
||||
}
|
||||
vm.duration = nil
|
||||
vm?.duration = nil
|
||||
setContextWindows(windows)
|
||||
UIView.animateEaseOut(duration: config.animateDurationForBuildOutByDefault) {
|
||||
window.transform = .init(translationX: 0, y: 0-20-window.maxY)
|
||||
|
@ -124,21 +124,34 @@ fileprivate var updateToastsLayoutWorkItem: DispatchWorkItem?
|
|||
fileprivate extension ToastWindow {
|
||||
|
||||
static func setToastWindowsLayout(windows: [ToastWindow]) {
|
||||
var windows: [Window] = windows
|
||||
if let win = AppContext.current?.capsuleWindows[.top] {
|
||||
windows.insert(win, at: 0)
|
||||
}
|
||||
for (i, window) in windows.enumerated() {
|
||||
let config = window.toast.config
|
||||
let margin: CGFloat
|
||||
if let window = window as? ToastWindow {
|
||||
margin = window.toast.config.marginY
|
||||
} else if let window = window as? CapsuleWindow {
|
||||
margin = window.safeAreaInsets.top
|
||||
} else {
|
||||
margin = 8
|
||||
}
|
||||
var y = window.frame.origin.y
|
||||
if i == 0 {
|
||||
let topLayoutMargins = AppContext.appWindow?.layoutMargins.top ?? config.margin
|
||||
y = max(topLayoutMargins - config.margin, config.margin)
|
||||
let topLayoutMargins = AppContext.appWindow?.safeAreaInsets.top ?? margin
|
||||
y = max(topLayoutMargins, margin)
|
||||
} else {
|
||||
if i - 1 < windows.count && i > 0 {
|
||||
y = config.margin + windows[i-1].frame.maxY
|
||||
y = margin + windows[i-1].frame.maxY
|
||||
} else {
|
||||
y = config.margin
|
||||
y = margin
|
||||
}
|
||||
}
|
||||
window.maxY = y + window.frame.size.height
|
||||
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
|
||||
if let window = window as? ToastWindow {
|
||||
window.maxY = y + window.frame.size.height
|
||||
}
|
||||
UIView.animateEaseOut(duration: 0.68) {
|
||||
window.frame.origin.y = y
|
||||
}
|
||||
}
|
||||
|
@ -154,3 +167,10 @@ fileprivate extension ToastWindow {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
extension ToastWindow {
|
||||
static func updateToastWindowsLayout() {
|
||||
let wins = AppContext.current?.toastWindows ?? []
|
||||
updateToastWindowsLayout(windows: wins)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,19 +8,37 @@
|
|||
import UIKit
|
||||
|
||||
open class ToastProvider: HUDProvider<ToastViewModel, ToastTarget> {
|
||||
@discardableResult public required init(_ vm: ViewModel?, initializer: ((_ toast: Target) -> Void)?) {
|
||||
super.init(vm, initializer: initializer)
|
||||
|
||||
public typealias ViewModel = ToastViewModel
|
||||
public typealias Target = ToastTarget
|
||||
|
||||
@discardableResult @objc public required init(initializer: ((_ toast: Target) -> Void)?) {
|
||||
super.init(initializer: initializer)
|
||||
}
|
||||
|
||||
@discardableResult public required convenience init(initializer: ((_ toast: Target) -> Void)?) {
|
||||
self.init(nil, initializer: initializer)
|
||||
@discardableResult public convenience init(_ vm: ViewModel, initializer: ((_ toast: Target) -> Void)?) {
|
||||
self.init { toast in
|
||||
toast.vm = vm
|
||||
initializer?(toast)
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据ViewModel创建一个Target并显示
|
||||
/// - Parameter vm: 数据模型
|
||||
@discardableResult public convenience init(_ vm: ViewModel) {
|
||||
self.init(vm, initializer: nil)
|
||||
}
|
||||
|
||||
@discardableResult @objc public convenience init(_ text: String) {
|
||||
self.init(.message(text), initializer: nil)
|
||||
}
|
||||
|
||||
|
||||
/// 如果不存在就创建并弹出一个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) {
|
||||
@objc 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)
|
||||
|
@ -35,7 +53,7 @@ open class ToastProvider: HUDProvider<ToastViewModel, ToastTarget> {
|
|||
/// 查找HUD实例
|
||||
/// - Parameter identifier: 唯一标识符
|
||||
/// - Returns: HUD实例
|
||||
@discardableResult public static func find(identifier: String, update handler: ((_ toast: ToastTarget) -> Void)? = nil) -> [ToastTarget] {
|
||||
@discardableResult @objc 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) })
|
||||
|
|
|
@ -84,14 +84,17 @@ open class ToastTarget: BaseController, HUDTargetType {
|
|||
public var isRemovable = true
|
||||
|
||||
/// 视图模型
|
||||
@objc public var vm = ToastViewModel()
|
||||
@objc public var vm: ToastViewModel?
|
||||
|
||||
private var tapActionCallback: ((_ toast: ToastTarget) -> Void)?
|
||||
|
||||
|
||||
public override var title: String? {
|
||||
didSet {
|
||||
vm.title = title
|
||||
if let vm = vm {
|
||||
vm.title = title
|
||||
} else {
|
||||
vm = .title(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +138,7 @@ fileprivate extension ToastTarget {
|
|||
/// 拖拽事件
|
||||
/// - Parameter sender: 手势
|
||||
@objc func _onPanGesture(_ sender: UIPanGestureRecognizer) {
|
||||
vm.timeoutTimer?.invalidate()
|
||||
vm?.timeoutTimer?.invalidate()
|
||||
let point = sender.translation(in: sender.view)
|
||||
window?.transform = .init(translationX: 0, y: point.y)
|
||||
if sender.state == .recognized {
|
||||
|
@ -150,8 +153,8 @@ fileprivate extension ToastTarget {
|
|||
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
|
||||
self.window?.transform = .identity
|
||||
} completion: { done in
|
||||
let d = self.vm.duration
|
||||
self.vm.duration = d
|
||||
let d = self.vm?.duration
|
||||
self.vm?.duration = d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue