代码优化

This commit is contained in:
xaoxuu 2023-08-21 16:56:51 +08:00
parent a37cb52b79
commit f68b0c3a9b
24 changed files with 281 additions and 112 deletions

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD8EEF3628BC5C7200E660EA"
BuildableName = "PHDemo.app"
BlueprintName = "PHDemo"
ReferencedContainer = "container:PHDemo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD8EEF3628BC5C7200E660EA"
BuildableName = "PHDemo.app"
BlueprintName = "PHDemo"
ReferencedContainer = "container:PHDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD8EEF3628BC5C7200E660EA"
BuildableName = "PHDemo.app"
BlueprintName = "PHDemo"
ReferencedContainer = "container:PHDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -84,15 +84,15 @@ class DemoCapsuleVC: ListVC {
} }
} }
list.add(title: "不同位置、不同动画") { section in list.add(title: "不同位置、不同动画,队列推送") { section in
section.add(title: "顶部,默认滑入") { section.add(title: "顶部,默认动画") {
Capsule(.info("一条简短的消息")) Capsule(.info("一条简短的消息").queuedPush(true).duration(1))
} }
section.add(title: "中间,默认缩放") { section.add(title: "中间,默认动画") {
Capsule(.middle.info("一条简短的消息")) Capsule(.middle.queuedPush(true).info("一条简短的消息").duration(2))
} }
section.add(title: "中间,黑底白字,透明渐变") { section.add(title: "中间,黑底白字,透明渐变") {
Capsule(.middle.info("一条简短的消息")) { capsule in Capsule(.middle.queuedPush(true).info("一条简短的消息").duration(1)) { capsule in
capsule.config.tintColor = .white capsule.config.tintColor = .white
capsule.config.cardCornerRadius = 8 capsule.config.cardCornerRadius = 8
capsule.config.contentViewMask { mask in capsule.config.contentViewMask { mask in
@ -119,7 +119,7 @@ class DemoCapsuleVC: ListVC {
} }
} }
section.add(title: "底部,渐变背景,默认回弹滑入") { section.add(title: "底部,渐变背景,默认回弹滑入") {
Capsule(.bottom.enter("点击进入")) { capsule in Capsule(.bottom.queuedPush(true).enter("点击进入").duration(1)) { capsule in
capsule.config.tintColor = .white capsule.config.tintColor = .white
capsule.config.cardEdgeInsets = .init(top: 12, left: 20, bottom: 12, right: 20) capsule.config.cardEdgeInsets = .init(top: 12, left: 20, bottom: 12, right: 20)
capsule.config.customTextLabel { label in 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) .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)
}
} }

View File

@ -106,7 +106,7 @@ class DemoToastVC: ListVC {
} }
} }
section.add(title: "图标 + 一段长文本") { section.add(title: "图标 + 一段长文本") {
Toast(.note.message(message)) Toast(.note.message(message).duration(1))
} }
section.add(title: "网络图标 + 一段文本") { section.add(title: "网络图标 + 一段文本") {
Toast(.message("这是网络图标").icon(.init(string: "https://xaoxuu.com/assets/xaoxuu/avatar/rect-256@2x.png"))) 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: "禁止手势移除") { section.add(title: "禁止手势移除") {
let title = "这条消息很重要" let title = "这条消息很重要"
let message = "向上滑动将不会移除消息,您必须手动处理,用于重要但非阻塞性的事件。(通过代码处理或者在点击事件处理)" let message = "向上滑动将不会移除消息,您必须手动处理,用于重要但非阻塞性的事件。(通过代码处理或者在点击事件处理)"
Toast(.warning.title(title).message(message)) { toast in Toast(.warning.title(title).message(message).duration(.infinity)) { toast in
toast.isRemovable = false toast.isRemovable = false
toast.onTapped { toast in toast.onTapped { toast in
toast.pop() toast.pop()

View File

@ -30,8 +30,8 @@ public class AlertConfiguration: CommonConfiguration {
customBackgroundViewMask = callback customBackgroundViewMask = callback
} }
override var animateDurationForBuildInByDefault: CGFloat { override var animateDurationForBuildOutByDefault: CGFloat {
animateDurationForBuildIn ?? 0.6 animateDurationForBuildOut ?? 0.2
} }
} }

View File

@ -124,13 +124,6 @@ extension AlertTarget: DefaultLayout {
} }
} }
func updateTimeoutDuration() {
//
vm?.timeoutHandler = DispatchWorkItem(block: { [weak self] in
self?.pop()
})
}
} }
extension AlertTarget { extension AlertTarget {

View File

@ -16,7 +16,7 @@ extension AlertTarget {
return return
} }
setDefaultAxis() setDefaultAxis()
view.transform = .init(scaleX: 1.2, y: 1.2) view.transform = .init(scaleX: 1.12, y: 1.12)
view.alpha = 0 view.alpha = 0
navEvents[.onViewWillAppear]?(self) navEvents[.onViewWillAppear]?(self)
window.vc.addChild(self) window.vc.addChild(self)
@ -33,6 +33,7 @@ extension AlertTarget {
window.backgroundView.alpha = 1 window.backgroundView.alpha = 1
} completion: { done in } completion: { done in
self.navEvents[.onViewDidAppear]?(self) self.navEvents[.onViewDidAppear]?(self)
self.updateTimeoutDuration()
} }
window.alerts.append(self) window.alerts.append(self)
AlertTarget.updateAlertsLayout(alerts: window.alerts) AlertTarget.updateAlertsLayout(alerts: window.alerts)
@ -42,9 +43,9 @@ extension AlertTarget {
navEvents[.onViewWillDisappear]?(self) navEvents[.onViewWillDisappear]?(self)
AlertTarget.removeAlert(alert: self) AlertTarget.removeAlert(alert: self)
let duration = config.animateDurationForBuildOut ?? config.animateDurationForBuildOutByDefault let duration = config.animateDurationForBuildOut ?? config.animateDurationForBuildOutByDefault
UIView.animateEaseOut(duration: duration) { UIView.animateLinear(duration: duration) {
self.view.alpha = 0 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 } completion: { done in
self.view.removeFromSuperview() self.view.removeFromSuperview()
self.removeFromParent() self.removeFromParent()
@ -55,7 +56,7 @@ extension AlertTarget {
let count = window.alerts.count let count = window.alerts.count
if count == 0 { if count == 0 {
AppContext.alertWindow[windowScene] = nil AppContext.alertWindow[windowScene] = nil
UIView.animateEaseOut(duration: duration) { UIView.animateLinear(duration: duration) {
window.backgroundView.alpha = 0 window.backgroundView.alpha = 0
} completion: { done in } completion: { done in
// window使windowwindow // window使windowwindow
@ -74,6 +75,13 @@ extension AlertTarget {
} }
} }
func updateTimeoutDuration() {
//
vm?.timeoutHandler = DispatchWorkItem(block: { [weak self] in
self?.pop()
})
}
} }
// MARK: - layout // MARK: - layout

View File

@ -36,6 +36,6 @@ class AlertWindow: Window {
extension AlertTarget { extension AlertTarget {
var attachedWindow: AlertWindow? { var attachedWindow: AlertWindow? {
view.window as? AlertWindow view.window as? AlertWindow ?? AppContext.current?.alertWindow
} }
} }

View File

@ -35,11 +35,11 @@ public class CapsuleConfiguration: CommonConfiguration {
override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? 120 } override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? 120 }
override var animateDurationForBuildInByDefault: CGFloat { override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.8 animateDurationForBuildIn ?? 0.64
} }
override var animateDurationForBuildOutByDefault: CGFloat { override var animateDurationForBuildOutByDefault: CGFloat {
animateDurationForBuildOut ?? 0.8 animateDurationForBuildOut ?? 0.32
} }
var animateBuildIn: CustomAnimateHandler? var animateBuildIn: CustomAnimateHandler?

View File

@ -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() { private func setupImageView() {
// //
stopRotate(animateLayer) stopRotate(animateLayer)

View File

@ -11,17 +11,38 @@ extension CapsuleTarget {
@objc open func push() { @objc open func push() {
guard CapsuleConfiguration.isEnabled else { return } guard CapsuleConfiguration.isEnabled else { return }
guard let windowScene = preferredWindowScene ?? AppContext.windowScene else { return }
if windowScene != AppContext.windowScene {
AppContext.windowScene = windowScene
}
let isNew: Bool let isNew: Bool
let window: CapsuleWindow let window: CapsuleWindow
let position = vm?.position ?? .top let position = vm?.position ?? .top
if let w = AppContext.current?.capsuleWindows[position] { 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 isNew = false
window = w window = w
} else {
window = CapsuleWindow(capsule: self)
isNew = true
} }
} else {
//
isNew = true
window = CapsuleWindow(capsule: self)
windows[position] = nil
}
// frame // frame
let cardEdgeInsetsByDefault = config.cardEdgeInsetsByDefault let cardEdgeInsetsByDefault = config.cardEdgeInsetsByDefault
view.layoutIfNeeded() view.layoutIfNeeded()
@ -53,22 +74,22 @@ extension CapsuleTarget {
view.layer.cornerRadiusWithContinuous = config.cardCornerRadiusByDefault view.layer.cornerRadiusWithContinuous = config.cardCornerRadiusByDefault
window.rootViewController = self // toast.view.frame.sizewindow.frame.size window.rootViewController = self // toast.view.frame.sizewindow.frame.size
if let s = AppContext.windowScene {
if AppContext.capsuleWindows[s] == nil { AppContext.capsuleWindows[windowScene]?[position] = window
AppContext.capsuleWindows[s] = [:]
}
AppContext.capsuleWindows[s]?[position] = window
}
navEvents[.onViewWillAppear]?(self) navEvents[.onViewWillAppear]?(self)
if position == .top {
// toast // toast
ToastWindow.updateToastWindowsLayout() ToastWindow.updateToastWindowsLayout()
}
if isNew {
window.isHidden = false
func completion() { func completion() {
self.navEvents[.onViewDidAppear]?(self) self.navEvents[.onViewDidAppear]?(self)
self.updateTimeoutDuration()
} }
if isNew {
window.isHidden = false
if let animateBuildIn = config.animateBuildIn { if let animateBuildIn = config.animateBuildIn {
animateBuildIn(window, completion) animateBuildIn(window, completion)
} else { } else {
@ -82,16 +103,14 @@ extension CapsuleTarget {
completion() completion()
} }
case .middle: case .middle:
let d0 = duration * 0.2 window.transform = .init(translationX: 0, y: 24)
let d1 = duration UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5) {
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 = .identity window.transform = .identity
} completion: { done in } completion: { done in
completion() 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 window.alpha = 1
} }
case .bottom: case .bottom:
@ -103,8 +122,6 @@ extension CapsuleTarget {
completion() completion()
} }
} }
} }
} else { } else {
view.layoutIfNeeded() view.layoutIfNeeded()
@ -112,7 +129,7 @@ extension CapsuleTarget {
window.frame = newFrame window.frame = newFrame
window.layoutIfNeeded() window.layoutIfNeeded()
} completion: { done in } completion: { done in
self.navEvents[.onViewDidAppear]?(self) completion()
} }
} }
@ -120,11 +137,13 @@ extension CapsuleTarget {
@objc open func pop() { @objc open func pop() {
guard let window = attachedWindow, let windowScene = windowScene else { return } 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) navEvents[.onViewWillDisappear]?(self)
if position == .top {
// toast // toast
ToastWindow.updateToastWindowsLayout() ToastWindow.updateToastWindowsLayout()
}
func completion() { func completion() {
window.isHidden = true window.isHidden = true
window.transform = .identity window.transform = .identity
@ -135,31 +154,37 @@ extension CapsuleTarget {
} else { } else {
let duration = config.animateDurationForBuildOutByDefault let duration = config.animateDurationForBuildOutByDefault
let oldFrame = window.frame let oldFrame = window.frame
switch vm?.position { switch position {
case .top, .none: case .top:
UIView.animateEaseOut(duration: duration) { UIView.animateEaseIn(duration: duration) {
window.transform = .init(translationX: 0, y: -oldFrame.maxY - 20) window.transform = .init(translationX: 0, y: -oldFrame.maxY - 20)
} completion: { done in } completion: { done in
completion() completion()
} }
case .middle: case .middle:
UIView.animate(withDuration: duration * 0.6, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5) { let duration = config.animateDurationForBuildInByDefault * 1
window.transform = .init(scaleX: 0.001, y: 0.001) UIView.animateEaseIn(duration: duration) {
window.transform = .init(translationX: 0, y: -24)
} completion: { done in } completion: { done in
completion() 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 window.alpha = 0
} }
case .bottom: case .bottom:
let offsetY = AppContext.appBounds.height - oldFrame.maxY + 100 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) window.transform = .init(translationX: 0, y: offsetY)
} completion: { done in } completion: { done in
completion() 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()
})
}
} }

View File

@ -68,7 +68,9 @@ open class CapsuleProvider: HUDProvider<CapsuleViewModel, CapsuleTarget> {
/// - Parameter identifier: /// - Parameter identifier:
/// - Returns: HUD /// - Returns: HUD
@discardableResult public static func find(identifier: String, update handler: ((_ capsule: CapsuleTarget) -> Void)? = nil) -> [CapsuleTarget] { @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 { if let handler = handler {
arr.forEach({ $0.update(handler: handler) }) arr.forEach({ $0.update(handler: handler) })
} }

View File

@ -17,24 +17,38 @@ import UIKit
@objc public var position: Position = .top @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 self.position = position
return self return self
} }
public static var top: Self { static var top: Self {
let obj = Self.init() let obj = Self.init()
obj.position = .top obj.position = .top
return obj return obj
} }
public static var middle: Self { static var middle: Self {
let obj = Self.init() let obj = Self.init()
obj.position = .middle obj.position = .middle
return obj return obj
} }
public static var bottom: Self { static var bottom: Self {
let obj = Self.init() let obj = Self.init()
obj.position = .bottom obj.position = .bottom
return obj return obj
} }
func queuedPush(_ queuedPush: Bool) -> Self {
self.queuedPush = queuedPush
return self
}
} }

