From f68b0c3a9bbe36066b320b6c64e77791863ca2b2 Mon Sep 17 00:00:00 2001 From: xaoxuu Date: Mon, 21 Aug 2023 16:56:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcshareddata/xcschemes/PHDemo.xcscheme | 77 ++++++++++++ PHDemo/PHDemo/DemoCapsuleVC.swift | 51 ++++++-- PHDemo/PHDemo/DemoToastVC.swift | 4 +- Sources/ProHUD/Alert/AlertConfiguration.swift | 4 +- Sources/ProHUD/Alert/AlertDefaultLayout.swift | 7 -- Sources/ProHUD/Alert/AlertManager.swift | 16 ++- Sources/ProHUD/Alert/AlertWindow.swift | 2 +- .../ProHUD/Capsule/CapsuleConfiguration.swift | 4 +- .../ProHUD/Capsule/CapsuleDefaultLayout.swift | 11 -- Sources/ProHUD/Capsule/CapsuleManager.swift | 112 ++++++++++++------ Sources/ProHUD/Capsule/CapsuleProvider.swift | 4 +- Sources/ProHUD/Capsule/CapsuleViewModel.swift | 22 +++- Sources/ProHUD/Capsule/CapsuleWindow.swift | 2 +- .../Core/Controllers/BaseController.swift | 4 + Sources/ProHUD/Core/Protocols/Provider.swift | 4 +- Sources/ProHUD/Core/Utils/AppContext.swift | 4 + Sources/ProHUD/Core/Utils/ViewExts.swift | 8 +- Sources/ProHUD/Sheet/SheetConfiguration.swift | 4 +- Sources/ProHUD/Sheet/SheetDefaultLayout.swift | 10 +- Sources/ProHUD/Sheet/SheetManager.swift | 3 +- Sources/ProHUD/Sheet/SheetTarget.swift | 3 - Sources/ProHUD/Toast/ToastConfiguration.swift | 4 +- Sources/ProHUD/Toast/ToastDefaultLayout.swift | 11 -- Sources/ProHUD/Toast/ToastManager.swift | 22 +++- 24 files changed, 281 insertions(+), 112 deletions(-) create mode 100644 PHDemo/PHDemo.xcodeproj/xcshareddata/xcschemes/PHDemo.xcscheme diff --git a/PHDemo/PHDemo.xcodeproj/xcshareddata/xcschemes/PHDemo.xcscheme b/PHDemo/PHDemo.xcodeproj/xcshareddata/xcschemes/PHDemo.xcscheme new file mode 100644 index 0000000..f25f63e --- /dev/null +++ b/PHDemo/PHDemo.xcodeproj/xcshareddata/xcschemes/PHDemo.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PHDemo/PHDemo/DemoCapsuleVC.swift b/PHDemo/PHDemo/DemoCapsuleVC.swift index e8c8127..8bf9bc4 100644 --- a/PHDemo/PHDemo/DemoCapsuleVC.swift +++ b/PHDemo/PHDemo/DemoCapsuleVC.swift @@ -84,15 +84,15 @@ class DemoCapsuleVC: ListVC { } } - list.add(title: "不同位置、不同动画") { section in - section.add(title: "顶部,默认滑入") { - Capsule(.info("一条简短的消息")) + list.add(title: "不同位置、不同动画,队列推送") { section in + section.add(title: "顶部,默认动画") { + Capsule(.info("一条简短的消息").queuedPush(true).duration(1)) } - section.add(title: "中间,默认缩放") { - Capsule(.middle.info("一条简短的消息")) + section.add(title: "中间,默认动画") { + Capsule(.middle.queuedPush(true).info("一条简短的消息").duration(2)) } section.add(title: "中间,黑底白字,透明渐变") { - Capsule(.middle.info("一条简短的消息")) { capsule in + Capsule(.middle.queuedPush(true).info("一条简短的消息").duration(1)) { capsule in capsule.config.tintColor = .white capsule.config.cardCornerRadius = 8 capsule.config.contentViewMask { mask in @@ -119,7 +119,7 @@ class DemoCapsuleVC: ListVC { } } section.add(title: "底部,渐变背景,默认回弹滑入") { - Capsule(.bottom.enter("点击进入")) { capsule in + Capsule(.bottom.queuedPush(true).enter("点击进入").duration(1)) { capsule in capsule.config.tintColor = .white capsule.config.cardEdgeInsets = .init(top: 12, left: 20, bottom: 12, right: 20) capsule.config.customTextLabel { label in @@ -146,6 +146,25 @@ class DemoCapsuleVC: ListVC { } } } + list.add(title: "lazy push") { section in + section.add(title: "id:1, text:1") { + Capsule(.test1("111:111")) + } + section.add(title: "id:1, text:2") { + Capsule(.test1("111:222")) + } + section.add(title: "id:2, text:1") { + Capsule(.test2("222:111")) + } + section.add(title: "id:2, text:2") { + Capsule(.test2("222:222")) + } + + section.add(title: "id:2, text:2") { + Capsule(.test2("222:222")) + Capsule(.test2("222:111")) + } + } } } @@ -179,4 +198,22 @@ extension CapsuleViewModel { .message(text) } + static func test1(_ text: String) -> CapsuleViewModel { + .identifier("id:1") + .icon(.init(systemName: "video.circle.fill")) + .tintColor(.systemGreen) + .duration(1) + .queuedPush(true) + .message(text) + } + + static func test2(_ text: String) -> CapsuleViewModel { + .identifier("id:2") + .icon(.init(systemName: "mic.circle.fill")) + .tintColor(.systemOrange) + .duration(1) + .queuedPush(true) + .message(text) + } + } diff --git a/PHDemo/PHDemo/DemoToastVC.swift b/PHDemo/PHDemo/DemoToastVC.swift index e84f7e9..5b689a7 100644 --- a/PHDemo/PHDemo/DemoToastVC.swift +++ b/PHDemo/PHDemo/DemoToastVC.swift @@ -106,7 +106,7 @@ class DemoToastVC: ListVC { } } section.add(title: "图标 + 一段长文本") { - Toast(.note.message(message)) + Toast(.note.message(message).duration(1)) } section.add(title: "网络图标 + 一段文本") { Toast(.message("这是网络图标").icon(.init(string: "https://xaoxuu.com/assets/xaoxuu/avatar/rect-256@2x.png"))) @@ -167,7 +167,7 @@ class DemoToastVC: ListVC { section.add(title: "禁止手势移除") { let title = "这条消息很重要" let message = "向上滑动将不会移除消息,您必须手动处理,用于重要但非阻塞性的事件。(通过代码处理或者在点击事件处理)" - Toast(.warning.title(title).message(message)) { toast in + Toast(.warning.title(title).message(message).duration(.infinity)) { toast in toast.isRemovable = false toast.onTapped { toast in toast.pop() diff --git a/Sources/ProHUD/Alert/AlertConfiguration.swift b/Sources/ProHUD/Alert/AlertConfiguration.swift index a3595a1..c02e8a8 100644 --- a/Sources/ProHUD/Alert/AlertConfiguration.swift +++ b/Sources/ProHUD/Alert/AlertConfiguration.swift @@ -30,8 +30,8 @@ public class AlertConfiguration: CommonConfiguration { customBackgroundViewMask = callback } - override var animateDurationForBuildInByDefault: CGFloat { - animateDurationForBuildIn ?? 0.6 + override var animateDurationForBuildOutByDefault: CGFloat { + animateDurationForBuildOut ?? 0.2 } } diff --git a/Sources/ProHUD/Alert/AlertDefaultLayout.swift b/Sources/ProHUD/Alert/AlertDefaultLayout.swift index d0c3c2a..5b4032e 100644 --- a/Sources/ProHUD/Alert/AlertDefaultLayout.swift +++ b/Sources/ProHUD/Alert/AlertDefaultLayout.swift @@ -124,13 +124,6 @@ extension AlertTarget: DefaultLayout { } } - func updateTimeoutDuration() { - // 设置持续时间 - vm?.timeoutHandler = DispatchWorkItem(block: { [weak self] in - self?.pop() - }) - } - } extension AlertTarget { diff --git a/Sources/ProHUD/Alert/AlertManager.swift b/Sources/ProHUD/Alert/AlertManager.swift index c3c84d0..a356cda 100644 --- a/Sources/ProHUD/Alert/AlertManager.swift +++ b/Sources/ProHUD/Alert/AlertManager.swift @@ -16,7 +16,7 @@ extension AlertTarget { return } setDefaultAxis() - view.transform = .init(scaleX: 1.2, y: 1.2) + view.transform = .init(scaleX: 1.12, y: 1.12) view.alpha = 0 navEvents[.onViewWillAppear]?(self) window.vc.addChild(self) @@ -33,6 +33,7 @@ extension AlertTarget { window.backgroundView.alpha = 1 } completion: { done in self.navEvents[.onViewDidAppear]?(self) + self.updateTimeoutDuration() } window.alerts.append(self) AlertTarget.updateAlertsLayout(alerts: window.alerts) @@ -42,9 +43,9 @@ extension AlertTarget { navEvents[.onViewWillDisappear]?(self) AlertTarget.removeAlert(alert: self) let duration = config.animateDurationForBuildOut ?? config.animateDurationForBuildOutByDefault - UIView.animateEaseOut(duration: duration) { + UIView.animateLinear(duration: duration) { self.view.alpha = 0 - self.view.transform = .init(scaleX: 1.08, y: 1.08) + self.view.transform = .init(scaleX: 1.05, y: 1.05) } completion: { done in self.view.removeFromSuperview() self.removeFromParent() @@ -55,7 +56,7 @@ extension AlertTarget { let count = window.alerts.count if count == 0 { AppContext.alertWindow[windowScene] = nil - UIView.animateEaseOut(duration: duration) { + UIView.animateLinear(duration: duration) { window.backgroundView.alpha = 0 } completion: { done in // 这里设置一下window属性,会使window的生命周期被延长到此处,即动画执行过程中window不会被提前释放 @@ -74,6 +75,13 @@ extension AlertTarget { } } + func updateTimeoutDuration() { + // 设置持续时间 + vm?.timeoutHandler = DispatchWorkItem(block: { [weak self] in + self?.pop() + }) + } + } // MARK: - layout diff --git a/Sources/ProHUD/Alert/AlertWindow.swift b/Sources/ProHUD/Alert/AlertWindow.swift index ec2e6f0..bd09c7a 100644 --- a/Sources/ProHUD/Alert/AlertWindow.swift +++ b/Sources/ProHUD/Alert/AlertWindow.swift @@ -36,6 +36,6 @@ class AlertWindow: Window { extension AlertTarget { var attachedWindow: AlertWindow? { - view.window as? AlertWindow + view.window as? AlertWindow ?? AppContext.current?.alertWindow } } diff --git a/Sources/ProHUD/Capsule/CapsuleConfiguration.swift b/Sources/ProHUD/Capsule/CapsuleConfiguration.swift index 608e3a7..ff42e1e 100644 --- a/Sources/ProHUD/Capsule/CapsuleConfiguration.swift +++ b/Sources/ProHUD/Capsule/CapsuleConfiguration.swift @@ -35,11 +35,11 @@ public class CapsuleConfiguration: CommonConfiguration { override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? 120 } override var animateDurationForBuildInByDefault: CGFloat { - animateDurationForBuildIn ?? 0.8 + animateDurationForBuildIn ?? 0.64 } override var animateDurationForBuildOutByDefault: CGFloat { - animateDurationForBuildOut ?? 0.8 + animateDurationForBuildOut ?? 0.32 } var animateBuildIn: CustomAnimateHandler? diff --git a/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift b/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift index 05aa75b..bf0bc11 100644 --- a/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift +++ b/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift @@ -79,17 +79,6 @@ extension CapsuleTarget: DefaultLayout { } - private func updateTimeoutDuration() { - // 为空时使用默认规则 - if vm?.duration == nil { - vm?.duration = config.defaultDuration - } - // 设置持续时间 - vm?.timeoutHandler = DispatchWorkItem(block: { [weak self] in - self?.pop() - }) - } - private func setupImageView() { // 移除动画 stopRotate(animateLayer) diff --git a/Sources/ProHUD/Capsule/CapsuleManager.swift b/Sources/ProHUD/Capsule/CapsuleManager.swift index 55e9a1f..889dd42 100644 --- a/Sources/ProHUD/Capsule/CapsuleManager.swift +++ b/Sources/ProHUD/Capsule/CapsuleManager.swift @@ -11,17 +11,38 @@ extension CapsuleTarget { @objc open func push() { guard CapsuleConfiguration.isEnabled else { return } + guard let windowScene = preferredWindowScene ?? AppContext.windowScene else { return } + if windowScene != AppContext.windowScene { + AppContext.windowScene = windowScene + } + let isNew: Bool let window: CapsuleWindow let position = vm?.position ?? .top - if let w = AppContext.current?.capsuleWindows[position] { - isNew = false - window = w - } else { - window = CapsuleWindow(capsule: self) - isNew = true + if AppContext.capsuleWindows[windowScene] == nil { + AppContext.capsuleWindows[windowScene] = [:] } + var windows = AppContext.capsuleWindows[windowScene] ?? [:] + if let w = windows[position], w.isHidden == false { + // 此时同一位置已有capsule在显示 + if vm?.queuedPush == true { + // 加入队列 + self.preferredWindowScene = windowScene + AppContext.capsuleInQueue.append(self) + return + } else { + // 直接覆盖 + isNew = false + window = w + } + } else { + // 空闲状态下推送一个新的 + isNew = true + window = CapsuleWindow(capsule: self) + windows[position] = nil + } + // frame let cardEdgeInsetsByDefault = config.cardEdgeInsetsByDefault view.layoutIfNeeded() @@ -53,22 +74,22 @@ extension CapsuleTarget { view.layer.cornerRadiusWithContinuous = config.cardCornerRadiusByDefault window.rootViewController = self // 此时toast.view.frame.size会自动更新为window.frame.size - if let s = AppContext.windowScene { - if AppContext.capsuleWindows[s] == nil { - AppContext.capsuleWindows[s] = [:] - } - AppContext.capsuleWindows[s]?[position] = window - } + + AppContext.capsuleWindows[windowScene]?[position] = window + navEvents[.onViewWillAppear]?(self) - // 更新toast防止重叠 - ToastWindow.updateToastWindowsLayout() + if position == .top { + // 更新toast防止重叠 + ToastWindow.updateToastWindowsLayout() + } + func completion() { + self.navEvents[.onViewDidAppear]?(self) + self.updateTimeoutDuration() + } if isNew { window.isHidden = false - func completion() { - self.navEvents[.onViewDidAppear]?(self) - } if let animateBuildIn = config.animateBuildIn { animateBuildIn(window, completion) } else { @@ -82,16 +103,14 @@ extension CapsuleTarget { completion() } case .middle: - let d0 = duration * 0.2 - let d1 = duration - window.transform = .init(scaleX: 0.001, y: 0.001) - window.alpha = 0 - UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5) { + window.transform = .init(translationX: 0, y: 24) + UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5) { window.transform = .identity } completion: { done in completion() } - UIView.animate(withDuration: duration * 0.4, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1) { + window.alpha = 0 + UIView.animateLinear(duration: duration * 0.5) { window.alpha = 1 } case .bottom: @@ -103,8 +122,6 @@ extension CapsuleTarget { completion() } } - - } } else { view.layoutIfNeeded() @@ -112,7 +129,7 @@ extension CapsuleTarget { window.frame = newFrame window.layoutIfNeeded() } completion: { done in - self.navEvents[.onViewDidAppear]?(self) + completion() } } @@ -120,11 +137,13 @@ extension CapsuleTarget { @objc open func pop() { guard let window = attachedWindow, let windowScene = windowScene else { return } - AppContext.capsuleWindows[windowScene]?[vm?.position ?? .top] = nil + let position = vm?.position ?? .top + AppContext.capsuleWindows[windowScene]?[position] = nil navEvents[.onViewWillDisappear]?(self) - // 更新toast防止重叠 - ToastWindow.updateToastWindowsLayout() - + if position == .top { + // 更新toast防止重叠 + ToastWindow.updateToastWindowsLayout() + } func completion() { window.isHidden = true window.transform = .identity @@ -135,31 +154,37 @@ extension CapsuleTarget { } else { let duration = config.animateDurationForBuildOutByDefault let oldFrame = window.frame - switch vm?.position { - case .top, .none: - UIView.animateEaseOut(duration: duration) { + switch position { + case .top: + UIView.animateEaseIn(duration: duration) { window.transform = .init(translationX: 0, y: -oldFrame.maxY - 20) } completion: { done in completion() } case .middle: - UIView.animate(withDuration: duration * 0.6, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5) { - window.transform = .init(scaleX: 0.001, y: 0.001) + let duration = config.animateDurationForBuildInByDefault * 1 + UIView.animateEaseIn(duration: duration) { + window.transform = .init(translationX: 0, y: -24) } completion: { done in completion() } - UIView.animate(withDuration: duration * 0.4, delay: duration * 0.2, usingSpringWithDamping: 1, initialSpringVelocity: 0.5) { + UIView.animateLinear(duration: duration * 0.5, delay: duration * 0.3) { window.alpha = 0 } case .bottom: let offsetY = AppContext.appBounds.height - oldFrame.maxY + 100 - UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0) { + UIView.animateEaseIn(duration: duration) { window.transform = .init(translationX: 0, y: offsetY) } completion: { done in completion() } } - + } + if let next = AppContext.capsuleInQueue.first(where: { $0.preferredWindowScene == windowScene && $0.vm?.position == position }) { + AppContext.capsuleInQueue.removeAll(where: { $0 == next }) + DispatchQueue.main.asyncAfter(deadline: .now() + config.animateDurationForBuildOutByDefault * 0.8) { + next.push() + } } } @@ -174,4 +199,15 @@ extension CapsuleTarget { } } + func updateTimeoutDuration() { + // 为空时使用默认规则 + if vm?.duration == nil { + vm?.duration = config.defaultDuration + } + // 设置持续时间 + vm?.timeoutHandler = DispatchWorkItem(block: { [weak self] in + self?.pop() + }) + } + } diff --git a/Sources/ProHUD/Capsule/CapsuleProvider.swift b/Sources/ProHUD/Capsule/CapsuleProvider.swift index 161fc4a..f4e57f0 100644 --- a/Sources/ProHUD/Capsule/CapsuleProvider.swift +++ b/Sources/ProHUD/Capsule/CapsuleProvider.swift @@ -68,7 +68,9 @@ open class CapsuleProvider: HUDProvider { /// - 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 }) + let allPositions = AppContext.capsuleWindows.values.flatMap({ $0.values }) + let allCapsules = allPositions.compactMap({ $0.capsule }) + let arr = (allCapsules + AppContext.capsuleInQueue).filter({ $0.identifier == identifier }) if let handler = handler { arr.forEach({ $0.update(handler: handler) }) } diff --git a/Sources/ProHUD/Capsule/CapsuleViewModel.swift b/Sources/ProHUD/Capsule/CapsuleViewModel.swift index 5c5157b..518eacb 100644 --- a/Sources/ProHUD/Capsule/CapsuleViewModel.swift +++ b/Sources/ProHUD/Capsule/CapsuleViewModel.swift @@ -17,24 +17,38 @@ import UIKit @objc public var position: Position = .top - public func position(position: Position) -> Self { + // Capsule 在一个位置最多只显示一个实例 + // queuedPush: false 如果已有就直接覆盖 + // queuedPush: true 如果已有就排队等待 + @objc public var queuedPush: Bool = false + +} + +public extension CapsuleViewModel { + + func position(_ position: Position) -> Self { self.position = position return self } - public static var top: Self { + static var top: Self { let obj = Self.init() obj.position = .top return obj } - public static var middle: Self { + static var middle: Self { let obj = Self.init() obj.position = .middle return obj } - public static var bottom: Self { + static var bottom: Self { let obj = Self.init() obj.position = .bottom return obj } + func queuedPush(_ queuedPush: Bool) -> Self { + self.queuedPush = queuedPush + return self + } + } diff --git a/Sources/ProHUD/Capsule/CapsuleWindow.swift b/Sources/ProHUD/Capsule/CapsuleWindow.swift index fea0a79..2079b63 100644 --- a/Sources/ProHUD/Capsule/CapsuleWindow.swift +++ b/Sources/ProHUD/Capsule/CapsuleWindow.swift @@ -27,7 +27,7 @@ class CapsuleWindow: Window { windowLevel = .phCapsuleBottom } frame = .init(x: 0, y: 0, width: 128, height: 48) - isHidden = false + isHidden = true } required init?(coder: NSCoder) { diff --git a/Sources/ProHUD/Core/Controllers/BaseController.swift b/Sources/ProHUD/Core/Controllers/BaseController.swift index 9564294..e1bcea0 100644 --- a/Sources/ProHUD/Core/Controllers/BaseController.swift +++ b/Sources/ProHUD/Core/Controllers/BaseController.swift @@ -9,6 +9,9 @@ import UIKit open class BaseController: UIViewController { + /// 需要显示到那个UIWindowScene上 + var preferredWindowScene: UIWindowScene? + /// ID标识 public var identifier = String(Date().timeIntervalSince1970) @@ -23,6 +26,7 @@ open class BaseController: UIViewController { open var customView: UIView? public internal(set) var isViewDisplayed = false + /// 按钮事件 var buttonEvents = [UIView: () -> Void]() diff --git a/Sources/ProHUD/Core/Protocols/Provider.swift b/Sources/ProHUD/Core/Protocols/Provider.swift index 134eb65..4eb8f9f 100644 --- a/Sources/ProHUD/Core/Protocols/Provider.swift +++ b/Sources/ProHUD/Core/Protocols/Provider.swift @@ -32,9 +32,7 @@ open class HUDProvider: NSOb } var t = Target() initializer(t) - DispatchQueue.main.async { - t.push() - } + t.push() } } diff --git a/Sources/ProHUD/Core/Utils/AppContext.swift b/Sources/ProHUD/Core/Utils/AppContext.swift index 88c1230..34e2159 100644 --- a/Sources/ProHUD/Core/Utils/AppContext.swift +++ b/Sources/ProHUD/Core/Utils/AppContext.swift @@ -37,6 +37,7 @@ public struct AppContext { static var alertWindow: [UIWindowScene: AlertWindow] = [:] static var sheetWindows: [UIWindowScene: [SheetWindow]] = [:] static var capsuleWindows: [UIWindowScene: [CapsuleViewModel.Position: CapsuleWindow]] = [:] + static var capsuleInQueue: [CapsuleTarget] = [] static var current: AppContext? { guard let windowScene = windowScene else { return nil } @@ -123,5 +124,8 @@ extension AppContext { var capsuleWindows: [CapsuleViewModel.Position: CapsuleWindow] { Self.capsuleWindows[windowScene] ?? [:] } + var alertWindow: AlertWindow? { + Self.alertWindow[windowScene] + } } diff --git a/Sources/ProHUD/Core/Utils/ViewExts.swift b/Sources/ProHUD/Core/Utils/ViewExts.swift index 092e4bd..e61fb9f 100644 --- a/Sources/ProHUD/Core/Utils/ViewExts.swift +++ b/Sources/ProHUD/Core/Utils/ViewExts.swift @@ -9,8 +9,14 @@ import UIKit extension UIView { + static func animateLinear(duration: TimeInterval, delay: TimeInterval = 0, animations: @escaping () -> Void, completion: ((_ done: Bool) -> Void)? = nil) { + animate(withDuration: duration, delay: delay, options: [.allowUserInteraction], animations: animations, completion: completion) + } + static func animateEaseIn(duration: TimeInterval, animations: @escaping () -> Void, completion: ((_ done: Bool) -> Void)? = nil) { + animate(withDuration: duration, delay: 0, options: [.allowUserInteraction, .curveEaseIn], animations: animations, completion: completion) + } static func animateEaseOut(duration: TimeInterval, animations: @escaping () -> Void, completion: ((_ done: Bool) -> Void)? = nil) { - animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.75, options: [.allowUserInteraction, .curveEaseOut], animations: animations, completion: completion) + animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5, options: [.allowUserInteraction, .curveEaseOut], animations: animations, completion: completion) } } diff --git a/Sources/ProHUD/Sheet/SheetConfiguration.swift b/Sources/ProHUD/Sheet/SheetConfiguration.swift index 58665df..0e7b4dd 100644 --- a/Sources/ProHUD/Sheet/SheetConfiguration.swift +++ b/Sources/ProHUD/Sheet/SheetConfiguration.swift @@ -51,11 +51,11 @@ public class SheetConfiguration: CommonConfiguration { override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? (AppContext.appBounds.height - 50) } override var animateDurationForBuildInByDefault: CGFloat { - animateDurationForBuildIn ?? 0.5 + animateDurationForBuildIn ?? 0.38 } override var animateDurationForBuildOutByDefault: CGFloat { - animateDurationForBuildOut ?? 0.5 + animateDurationForBuildOut ?? 0.24 } override var cardCornerRadiusByDefault: CGFloat { cardCornerRadius ?? 32 } diff --git a/Sources/ProHUD/Sheet/SheetDefaultLayout.swift b/Sources/ProHUD/Sheet/SheetDefaultLayout.swift index 6b34277..8feafdb 100644 --- a/Sources/ProHUD/Sheet/SheetDefaultLayout.swift +++ b/Sources/ProHUD/Sheet/SheetDefaultLayout.swift @@ -44,18 +44,16 @@ extension SheetTarget: DefaultLayout { // mask loadContentMaskViewIfNeeded() // layout + let windowWidth = AppContext.appBounds.width let maxWidth = config.cardMaxWidthByDefault - var width = AppContext.appBounds.width - config.windowEdgeInset * 2 - if width > maxWidth { - // landscape iPhone or iPad - width = maxWidth - } + let autoWidth = windowWidth - config.windowEdgeInset * 2 + let width = min(autoWidth, maxWidth) contentView.snp.remakeConstraints { make in if config.isFullScreen { make.edges.equalToSuperview() } else { make.centerX.equalToSuperview() - if UIDevice.current.userInterfaceIdiom == .pad && width >= maxWidth { + if UIDevice.current.userInterfaceIdiom == .pad && width < autoWidth - 40 { // iPad且窗口宽度较宽时居中弹出 make.centerY.equalToSuperview() } else { diff --git a/Sources/ProHUD/Sheet/SheetManager.swift b/Sources/ProHUD/Sheet/SheetManager.swift index b726ac0..9fbfedd 100644 --- a/Sources/ProHUD/Sheet/SheetManager.swift +++ b/Sources/ProHUD/Sheet/SheetManager.swift @@ -27,6 +27,7 @@ extension SheetTarget { setContextWindows(windows) } if isNew { + _translateOut() navEvents[.onViewWillAppear]?(self) window.sheet.translateIn { [weak self] in guard let self = self else { return } @@ -89,7 +90,7 @@ extension SheetTarget { } func translateOut(completion: (() -> Void)?) { - UIView.animateEaseOut(duration: config.animateDurationForBuildOutByDefault) { + UIView.animateLinear(duration: config.animateDurationForBuildOutByDefault) { self._translateOut() if self.config.stackDepthEffect { AppContext.appWindow?.transform = .identity diff --git a/Sources/ProHUD/Sheet/SheetTarget.swift b/Sources/ProHUD/Sheet/SheetTarget.swift index b83287a..1e237da 100644 --- a/Sources/ProHUD/Sheet/SheetTarget.swift +++ b/Sources/ProHUD/Sheet/SheetTarget.swift @@ -20,7 +20,6 @@ open class SheetTarget: BaseController, HUDTargetType { public lazy var backgroundView: UIView = { let v = UIView() v.backgroundColor = .init(white: 0, alpha: 0.5) - v.alpha = 0 return v }() @@ -64,8 +63,6 @@ open class SheetTarget: BaseController, HUDTargetType { reloadData(animated: false) - _translateOut() - navEvents[.onViewDidLoad]?(self) } diff --git a/Sources/ProHUD/Toast/ToastConfiguration.swift b/Sources/ProHUD/Toast/ToastConfiguration.swift index d9f34f5..4bba30d 100644 --- a/Sources/ProHUD/Toast/ToastConfiguration.swift +++ b/Sources/ProHUD/Toast/ToastConfiguration.swift @@ -42,11 +42,11 @@ public class ToastConfiguration: CommonConfiguration { } override var animateDurationForBuildInByDefault: CGFloat { - animateDurationForBuildIn ?? 0.8 + animateDurationForBuildIn ?? 0.64 } override var animateDurationForBuildOutByDefault: CGFloat { - animateDurationForBuildIn ?? 0.8 + animateDurationForBuildIn ?? 0.32 } } diff --git a/Sources/ProHUD/Toast/ToastDefaultLayout.swift b/Sources/ProHUD/Toast/ToastDefaultLayout.swift index fcc3d40..706fd25 100644 --- a/Sources/ProHUD/Toast/ToastDefaultLayout.swift +++ b/Sources/ProHUD/Toast/ToastDefaultLayout.swift @@ -134,17 +134,6 @@ 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) diff --git a/Sources/ProHUD/Toast/ToastManager.swift b/Sources/ProHUD/Toast/ToastManager.swift index 52b31f7..3c02513 100644 --- a/Sources/ProHUD/Toast/ToastManager.swift +++ b/Sources/ProHUD/Toast/ToastManager.swift @@ -66,16 +66,21 @@ extension ToastTarget { setContextWindows(windows) } ToastWindow.updateToastWindowsLayout(windows: windows) + + func completion() { + self.navEvents[.onViewDidAppear]?(self) + self.updateTimeoutDuration() + } if isNew { window.transform = .init(translationX: 0, y: -window.frame.maxY) UIView.animateEaseOut(duration: config.animateDurationForBuildInByDefault) { window.transform = .identity } completion: { done in - self.navEvents[.onViewDidAppear]?(self) + completion() } } else { view.layoutIfNeeded() - self.navEvents[.onViewDidAppear]?(self) + completion() } } @@ -94,7 +99,7 @@ extension ToastTarget { } vm?.duration = nil setContextWindows(windows) - UIView.animateEaseOut(duration: config.animateDurationForBuildOutByDefault) { + UIView.animateLinear(duration: config.animateDurationForBuildOutByDefault) { window.transform = .init(translationX: 0, y: 0-20-window.maxY) } completion: { done in self.view.removeFromSuperview() @@ -115,6 +120,17 @@ extension ToastTarget { } } + func updateTimeoutDuration() { + // 为空时使用默认规则 + if vm?.duration == nil { + vm?.duration = config.defaultDuration + } + // 设置持续时间 + vm?.timeoutHandler = DispatchWorkItem(block: { [weak self] in + self?.pop() + }) + } + } // MARK: - layout