diff --git a/MVMCoreUI/Alerts/AlertOperation.swift b/MVMCoreUI/Alerts/AlertOperation.swift index bbffbd32..088a6ff5 100644 --- a/MVMCoreUI/Alerts/AlertOperation.swift +++ b/MVMCoreUI/Alerts/AlertOperation.swift @@ -51,30 +51,33 @@ public class AlertOperation: MVMCoreOperation { // Observe for when it is removed. observeForCurrentAlertViewDismissal() - // Adds the presentation to the animation queue. - let blockingOperation = MVMCoreOperation() - self.blockingOperation = blockingOperation - Task { @MainActor in - MVMCoreNavigationHandler.shared()?.present(alertController, animated: true, delegate: nil) { [weak self] in - guard let self = self else { - blockingOperation.markAsFinished() - return - } - Task { - // We finished but it was not displayed yet. It's possible that it was cancelled. Finish this task - if await !self.properties.getIsDisplayed() { - self.markAsFinished() - } else { - (CoreUIObject.sharedInstance()?.loggingDelegate as? MVMCoreUILoggingDelegateProtocol)?.logAlert(with: self.alertObject) - if self.isCancelled { - await self.dismissAlertView() - } - } - } + Task(priority: .high) { + guard let viewControllerToPresentOn = await NavigationHandler.shared().getViewControllerToPresentOn() else { + markAsFinished() + return } - // Block navigations until this alert is removed. - MVMCoreNavigationHandler.shared()?.addNavigationOperation(blockingOperation) + // Presents the alert. + let presentationOperation = await NavigationOperation(with: .present(viewController: alertController, onController: viewControllerToPresentOn), tryToReplace: false) + + let blockingOperation = MVMCoreOperation() + blockingOperation.addDependency(presentationOperation) + self.blockingOperation = blockingOperation + + // Block other navigation until this alert is removed. + NavigationHandler.shared().navigationQueue.addOperation(blockingOperation) + + await NavigationHandler.shared().navigate(with: presentationOperation) + + // We finished but it was not displayed yet. It's possible that it was cancelled. Finish this task + if await !self.properties.getIsDisplayed() { + self.markAsFinished() + } else { + (CoreUIObject.sharedInstance()?.loggingDelegate as? MVMCoreUILoggingDelegateProtocol)?.logAlert(with: self.alertObject) + if self.isCancelled { + await self.dismissAlertView() + } + } } } @@ -89,10 +92,13 @@ public class AlertOperation: MVMCoreOperation { private func dismissAlertView() async { guard await properties.getIsDisplayed() else { return } await withCheckedContinuation { continuation in - Task { @MainActor in - MVMCoreNavigationHandler.shared()?.dismiss(alertController, animated: true, delegate: nil) { - continuation.resume() - } + Task { + let dismissOperation = await NavigationOperation(with: .dismiss(viewController: alertController)) + dismissOperation.queuePriority = .veryHigh + let task = Task(priority: .high) { await NavigationHandler.shared().navigate(with: dismissOperation) } + blockingOperation?.markAsFinished() + _ = await task.result + continuation.resume() } } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryField.swift index 502682c2..910712d5 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/MdnEntryField.swift @@ -144,7 +144,9 @@ import MVMCore picker.displayedPropertyKeys = ["phoneNumbers"] picker.predicateForEnablingContact = NSPredicate(format: "phoneNumbers.@count > 0") picker.predicateForSelectionOfProperty = NSPredicate(format: "key == 'phoneNumbers'") - MVMCoreNavigationHandler.shared()?.present(picker, animated: true) + Task(priority: .userInitiated) { + await NavigationHandler.shared().present(viewController: picker, animated: true) + } } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift index 9e11ac02..4c677029 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift @@ -114,7 +114,7 @@ import MVMCore open func performBlockOperation(with block: @escaping (MVMCoreBlockOperation) -> Void) { let operation = MVMCoreBlockOperation(block: block)! - MVMCoreNavigationHandler.shared()?.addNavigationOperation(operation) + NavigationHandler.shared().navigationQueue.addOperation(operation) } /// Collapses after a delay diff --git a/MVMCoreUI/Containers/NavigationController/NavigationController.swift b/MVMCoreUI/Containers/NavigationController/NavigationController.swift index eb43201a..8f72a792 100644 --- a/MVMCoreUI/Containers/NavigationController/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController/NavigationController.swift @@ -7,9 +7,12 @@ // import UIKit +import MVMCore +import Combine @objcMembers open class NavigationController: UINavigationController, MVMCoreViewManagerViewControllerProtocol { public weak var manager: (UIViewController & MVMCoreViewManagerProtocol)? + private var cancellables: Set = [] /// Getter for the main navigation controller public static func navigationController() -> Self? { @@ -20,9 +23,9 @@ import UIKit public static func setupNavigationController() -> Self? { let navigationController = self.init() MVMCoreUISession.sharedGlobal()?.navigationController = navigationController - MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController - MVMCoreNavigationHandler.shared()?.navigationController = navigationController - MVMCoreNavigationHandler.shared()?.addDelegate(navigationController) + NavigationHandler.shared().viewControllerToPresentOn = navigationController + NavigationHandler.shared().navigationController = navigationController + navigationController.subscribe() navigationController.setNavigationBarUI(with: NavigationItemModel()) return navigationController } @@ -34,15 +37,45 @@ import UIKit return navigationController } - /// Convenience function to return the navigation model of the lowest controller traversing managers if applicable. + /** Subscribes for events. + Updates the navigation item of the new view controller when one is pushed. + Based on ``NavigationItemModelProtocol`` of ``PageProtocol/pageModel``. Traverses the manager for the view controller if necessary. + */ + @MainActor + public func subscribe() { + NavigationHandler.shared().onNavigation.sink { [weak self] (event, operation) in + guard let self = self, + self == operation.navigationType.getNavigationController(), + let viewController = NavigationHandler.shared().getViewControllers(for: self).last, + let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) else { return } + switch event { + case .willNavigate: + if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) { + MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: controller) + } + if let model = getNavigationModel(from: newViewController) { + self.setNavigationItem(with: model, for: viewController) + self.setNavigationBarUI(with: model) + } + self.manager?.willDisplay?(newViewController) + case .didNavigate: + self.manager?.displayedViewController?(newViewController) + if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) { + controller.viewControllerReady?(inManager: self) + } + @unknown default: break + } + }.store(in: &cancellables) + } + + /// Convenience function to return the navigation model of the view controller. public func getNavigationModel(from viewController: UIViewController) -> NavigationItemModelProtocol? { return (viewController as? PageProtocol)?.pageModel?.navigationBar } /// Verifies the controller is the currently displayed controller. public func isDisplayed(viewController: UIViewController) -> Bool { - guard let topViewController = topViewController, - viewController == MVMCoreUIUtility.getViewControllerTraversingManagers(topViewController) else { + guard viewController == getViewController() else { return false } return true @@ -84,8 +117,10 @@ extension NavigationController: MVMCoreViewManagerProtocol { manager?.willDisplay?(viewController) } + /// Updates the navigation item/bar of the current view controller based on the passed in model and view controller. + @MainActor private func updateNavigationView(with model: NavigationItemModelProtocol, for viewController: UIViewController) { - guard let topViewController = topViewController else { return } + guard let topViewController = NavigationHandler.shared().getViewControllers(for: self).last else { return } setNavigationItem(with: model, for: topViewController, coordinatingWith: viewController as? PageBehaviorHandlerProtocol) setNavigationBarUI(with: model) @@ -99,34 +134,6 @@ extension NavigationController: MVMCoreViewManagerProtocol { } } -extension NavigationController: MVMCorePresentationDelegateProtocol { - public func navigationController(_ navigationController: UINavigationController, prepareDisplayFor viewController: UIViewController) { - if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) { - MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: controller) - } - guard self == navigationController, - let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController), - let model = getNavigationModel(from: newViewController) else { return } - setNavigationItem(with: model, for: viewController) - setNavigationBarUI(with: model) - } - - public func navigationController(_ navigationController: UINavigationController, willDisplay viewController: UIViewController) { - guard self == navigationController, - let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) else { return } - manager?.willDisplay?(newViewController) - } - - public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) { - guard self == navigationController, - let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) else { return } - manager?.displayedViewController?(newViewController) - if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) { - controller.viewControllerReady?(inManager: self) - } - } -} - extension UIColor { func image(_ size: CGSize = CGSize(width: 1, height: 1)) -> UIImage { return UIGraphicsImageRenderer(size: size).image { rendererContext in diff --git a/MVMCoreUI/Containers/NavigationController/UINavigationController+Extension.swift b/MVMCoreUI/Containers/NavigationController/UINavigationController+Extension.swift index e1d3a247..625f0a25 100644 --- a/MVMCoreUI/Containers/NavigationController/UINavigationController+Extension.swift +++ b/MVMCoreUI/Containers/NavigationController/UINavigationController+Extension.swift @@ -7,6 +7,7 @@ // import Foundation +import MVMCore public extension UINavigationController { @@ -38,7 +39,7 @@ public extension UINavigationController { if model.hidesSystemBackButton, model.alwaysShowBackButton != false { if let backButtonModel = model.backButton, - MVMCoreNavigationHandler.shared()?.getViewControllers(for: self)?.count ?? 0 > 1 || model.alwaysShowBackButton ?? false { + NavigationHandler.shared().getViewControllers(for: self).count > 1 || model.alwaysShowBackButton ?? false { leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) } if let leftItemModels = model.additionalLeftButtons { @@ -113,4 +114,11 @@ public extension UINavigationController { setNavigationBarHidden(model.hidden, animated: true) } + + @MainActor + func getViewController() -> UIViewController? { + guard let topViewController = getViewControllers().last, + let viewController = MVMCoreUIUtility.getViewControllerTraversingManagers(topViewController) else { return nil } + return viewController + } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index 8b509827..20f4449c 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -20,7 +20,8 @@ public protocol StatusBarUI { // Navigation bar update functions public extension MVMCoreUISplitViewController { - /// Updates the state for various controls (navigation, tab, progress) for the controller. + /// Updates the state for various controls (top navigation controller item, tab, progress) for the controller. + @MainActor func updateState(with viewController: UIViewController) { guard let navigationController = navigationController, navigationController.isDisplayed(viewController: viewController) else { return } @@ -30,7 +31,9 @@ public extension MVMCoreUISplitViewController { } // MARK: - Progress Bar - /// Updates the progress bar based on the page json for the view controller. + /** Updates the progress bar based on the page json for the view controller. + Uses a string value between 0 and 100 from key progressPercent in the MVMCoreViewControllerProtocol.loadObject.pageJSON. + */ func updateProgressBar(for viewController: UIViewController) { guard let viewController = viewController as? MVMCoreViewControllerProtocol else { return } var progress: Float = 0.0 @@ -42,12 +45,16 @@ public extension MVMCoreUISplitViewController { } // MARK: - Tab Bar - /// Updates the tab bar based on the page json for the view controller. + /** Updates the tab bar based on the page json for the view controller. + For the index: checks the view controller's pageModel (``PageProtocol``) property ``TabPageModelProtocol/tabBarIndex``, else tabBarIndex in action map that led to this page, else the previous tab bar index of this page. + For hidden: checks the view controller's pageModel (``PageProtocol``) property ``TabPageModelProtocol/tabBarHidden``, else tabBarHidden in action map that led to this page, else it is visibile. + */ + @MainActor func updateTabBar(for viewController: UIViewController) { let mvmViewController = viewController as? MVMCoreViewControllerProtocol tabBar?.delegateObject = mvmViewController?.delegateObject?() as? MVMCoreUIDelegateObject - let navigationIndex = (MVMCoreNavigationHandler.shared()?.getViewControllers(for: navigationController)?.count ?? 1) - 1 + let navigationIndex = (navigationController != nil ? NavigationHandler.shared().getViewControllers(for: navigationController!).count : 1) - 1 // Set the highlighted index. In terms of priority, Page > Action > Previous. if let index = ((viewController as? PageProtocol)?.pageModel as? TabPageModelProtocol)?.tabBarIndex { @@ -80,10 +87,13 @@ public extension MVMCoreUISplitViewController { } // MARK: - Navigation Bar - /// Convenience function. Sets the navigation and split view properties for the view controller. Panel access is determined if view controller is a detail view protocol. + /** Convenience function. Sets the navigation and split view properties for the view controller. + Panel access is determined if view controller is a ``MVMCoreUIDetailViewProtocol`` + */ + @MainActor func setNavigationBar(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) { guard navigationController == self.navigationController, - viewController == getCurrentDetailViewController() else { + self.navigationController?.isDisplayed(viewController: viewController) == true else { /// Not the split view navigation controller, skip split functions. return } @@ -97,9 +107,13 @@ public extension MVMCoreUISplitViewController { setNavigationIconColor(navigationItemModel.tintColor.uiColor) } - /// Sets the left navigation items for the view controller based on model and splitview. + /** Sets the left navigation items for the top view controller based on the model and viewController. + Panel access is determined if view controller is a ``MVMCoreUIDetailViewProtocol`` + */ + @MainActor func setLeftNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) { - guard let topViewController = navigationController.topViewController else { return } + let viewControllers = NavigationHandler.shared().getViewControllers(for: navigationController) + guard let topViewController = viewControllers.last else { return } var leftItems: [UIBarButtonItem] = [] let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject @@ -110,7 +124,7 @@ public extension MVMCoreUISplitViewController { if let forceBackButton = navigationItemModel?.alwaysShowBackButton { showBackButton = forceBackButton } else { - showBackButton = MVMCoreNavigationHandler.shared()?.getViewControllers(for: navigationController)?.count ?? 0 > 1 + showBackButton = viewControllers.count > 1 } if showBackButton { if let backButtonModel = navigationItemModel?.backButton { @@ -144,9 +158,12 @@ public extension MVMCoreUISplitViewController { topViewController.navigationItem.setLeftBarButtonItems(leftItems.count > 0 ? leftItems : nil, animated: !DisableAnimations.boolValue) } - /// Sets the right navigation items for the view controller based on model and splitview. + /** Sets the right navigation items for the top view controller based on the model and viewController. + Panel access is determined if view controller is a ``MVMCoreUIDetailViewProtocol`` + */ + @MainActor func setRightNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) { - guard let topViewController = navigationController.topViewController else { return } + guard let topViewController = NavigationHandler.shared().getViewControllers(for: navigationController).last else { return } let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject var rightItems: [UIBarButtonItem] = [] @@ -173,6 +190,9 @@ public extension MVMCoreUISplitViewController { topViewController.navigationItem.setRightBarButtonItems(rightItems.count > 0 ? rightItems : nil, animated: !DisableAnimations.boolValue) } + /** If the current detail view controller has a navigation model. + ``NavigationController/getNavigationModel(from:)`` from ``getCurrentDetailViewController()`` + */ @objc func navigationBarModelExists() -> Bool { // Legacy Navigation guard let currentViewController = getCurrentDetailViewController(), @@ -180,8 +200,9 @@ public extension MVMCoreUISplitViewController { return true } - /// Convenience function to update the navigation bar if the controller is the current lowest controller. - @objc func updateNavigationBarFor(viewController: UIViewController) { + /// Convenience function to update the navigation bar if the controller is the current detail controller. + @MainActor @objc + func updateNavigationBarFor(viewController: UIViewController) { guard let navigationController = navigationController, navigationController.isDisplayed(viewController: viewController), let model = navigationController.getNavigationModel(from: viewController) else { return } @@ -201,9 +222,13 @@ public extension MVMCoreUISplitViewController { return .default } - /// Updates the status bar background color and style. - @objc func setStatusBarForCurrentViewController() { - let viewController = getCurrentViewController() as? MVMCoreUIDetailViewProtocol + /** Updates the status bar background color and style for the passed view controller. + The background color is fetched from ``MVMCoreUIDetailViewProtocol/defaultStatusBarBackgroundColor()``, else the current navigation bar background, else the current status bar background color. + The backgroundStytle is fetched from ``MVMCoreUIDetailViewProtocol/defaultStatusBarStyle()`` else ``getStatusBarStyle(for:)`` + */ + @MainActor + func setStatusBar(for viewController: UIViewController?) { + let viewController = viewController as? MVMCoreUIDetailViewProtocol let backgroundColor = viewController?.defaultStatusBarBackgroundColor?() ?? navigationController?.navigationBar.standardAppearance.backgroundColor ?? statusBarView?.backgroundColor @@ -213,6 +238,14 @@ public extension MVMCoreUISplitViewController { setStatusBarBackgroundColor(backgroundColor, style: style) } + + /** Updates the status bar background color and style for the current view controller. + See ``setStatusBar(for:)`` + */ + @MainActor + @objc func setStatusBarForCurrentViewController() { + setStatusBar(for: getCurrentViewController()) + } } extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol { @@ -235,6 +268,17 @@ extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol { } @objc public extension MVMCoreUISplitViewController { + @objc func goBack() { + Task(priority: .userInitiated) { @MainActor in + if let viewController = getCurrentDetailViewController() as? MVMCoreUIDetailViewProtocol, + let backButtonPressed = viewController.backButtonPressed { + backButtonPressed() + } else { + await NavigationHandler.shared().popTopViewController() + } + } + } + /// Subscribes for notification events. @objc func subscribeForNotifications() { guard cancellables == nil else { return } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index 045dd0bf..c43d17d9 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -7,7 +7,6 @@ // #import "MVMCoreUISplitViewController.h" -@import MVMCore.MVMCoreNavigationHandler; @import MVMCore.MVMCoreDispatchUtility; @import MVMCore.MVMCoreViewManagerProtocol; @import MVMCore.MVMCoreActionUtility; @@ -192,14 +191,7 @@ CGFloat const PanelAnimationDuration = 0.2; } - (IBAction)backButtonPressed:(id)sender { - [MVMCoreDispatchUtility performBlockOnMainThread:^{ - UIViewController *detailViewController = [self getCurrentDetailViewController]; - if ([detailViewController conformsToProtocol:@protocol(MVMCoreUIDetailViewProtocol)] && [detailViewController respondsToSelector:@selector(backButtonPressed)]) { - [((UIViewController *)detailViewController) backButtonPressed]; - } else { - [[MVMCoreNavigationHandler sharedNavigationHandler] popTopViewControllerAnimated:YES]; - } - }]; + [self goBack]; } - (IBAction)rightPanelButtonPressed:(id)sender { @@ -258,7 +250,7 @@ CGFloat const PanelAnimationDuration = 0.2; - (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended { NSMutableArray *leftBarButtonItems = [NSMutableArray array]; - if (self.navigationController && [MVMCoreNavigationHandler.sharedNavigationHandler getViewControllersForNavigationController:self.navigationController].count > 1) { + if (self.navigationController && [self.navigationController getViewControllers].count > 1) { [leftBarButtonItems addObject:self.backButton]; } if ((accessible && !extended) && self.leftPanelButton) { @@ -1074,7 +1066,7 @@ CGFloat const PanelAnimationDuration = 0.2; // Returns the desired view or falls back. Hot fix until we can get away from using these functions... + (CGRect)getBounds:(UIView *)desiredView { - UIView *view = desiredView ?: [MVMCoreNavigationHandler sharedNavigationHandler].navigationController.view ?: [MVMCoreGetterUtility getKeyWindow].rootViewController.view; + UIView *view = desiredView ?: [self mainSplitViewController].navigationController.view ?: [MVMCoreGetterUtility getKeyWindow].rootViewController.view; return view ? view.bounds : [UIScreen mainScreen].bounds; } @@ -1099,17 +1091,7 @@ CGFloat const PanelAnimationDuration = 0.2; } - (UIViewController *)getCurrentVisibleController { - UIViewController *baseViewController = [MVMCoreNavigationHandler sharedNavigationHandler].viewControllerToPresentOn ?: [MVMCoreGetterUtility getKeyWindow].rootViewController; - UIViewController *viewController = nil; - while (baseViewController.presentedViewController && !baseViewController.presentedViewController.isBeingDismissed) { - viewController = baseViewController.presentedViewController; - baseViewController = viewController; - } - // if it is not presented viewcontroller, existing BAU logic will be working - if (!viewController) { - viewController = [MVMCoreUIUtility getViewControllerTraversingManagers:self.navigationController.topViewController]; - } - return viewController; + return [MVMCoreUIUtility getCurrentVisibleController]; } - (UIViewController *)getCurrentDetailViewController { diff --git a/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift b/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift index db339cf8..7bdee349 100644 --- a/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift +++ b/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift @@ -205,7 +205,7 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, } } - public func navigationController(_ navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { + public func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { // Only percent interact if we've already loaded the view controller guard let customInteractor = customInteractor, let index = index, @@ -229,7 +229,7 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, hideNavigationBarLine(true) } - public func navigationController(_ navigationController: UINavigationController, willDisplay viewController: UIViewController) { + public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { guard navigationController == subNavigationController else { return } if let viewController = viewController as? UIViewController & MVMCoreViewManagerViewControllerProtocol & MVMCoreViewControllerProtocol { @@ -247,7 +247,7 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, commitTo(controller: viewController) } - public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) { + public func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { guard navigationController == subNavigationController else { return } // Need to track swipe action. if needToTrackTabSelect { @@ -272,7 +272,9 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, if let controller = viewControllers[indexPath.row] { // Load controller from the cache needToTrackTabSelect = true - MVMCoreNavigationHandler.shared()?.replaceTopViewController(with: controller, navigationController: subNavigationController, animated: true, delegate: self, replaceInStack: false, completionHandler: nil) + Task(priority: .userInitiated) { + await NavigationHandler.shared().replace(viewController: controller, navigationController: subNavigationController, tryToReplace: false, animated: true) + } } else if let tabsModel = tabs.tabsModel, let action = tabsModel.tabs[indexPath.row].action { // Perform the tab action diff --git a/MVMCoreUI/Notification/NotificationHandler.swift b/MVMCoreUI/Notification/NotificationHandler.swift index c1e064d7..9ecc2efb 100644 --- a/MVMCoreUI/Notification/NotificationHandler.swift +++ b/MVMCoreUI/Notification/NotificationHandler.swift @@ -268,7 +268,7 @@ public class NotificationOperation: MVMCoreOperation { } })! transitionOperation.completionBlock = completionBlock - MVMCoreNavigationHandler.shared()?.addNavigationOperation(transitionOperation) + NavigationHandler.shared().navigationQueue.addOperation(transitionOperation) return transitionOperation } @@ -314,6 +314,8 @@ open class NotificationHandler { private var transitionDelegate: NotificationTransitionDelegateProtocol + private var cancellable: Cancellable? + // MARK: - Publishers /// Publishes when a notification will show. @@ -354,7 +356,27 @@ open class NotificationHandler { /// Registers to know when pages change. private func registerForPageChanges() { - MVMCoreNavigationHandler.shared()?.addDelegate(self) + cancellable = NavigationHandler.shared().onNavigation + .filter({ $0.0 == .didNavigate }) + .sink { [weak self] (event, operation) in + guard let self = self else { return } + Task { + // Update displayable for each top alert operation when page type changes, in top queue priority order. + guard self.queue.operations.count > 0, + let navigationController = await operation.navigationType.getNavigationController(), + await navigationController == MVMCoreUISplitViewController.main()?.navigationController, + let viewController = await navigationController.getViewController() else { return } + let pageType = (viewController as? MVMCoreViewControllerProtocol)?.pageType + self.queue.operations.compactMap { + $0 as? NotificationOperation + }.sorted { + $0.notificationModel.priority.rawValue > $1.notificationModel.priority.rawValue + }.forEach { + $0.updateDisplayable(by: pageType) + } + self.reevaluteQueue() + } + } } /// Checks for new top alert json @@ -534,26 +556,6 @@ open class NotificationHandler { } } -extension NotificationHandler: MVMCorePresentationDelegateProtocol { - // Update displayable for each top alert operation when page type changes, in top queue priority order. - public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) { - guard queue.operations.count > 0 else { return } - let viewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) - guard viewController == MVMCoreUISplitViewController.main()?.getCurrentViewController() else { return } - let pageType = (viewController as? MVMCoreViewControllerProtocol)?.pageType - Task { - queue.operations.compactMap { - $0 as? NotificationOperation - }.sorted { - $0.notificationModel.priority.rawValue > $1.notificationModel.priority.rawValue - }.forEach { - $0.updateDisplayable(by: pageType) - } - reevaluteQueue() - } - } -} - extension NotificationOperation { /// Updates the operation and notification with the new model. public func update(with model: NotificationModel, delegateObject: MVMCoreUIDelegateObject?) { diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift index b2c3d42c..49a3d5e9 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift @@ -57,6 +57,8 @@ import SafariServices @MainActor open func openURL(inSafariWebView url: URL) { let safariViewController = SFSafariViewController(url: url) - MVMCoreNavigationHandler.shared()?.present(safariViewController, animated: true) + Task(priority: .high) { + await NavigationHandler.shared().present(viewController: safariViewController) + } } } diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility+Extension.swift b/MVMCoreUI/Utility/MVMCoreUIUtility+Extension.swift index ffb74ef9..e04de6bb 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility+Extension.swift +++ b/MVMCoreUI/Utility/MVMCoreUIUtility+Extension.swift @@ -7,7 +7,7 @@ // import UIKit - +import MVMCore public extension MVMCoreUIUtility { @@ -66,3 +66,25 @@ public extension MVMCoreUIUtility { return nil } } + +@objc +public extension MVMCoreUIUtility { + @objc @MainActor + static func getVisibleViewController() -> UIViewController? { + var viewController = NavigationHandler.shared().getViewControllerToPresentOn() + while let presentedController = viewController?.presentedViewController, + !presentedController.isBeingDismissed { + viewController = presentedController + } + if let navigationController = viewController as? UINavigationController { + viewController = navigationController.topViewController + } + if let viewController = viewController { + return getViewControllerTraversingManagers(viewController) + } else if let viewController = MVMCoreUISession.sharedGlobal()?.navigationController?.topViewController { + return getViewControllerTraversingManagers(viewController) + } else { + return nil + } + } +} diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index 01a8c435..ad7366df 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -11,7 +11,6 @@ #import "MVMCoreUISession.h" #import "MVMCoreUISplitViewController.h" #import -@import MVMCore.MVMCoreNavigationHandler; @import MVMCore.MVMCoreGetterUtility; @implementation MVMCoreUIUtility @@ -52,19 +51,10 @@ } + (UIViewController *)getCurrentVisibleController { - UIViewController *baseViewController = [MVMCoreNavigationHandler sharedNavigationHandler].viewControllerToPresentOn ?: [MVMCoreGetterUtility getKeyWindow].rootViewController; - UIViewController *viewController = nil; - while (baseViewController.presentedViewController && !baseViewController.presentedViewController.isBeingDismissed) { - viewController = baseViewController.presentedViewController; - baseViewController = viewController; - } - if ([viewController isKindOfClass:[UINavigationController class]]) { - viewController = ((UINavigationController *)viewController).topViewController; - } - // if it is not presented viewcontroller, existing BAU logic will be working - if (!viewController) { - viewController = [self getViewControllerTraversingManagers:[MVMCoreUISession sharedGlobal].navigationController.topViewController]; - } + __block UIViewController *viewController = nil; + [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ + viewController = [self getVisibleViewController]; + }]; return viewController; }