Skip to content

Commit 8aef7b1

Browse files
committed
add multi windows
1 parent 16f2557 commit 8aef7b1

File tree

3 files changed

+81
-20
lines changed

3 files changed

+81
-20
lines changed

Source/Infra/EKWindow.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class EKWindow: UIWindow {
1212

1313
var isAbleToReceiveTouches = false
1414

15-
init(with rootVC: UIViewController) {
15+
init(with rootVC: UIViewController, level: UIWindow.Level) {
1616
if #available(iOS 13.0, *) {
1717
// TODO: Patched to support SwiftUI out of the box but should require attendance
1818
if let scene = UIApplication.shared.connectedScenes.filter({$0.activationState == .foregroundActive}).first as? UIWindowScene {
@@ -23,6 +23,7 @@ class EKWindow: UIWindow {
2323
} else {
2424
super.init(frame: UIScreen.main.bounds)
2525
}
26+
windowLevel = level
2627
backgroundColor = .clear
2728
rootViewController = rootVC
2829
accessibilityViewIsModal = true
@@ -37,7 +38,7 @@ class EKWindow: UIWindow {
3738
return super.hitTest(point, with: event)
3839
}
3940

40-
guard let rootVC = EKWindowProvider.shared.rootVC else {
41+
guard let rootVC = EKWindowProvider.provider(for: windowLevel).rootVC else {
4142
return nil
4243
}
4344

Source/Infra/EKWindowProvider.swift

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,65 @@ final class EKWindowProvider: EntryPresenterDelegate, EKProvider {
1212

1313
/** The artificial safe area insets */
1414
var safeAreaInsets: UIEdgeInsets {
15-
return entryWindow?.rootViewController?.view?.safeAreaInsets ?? UIApplication.shared.keyWindow?.rootViewController?.view.safeAreaInsets ?? .zero
15+
return EKWindowProvider.provider(for: .normal).entryWindow?.rootViewController?.view?.safeAreaInsets ?? UIApplication.shared.keyWindow?.rootViewController?.view.safeAreaInsets ?? .zero
1616
}
1717

18+
private static var providers: [UIWindow.Level: EKWindowProvider] = [:]
19+
1820
/** Single access point */
19-
static let shared = EKWindowProvider()
21+
static func provider(for level: UIWindow.Level) -> EKWindowProvider {
22+
if let provider = providers[level] {
23+
return provider
24+
} else {
25+
let provider = EKWindowProvider(level: level)
26+
providers[level] = provider
27+
return provider
28+
}
29+
}
30+
31+
static var topMostProvider: EKWindowProvider? {
32+
if let topMostLevel = providers.keys.sorted(by: { $0 > $1 }).first {
33+
return provider(for: topMostLevel)
34+
}
35+
return nil
36+
}
37+
38+
static func isCurrentlyDisplaying(entryNamed name: String? = nil) -> Bool {
39+
for provider in providers.values
40+
where provider.isCurrentlyDisplaying(entryNamed: name) {
41+
return true
42+
}
43+
return false
44+
}
45+
46+
static func queueContains(entryNamed name: String? = nil) -> Bool {
47+
for provider in providers.values
48+
where provider.queueContains(entryNamed: name) {
49+
return true
50+
}
51+
return false
52+
}
53+
54+
static func dismiss(_ descriptor: SwiftEntryKit.EntryDismissalDescriptor, with completion: SwiftEntryKit.DismissCompletionHandler? = nil) {
55+
56+
switch descriptor {
57+
case .displayed:
58+
topMostProvider?.rootVC?.animateOutLastEntry(completionHandler: completion)
59+
default:
60+
let dispatchGroup = DispatchGroup()
61+
for provider in providers.values {
62+
dispatchGroup.enter()
63+
provider.dismiss(descriptor) {
64+
dispatchGroup.leave()
65+
}
66+
}
67+
dispatchGroup.notify(queue: .main, execute: completion ?? {})
68+
}
69+
}
70+
71+
static func layoutIfNeeded() {
72+
providers.values.forEach { $0.layoutIfNeeded() }
73+
}
2074

2175
/** Current entry window */
2276
var entryWindow: EKWindow!
@@ -37,8 +91,12 @@ final class EKWindowProvider: EntryPresenterDelegate, EKProvider {
3791

3892
private weak var entryView: EKEntryView!
3993

94+
private let windowLevel: UIWindow.Level
95+
4096
/** Cannot be instantiated, customized, inherited */
41-
private init() {}
97+
private init(level: UIWindow.Level) {
98+
windowLevel = level
99+
}
42100

43101
var isResponsiveToTouches: Bool {
44102
set {
@@ -59,7 +117,6 @@ final class EKWindowProvider: EntryPresenterDelegate, EKProvider {
59117
}
60118
entryVC.setStatusBarStyle(for: attributes)
61119

62-
entryWindow.windowLevel = attributes.windowLevel.value
63120
if presentInsideKeyWindow {
64121
entryWindow.makeKeyAndVisible()
65122
} else {
@@ -74,7 +131,7 @@ final class EKWindowProvider: EntryPresenterDelegate, EKProvider {
74131
let entryVC: EKRootViewController
75132
if entryWindow == nil {
76133
entryVC = EKRootViewController(with: self)
77-
entryWindow = EKWindow(with: entryVC)
134+
entryWindow = EKWindow(with: entryVC, level: windowLevel)
78135
mainRollbackWindow = UIApplication.shared.keyWindow
79136
} else {
80137
entryVC = rootVC!
@@ -146,6 +203,9 @@ final class EKWindowProvider: EntryPresenterDelegate, EKProvider {
146203

147204
/** Clear all entries immediately and display to the rollback window */
148205
func displayRollbackWindow() {
206+
defer {
207+
EKWindowProvider.providers.removeValue(forKey: windowLevel)
208+
}
149209
if #available(iOS 13.0, *) {
150210
entryWindow.windowScene = nil
151211
}

Source/SwiftEntryKit.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ extension UIApplication {
2020

2121
static var WindowProviderKey = UnsafeRawPointer(bitPattern: "windowProvider".hashValue)!
2222

23-
var entryProvider: EKWindowProvider {
24-
return EKWindowProvider.shared
23+
func entryProvider(for level: UIWindow.Level) -> EKWindowProvider {
24+
return EKWindowProvider.provider(for: level)
2525
}
26-
27-
public var entryWindow: UIWindow? {
28-
return entryProvider.entryWindow
26+
27+
func entryWindow(for level: UIWindow.Level) -> UIWindow? {
28+
return EKWindowProvider.provider(for: level).entryWindow
2929
}
3030

3131
}
@@ -328,7 +328,7 @@ extension SwiftEntryKit {
328328
- parameter name: The name of the entry. Its default value is *nil*.
329329
*/
330330
public class func isCurrentlyDisplaying(entryNamed name: String? = nil) -> Bool {
331-
return EKWindowProvider.shared.isCurrentlyDisplaying(entryNamed: name)
331+
return EKWindowProvider.isCurrentlyDisplaying(entryNamed: name)
332332
}
333333

334334
/**
@@ -347,7 +347,7 @@ extension SwiftEntryKit {
347347
- parameter name: The name of the entry. Its default value is *nil*.
348348
*/
349349
public class func queueContains(entryNamed name: String? = nil) -> Bool {
350-
return EKWindowProvider.shared.queueContains(entryNamed: name)
350+
return EKWindowProvider.queueContains(entryNamed: name)
351351
}
352352

353353
/**
@@ -363,7 +363,7 @@ extension SwiftEntryKit {
363363
DispatchQueue.main.async {
364364
view.entryPresenting = UIApplication.shared
365365
view.entrySender = sender
366-
EKWindowProvider.shared.display(view: view, using: attributes, presentInsideKeyWindow: presentInsideKeyWindow, rollbackWindow: rollbackWindow)
366+
EKWindowProvider.provider(for: attributes.windowLevel.value).display(view: view, using: attributes, presentInsideKeyWindow: presentInsideKeyWindow, rollbackWindow: rollbackWindow)
367367
}
368368
}
369369

@@ -380,7 +380,7 @@ extension SwiftEntryKit {
380380
DispatchQueue.main.async {
381381
viewController.entryPresenting = UIApplication.shared
382382
viewController.entrySender = sender
383-
EKWindowProvider.shared.display(viewController: viewController, using: attributes, presentInsideKeyWindow: presentInsideKeyWindow, rollbackWindow: rollbackWindow)
383+
EKWindowProvider.provider(for: attributes.windowLevel.value).display(viewController: viewController, using: attributes, presentInsideKeyWindow: presentInsideKeyWindow, rollbackWindow: rollbackWindow)
384384
}
385385
}
386386

@@ -393,7 +393,7 @@ extension SwiftEntryKit {
393393
*/
394394
public class func transform(to view: UIView) {
395395
DispatchQueue.main.async {
396-
EKWindowProvider.shared.transform(to: view)
396+
EKWindowProvider.topMostProvider?.transform(to: view)
397397
}
398398
}
399399

@@ -406,7 +406,7 @@ extension SwiftEntryKit {
406406
*/
407407
public class func dismiss(_ descriptor: EntryDismissalDescriptor = .displayed, with completion: DismissCompletionHandler? = nil) {
408408
DispatchQueue.main.async {
409-
EKWindowProvider.shared.dismiss(descriptor, with: completion)
409+
EKWindowProvider.dismiss(descriptor, with: completion)
410410
}
411411
}
412412

@@ -418,10 +418,10 @@ extension SwiftEntryKit {
418418
*/
419419
public class func layoutIfNeeded() {
420420
if Thread.isMainThread {
421-
EKWindowProvider.shared.layoutIfNeeded()
421+
EKWindowProvider.layoutIfNeeded()
422422
} else {
423423
DispatchQueue.main.async {
424-
EKWindowProvider.shared.layoutIfNeeded()
424+
EKWindowProvider.layoutIfNeeded()
425425
}
426426
}
427427
}

0 commit comments

Comments
 (0)