-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1f89a48
commit 82b382e
Showing
10 changed files
with
680 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// | ||
// ModalEventsManager.swift | ||
// react-native-ios-modal | ||
// | ||
// Created by Dominic Go on 10/2/24. | ||
// | ||
|
||
import UIKit | ||
import DGSwiftUtilities | ||
|
||
|
||
public final class ModalEventsManager { | ||
|
||
// MARK: - Properties | ||
// ------------------ | ||
|
||
public weak var window: UIWindow?; | ||
private(set) public var modalRegistry: ModalRegistry = .init(); | ||
|
||
// MARK: - Init | ||
// ------------ | ||
|
||
public init(withWindow window: UIWindow){ | ||
self.window = window; | ||
}; | ||
|
||
func registerModalIfNeeded( | ||
_ modalVC: UIViewController, | ||
isAboutToBePresented: Bool | ||
){ | ||
let window = | ||
self.window | ||
?? modalVC.view.window | ||
?? UIApplication.shared.activeWindow; | ||
|
||
guard let window = window else { | ||
return; | ||
}; | ||
|
||
if self.window == nil { | ||
self.window = window; | ||
}; | ||
|
||
let currentModalLevel = window.currentModalLevel ?? -1; | ||
|
||
let nextModalFocusIndex = isAboutToBePresented | ||
? currentModalLevel + 1 | ||
: modalVC.modalLevel ?? -1; | ||
|
||
self.modalRegistry.registerModalIfNeeded( | ||
modalVC, | ||
modalFocusIndex: nextModalFocusIndex | ||
); | ||
}; | ||
|
||
// MARK: - Methods Invoked Via Swizzling | ||
// ------------------------------------- | ||
|
||
public func notifyOnModalWillPresent( | ||
forViewController modalVC: UIViewController, | ||
targetWindow: UIWindow? | ||
){ | ||
|
||
guard let targetWindow = targetWindow else { | ||
return | ||
}; | ||
|
||
let eventManager = | ||
ModalEventsManagerRegistry.shared.getManager(forWindow: targetWindow); | ||
|
||
eventManager.registerModalIfNeeded( | ||
modalVC, | ||
isAboutToBePresented: true | ||
); | ||
|
||
let modalEntries = eventManager.modalRegistry.getEntriesGrouped(); | ||
|
||
modalEntries.topMostModal!.setModalFocusState(.focusing); | ||
modalEntries.secondTopMostModal?.setModalFocusState(.blurring); | ||
|
||
modalEntries.otherModals?.forEach { | ||
$0.setModalFocusState(.blurred); | ||
}; | ||
}; | ||
|
||
public func notifyOnModalDidPresent( | ||
forViewController modalVC: UIViewController, | ||
targetWindow: UIWindow? | ||
){ | ||
guard let targetWindow = targetWindow else { | ||
return | ||
}; | ||
|
||
let eventManager = | ||
ModalEventsManagerRegistry.shared.getManager(forWindow: targetWindow); | ||
|
||
let modalEntries = eventManager.modalRegistry.getEntriesGrouped(); | ||
|
||
modalEntries.topMostModal!.setModalFocusState(.focused); | ||
modalEntries.secondTopMostModal?.setModalFocusState(.blurred); | ||
|
||
modalEntries.otherModals?.forEach { | ||
$0.setModalFocusState(.blurred); | ||
}; | ||
}; | ||
|
||
public func notifyOnModalWillDismiss( | ||
forViewController modalVC: UIViewController, | ||
targetWindow: UIWindow? | ||
){ | ||
guard let targetWindow = targetWindow else { | ||
return | ||
}; | ||
|
||
let eventManager = | ||
ModalEventsManagerRegistry.shared.getManager(forWindow: targetWindow); | ||
|
||
let modalEntries = eventManager.modalRegistry.getEntriesGrouped(); | ||
|
||
modalEntries.topMostModal!.setModalFocusState(.blurring); | ||
modalEntries.secondTopMostModal?.setModalFocusState(.focusing); | ||
|
||
modalEntries.otherModals?.forEach { | ||
$0.setModalFocusState(.blurred); | ||
}; | ||
}; | ||
|
||
public func notifyOnModalDidDismiss( | ||
forViewController modalVC: UIViewController, | ||
targetWindow: UIWindow? | ||
){ | ||
guard let targetWindow = targetWindow else { | ||
return | ||
}; | ||
|
||
let eventManager = | ||
ModalEventsManagerRegistry.shared.getManager(forWindow: targetWindow); | ||
|
||
let modalEntries = eventManager.modalRegistry.getEntriesGrouped(); | ||
|
||
modalEntries.topMostModal!.setModalFocusState(.blurred); | ||
modalEntries.secondTopMostModal?.setModalFocusState(.focused); | ||
|
||
modalEntries.otherModals?.forEach { | ||
$0.setModalFocusState(.blurred); | ||
}; | ||
}; | ||
}; | ||
|
148 changes: 148 additions & 0 deletions
148
ios/Temp/ModalEventManager/ModalEventsManagerRegistry.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// | ||
// ModalEventsManagerRegistry.swift | ||
// react-native-ios-modal | ||
// | ||
// Created by Dominic Go on 10/2/24. | ||
// | ||
|
||
import UIKit | ||
import DGSwiftUtilities | ||
|
||
|
||
public final class ModalEventsManagerRegistry: Singleton { | ||
|
||
public typealias `Self` = ModalEventsManagerRegistry; | ||
|
||
// MARK: Static Properties | ||
// ----------------------- | ||
|
||
public static let shared: Self = .init(); | ||
|
||
// MARK: - Static Properties (Swizzling-Related) | ||
// --------------------------------------------- | ||
|
||
private(set) public static var isSwizzled = false; | ||
public static var shouldSwizzle = true; | ||
|
||
// MARK: - Properties | ||
// ------------------ | ||
|
||
public var instanceRegistry: Dictionary<String, ModalEventsManager> = [:] | ||
|
||
// MARK: - Init | ||
// ------------ | ||
|
||
public init(){ | ||
self.swizzleIfNeeded(); | ||
}; | ||
|
||
// MARK: - Methods | ||
// --------------- | ||
|
||
public func getManager(forWindow window: UIWindow) -> ModalEventsManager { | ||
let match = self.instanceRegistry[window.synthesizedStringID]; | ||
|
||
|
||
if let match = match, | ||
match.window != nil | ||
{ | ||
return match; | ||
}; | ||
|
||
let newManager: ModalEventsManager = .init(withWindow: window); | ||
self.instanceRegistry[window.synthesizedStringID] = newManager; | ||
|
||
return newManager; | ||
}; | ||
|
||
// MARK: - Methods (Swizzling-Related) | ||
// ---------------------------------- | ||
|
||
public func swizzleIfNeeded(){ | ||
guard Self.shouldSwizzle, | ||
!Self.isSwizzled | ||
else { return }; | ||
|
||
Self.isSwizzled = true; | ||
self._swizzlePresent(); | ||
self._swizzleDismiss(); | ||
}; | ||
|
||
private func _swizzlePresent(){ | ||
SwizzlingHelpers.swizzlePresent() { originalImp, selector in | ||
return { _self, vcToPresent, animated, completion in | ||
|
||
let currentWindow = | ||
_self.view.window | ||
?? vcToPresent.view.window | ||
?? UIApplication.shared.activeWindow; | ||
|
||
guard let currentWindow = currentWindow else { | ||
#if DEBUG | ||
fatalError("Unable to get window") | ||
#else | ||
return; | ||
#endif | ||
}; | ||
|
||
let eventManager = self.getManager(forWindow: currentWindow); | ||
|
||
eventManager.notifyOnModalWillPresent( | ||
forViewController: vcToPresent, | ||
targetWindow: currentWindow | ||
); | ||
|
||
// Call the original implementation. | ||
originalImp(_self, selector, vcToPresent, animated){ | ||
eventManager.notifyOnModalDidPresent( | ||
forViewController: vcToPresent, | ||
targetWindow: currentWindow | ||
); | ||
completion?(); | ||
}; | ||
}; | ||
}; | ||
}; | ||
|
||
private func _swizzleDismiss(){ | ||
SwizzlingHelpers.swizzleDismiss() { originalImp, selector in | ||
return { _self, animated, completion in | ||
|
||
let currentWindow = | ||
_self.view.window | ||
?? _self.presentedViewController?.view.window | ||
?? UIApplication.shared.activeWindow; | ||
|
||
let modalVC = | ||
_self.presentedViewController | ||
?? currentWindow?.topmostPresentedViewController; | ||
|
||
guard let currentWindow = currentWindow, | ||
let modalVC = modalVC | ||
else { | ||
#if DEBUG | ||
fatalError("Unable to get window or presented view controller") | ||
#else | ||
return; | ||
#endif | ||
}; | ||
|
||
let eventManager = self.getManager(forWindow: currentWindow); | ||
|
||
eventManager.notifyOnModalWillDismiss( | ||
forViewController: modalVC, | ||
targetWindow: currentWindow | ||
); | ||
|
||
// Call the original implementation. | ||
originalImp(_self, selector, animated){ | ||
eventManager.notifyOnModalDidDismiss( | ||
forViewController: modalVC, | ||
targetWindow: currentWindow | ||
); | ||
completion?(); | ||
}; | ||
}; | ||
}; | ||
}; | ||
}; |
31 changes: 31 additions & 0 deletions
31
ios/Temp/ModalEventManager/ModalFocusEventNotifiable.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// | ||
// ModalFocusEventNotifiable.swift | ||
// | ||
// | ||
// Created by Dominic Go on 6/15/24. | ||
// | ||
|
||
import Foundation | ||
|
||
public protocol ModalFocusEventNotifiable: AnyObject { | ||
|
||
func notifyForModalFocusStateChange( | ||
prevState: ModalFocusState?, | ||
currentState: ModalFocusState, | ||
nextState: ModalFocusState | ||
); | ||
}; | ||
|
||
// MARK: - ModalFocusEventNotifiable+Default | ||
// ----------------------------------------- | ||
|
||
public extension ModalFocusEventNotifiable { | ||
|
||
func notifyForModalFocusStateChange( | ||
prevState: ModalFocusState?, | ||
currentState: ModalFocusState, | ||
nextState: ModalFocusState | ||
) { | ||
// no-op | ||
}; | ||
}; |
Oops, something went wrong.