diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index f1fd5fed..a5657dd4 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -124,7 +124,7 @@ import MVMCore didSet { if !updateSelectionOnly { layoutIfNeeded() - (model as? CheckboxModel)?.checked = isSelected + (model as? CheckboxModel)?.selected = isSelected shapeLayer?.removeAllAnimations() updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) @@ -419,8 +419,8 @@ import MVMCore isAnimated = model.animated isRound = model.round - if model.checked { - checkAndBypassAnimations(selected: model.checked) + if model.selected { + checkAndBypassAnimations(selected: model.selected) } isEnabled = model.enabled diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index 34f0fd22..b7d79570 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -15,7 +15,7 @@ public static var identifier: String = "checkbox" public var backgroundColor: Color? public var accessibilityIdentifier: String? - public dynamic var checked: Bool = false + public dynamic var selected: Bool = false public var enabled: Bool = true public var animated: Bool = true public var inverted: Bool = false @@ -69,26 +69,22 @@ // MARK: - Form Validation //-------------------------------------------------- - public func formFieldValue() -> AnyHashable? { checked } + public func formFieldValue() -> AnyHashable? { selected } //-------------------------------------------------- // MARK: - Selectable Protocol //-------------------------------------------------- public func select(as isSelected: Bool) { - checked = isSelected + selected = isSelected } - - public var selectedValue: Bool { - checked - } - + //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- public init(isChecked: Bool = false) { - self.checked = isChecked + self.selected = isChecked baseValue = isChecked } @@ -142,10 +138,10 @@ } if let checked = try typeContainer.decodeIfPresent(Bool.self, forKey: .checked) { - self.checked = checked + self.selected = checked } - baseValue = checked + baseValue = selected if let animated = try typeContainer.decodeIfPresent(Bool.self, forKey: .animated) { self.animated = animated @@ -179,7 +175,7 @@ try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(borderColor, forKey: .borderColor) try container.encode(borderWidth, forKey: .borderWidth) - try container.encode(checked, forKey: .checked) + try container.encode(selected, forKey: .checked) try container.encode(inverted, forKey: .inverted) try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(checkColor, forKey: .checkColor) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift index 304b23c4..3513a03b 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift @@ -98,7 +98,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) self.constrainKnob() } - toggleModel?.state = isOn + toggleModel?.selected = isOn _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") setNeedsLayout() @@ -381,7 +381,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) containerTintColor.off = model.offTintColor.uiColor knobTintColor.on = model.onKnobTintColor.uiColor knobTintColor.off = model.offKnobTintColor.uiColor - isOn = model.state + isOn = model.selected changeStateNoAnimation(isOn) isAnimated = model.animated isEnabled = model.enabled diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift index e59a4738..565d8d43 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, SelectableMoleculeModel { +public class ToggleModel: NSObject, MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol, SelectableMoleculeModel { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -15,7 +15,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo public static var identifier: String = "toggle" public var accessibilityIdentifier: String? public var backgroundColor: Color? - public var state: Bool = false + public dynamic var selected: Bool = false public var animated: Bool = true public var enabled: Bool = true public var action: ActionModelProtocol? @@ -56,18 +56,14 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo // MARK: - Form Valdiation //-------------------------------------------------- - public func formFieldValue() -> AnyHashable? { state } + public func formFieldValue() -> AnyHashable? { selected } //-------------------------------------------------- // MARK: - Selectable Protocol //-------------------------------------------------- public func select(as isSelected: Bool) { - state = isSelected - } - - public var selectedValue: Bool { - state + selected = isSelected } //-------------------------------------------------- @@ -75,7 +71,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo //-------------------------------------------------- public init(_ state: Bool) { - self.state = state + self.selected = state baseValue = state } @@ -87,7 +83,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo let typeContainer = try decoder.container(keyedBy: CodingKeys.self) if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { - self.state = state + self.selected = state } if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { @@ -121,7 +117,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) - baseValue = state + baseValue = selected fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { self.groupName = groupName @@ -135,7 +131,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) try container.encode(moleculeName, forKey: .moleculeName) - try container.encode(state, forKey: .state) + try container.encode(selected, forKey: .state) try container.encode(animated, forKey: .animated) try container.encode(enabled, forKey: .enabled) try container.encode(onTintColor, forKey: .onTintColor) diff --git a/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift b/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift index 27cbe69a..b4db84d5 100644 --- a/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift +++ b/MVMCoreUI/Behaviors/SelectAllBoxesBehavior.swift @@ -9,8 +9,8 @@ /// 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 } +@objc public protocol SelectableMoleculeModel: AnyObject { + @objc dynamic var selected: Bool { get set } func select(as isSelected: Bool) } @@ -84,31 +84,33 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior, PageMolecu public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject) { - let selectableModels: [SelectableMoleculeModel] = rootMolecules.allMoleculesOfType() + let selectableModels: [(NSObject & SelectableMoleculeModel)] = rootMolecules.allMoleculesOfType() guard !selectableModels.isEmpty else { return } - + for model in selectableModels { - if let checkboxModel = model as? CheckboxModel, let key = checkboxModel.fieldKey { + if let key = (model as? FormFieldProtocol)?.fieldKey { + valuesMirror[key] = model.selected + setObserver(model, fieldKey: key) + } + } + } + + func setObserver(_ model: T, fieldKey: String) where T: (NSObject & SelectableMoleculeModel) { + + observers[fieldKey] = model.observe(\.selected, options: [.new]) { [weak self] model, change in + guard let self = self, + let isChecked = change.newValue + else { return } + + self.valuesMirror[fieldKey] = isChecked + + // If all are models are in the opposite state of the behavior, then realign. + if self.selectAllIsMisaligned() { + self.realignPageBehavior(asSelectAll: true) - valuesMirror[key] = checkboxModel.checked - - observers[key] = checkboxModel.observe(\.checked, options: [.new]) { [weak self] model, change in - guard let self = self, - let isChecked = change.newValue, - let key = model.fieldKey - else { return } - - self.valuesMirror[key] = isChecked - - // If all are models are in the opposite state of the behavior, then realign. - if self.selectAllIsMisaligned() { - self.realignPageBehavior(asSelectAll: true) - - } else if self.deselectAllIsMisaligned() { - self.realignPageBehavior(asSelectAll: false) - } - } + } else if self.deselectAllIsMisaligned() { + self.realignPageBehavior(asSelectAll: false) } } } @@ -201,13 +203,13 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior, PageMolecu /// - 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 { - didSelectAllState && !model.selectedValue + didSelectAllState && !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: SelectableMoleculeModel) -> Bool { - !didSelectAllState && model.selectedValue + !didSelectAllState && model.selected } }