diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index 9ed1c849..0b98d1fc 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -7,7 +7,7 @@ // -@objcMembers public class CheckboxModel: MoleculeModelProtocol, SelectableModel, FormFieldProtocol { +@objcMembers public class CheckboxModel: MoleculeModelProtocol, SelectableMoleculeModel, FormFieldProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift index 4990df73..e59a4738 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 { +public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol, SelectableMoleculeModel { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -53,11 +53,23 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo } //-------------------------------------------------- - // MARK: - Methods + // MARK: - Form Valdiation //-------------------------------------------------- public func formFieldValue() -> AnyHashable? { state } + //-------------------------------------------------- + // MARK: - Selectable Protocol + //-------------------------------------------------- + + public func select(as isSelected: Bool) { + state = isSelected + } + + public var selectedValue: Bool { + state + } + //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- diff --git a/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift b/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift index a197a4d7..93d7367e 100644 --- a/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift +++ b/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift @@ -6,7 +6,10 @@ // Copyright © 2021 Verizon Wireless. All rights reserved. // -public protocol SelectableModel { +/// Protocol to apply to any model of a UI Control with a binary on/off nature. +/// +/// Example classes: Checkbox or Switch. +public protocol SelectableMoleculeModel { var selectedValue: Bool { get } func select(as isSelected: Bool) } @@ -14,16 +17,48 @@ public protocol SelectableModel { public class SelectAllBoxesBehaviorModel: PageBehaviorModelProtocol { public class var identifier: String { "pageSelectAllBoxesBehavior" } public var shouldAllowMultipleInstances: Bool { false } + public var selectAllTitle: String = "Select All" + public var deselectAllTitle: String = "Deselect All" public init() { } + + //-------------------------------------------------- + // MARK: - Codable + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case selectAllTitle + case deselectAllTitle + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + if let selectAllTitle = try typeContainer.decodeIfPresent(String.self, forKey: .selectAllTitle) { + self.selectAllTitle = selectAllTitle + } + + if let deselectAllTitle = try typeContainer.decodeIfPresent(String.self, forKey: .deselectAllTitle) { + self.deselectAllTitle = deselectAllTitle + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(selectAllTitle, forKey: .selectAllTitle) + try container.encode(deselectAllTitle, forKey: .deselectAllTitle) + } } - +/// Selects all the control models presented on a page. public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - var didSelectAll = false + /// Status of the select all behavior. Initially false as the action has not been enaged. + var selectAllState = false + + var model: PageBehaviorModelProtocol //-------------------------------------------------- // MARK: - Delegate @@ -35,7 +70,8 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior { // MARK: - Init //-------------------------------------------------- - public required init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) { + required public init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) { + self.model = model self.delegate = delegateObject } @@ -43,31 +79,40 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior { // MARK: - Custom Action //-------------------------------------------------- - // To select or deselect all controls adhereing to + /// To select or deselect all controls adhereing to `SelectableMoleculeModel` + /// + /// - Parameters: + /// - actionType: The action type of the passed action model. + /// - information: information of the passed action model. + /// - additionalData: Additional information of the + /// - Returns: Boolean determines if the action has been handled. public func handleAction(type actionType: String?, information: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) -> Bool { + // Verify we have the correct action type and necessary values. guard actionType == "selectAllBoxes", - let selectableModels: [SelectableModel] = delegate?.moleculeDelegate?.getRootMolecules().allMoleculesOfType(), - !selectableModels.isEmpty + let selectableModels: [SelectableMoleculeModel] = delegate?.moleculeDelegate?.getRootMolecules().allMoleculesOfType(), + !selectableModels.isEmpty, + let model = model as? SelectAllBoxesBehaviorModel else { return false } - didSelectAll.toggle() - - let navButtonTitle: String? = (didSelectAll ? information!["deSelectAllTitle"] : information!["selectAllTitle"]) as? String + // Flip the selected state of the behavior. + selectAllState.toggle() + // Iterate through selectable molecules. for selectableModel in selectableModels { if toSelect(model: selectableModel) || toDeselect(model: selectableModel) { - selectableModel.select(as: didSelectAll) + selectableModel.select(as: selectAllState) } } + // Get title to update the nav button title. + let navButtonTitle: String? = selectAllState ? model.deselectAllTitle : model.selectAllTitle + MVMCoreDispatchUtility.performBlock(onMainThread: { - // 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.navigationItem.rightBarButtonItem?.title = navButtonTitle controller.manager?.refreshNavigationUI() } @@ -76,11 +121,17 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior { return true } - func toSelect(model: SelectableModel) -> Bool { - didSelectAll && !model.selectedValue + /// 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: SelectableMoleculeModel) -> Bool { + selectAllState && !model.selectedValue } - func toDeselect(model: SelectableModel) -> Bool { - !didSelectAll && model.selectedValue + /// 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: SelectableMoleculeModel) -> Bool { + !selectAllState && model.selectedValue } }