View File

@ -27,7 +27,7 @@ class CapsuleWindow: Window {
windowLevel = .phCapsuleBottom windowLevel = .phCapsuleBottom
} }
frame = .init(x: 0, y: 0, width: 128, height: 48) frame = .init(x: 0, y: 0, width: 128, height: 48)
isHidden = false isHidden = true
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {

View File

@ -9,6 +9,9 @@ import UIKit
open class BaseController: UIViewController { open class BaseController: UIViewController {
/// UIWindowScene
var preferredWindowScene: UIWindowScene?
/// ID /// ID
public var identifier = String(Date().timeIntervalSince1970) public var identifier = String(Date().timeIntervalSince1970)
@ -23,6 +26,7 @@ open class BaseController: UIViewController {
open var customView: UIView? open var customView: UIView?
public internal(set) var isViewDisplayed = false public internal(set) var isViewDisplayed = false
/// ///
var buttonEvents = [UIView: () -> Void]() var buttonEvents = [UIView: () -> Void]()

View File

@ -32,9 +32,7 @@ open class HUDProvider<ViewModel: HUDViewModelType, Target: HUDTargetType>: NSOb
} }
var t = Target() var t = Target()
initializer(t) initializer(t)
DispatchQueue.main.async {
t.push() t.push()
} }
}
} }

