diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index ef9b278f..9d145d5a 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -74,7 +74,6 @@ 0A25209824645B76000FA9F6 /* TextViewEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A25209724645B76000FA9F6 /* TextViewEntryFieldModel.swift */; }; 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; 0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */; }; - 0A423A202684E7A0008EC258 /* SelectAllNavigationLabelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A423A1F2684E7A0008EC258 /* SelectAllNavigationLabelButton.swift */; }; 0A51F3E22475CB73002E08B6 /* LoadingSpinnerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A51F3E02475CB73002E08B6 /* LoadingSpinnerModel.swift */; }; 0A51F3E32475CB73002E08B6 /* LoadingSpinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A51F3E12475CB73002E08B6 /* LoadingSpinner.swift */; }; 0A5D59C223AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */; }; @@ -109,7 +108,6 @@ 0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D091C2433796500D2E6C0 /* CarouselIndicator.swift */; }; 0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Toggle.swift */; }; 0AA4D2E125CAEC72008DB32D /* AccessibilityModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA4D2E025CAEC72008DB32D /* AccessibilityModelProtocol.swift */; }; - 0AAB7855267B86F900DD6437 /* SelectAllBoxesBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AAB7854267B86F900DD6437 /* SelectAllBoxesBehavior.swift */; }; 0AB000BA24BF63490090C5E7 /* ModalListPageTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB000B924BF63490090C5E7 /* ModalListPageTemplateModel.swift */; }; 0AB000BC24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB000BB24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift */; }; 0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */; }; @@ -639,7 +637,6 @@ 0A25209724645B76000FA9F6 /* TextViewEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewEntryFieldModel.swift; sourceTree = ""; }; 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = ""; }; 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = ""; }; - 0A423A1F2684E7A0008EC258 /* SelectAllNavigationLabelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectAllNavigationLabelButton.swift; sourceTree = ""; }; 0A51F3E02475CB73002E08B6 /* LoadingSpinnerModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingSpinnerModel.swift; sourceTree = ""; }; 0A51F3E12475CB73002E08B6 /* LoadingSpinner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingSpinner.swift; sourceTree = ""; }; 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleGuidelinesProtocol.swift; sourceTree = ""; }; @@ -678,7 +675,6 @@ 0AA33B33239813C50067DD0F /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = ""; }; 0AA33B392398524F0067DD0F /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = ""; }; 0AA4D2E025CAEC72008DB32D /* AccessibilityModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityModelProtocol.swift; sourceTree = ""; }; - 0AAB7854267B86F900DD6437 /* SelectAllBoxesBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectAllBoxesBehavior.swift; sourceTree = ""; }; 0AB000B924BF63490090C5E7 /* ModalListPageTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalListPageTemplateModel.swift; sourceTree = ""; }; 0AB000BB24BF64A50090C5E7 /* ModalStackPageTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalStackPageTemplateModel.swift; sourceTree = ""; }; 0AB764D024460F6300E7FE72 /* UIDatePicker+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDatePicker+Extension.swift"; sourceTree = ""; }; @@ -1324,7 +1320,6 @@ 27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */, D23A900826125FFB007E14CE /* GetContactBehavior.swift */, 0A01DE262626236300C2CAAC /* PlayAudioBehavior.swift */, - 0AAB7854267B86F900DD6437 /* SelectAllBoxesBehavior.swift */, ); path = Behaviors; sourceTree = ""; @@ -1763,7 +1758,6 @@ D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */, D23EA7FD247EBBB700D60C34 /* NavigationLabelButtonModel.swift */, D23EA7FF247EBD6C00D60C34 /* LabelBarButtonItem.swift */, - 0A423A1F2684E7A0008EC258 /* SelectAllNavigationLabelButton.swift */, ); path = Buttons; sourceTree = ""; @@ -2727,7 +2721,6 @@ 011D959B240451E3000E3791 /* RuleRequiredModel.swift in Sources */, 526A265C240D1FF700B0D828 /* ListTwoColumnCompareChangesModel.swift in Sources */, D2A92886241ACD99004E01C6 /* ProgrammaticTableViewController.swift in Sources */, - 0AAB7855267B86F900DD6437 /* SelectAllBoxesBehavior.swift in Sources */, BBAA4F05243D8E3B005AAD5F /* RadioBoxesModel.swift in Sources */, 01509D952327ED1900EF99AA /* HeadlineBodyLinkToggle.swift in Sources */, AA104ADA244734DB004D2810 /* HeadersH1LandingPageHeader.swift in Sources */, @@ -2739,7 +2732,6 @@ D264FA8E243BCD9A00D98315 /* CollectionTemplate.swift in Sources */, 0A7EF85B23D8A52800B2AAD1 /* EntryFieldModel.swift in Sources */, AA633B3124989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift in Sources */, - 0A423A202684E7A0008EC258 /* SelectAllNavigationLabelButton.swift in Sources */, 8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */, D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */, D2FD4A4925199BD9000C28A9 /* AccessibilityProtocol.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index 12330ada..c4f69f27 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -6,6 +6,12 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // +/// Protocol to apply to any model of a UI Control with a binary on/off nature. +/// +/// Example classes: Checkbox or Toggle. +@objc public protocol SelectableMoleculeModelProtocol { + var selected: Bool { get set } +} @objcMembers public class CheckboxModel: MoleculeModelProtocol, SelectableMoleculeModelProtocol, FormFieldProtocol { //-------------------------------------------------- @@ -79,7 +85,7 @@ self.selected = isChecked baseValue = isChecked } - + //-------------------------------------------------- // MARK: - Codec //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift index 056035b4..5be014fb 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift @@ -7,7 +7,7 @@ // -public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol, SelectableMoleculeModelProtocol { +public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/SelectAllNavigationLabelButton.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/SelectAllNavigationLabelButton.swift deleted file mode 100644 index 59bf6136..00000000 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/SelectAllNavigationLabelButton.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// SelectAllNavigationLabelButton.swift -// MVMCoreUI -// -// Created by Kevin Christiano on 6/24/21. -// Copyright © 2021 Verizon Wireless. All rights reserved. -// - - -public class SelectAllNavigationLabelButton: NavigationButtonModelProtocol, MoleculeModelProtocol { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - public var backgroundColor: Color? - public static var identifier: String = "selectAllNavigationLabelButton" - public var accessibilityIdentifier: String? - public var willSelect: Bool = true - public var selectAllTitle: String = "Select All" - public var deselectAllTitle: String = "Deselect All" - public var action: ActionModelProtocol - - public var selectionTitle: String { - willSelect ? selectAllTitle : deselectAllTitle - } - - //-------------------------------------------------- - // MARK: - Initializer - //-------------------------------------------------- - - public init(willSelect: Bool, selectAllTitle: String, deselectAllTitle: String, action: ActionModelProtocol) { - self.willSelect = willSelect - self.action = action - self.selectAllTitle = selectAllTitle - self.deselectAllTitle = deselectAllTitle - } - - //-------------------------------------------------- - // MARK: - Keys - //-------------------------------------------------- - - private enum CodingKeys: String, CodingKey { - case moleculeName - case accessibilityIdentifier - case willSelect - case selectAllTitle - case deSelectAllTitle - case action - } - - //-------------------------------------------------- - // MARK: - Codec - //-------------------------------------------------- - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) - selectAllTitle = try typeContainer.decode(String.self, forKey: .selectAllTitle) - deselectAllTitle = try typeContainer.decode(String.self, forKey: .deSelectAllTitle) - willSelect = try typeContainer.decode(Bool.self, forKey: .willSelect) - action = try typeContainer.decodeModel(codingKey: .action) - } - - open func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(moleculeName, forKey: .moleculeName) - try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) - try container.encode(selectAllTitle, forKey: .selectAllTitle) - try container.encode(deselectAllTitle, forKey: .deSelectAllTitle) - try container.encode(willSelect, forKey: .willSelect) - try container.encodeModel(action, forKey: .action) - } - - //-------------------------------------------------- - // MARK: - Methods - //-------------------------------------------------- - - /// Convenience function that creates a BarButtonItem for the model. - public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { - LabelBarButtonItem.create(with: selectionTitle, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) - } -} diff --git a/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift b/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift deleted file mode 100644 index 3877bfd1..00000000 --- a/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift +++ /dev/null @@ -1,178 +0,0 @@ -// -// SelectAllBoxesBehavior.swift -// MVMCoreUI -// -// Created by Kevin Christiano on 6/17/21. -// Copyright © 2021 Verizon Wireless. All rights reserved. -// - -/// Protocol to apply to any model of a UI Control with a binary on/off nature. -/// -/// Example classes: Checkbox or Toggle. -@objc public protocol SelectableMoleculeModelProtocol { - var selected: Bool { get set } -} - -public class SelectAllBoxesBehaviorModel: PageBehaviorModelProtocol { - public class var identifier: String { "pageSelectAllBoxesBehavior" } - public var shouldAllowMultipleInstances: Bool { false } - public init() { } -} - -/// Selects all the control models presented on a page. -public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior, PageMoleculeTransformationBehavior { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - /// Status of the select all behavior. Initially false as the action has not been enaged. - var willSelectAllState: Bool { - get { getSelectAllNavbutton()?.willSelect ?? false } - set { getSelectAllNavbutton()?.willSelect = newValue } - } - - /// Reference to the general PageBehaviorModel. - var model: PageBehaviorModelProtocol - - /// A store representing the values of the `SelectableMoleculeModel`. - private var valuesMirror = [String: Bool]() - - //-------------------------------------------------- - // MARK: - Delegate - //-------------------------------------------------- - - weak var delegate: MVMCoreUIDelegateObject? - - //-------------------------------------------------- - // MARK: - Init - //-------------------------------------------------- - - required public init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) { - self.model = model - self.delegate = delegateObject - } - - public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject) { - - let selectableModels: [SelectableMoleculeModelProtocol] = rootMolecules.allMoleculesOfType() - - guard !selectableModels.isEmpty else { return } - - for model in selectableModels { - if let key = (model as? FormFieldProtocol)?.fieldKey { - valuesMirror[key] = model.selected - } - } - } - - //-------------------------------------------------- - // MARK: - Custom Action - //-------------------------------------------------- - - // To select or deselect all controls adhereing to `SelectableMoleculeModel` - public func handleAction(type actionType: String?, information: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) -> Bool { - - // Verify we have the correct action type and necessary values. - if actionType == "selectAllBoxes" { - guard let selectableModels: [SelectableMoleculeModelProtocol] = delegate?.moleculeDelegate?.getRootMolecules().allMoleculesOfType(), - !selectableModels.isEmpty - else { return false } - - // Hold value dues to asynch behavior of page refreshing. - let newSelectedState = willSelectAllState - - // Iterate through selectable molecules. - for selectableModel in selectableModels { - if toSelect(model: selectableModel) || toDeselect(model: selectableModel) { - selectableModel.selected = newSelectedState - } - } - - willSelectAllState.toggle() - updatePageNavigationUI() - return true - - } else if actionType == "boxSelected" { - guard let checkboxModel = (additionalData?["sourceModel"] as? CheckboxModel), - let fieldKey = checkboxModel.fieldKey - else { return false } - - self.valuesMirror[fieldKey] = checkboxModel.selected - - // If all are models are in the opposite state of the behavior, then realign. - if self.selectAllIsMisaligned() { - self.realignPageBehaviorAs(willSelectAll: false) - - } else if self.deselectAllIsMisaligned() { - self.realignPageBehaviorAs(willSelectAll: true) - } - - return true - - } else { - return false - } - } - - //-------------------------------------------------- - // MARK: - Methods - //-------------------------------------------------- - - func getSelectAllNavbutton() -> SelectAllNavigationLabelButton? { - guard let controller = self.delegate?.moleculeDelegate as? PageProtocol, - let rightNavButtonModels = controller.pageModel?.navigationBar?.additionalRightButtons - else { return nil } - - var navButton: SelectAllNavigationLabelButton? = nil - - for navModel in rightNavButtonModels { - if let model = navModel as? SelectAllNavigationLabelButton { - navButton = model - } - } - - return navButton - } - - /// In the event that the user manually selects or deselects all `SelectableMoleculeModel` - /// the behavior will need to reflect the inverse of its previously expected action. - /// Initiates the navigation and page behavior realignment - /// - Parameter asSelectAll: The actual value willSelectAllState ought to be. - func realignPageBehaviorAs(willSelectAll: Bool) { - willSelectAllState = willSelectAll - updatePageNavigationUI() - } - - /// Updates the navigation UI to correctly reflect the behavior's state. - func updatePageNavigationUI() { - MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in - (self?.delegate?.moleculeDelegate as? ViewController)?.handleNewDataAndUpdateUI() - }) - } - - /// Convenience function for readability to confirmt he state of the behavior. - /// - Returns: Boolean indicating that the behavior's `willSelectAllState` is false while all model values are true (selected). - func selectAllIsMisaligned() -> Bool { - willSelectAllState && valuesMirror.values.allSatisfy { $0 == true } - } - - /// Convenience function for readability to confirmt he state of the behavior. - /// - Returns: Boolean indicating that the behavior's `willSelectAllState` is true while all model values are false (deselected). - func deselectAllIsMisaligned() -> Bool { - !willSelectAllState && valuesMirror.values.allSatisfy { $0 == false } - } - - /// Convenience function making it easier to read if a current selectable model should be acted on. - /// - Parameter model: A model object assined to the SelectableModel protocol - /// - Returns: Boolean determining if the passed model should be selected. - func toSelect(model: SelectableMoleculeModelProtocol) -> Bool { - willSelectAllState && !model.selected - } - - /// Convenience function making it easier to read if a current selectable model should be acted on. - /// - Parameter model: A model object assined to the SelectableModel protocol - /// - Returns: Boolean determining if the passed model should be deselected. - func toDeselect(model: SelectableMoleculeModelProtocol) -> Bool { - !willSelectAllState && model.selected - } -} diff --git a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift index 3fc511f3..c95964b2 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift @@ -127,7 +127,6 @@ open class CoreUIModelMapping: ModelMapping { ModelRegistry.register(NavigationItemModel.self) ModelRegistry.register(NavigationImageButtonModel.self) ModelRegistry.register(NavigationLabelButtonModel.self) - ModelRegistry.register(SelectAllNavigationLabelButton.self) // MARK:- Other Organisms ModelRegistry.register(handler: Carousel.self, for: CarouselModel.self) @@ -224,7 +223,6 @@ open class CoreUIModelMapping: ModelMapping { ModelRegistry.register(handler: ScreenBrightnessModifierBehavior.self, for: ScreenBrightnessModifierBehaviorModel.self) ModelRegistry.register(handler: PageGetContactBehavior.self, for: PageGetContactBehaviorModel.self) ModelRegistry.register(handler: PagePlayAudioBehavior.self, for: PagePlayAudioBehaviorModel.self) - ModelRegistry.register(handler: SelectAllBoxesBehavior.self, for: SelectAllBoxesBehaviorModel.self) } open override class func registerActions() {