diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 72326c6c..0b0e49c6 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -303,6 +303,7 @@ D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; 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 */; }; D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; }; @@ -796,6 +797,7 @@ D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; 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 = ""; }; D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = ""; }; @@ -2077,6 +2079,7 @@ children = ( 012A88C7238DB02000FE3DA1 /* MoleculeDelegateProtocol.swift */, 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, + D20F3B43252E00E4004B3F56 /* PageProtocol.swift */, 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */, D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */, 011B58EE23A2AA850085F53C /* ModelProtocols */, @@ -2345,6 +2348,7 @@ 525239C02407BCFF00454969 /* ListTwoColumnPriceDetailsModel.swift in Sources */, D2E2A99A23D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift in Sources */, D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */, + D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */, 8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */, 94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */, D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index 9ebb8cfb..2e228ee5 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -35,12 +35,12 @@ import Foundation } /// Convenience function for legacy classes - public func getMoleculeModelForJSON(_ json: [String: Any]) throws -> MoleculeModelProtocol? { + public func getMoleculeModelForJSON(_ json: [String: Any], delegateObject: DelegateObject? = nil) throws -> MoleculeModelProtocol? { guard let moleculeName = json.optionalStringForKey(KeyMoleculeName), let type = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) else { throw ModelRegistry.Error.decoderErrorModelNotMapped() } - guard let model = try type.decode(jsonDict: json) as? MoleculeModelProtocol else { + guard let model = try type.decode(jsonDict: json, delegateObject: delegateObject) as? MoleculeModelProtocol else { throw ModelRegistry.Error.decoderError } return model diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index d8c8c7ac..15ef632e 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -8,25 +8,25 @@ import Foundation -public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { - public class var identifier: String { +open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { + open class var identifier: String { return "navigationBar" } - public var title: String? - public var hidden: Bool - public var backgroundColor: Color? - public var tintColor: Color - public var line: LineModel? - public var hidesSystemBackButton = true + open var title: String? + open var hidden: Bool + open var backgroundColor: Color? + open var tintColor: Color + open var line: LineModel? + open var hidesSystemBackButton = true /// If true, we add the button in the backButton property. If false we do not add the button. If nil, we add the button if the controller is not the bottom of the stack - public var alwaysShowBackButton: Bool? - public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? + open var alwaysShowBackButton: Bool? + open var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? - public var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? - public var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? - public var titleView: MoleculeModelProtocol? + open var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? + open var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? + open var titleView: MoleculeModelProtocol? public init() { hidden = false @@ -44,8 +44,6 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt case line case alwaysShowBackButton case backButton - case showLeftPanelButton - case showRightPanelButton case additionalLeftButtons case additionalRightButtons case titleView diff --git a/MVMCoreUI/Atomic/Protocols/PageProtocol.swift b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift new file mode 100644 index 00000000..3c2a338b --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift @@ -0,0 +1,28 @@ +// +// PageProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/7/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol PageProtocol { + var pageModel: PageModelProtocol? { get set } + + mutating func updateNavigation(with model: NavigationItemModelProtocol & MoleculeModelProtocol) +} + +public extension PageProtocol where Self: UIViewController { + mutating func updateNavigation(with model: NavigationItemModelProtocol & MoleculeModelProtocol) { + pageModel?.navigationBar = model + if var manager = ((self as? MVMCoreViewManagerViewControllerProtocol)?.manager as? PageProtocol) { + // Go through the manager if possible. + manager.updateNavigation(with: model) + } else if let navigationController = navigationController { + NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: self) + MVMCoreUISplitViewController.setNavigationBarUI(for: self, navigationController: navigationController, navigationItemModel: model, leftPanelAccessible: (self as? MVMCoreUIDetailViewProtocol)?.isLeftPanelAccessible?(), rightPanelAccessible: (self as? MVMCoreUIDetailViewProtocol)?.isRightPanelAccessible?()) + } + } +} diff --git a/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift b/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift index 4696ce38..04bc080b 100644 --- a/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift @@ -23,7 +23,7 @@ public extension TemplateProtocol where Self: ViewController { let decoder = JSONDecoder() try decoder.add(delegateObject: delegateObjectIVar) self.templateModel = try decodeTemplate(using: decoder, from: data) - self.pageModel = templateModel as? MVMControllerModelProtocol + self.model = templateModel as? MVMControllerModelProtocol } func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> TemplateModel { diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index f0d6ab84..dcd02c40 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -8,15 +8,23 @@ import UIKit - -@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol { +@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol, PageProtocol { + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @objc public var pageType: String? @objc public var loadObject: MVMCoreLoadObject? - public var pageModel: MVMControllerModelProtocol? + public var model: MVMControllerModelProtocol? + public var pageModel: PageModelProtocol? { + get { + return model + } + set { + model = newValue as? MVMControllerModelProtocol + } + } /// Set if this page is containted in a manager. public var manager: (UIViewController & MVMCoreViewManagerProtocol)? @@ -214,18 +222,18 @@ import UIKit /// Creates a legacy navigation model. open func createDefaultLegacyNavigationModel() -> NavigationItemModel { let navigationModel = NavigationItemModel() - navigationModel.title = pageModel?.screenHeading + navigationModel.title = model?.screenHeading return navigationModel } /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, open func handleNewData() { if formValidator == nil { - let rules = pageModel?.formRules + let rules = model?.formRules formValidator = FormValidator(rules) } - if let backgroundColor = pageModel?.backgroundColor { + if let backgroundColor = model?.backgroundColor { view.backgroundColor = backgroundColor.uiColor } @@ -239,11 +247,11 @@ import UIKit open func getNavigationModel() -> NavigationItemModelProtocol? { // TODO: remove legacy. Temporary, convert legacy to navigation model. - if pageModel?.navigationBar == nil { + if model?.navigationBar == nil { let navigationItem = createDefaultLegacyNavigationModel() - pageModel?.navigationBar = navigationItem + model?.navigationBar = navigationItem } - return pageModel?.navigationBar + return model?.navigationBar } /// Sets the navigation item for this view controller. @@ -252,9 +260,6 @@ import UIKit let navigationController = navigationController else { return } - // We additionally want our left items - navigationItem.leftItemsSupplementBackButton = true - // Utilize helper function to set the navigation item state. NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self) } @@ -324,7 +329,7 @@ import UIKit guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self else { return } MVMCoreUISplitViewController.main()?.tabBar?.delegateObject = delegateObjectIVar - if let index = (pageModel as? TabPageModelProtocol)?.tabBarIndex { + if let index = (model as? TabPageModelProtocol)?.tabBarIndex { MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) } else if let index = loadObject?.requestParameters?.actionMap?["tabBarIndex"] as? Int { MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) @@ -335,7 +340,7 @@ import UIKit self.tabBarIndex = index } - if let hidden = (pageModel as? TabPageModelProtocol)?.tabBarHidden { + if let hidden = (model as? TabPageModelProtocol)?.tabBarHidden { MVMCoreUISplitViewController.main()?.updateTabBarShowing(!hidden) } else if let hidden = loadObject?.requestParameters?.actionMap?["tabBarHidden"] as? Bool { MVMCoreUISplitViewController.main()?.updateTabBarShowing(!hidden) @@ -594,6 +599,6 @@ import UIKit //-------------------------------------------------- func executeBehaviors(_ behaviorBlock:(_ behavior:T)->Void) { - pageModel?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) } + model?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) } } } diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 22864027..89ea446c 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -48,6 +48,7 @@ import UIKit viewController.navigationItem.title = navigationItemModel.title viewController.navigationItem.accessibilityLabel = navigationItemModel.title viewController.navigationItem.hidesBackButton = navigationItemModel.hidesSystemBackButton + viewController.navigationItem.leftItemsSupplementBackButton = !navigationItemModel.hidesSystemBackButton setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setNavigationTitleView(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) } @@ -97,7 +98,8 @@ import UIKit /// Convenience setter for legacy files public static func setNavigationItem(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { - guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject + guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON, delegateObject: delegate) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") } setNavigationItem(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) @@ -105,7 +107,8 @@ import UIKit /// Convenience setter for legacy files public static func setNavigationBarUI(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { - guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject + guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON, delegateObject: delegate) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") } setNavigationBarUI(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h index 2ec18da9..60ce0873 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h @@ -15,6 +15,12 @@ NS_ASSUME_NONNULL_BEGIN @optional +/// Returns if the left panel should be accessible. +- (BOOL)isLeftPanelAccessible; + +/// Returns if the right panel should be accessible. +- (BOOL)isRightPanelAccessible; + - (void)panelWillAppear:(nonnull NSObject *)panel; - (void)panelWillAppear:(nonnull NSObject *)panel overtakingDetail:(BOOL)willOvertake; - (void)panelDidAppear:(nonnull NSObject *)panel; diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index d37d88f1..8e14720d 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -111,7 +111,8 @@ public extension MVMCoreUISplitViewController { // MARK: - Legacy Functions /// Convenience setter for legacy files. Sets the navigation item for the view controller based on the json and splitview controller @objc static func setNavigationBarUI(for viewController: UIViewController, navigationController: UINavigationController, navigationJSON: [String: Any], leftPanelAccessible: Bool, rightPanelAccessible: Bool, progress: NSNumber?) throws { - guard let navigationItemModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject + guard let navigationItemModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON, delegateObject: delegate) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") } guard let splitView = MVMCoreUISplitViewController.main(),