View File

@ -37,6 +37,7 @@ public struct AppContext {
static var alertWindow: [UIWindowScene: AlertWindow] = [:] static var alertWindow: [UIWindowScene: AlertWindow] = [:]
static var sheetWindows: [UIWindowScene: [SheetWindow]] = [:] static var sheetWindows: [UIWindowScene: [SheetWindow]] = [:]
static var capsuleWindows: [UIWindowScene: [CapsuleViewModel.Position: CapsuleWindow]] = [:] static var capsuleWindows: [UIWindowScene: [CapsuleViewModel.Position: CapsuleWindow]] = [:]
static var capsuleInQueue: [CapsuleTarget] = []
static var current: AppContext? { static var current: AppContext? {
guard let windowScene = windowScene else { return nil } guard let windowScene = windowScene else { return nil }
@ -123,5 +124,8 @@ extension AppContext {
var capsuleWindows: [CapsuleViewModel.Position: CapsuleWindow] { var capsuleWindows: [CapsuleViewModel.Position: CapsuleWindow] {
Self.capsuleWindows[windowScene] ?? [:] Self.capsuleWindows[windowScene] ?? [:]
} }
var alertWindow: AlertWindow? {
Self.alertWindow[windowScene]
}
} }

View File

@ -9,8 +9,14 @@ import UIKit
extension UIView { 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) { 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)
} }
} }

