mirror of https://github.com/xaoxuu/ProHUD
适配iPad多窗口
This commit is contained in:
parent
27138a49e9
commit
b53a09ad75
|
@ -51,10 +51,8 @@ extension CALayer {
|
||||||
var cornerRadiusWithContinuous: CGFloat {
|
var cornerRadiusWithContinuous: CGFloat {
|
||||||
set {
|
set {
|
||||||
cornerRadius = newValue
|
cornerRadius = newValue
|
||||||
if #available(iOS 13.0, *) {
|
if cornerCurve != .continuous {
|
||||||
cornerCurve = .continuous
|
cornerCurve = .continuous
|
||||||
} else {
|
|
||||||
// Fallback on earlier versions
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get { cornerRadius }
|
get { cornerRadius }
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<key>UIApplicationSceneManifest</key>
|
<key>UIApplicationSceneManifest</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIApplicationSupportsMultipleScenes</key>
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
<false/>
|
<true/>
|
||||||
<key>UISceneConfigurations</key>
|
<key>UISceneConfigurations</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIWindowSceneSessionRoleApplication</key>
|
<key>UIWindowSceneSessionRoleApplication</key>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import ProHUD
|
||||||
|
|
||||||
class ListVC: UITableViewController {
|
class ListVC: UITableViewController {
|
||||||
|
|
||||||
|
@ -46,6 +47,7 @@ class ListVC: UITableViewController {
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
tableView.deselectRow(at: indexPath, animated: true)
|
tableView.deselectRow(at: indexPath, animated: true)
|
||||||
|
AppContext.workspace = self
|
||||||
list.sections[indexPath.section].rows[indexPath.row].action()
|
list.sections[indexPath.section].rows[indexPath.row].action()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "ProHUD",
|
name: "ProHUD",
|
||||||
platforms: [.iOS(.v10)],
|
platforms: [.iOS(.v13)],
|
||||||
products: [
|
products: [
|
||||||
.library(name: "ProHUD", targets: ["ProHUD"]),
|
.library(name: "ProHUD", targets: ["ProHUD"]),
|
||||||
],
|
],
|
||||||
|
|
|
@ -68,7 +68,8 @@ extension Alert: DefaultLayout {
|
||||||
if contentView.superview != view {
|
if contentView.superview != view {
|
||||||
view.insertSubview(contentView, at: 0)
|
view.insertSubview(contentView, at: 0)
|
||||||
}
|
}
|
||||||
if config.enableShadow && AlertWindow.alerts.count > 0 {
|
let alerts = window?.alerts ?? []
|
||||||
|
if config.enableShadow && alerts.count > 0 {
|
||||||
contentView.clipsToBounds = false
|
contentView.clipsToBounds = false
|
||||||
contentView.layer.shadowRadius = 4
|
contentView.layer.shadowRadius = 4
|
||||||
contentView.layer.shadowOpacity = 0.08
|
contentView.layer.shadowOpacity = 0.08
|
||||||
|
@ -242,7 +243,8 @@ extension Alert {
|
||||||
|
|
||||||
public override func viewDidLayoutSubviews() {
|
public override func viewDidLayoutSubviews() {
|
||||||
super.viewDidLayoutSubviews()
|
super.viewDidLayoutSubviews()
|
||||||
if config.enableShadow && AlertWindow.alerts.count > 1 {
|
let alerts = window?.alerts ?? []
|
||||||
|
if config.enableShadow && alerts.count > 1 {
|
||||||
contentView.layer.shadowPath = UIBezierPath.init(rect: contentView.bounds).cgPath
|
contentView.layer.shadowPath = UIBezierPath.init(rect: contentView.bounds).cgPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,14 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
extension Alert: HUD {
|
extension Alert: HUD {
|
||||||
|
public func push(scene: UIWindowScene?) {
|
||||||
|
push()
|
||||||
|
}
|
||||||
public func push() {
|
public func push() {
|
||||||
guard AlertWindow.alerts.contains(self) == false else {
|
let window = createAttachedWindowIfNotExists()
|
||||||
|
guard window.alerts.contains(self) == false else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let window = attachedWindow
|
|
||||||
view.transform = .init(scaleX: 1.2, y: 1.2)
|
view.transform = .init(scaleX: 1.2, y: 1.2)
|
||||||
view.alpha = 0
|
view.alpha = 0
|
||||||
navEvents[.onViewWillAppear]?(self)
|
navEvents[.onViewWillAppear]?(self)
|
||||||
|
@ -32,13 +34,13 @@ extension Alert: HUD {
|
||||||
} completion: { done in
|
} completion: { done in
|
||||||
self.navEvents[.onViewDidAppear]?(self)
|
self.navEvents[.onViewDidAppear]?(self)
|
||||||
}
|
}
|
||||||
AlertWindow.alerts.append(self)
|
window.alerts.append(self)
|
||||||
Alert.updateAlertsLayout()
|
Alert.updateAlertsLayout(alerts: window.alerts)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func pop() {
|
public func pop() {
|
||||||
navEvents[.onViewWillDisappear]?(self)
|
navEvents[.onViewWillDisappear]?(self)
|
||||||
let window = attachedWindow
|
let window = window ?? createAttachedWindowIfNotExists()
|
||||||
Alert.removeAlert(alert: self)
|
Alert.removeAlert(alert: self)
|
||||||
let duration = config.animateDurationForBuildOut ?? config.animateDurationForBuildOutByDefault
|
let duration = config.animateDurationForBuildOut ?? config.animateDurationForBuildOutByDefault
|
||||||
UIView.animateEaseOut(duration: duration) {
|
UIView.animateEaseOut(duration: duration) {
|
||||||
|
@ -50,13 +52,14 @@ extension Alert: HUD {
|
||||||
self.navEvents[.onViewDidDisappear]?(self)
|
self.navEvents[.onViewDidDisappear]?(self)
|
||||||
}
|
}
|
||||||
// hide window
|
// hide window
|
||||||
let count = AlertWindow.alerts.count
|
let count = window.alerts.count
|
||||||
if count == 0 && AlertWindow.current != nil {
|
if count == 0 {
|
||||||
UIView.animateEaseOut(duration: duration) {
|
UIView.animateEaseOut(duration: duration) {
|
||||||
window.backgroundView.alpha = 0
|
window.backgroundView.alpha = 0
|
||||||
} completion: { done in
|
} completion: { done in
|
||||||
if AlertWindow.alerts.count == 0 {
|
// 此时不能用self.window,因为alert已经释放掉了
|
||||||
AlertWindow.current = nil
|
if window.alerts.count == 0, let scene = window.windowScene {
|
||||||
|
AppContext.alertWindow[scene] = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +75,7 @@ public extension Alert {
|
||||||
/// - handler: 实例创建代码
|
/// - handler: 实例创建代码
|
||||||
static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ alert: Alert) -> Void, onExists: ((_ alert: Alert) -> Void)? = nil) {
|
static func lazyPush(identifier: String? = nil, file: String = #file, line: Int = #line, handler: @escaping (_ alert: Alert) -> Void, onExists: ((_ alert: Alert) -> Void)? = nil) {
|
||||||
let id = identifier ?? (file + "#\(line)")
|
let id = identifier ?? (file + "#\(line)")
|
||||||
if let vc = AlertWindow.alerts.last(where: { $0.identifier == id }) {
|
if let vc = find(identifier: id).last {
|
||||||
vc.update(handler: onExists ?? handler)
|
vc.update(handler: onExists ?? handler)
|
||||||
} else {
|
} else {
|
||||||
Alert { alert in
|
Alert { alert in
|
||||||
|
@ -96,7 +99,7 @@ public extension Alert {
|
||||||
/// - Parameter identifier: 唯一标识符
|
/// - Parameter identifier: 唯一标识符
|
||||||
/// - Returns: HUD实例
|
/// - Returns: HUD实例
|
||||||
@discardableResult static func find(identifier: String, update handler: ((_ alert: Alert) -> Void)? = nil) -> [Alert] {
|
@discardableResult static func find(identifier: String, update handler: ((_ alert: Alert) -> Void)? = nil) -> [Alert] {
|
||||||
let arr = AlertWindow.alerts.filter({ $0.identifier == identifier })
|
let arr = AppContext.alertWindow.values.flatMap({ $0.alerts }).filter({ $0.identifier == identifier })
|
||||||
if let handler = handler {
|
if let handler = handler {
|
||||||
arr.forEach({ $0.update(handler: handler) })
|
arr.forEach({ $0.update(handler: handler) })
|
||||||
}
|
}
|
||||||
|
@ -106,8 +109,8 @@ public extension Alert {
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate extension Alert {
|
fileprivate extension Alert {
|
||||||
static func updateAlertsLayout() {
|
static func updateAlertsLayout(alerts: [Alert]) {
|
||||||
for (i, a) in AlertWindow.alerts.reversed().enumerated() {
|
for (i, a) in alerts.reversed().enumerated() {
|
||||||
let scale = CGFloat(pow(0.9, CGFloat(i)))
|
let scale = CGFloat(pow(0.9, CGFloat(i)))
|
||||||
UIView.animate(withDuration: 1.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5, options: [.allowUserInteraction, .curveEaseInOut], animations: {
|
UIView.animate(withDuration: 1.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5, options: [.allowUserInteraction, .curveEaseInOut], animations: {
|
||||||
let y = 0 - a.config.stackDepth * CGFloat(i) * CGFloat(pow(0.85, CGFloat(i)))
|
let y = 0 - a.config.stackDepth * CGFloat(i) * CGFloat(pow(0.85, CGFloat(i)))
|
||||||
|
@ -118,25 +121,29 @@ fileprivate extension Alert {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var attachedWindow: AlertWindow {
|
func createAttachedWindowIfNotExists() -> AlertWindow {
|
||||||
AlertWindow.attachedWindow(config: config)
|
AlertWindow.createAttachedWindowIfNotExists(config: config)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func removeAlert(alert: Alert) {
|
static func removeAlert(alert: Alert) {
|
||||||
if AlertWindow.alerts.count > 1 {
|
guard var alerts = alert.window?.alerts else {
|
||||||
for (i, a) in AlertWindow.alerts.enumerated() {
|
return
|
||||||
|
}
|
||||||
|
if alerts.count > 1 {
|
||||||
|
for (i, a) in alerts.enumerated() {
|
||||||
if a == alert {
|
if a == alert {
|
||||||
if i < AlertWindow.alerts.count {
|
if i < alerts.count {
|
||||||
AlertWindow.alerts.remove(at: i)
|
alerts.remove(at: i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateAlertsLayout()
|
updateAlertsLayout(alerts: alerts)
|
||||||
} else if AlertWindow.alerts.count == 1 {
|
} else if alerts.count == 1 {
|
||||||
AlertWindow.alerts.removeAll()
|
alerts.removeAll()
|
||||||
} else {
|
} else {
|
||||||
print("‼️代码漏洞:已经没有alert了")
|
print("‼️代码漏洞:已经没有alert了")
|
||||||
}
|
}
|
||||||
|
alert.window?.alerts = alerts
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,29 +7,43 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
extension Alert {
|
||||||
|
var window: AlertWindow? {
|
||||||
|
get {
|
||||||
|
guard let windowScene = windowScene else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return AppContext.alertWindow[windowScene]
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
guard let windowScene = windowScene else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
AppContext.alertWindow[windowScene] = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AlertWindow: Window {
|
class AlertWindow: Window {
|
||||||
|
|
||||||
static var current: AlertWindow?
|
var alerts: [Alert] = []
|
||||||
|
|
||||||
static var alerts = [Alert]()
|
|
||||||
|
|
||||||
override var usingBackground: Bool { true }
|
override var usingBackground: Bool { true }
|
||||||
|
|
||||||
static func attachedWindow(config: Configuration) -> AlertWindow {
|
static func createAttachedWindowIfNotExists(config: Configuration) -> AlertWindow {
|
||||||
if let w = AlertWindow.current {
|
let windowScene = AppContext.windowScene
|
||||||
|
if let windowScene = windowScene, let w = AppContext.alertWindow[windowScene] {
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
let w: AlertWindow
|
let w: AlertWindow
|
||||||
if #available(iOS 13.0, *) {
|
if let scene = windowScene {
|
||||||
if let scene = AppContext.windowScene {
|
|
||||||
w = .init(windowScene: scene)
|
w = .init(windowScene: scene)
|
||||||
} else {
|
} else {
|
||||||
w = .init(frame: AppContext.appBounds)
|
w = .init(frame: AppContext.appBounds)
|
||||||
}
|
}
|
||||||
} else {
|
if let windowScene = windowScene {
|
||||||
w = .init(frame: AppContext.appBounds)
|
AppContext.alertWindow[windowScene] = w
|
||||||
}
|
}
|
||||||
AlertWindow.current = w
|
|
||||||
// 比原生alert层级低一点
|
// 比原生alert层级低一点
|
||||||
w.windowLevel = .init(rawValue: UIWindow.Level.alert.rawValue - 1)
|
w.windowLevel = .init(rawValue: UIWindow.Level.alert.rawValue - 1)
|
||||||
return w
|
return w
|
||||||
|
|
|
@ -13,7 +13,6 @@ public class Configuration: NSObject {
|
||||||
public static var enablePrint = true
|
public static var enablePrint = true
|
||||||
|
|
||||||
public lazy var dynamicBackgroundColor: UIColor = {
|
public lazy var dynamicBackgroundColor: UIColor = {
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
let color = UIColor { (traitCollection: UITraitCollection) -> UIColor in
|
let color = UIColor { (traitCollection: UITraitCollection) -> UIColor in
|
||||||
if traitCollection.userInterfaceStyle == .dark {
|
if traitCollection.userInterfaceStyle == .dark {
|
||||||
return .init(white: 0.15, alpha: 1)
|
return .init(white: 0.15, alpha: 1)
|
||||||
|
@ -22,15 +21,10 @@ public class Configuration: NSObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return color
|
return color
|
||||||
} else {
|
|
||||||
// Fallback on earlier versions
|
|
||||||
}
|
|
||||||
return .init(white: 1, alpha: 1)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
/// 动态颜色(适配iOS13)
|
/// 动态颜色(适配iOS13)
|
||||||
public lazy var dynamicTextColor: UIColor = {
|
public lazy var dynamicTextColor: UIColor = {
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
let color = UIColor { (traitCollection: UITraitCollection) -> UIColor in
|
let color = UIColor { (traitCollection: UITraitCollection) -> UIColor in
|
||||||
if traitCollection.userInterfaceStyle == .dark {
|
if traitCollection.userInterfaceStyle == .dark {
|
||||||
return .init(white: 1, alpha: 1)
|
return .init(white: 1, alpha: 1)
|
||||||
|
@ -39,10 +33,6 @@ public class Configuration: NSObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return color
|
return color
|
||||||
} else {
|
|
||||||
// Fallback on earlier versions
|
|
||||||
}
|
|
||||||
return .init(white: 0.1, alpha: 1)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
/// 主标签文本颜色
|
/// 主标签文本颜色
|
||||||
|
|
|
@ -5,9 +5,17 @@
|
||||||
// Created by xaoxuu on 2022/8/29.
|
// Created by xaoxuu on 2022/8/29.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import UIKit
|
||||||
|
|
||||||
public protocol HUD {
|
public protocol HUD {
|
||||||
func push()
|
func push()
|
||||||
|
func push(workspace: Workspace?)
|
||||||
func pop()
|
func pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension HUD {
|
||||||
|
func push(workspace: Workspace?) {
|
||||||
|
AppContext.workspace = workspace
|
||||||
|
push()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,41 +7,85 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
public protocol Workspace {}
|
||||||
|
|
||||||
|
extension UIWindowScene: Workspace {}
|
||||||
|
extension UIView: Workspace {}
|
||||||
|
extension UIViewController: Workspace {}
|
||||||
|
|
||||||
|
extension Workspace {
|
||||||
|
var windowScene: UIWindowScene? {
|
||||||
|
if let self = self as? UIWindowScene {
|
||||||
|
return self
|
||||||
|
} else if let self = self as? UIWindow {
|
||||||
|
return self.windowScene
|
||||||
|
} else if let self = self as? UIView {
|
||||||
|
return self.window?.windowScene
|
||||||
|
} else if let self = self as? UIViewController {
|
||||||
|
return self.view.window?.windowScene
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct AppContext {
|
public struct AppContext {
|
||||||
|
|
||||||
@available(iOS 13.0, *)
|
|
||||||
private static var storedAppWindowScene: UIWindowScene?
|
private static var storedAppWindowScene: UIWindowScene?
|
||||||
|
|
||||||
private static var storedAppWindow: UIWindow?
|
/// 一个scene关联一个toast
|
||||||
|
static var toastWindows: [UIWindowScene: [ToastWindow]] = [:]
|
||||||
|
static var alertWindow: [UIWindowScene: AlertWindow] = [:]
|
||||||
|
static var sheetWindows: [UIWindowScene: [SheetWindow]] = [:]
|
||||||
|
|
||||||
private init() {}
|
static var current: AppContext? {
|
||||||
|
guard let windowScene = windowScene else { return nil }
|
||||||
|
if let ctx = allContexts[windowScene] {
|
||||||
|
return ctx
|
||||||
|
} else {
|
||||||
|
let ctx: AppContext = .init(windowScene: windowScene)
|
||||||
|
allContexts[windowScene] = ctx
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static var allContexts = [UIWindowScene: AppContext]()
|
||||||
|
private let windowScene: UIWindowScene
|
||||||
|
|
||||||
|
private init(windowScene: UIWindowScene) {
|
||||||
|
self.windowScene = windowScene
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 单窗口应用无需设置,多窗口应用需要指定显示到哪个windowScene上
|
||||||
|
/// workspace可以是windowScene/window/view/viewController
|
||||||
|
public static var workspace: Workspace? {
|
||||||
|
get { windowScene }
|
||||||
|
set {
|
||||||
|
windowScene = newValue?.windowScene
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension AppContext {
|
extension AppContext {
|
||||||
|
|
||||||
@available(iOS 13.0, *)
|
static var foregroundActiveWindowScenes: [UIWindowScene] {
|
||||||
|
return UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }).filter({ $0.activationState == .foregroundActive })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 如果设置了workspace,就是workspace所对应的windowScene,否则就是最后一个打开的应用程序窗口的windowScene
|
||||||
static var windowScene: UIWindowScene? {
|
static var windowScene: UIWindowScene? {
|
||||||
set { storedAppWindowScene = newValue }
|
set { storedAppWindowScene = newValue }
|
||||||
get {
|
get {
|
||||||
if let ws = storedAppWindowScene {
|
if let ws = storedAppWindowScene {
|
||||||
return ws
|
return ws
|
||||||
} else {
|
} else {
|
||||||
return UIApplication.shared.connectedScenes.first(where: { scene in
|
return foregroundActiveWindowScenes.last
|
||||||
guard let ws = scene as? UIWindowScene else { return false }
|
|
||||||
return ws.activationState == .foregroundActive
|
|
||||||
}) as? UIWindowScene
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 所有的窗口
|
/// 所有的窗口
|
||||||
static var windows: [UIWindow] {
|
static var windows: [UIWindow] {
|
||||||
if #available(iOS 13.0, *) {
|
windowScene?.windows ?? UIApplication.shared.windows
|
||||||
return windowScene?.windows ?? UIApplication.shared.windows
|
|
||||||
} else {
|
|
||||||
return UIApplication.shared.windows
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 可见的窗口
|
/// 可见的窗口
|
||||||
|
@ -51,28 +95,31 @@ public extension AppContext {
|
||||||
|
|
||||||
/// App主程序窗口
|
/// App主程序窗口
|
||||||
static var appWindow: UIWindow? {
|
static var appWindow: UIWindow? {
|
||||||
get {
|
visibleWindows.filter { window in
|
||||||
if let w = storedAppWindow {
|
|
||||||
return w
|
|
||||||
} else {
|
|
||||||
return visibleWindows.filter { window in
|
|
||||||
return "\(type(of: window))" == "UIWindow" && window.windowLevel == .normal
|
return "\(type(of: window))" == "UIWindow" && window.windowLevel == .normal
|
||||||
}.first
|
}.first
|
||||||
}
|
}
|
||||||
}
|
|
||||||
set { storedAppWindow = newValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// App主程序窗口的尺寸
|
/// App主程序窗口的尺寸
|
||||||
static var appBounds: CGRect {
|
static var appBounds: CGRect {
|
||||||
if #available(iOS 13.0, *) {
|
appWindow?.bounds ?? UIScreen.main.bounds
|
||||||
return appWindow?.bounds ?? UIScreen.main.bounds
|
|
||||||
} else {
|
|
||||||
return UIScreen.main.bounds
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// App主程序窗口的安全边距
|
/// App主程序窗口的安全边距
|
||||||
static var safeAreaInsets: UIEdgeInsets { appWindow?.safeAreaInsets ?? .zero }
|
static var safeAreaInsets: UIEdgeInsets { appWindow?.safeAreaInsets ?? .zero }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - instance manage
|
||||||
|
|
||||||
|
extension AppContext {
|
||||||
|
var sheetWindows: [SheetWindow] {
|
||||||
|
Self.sheetWindows[windowScene] ?? []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AppContext {
|
||||||
|
var toastWindows: [ToastWindow] {
|
||||||
|
Self.toastWindows[windowScene] ?? []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,10 +13,8 @@ extension CALayer {
|
||||||
var cornerRadiusWithContinuous: CGFloat {
|
var cornerRadiusWithContinuous: CGFloat {
|
||||||
set {
|
set {
|
||||||
cornerRadius = newValue
|
cornerRadius = newValue
|
||||||
if #available(iOS 13.0, *) {
|
if cornerCurve != .continuous {
|
||||||
cornerCurve = .continuous
|
cornerCurve = .continuous
|
||||||
} else {
|
|
||||||
// Fallback on earlier versions
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get { cornerRadius }
|
get { cornerRadius }
|
||||||
|
|
|
@ -9,11 +9,7 @@ import UIKit
|
||||||
|
|
||||||
extension UIImage {
|
extension UIImage {
|
||||||
public convenience init?(inProHUD named: String) {
|
public convenience init?(inProHUD named: String) {
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
self.init(named: named, in: .module, with: .none)
|
self.init(named: named, in: .module, with: .none)
|
||||||
} else {
|
|
||||||
self.init(named: named)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +20,7 @@ internal var isPortrait: Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||||
if UIApplication.shared.statusBarOrientation.isPortrait {
|
if AppContext.windowScene?.interfaceOrientation.isPortrait == true {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,6 @@ class Window: UIWindow {
|
||||||
rootViewController = vc
|
rootViewController = vc
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 13.0, *)
|
|
||||||
override init(windowScene: UIWindowScene) {
|
override init(windowScene: UIWindowScene) {
|
||||||
super.init(windowScene: windowScene)
|
super.init(windowScene: windowScene)
|
||||||
setup()
|
setup()
|
||||||
|
|
|
@ -51,7 +51,7 @@ public extension Sheet {
|
||||||
/// - Parameter identifier: 唯一标识符
|
/// - Parameter identifier: 唯一标识符
|
||||||
/// - Returns: HUD实例
|
/// - Returns: HUD实例
|
||||||
@discardableResult static func find(identifier: String, update handler: ((_ sheet: Sheet) -> Void)? = nil) -> [Sheet] {
|
@discardableResult static func find(identifier: String, update handler: ((_ sheet: Sheet) -> Void)? = nil) -> [Sheet] {
|
||||||
let arr = SheetWindow.windows.compactMap({ $0.sheet }).filter({ $0.identifier == identifier })
|
let arr = AppContext.sheetWindows.values.flatMap({ $0 }).compactMap({ $0.sheet }).filter({ $0.identifier == identifier })
|
||||||
if let handler = handler {
|
if let handler = handler {
|
||||||
arr.forEach({ $0.update(handler: handler) })
|
arr.forEach({ $0.update(handler: handler) })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// File.swift
|
// SheetWindow.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by xaoxuu on 2022/9/8.
|
// Created by xaoxuu on 2022/9/8.
|
||||||
|
@ -7,23 +7,33 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class SheetWindow: Window {
|
|
||||||
|
|
||||||
static var windows = [SheetWindow]()
|
private extension Sheet {
|
||||||
|
func getContextWindows() -> [SheetWindow] {
|
||||||
|
guard let windowScene = windowScene else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return AppContext.sheetWindows[windowScene] ?? []
|
||||||
|
}
|
||||||
|
func setContextWindows(_ windows: [SheetWindow]) {
|
||||||
|
guard let windowScene = windowScene else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
AppContext.sheetWindows[windowScene] = windows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SheetWindow: Window {
|
||||||
|
|
||||||
var sheet: Sheet
|
var sheet: Sheet
|
||||||
|
|
||||||
init(sheet: Sheet) {
|
init(sheet: Sheet) {
|
||||||
self.sheet = sheet
|
self.sheet = sheet
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
if let scene = AppContext.windowScene {
|
if let scene = AppContext.windowScene {
|
||||||
super.init(windowScene: scene)
|
super.init(windowScene: scene)
|
||||||
} else {
|
} else {
|
||||||
super.init(frame: AppContext.appBounds)
|
super.init(frame: AppContext.appBounds)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
super.init(frame: AppContext.appBounds)
|
|
||||||
}
|
|
||||||
sheet.window = self
|
sheet.window = self
|
||||||
windowLevel = .init(rawValue: UIWindow.Level.alert.rawValue - 2)
|
windowLevel = .init(rawValue: UIWindow.Level.alert.rawValue - 2)
|
||||||
isHidden = false
|
isHidden = false
|
||||||
|
@ -36,6 +46,7 @@ class SheetWindow: Window {
|
||||||
static func push(sheet: Sheet) {
|
static func push(sheet: Sheet) {
|
||||||
let isNew: Bool
|
let isNew: Bool
|
||||||
let window: SheetWindow
|
let window: SheetWindow
|
||||||
|
var windows = AppContext.current?.sheetWindows ?? []
|
||||||
if let w = windows.first(where: { $0.sheet == sheet }) {
|
if let w = windows.first(where: { $0.sheet == sheet }) {
|
||||||
isNew = false
|
isNew = false
|
||||||
window = w
|
window = w
|
||||||
|
@ -46,6 +57,7 @@ class SheetWindow: Window {
|
||||||
window.rootViewController = sheet
|
window.rootViewController = sheet
|
||||||
if windows.contains(window) == false {
|
if windows.contains(window) == false {
|
||||||
windows.append(window)
|
windows.append(window)
|
||||||
|
sheet.setContextWindows(windows)
|
||||||
}
|
}
|
||||||
if isNew {
|
if isNew {
|
||||||
sheet.navEvents[.onViewWillAppear]?(sheet)
|
sheet.navEvents[.onViewWillAppear]?(sheet)
|
||||||
|
@ -58,6 +70,7 @@ class SheetWindow: Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func pop(sheet: Sheet) {
|
static func pop(sheet: Sheet) {
|
||||||
|
var windows = sheet.getContextWindows()
|
||||||
guard let window = windows.first(where: { $0.sheet == sheet }) else {
|
guard let window = windows.first(where: { $0.sheet == sheet }) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -72,6 +85,7 @@ class SheetWindow: Window {
|
||||||
} else {
|
} else {
|
||||||
consolePrint("‼️代码漏洞:已经没有sheet了")
|
consolePrint("‼️代码漏洞:已经没有sheet了")
|
||||||
}
|
}
|
||||||
|
sheet.setContextWindows(windows)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ public extension Toast {
|
||||||
/// - Parameter identifier: 唯一标识符
|
/// - Parameter identifier: 唯一标识符
|
||||||
/// - Returns: HUD实例
|
/// - Returns: HUD实例
|
||||||
@discardableResult static func find(identifier: String, update handler: ((_ toast: Toast) -> Void)? = nil) -> [Toast] {
|
@discardableResult static func find(identifier: String, update handler: ((_ toast: Toast) -> Void)? = nil) -> [Toast] {
|
||||||
let arr = ToastWindow.windows.compactMap({ $0.toast }).filter({ $0.identifier == identifier })
|
let arr = AppContext.toastWindows.values.flatMap({ $0 }).compactMap({ $0.toast }).filter({ $0.identifier == identifier })
|
||||||
if let handler = handler {
|
if let handler = handler {
|
||||||
arr.forEach({ $0.update(handler: handler) })
|
arr.forEach({ $0.update(handler: handler) })
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,22 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class ToastWindow: Window {
|
private extension Toast {
|
||||||
|
func getContextWindows() -> [ToastWindow] {
|
||||||
|
guard let windowScene = windowScene else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return AppContext.toastWindows[windowScene] ?? []
|
||||||
|
}
|
||||||
|
func setContextWindows(_ windows: [ToastWindow]) {
|
||||||
|
guard let windowScene = windowScene else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
AppContext.toastWindows[windowScene] = windows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static var windows = [ToastWindow]()
|
class ToastWindow: Window {
|
||||||
|
|
||||||
var toast: Toast
|
var toast: Toast
|
||||||
|
|
||||||
|
@ -18,9 +31,7 @@ class ToastWindow: Window {
|
||||||
init(toast: Toast) {
|
init(toast: Toast) {
|
||||||
self.toast = toast
|
self.toast = toast
|
||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
windowScene = AppContext.windowScene
|
windowScene = AppContext.windowScene
|
||||||
}
|
|
||||||
toast.window = self
|
toast.window = self
|
||||||
windowLevel = .init(rawValue: UIWindow.Level.alert.rawValue + 1000)
|
windowLevel = .init(rawValue: UIWindow.Level.alert.rawValue + 1000)
|
||||||
layer.shadowRadius = 8
|
layer.shadowRadius = 8
|
||||||
|
@ -41,6 +52,7 @@ class ToastWindow: Window {
|
||||||
static func push(toast: Toast) {
|
static func push(toast: Toast) {
|
||||||
let isNew: Bool
|
let isNew: Bool
|
||||||
let window: ToastWindow
|
let window: ToastWindow
|
||||||
|
var windows = AppContext.current?.toastWindows ?? []
|
||||||
if let w = windows.first(where: { $0.toast == toast }) {
|
if let w = windows.first(where: { $0.toast == toast }) {
|
||||||
isNew = false
|
isNew = false
|
||||||
window = w
|
window = w
|
||||||
|
@ -64,8 +76,9 @@ class ToastWindow: Window {
|
||||||
window.rootViewController = toast // 此时toast.view.frame.size会自动更新为window.frame.size
|
window.rootViewController = toast // 此时toast.view.frame.size会自动更新为window.frame.size
|
||||||
if windows.contains(window) == false {
|
if windows.contains(window) == false {
|
||||||
windows.append(window)
|
windows.append(window)
|
||||||
|
toast.setContextWindows(windows)
|
||||||
}
|
}
|
||||||
updateToastWindowsLayout()
|
updateToastWindowsLayout(windows: windows)
|
||||||
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) {
|
||||||
|
@ -80,24 +93,26 @@ class ToastWindow: Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func pop(toast: Toast) {
|
static func pop(toast: Toast) {
|
||||||
|
var windows = toast.getContextWindows()
|
||||||
guard let window = windows.first(where: { $0.toast == toast }) else {
|
guard let window = windows.first(where: { $0.toast == toast }) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if windows.count > 1 {
|
if windows.count > 1 {
|
||||||
windows.removeAll { $0 == window }
|
windows.removeAll { $0 == window }
|
||||||
updateToastWindowsLayout()
|
updateToastWindowsLayout(windows: windows)
|
||||||
} else if windows.count == 1 {
|
} else if windows.count == 1 {
|
||||||
windows.removeAll()
|
windows.removeAll()
|
||||||
} else {
|
} else {
|
||||||
consolePrint("‼️代码漏洞:已经没有toast了")
|
consolePrint("‼️代码漏洞:已经没有toast了")
|
||||||
}
|
}
|
||||||
toast.vm.duration = nil
|
toast.vm.duration = nil
|
||||||
|
toast.setContextWindows(windows)
|
||||||
UIView.animateEaseOut(duration: toast.config.animateDurationForBuildOutByDefault) {
|
UIView.animateEaseOut(duration: toast.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
|
||||||
window.toast.view.removeFromSuperview()
|
toast.view.removeFromSuperview()
|
||||||
window.toast.removeFromParent()
|
toast.removeFromParent()
|
||||||
window.toast.navEvents[.onViewDidDisappear]?(window.toast)
|
toast.navEvents[.onViewDidDisappear]?(toast)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +123,7 @@ fileprivate var updateToastsLayoutWorkItem: DispatchWorkItem?
|
||||||
|
|
||||||
fileprivate extension ToastWindow {
|
fileprivate extension ToastWindow {
|
||||||
|
|
||||||
static func setToastWindowsLayout() {
|
static func setToastWindowsLayout(windows: [ToastWindow]) {
|
||||||
for (i, window) in windows.enumerated() {
|
for (i, window) in windows.enumerated() {
|
||||||
let config = window.toast.config
|
let config = window.toast.config
|
||||||
var y = window.frame.origin.y
|
var y = window.frame.origin.y
|
||||||
|
@ -128,10 +143,10 @@ fileprivate extension ToastWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func updateToastWindowsLayout() {
|
static func updateToastWindowsLayout(windows: [ToastWindow]) {
|
||||||
updateToastsLayoutWorkItem?.cancel()
|
updateToastsLayoutWorkItem?.cancel()
|
||||||
updateToastsLayoutWorkItem = DispatchWorkItem {
|
updateToastsLayoutWorkItem = DispatchWorkItem {
|
||||||
setToastWindowsLayout()
|
setToastWindowsLayout(windows: windows)
|
||||||
}
|
}
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now()+0.001, execute: updateToastsLayoutWorkItem!)
|
DispatchQueue.main.asyncAfter(deadline: .now()+0.001, execute: updateToastsLayoutWorkItem!)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue