From 1d556b65630b543148a4f6ab69dc212931e6721d Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 11 Jun 2021 10:31:03 -0400 Subject: [PATCH] manager changes to allow manager to handle navigation bar logic --- MVMCoreUI.xcodeproj/project.pbxproj | 4 - .../NavigationBarRefreshProtocol.swift | 34 --------- .../BaseControllers/ViewController.swift | 56 ++++---------- MVMCoreUI/Behaviors/GetContactBehavior.swift | 12 --- .../Containers/NavigationController.swift | 74 ++++++++++++++++++- .../MVMCoreUIDetailViewProtocol.h | 3 - ...MCoreUISplitViewController+Extension.swift | 32 +++++++- .../MVMCoreUISplitViewController.m | 17 ++--- MVMCoreUI/Utility/MVMCoreUIUtility.h | 3 + MVMCoreUI/Utility/MVMCoreUIUtility.m | 13 +++- 10 files changed, 136 insertions(+), 112 deletions(-) delete mode 100644 MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index d48fa228..c9833c3f 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -323,7 +323,6 @@ D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C7008250BF99B0095B21C /* TopNotificationModel.swift */; }; D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */; }; D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20F3B43252E00E4004B3F56 /* PageProtocol.swift */; }; - D20F3B5E252F9B5E004B3F56 /* NavigationBarRefreshProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D2169301251E51E7002A6324 /* SectionListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2169300251E51E7002A6324 /* SectionListTemplate.swift */; }; @@ -887,7 +886,6 @@ D20C7008250BF99B0095B21C /* TopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopNotificationModel.swift; sourceTree = ""; }; D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertView+Extension.swift"; sourceTree = ""; }; D20F3B43252E00E4004B3F56 /* PageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageProtocol.swift; sourceTree = ""; }; - D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarRefreshProtocol.swift; sourceTree = ""; }; D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; D2169300251E51E7002A6324 /* SectionListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionListTemplate.swift; sourceTree = ""; }; @@ -2319,7 +2317,6 @@ D20F3B43252E00E4004B3F56 /* PageProtocol.swift */, 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */, D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */, - D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */, 011B58EE23A2AA850085F53C /* ModelProtocols */, ); path = Protocols; @@ -2754,7 +2751,6 @@ D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */, 0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */, - D20F3B5E252F9B5E004B3F56 /* NavigationBarRefreshProtocol.swift in Sources */, D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */, D260105D23D0BCD400764D80 /* Stack.swift in Sources */, 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift b/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift deleted file mode 100644 index 2d4002e3..00000000 --- a/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// NavigationBarRefreshProtocol.swift -// MVMCoreUI -// -// Created by Scott Pfeil on 10/8/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import Foundation - -/// A protocol to inform that we should refresh the navigation bar ui. -@objc public protocol NavigationBarRefreshProtocol { - @objc func refreshNavigationUI() -} - -extension UIViewController: NavigationBarRefreshProtocol { - - /// Convenience function to refresh the navigation bar ui. A separate function for others to use. - @objc public static func refreshNavigationUI(for viewController: UIViewController) { - guard let model = (viewController as? PageProtocol)?.pageModel?.navigationBar else { return } - if let navigationController = viewController.navigationController { - NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: viewController) - MVMCoreUISplitViewController.setNavigationBarUI(for: viewController, navigationController: navigationController, navigationItemModel: model) - } - if let manager = ((viewController as? MVMCoreViewManagerViewControllerProtocol)?.manager as? NavigationBarRefreshProtocol) { - // Refresh the manager if possible. - manager.refreshNavigationUI() - } - } - - public func refreshNavigationUI() { - UIViewController.refreshNavigationUI(for: self) - } -} diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index cd67632f..f86061e5 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -114,17 +114,6 @@ import UIKit try parsePageJSON() MVMCoreDispatchUtility.performBlock(onMainThread: { self.handleNewDataAndUpdateUI() - - if MVMCoreUIUtility.getCurrentVisibleController() == self { - // Update navigation bar if showing. - self.setNavigationBar() - self.manager?.refreshNavigationUI() - } - // Update splitview properties - if self == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() { - MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(self.bottomProgress() ?? 0) - self.updateTabBar() - } }) } catch { if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: pageType))") { @@ -237,6 +226,11 @@ import UIKit /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, open func handleNewData() { + if model?.navigationBar == nil { + let navigationItem = createDefaultLegacyNavigationModel() + model?.navigationBar = navigationItem + } + executeBehaviors { (behavior: PageMoleculeTransformationBehavior) in behavior.onPageNew(rootMolecules: getRootMolecules(), delegateObjectIVar) } @@ -250,8 +244,14 @@ import UIKit view.backgroundColor = backgroundColor.uiColor } - // Sets up the navigation item based on the data. - setNavigationItem() + // Update splitview properties + if self == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() { + MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(bottomProgress() ?? 0) + updateTabBar() + } + + // Notify the manager of new data + manager?.newDataReceived?(in: self) } //-------------------------------------------------- @@ -267,28 +267,6 @@ import UIKit return model?.navigationBar } - /// Sets the navigation item for this view controller. - open func setNavigationItem() { - guard let navigationItemModel = getNavigationModel(), - let navigationController = navigationController - else { return } - - // Utilize helper function to set the navigation item state. - NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self) - } - - /// Sets the appearance of the navigation bar based on the model. - open func setNavigationBar() { - guard let navigationItemModel = getNavigationModel(), - let navigationController = navigationController else { - MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() - return - } - - // Utilize helper function to set the split view and navigation item state. - MVMCoreUISplitViewController.setNavigationBarUI(for: self, navigationController: navigationController, navigationItemModel: navigationItemModel) - } - //-------------------------------------------------- // MARK: - TabBar //-------------------------------------------------- @@ -378,9 +356,6 @@ import UIKit updateTabBar() } - // Update the navigation bar ui when view is appearing. - setNavigationBar() - // Track. MVMCoreUISession.sharedGlobal()?.currentPageType = pageType MVMCoreUILoggingHandler.shared()?.defaultLogPageState(forController: self) @@ -536,11 +511,6 @@ import UIKit // MARK: - MVMCoreUIDetailViewProtocol //-------------------------------------------------- - // Reset the navigation state. - public func splitViewDidReset() { - setNavigationBar() - } - public func isLeftPanelAccessible() -> Bool { // TODO: Remove when hamburger menu is fully phased out. if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { diff --git a/MVMCoreUI/Behaviors/GetContactBehavior.swift b/MVMCoreUI/Behaviors/GetContactBehavior.swift index 9b488e68..c69bdbce 100644 --- a/MVMCoreUI/Behaviors/GetContactBehavior.swift +++ b/MVMCoreUI/Behaviors/GetContactBehavior.swift @@ -43,18 +43,6 @@ public class PageGetContactBehavior: PageVisibilityBehavior { // TODO: move to protocol function instead guard let controller = self?.delegate?.moleculeDelegate as? ViewController else { return } controller.handleNewDataAndUpdateUI() - - if MVMCoreUIUtility.getCurrentVisibleController() == controller { - // Update navigation bar if showing. - controller.setNavigationBar() - controller.manager?.refreshNavigationUI() - } - - // Update splitview properties - if controller == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() { - MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(controller.bottomProgress() ?? 0) - controller.updateTabBar() - } }) } } diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 331656b7..bcbf927e 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -8,8 +8,9 @@ import UIKit -@objcMembers open class NavigationController: UINavigationController { +@objcMembers open class NavigationController: UINavigationController, MVMCoreViewManagerViewControllerProtocol { public var separatorView: Line? + public var manager: (UIViewController & MVMCoreViewManagerProtocol)? /// Getter for the main navigation controller public static func navigationController() -> Self? { @@ -33,6 +34,7 @@ import UIKit MVMCoreUISession.sharedGlobal()?.navigationController = navigationController MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController MVMCoreNavigationHandler.shared()?.navigationController = navigationController + MVMCoreNavigationHandler.shared()?.addDelegate(navigationController) return navigationController } @@ -103,4 +105,74 @@ import UIKit viewController.navigationItem.titleView = molecule } } + + /// Convenience function to return the navigation model of the lowest controller traversing managers + public static func getNavigationModel(from viewController: UIViewController) -> NavigationItemModelProtocol? { + let controller = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) + guard let model = (controller as? PageProtocol)?.pageModel?.navigationBar else { return nil } + return model + } +} + +extension NavigationController: MVMCoreViewManagerProtocol { + // TODO: change this to optional + public func getCurrentViewController() -> UIViewController { + return topViewController! + } + + public func containsPage(withPageType pageType: String?) -> Bool { + for case let controller as MVMCoreViewControllerProtocol in viewControllers { + if controller.pageType == pageType { + return true + } + } + return false + } + + public func newDataReceived(in viewController: UIViewController) { + if viewController == topViewController, + let model = Self.getNavigationModel(from: viewController) { + Self.setNavigationItem(navigationController: self, navigationItemModel: model, viewController: viewController) + Self.setNavigationBarUI(navigationController: self, navigationItemModel: model, viewController: viewController) + } + manager?.newDataReceived?(in: viewController) + } + + public func willDisplay(_ viewController: UIViewController) { + guard let topViewController = topViewController else { return } + if let model = Self.getNavigationModel(from: viewController) { + Self.setNavigationItem(navigationController: self, navigationItemModel: model, viewController: topViewController) + } + manager?.willDisplay?(viewController) + } + + public func displayedViewController(_ viewController: UIViewController) { + guard let topViewController = topViewController else { return } + if let model = Self.getNavigationModel(from: viewController) { + Self.setNavigationBarUI(navigationController: self, navigationItemModel: model, viewController: topViewController) + } + manager?.displayedViewController?(viewController) + } +} + +extension NavigationController: MVMCorePresentationDelegateProtocol { + public func navigationController(_ navigationController: UINavigationController, willDisplay viewController: UIViewController) { + guard self == navigationController else { return } + if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) { + MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: controller) + controller.viewControllerReady?(inManager: self) + } + if let model = Self.getNavigationModel(from: viewController) { + Self.setNavigationItem(navigationController: self, navigationItemModel: model, viewController: viewController) + } + manager?.willDisplay?(viewController) + } + + public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) { + guard self == navigationController else { return } + if let model = Self.getNavigationModel(from: viewController) { + Self.setNavigationBarUI(navigationController: self, navigationItemModel: model, viewController: viewController) + } + manager?.displayedViewController?(viewController) + } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h index 60ce0873..bd81fec5 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h @@ -48,9 +48,6 @@ NS_ASSUME_NONNULL_BEGIN - (UIStatusBarStyle)defaultStatusBarStyle; - (nullable UIColor *)defaultStatusBarBackgroundColor; -/// Called when the split view did reset. If this function found, the splitview assumes it is handling the split view state and does not do anything. If you have navigation item buttons, override this function and handle the panels manually. -- (void)splitViewDidReset; - @end NS_ASSUME_NONNULL_END diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index eb22eccb..835a2df9 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -24,7 +24,6 @@ public extension MVMCoreUISplitViewController { /// Sets the navigation item for the view controller based on the model and splitview controller private func set(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) { - NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setLeftPanelIsAccessible((viewController as? MVMCoreUIDetailViewProtocol)?.isLeftPanelAccessible?() ?? false, for: viewController, updateNavigationButtons: false) setRightPanelIsAccessible((viewController as? MVMCoreUIDetailViewProtocol)?.isRightPanelAccessible?() ?? false, for: viewController, updateNavigationButtons: false) @@ -104,4 +103,35 @@ public extension MVMCoreUISplitViewController { viewController.navigationItem.setRightBarButtonItems(rightItems.count > 0 ? rightItems : nil, animated: !DisableAnimations.boolValue) } + + @objc func navigationBarModelExists() -> Bool { + // Legacy Navigation + (navigationController?.topViewController as? PageProtocol)?.pageModel?.navigationBar != nil + } + + /// Convenience function to update the navigation + @objc func updateNavigationBarFor(viewController: UIViewController) { + guard viewController == getCurrentDetailViewController(), + let navigationController = navigationController, + let model = NavigationController.getNavigationModel(from: viewController) else { return } + set(for: viewController, navigationController: navigationController, navigationItemModel: model) + } +} + +extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol { + public func getCurrentViewController() -> UIViewController { + navigationController!.getCurrentViewController() + } + + public func containsPage(withPageType pageType: String?) -> Bool { + navigationController!.containsPage(withPageType: pageType) + } + + public func displayedViewController(_ viewController: UIViewController) { + updateNavigationBarFor(viewController: viewController) + } + + public func newDataReceived(in viewController: UIViewController) { + updateNavigationBarFor(viewController: viewController) + } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index 3b1de765..ab44e181 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -857,6 +857,7 @@ CGFloat const PanelAnimationDuration = 0.2; // The main view. NavigationController *navigationController = [NavigationController setupNavigationController]; + navigationController.manager = self; self.navigationController = navigationController; UIView *mainView = navigationController.view; @@ -988,9 +989,11 @@ CGFloat const PanelAnimationDuration = 0.2; } - (void)resetDrawers { - if ([self.navigationItemViewController respondsToSelector:@selector(splitViewDidReset)]) { - [((UIViewController *)self.navigationItemViewController) splitViewDidReset]; + if (!self.navigationItemViewController) { return; } + if ([self navigationBarModelExists]) { + [self updateNavigationBarForViewController:self.navigationItemViewController]; } else { + // Legacy [self setLeftPanelIsAccessible:self.leftPanelIsAccessible forViewController:self.navigationItemViewController updateNavigationButtons:YES]; [self setRightPanelIsAccessible:self.rightPanelIsAccessible forViewController:self.navigationItemViewController updateNavigationButtons:YES]; @@ -1066,10 +1069,7 @@ CGFloat const PanelAnimationDuration = 0.2; } // if it is not presented viewcontroller, existing BAU logic will be working if (!viewController) { - viewController = self.navigationController.topViewController; - if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { - viewController = [viewController performSelector:@selector(getCurrentViewController)]; - } + viewController = [MVMCoreUIUtility getViewControllerTraversingManagers:self.navigationController.topViewController]; } return viewController; } @@ -1077,10 +1077,7 @@ CGFloat const PanelAnimationDuration = 0.2; - (UIViewController *)getCurrentDetailViewController { __block UIViewController *viewController = nil; [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ - viewController = self.navigationController.topViewController; - if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { - viewController = [viewController performSelector:@selector(getCurrentViewController)]; - } + viewController = [MVMCoreUIUtility getViewControllerTraversingManagers:self.navigationController.topViewController]; }]; return viewController; } diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.h b/MVMCoreUI/Utility/MVMCoreUIUtility.h index 6852e936..171ebeea 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.h +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.h @@ -34,6 +34,9 @@ NS_ASSUME_NONNULL_BEGIN /// Gets the current visible view controller. Checks presented view controllers first, and then it checks on the NavigationController in the session object. + (UIViewController *)getCurrentVisibleController; +/// Gets the first non manager controller. ++ (UIViewController *)getViewControllerTraversingManagers:(UIViewController *)viewController; + /// Checks if the view or any descendents of the view is currently focused for voice over. + (BOOL)viewContainsAccessiblityFocus:(nonnull UIView *)view; diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index dd059fed..01f576c5 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -63,14 +63,19 @@ } // if it is not presented viewcontroller, existing BAU logic will be working if (!viewController) { - viewController = [MVMCoreUISession sharedGlobal].navigationController.topViewController; - if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { - viewController = [viewController performSelector:@selector(getCurrentViewController)]; - } + viewController = [self getViewControllerTraversingManagers:[MVMCoreUISession sharedGlobal].navigationController.topViewController]; } return viewController; } ++ (UIViewController *)getViewControllerTraversingManagers:(UIViewController *)viewController { + UIViewController *controller = viewController; + while ([controller conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { + controller = [controller performSelector:@selector(getCurrentViewController)]; + } + return controller; +} + + (BOOL)viewContainsAccessiblityFocus:(nonnull UIView *)view { if (!UIAccessibilityIsVoiceOverRunning()) { return NO;