View File

@ -51,11 +51,11 @@ public class SheetConfiguration: CommonConfiguration {
override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? (AppContext.appBounds.height - 50) } override var cardMaxHeightByDefault: CGFloat { cardMaxHeight ?? (AppContext.appBounds.height - 50) }
override var animateDurationForBuildInByDefault: CGFloat { override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.5 animateDurationForBuildIn ?? 0.38
} }
override var animateDurationForBuildOutByDefault: CGFloat { override var animateDurationForBuildOutByDefault: CGFloat {
animateDurationForBuildOut ?? 0.5 animateDurationForBuildOut ?? 0.24
} }
override var cardCornerRadiusByDefault: CGFloat { cardCornerRadius ?? 32 } override var cardCornerRadiusByDefault: CGFloat { cardCornerRadius ?? 32 }

View File

@ -44,18 +44,16 @@ extension SheetTarget: DefaultLayout {
// mask // mask
loadContentMaskViewIfNeeded() loadContentMaskViewIfNeeded()
// layout // layout
let windowWidth = AppContext.appBounds.width
let maxWidth = config.cardMaxWidthByDefault let maxWidth = config.cardMaxWidthByDefault
var width = AppContext.appBounds.width - config.windowEdgeInset * 2 let autoWidth = windowWidth - config.windowEdgeInset * 2
if width > maxWidth { let width = min(autoWidth, maxWidth)
// landscape iPhone or iPad
width = maxWidth
}
contentView.snp.remakeConstraints { make in contentView.snp.remakeConstraints { make in
if config.isFullScreen { if config.isFullScreen {
make.edges.equalToSuperview() make.edges.equalToSuperview()
} else { } else {
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
if UIDevice.current.userInterfaceIdiom == .pad && width >= maxWidth { if UIDevice.current.userInterfaceIdiom == .pad && width < autoWidth - 40 {
// iPad // iPad
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
} else { } else {

View File

@ -27,6 +27,7 @@ extension SheetTarget {
setContextWindows(windows) setContextWindows(windows)
} }
if isNew { if isNew {
_translateOut()
navEvents[.onViewWillAppear]?(self) navEvents[.onViewWillAppear]?(self)
window.sheet.translateIn { [weak self] in window.sheet.translateIn { [weak self] in
guard let self = self else { return } guard let self = self else { return }
@ -89,7 +90,7 @@ extension SheetTarget {
} }
func translateOut(completion: (() -> Void)?) { func translateOut(completion: (() -> Void)?) {
UIView.animateEaseOut(duration: config.animateDurationForBuildOutByDefault) { UIView.animateLinear(duration: config.animateDurationForBuildOutByDefault) {
self._translateOut() self._translateOut()
if self.config.stackDepthEffect { if self.config.stackDepthEffect {
AppContext.appWindow?.transform = .identity AppContext.appWindow?.transform = .identity

View File

@ -20,7 +20,6 @@ open class SheetTarget: BaseController, HUDTargetType {
public lazy var backgroundView: UIView = { public lazy var backgroundView: UIView = {
let v = UIView() let v = UIView()
v.backgroundColor = .init(white: 0, alpha: 0.5) v.backgroundColor = .init(white: 0, alpha: 0.5)
v.alpha = 0
return v return v
}() }()
@ -64,8 +63,6 @@ open class SheetTarget: BaseController, HUDTargetType {
reloadData(animated: false) reloadData(animated: false)
_translateOut()
navEvents[.onViewDidLoad]?(self) navEvents[.onViewDidLoad]?(self)
} }

View File

@ -42,11 +42,11 @@ public class ToastConfiguration: CommonConfiguration {
} }
override var animateDurationForBuildInByDefault: CGFloat { override var animateDurationForBuildInByDefault: CGFloat {
animateDurationForBuildIn ?? 0.8 animateDurationForBuildIn ?? 0.64
} }
override var animateDurationForBuildOutByDefault: CGFloat { override var animateDurationForBuildOutByDefault: CGFloat {
animateDurationForBuildIn ?? 0.8 animateDurationForBuildIn ?? 0.32
} }
} }

View File

@ -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() { func setupImageView() {
// //
stopRotate(animateLayer) stopRotate(animateLayer)

View File

@ -66,16 +66,21 @@ extension ToastTarget {
setContextWindows(windows) setContextWindows(windows)
} }
ToastWindow.updateToastWindowsLayout(windows: windows) ToastWindow.updateToastWindowsLayout(windows: windows)
func completion() {
self.navEvents[.onViewDidAppear]?(self)
self.updateTimeoutDuration()
}
if isNew { if isNew {
window.transform = .init(translationX: 0, y: -window.frame.maxY) window.transform = .init(translationX: 0, y: -window.frame.maxY)
UIView.animateEaseOut(duration: config.animateDurationForBuildInByDefault) { UIView.animateEaseOut(duration: config.animateDurationForBuildInByDefault) {
window.transform = .identity window.transform = .identity
} completion: { done in } completion: { done in
self.navEvents[.onViewDidAppear]?(self) completion()
} }
} else { } else {
view.layoutIfNeeded() view.layoutIfNeeded()
self.navEvents[.onViewDidAppear]?(self) completion()
} }
} }
@ -94,7 +99,7 @@ extension ToastTarget {
} }
vm?.duration = nil vm?.duration = nil
setContextWindows(windows) setContextWindows(windows)
UIView.animateEaseOut(duration: config.animateDurationForBuildOutByDefault) { UIView.animateLinear(duration: config.animateDurationForBuildOutByDefault) {
window.transform = .init(translationX: 0, y: 0-20-window.maxY) window.transform = .init(translationX: 0, y: 0-20-window.maxY)
} completion: { done in } completion: { done in
self.view.removeFromSuperview() 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 // MARK: - layout