generalizing the observing behavior

This commit is contained in:
Kevin G Christiano 2021-06-22 15:05:04 -04:00
parent be196a289e
commit 2b378f5597
5 changed files with 48 additions and 54 deletions

View File

@ -124,7 +124,7 @@ import MVMCore
didSet { didSet {
if !updateSelectionOnly { if !updateSelectionOnly {
layoutIfNeeded() layoutIfNeeded()
(model as? CheckboxModel)?.checked = isSelected (model as? CheckboxModel)?.selected = isSelected
shapeLayer?.removeAllAnimations() shapeLayer?.removeAllAnimations()
updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated)
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
@ -419,8 +419,8 @@ import MVMCore
isAnimated = model.animated isAnimated = model.animated
isRound = model.round isRound = model.round
if model.checked { if model.selected {
checkAndBypassAnimations(selected: model.checked) checkAndBypassAnimations(selected: model.selected)
} }
isEnabled = model.enabled isEnabled = model.enabled

View File

@ -15,7 +15,7 @@
public static var identifier: String = "checkbox" public static var identifier: String = "checkbox"
public var backgroundColor: Color? public var backgroundColor: Color?
public var accessibilityIdentifier: String? public var accessibilityIdentifier: String?
public dynamic var checked: Bool = false public dynamic var selected: Bool = false
public var enabled: Bool = true public var enabled: Bool = true
public var animated: Bool = true public var animated: Bool = true
public var inverted: Bool = false public var inverted: Bool = false
@ -69,26 +69,22 @@
// MARK: - Form Validation // MARK: - Form Validation
//-------------------------------------------------- //--------------------------------------------------
public func formFieldValue() -> AnyHashable? { checked } public func formFieldValue() -> AnyHashable? { selected }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Selectable Protocol // MARK: - Selectable Protocol
//-------------------------------------------------- //--------------------------------------------------
public func select(as isSelected: Bool) { public func select(as isSelected: Bool) {
checked = isSelected selected = isSelected
} }
public var selectedValue: Bool {
checked
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializer // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------
public init(isChecked: Bool = false) { public init(isChecked: Bool = false) {
self.checked = isChecked self.selected = isChecked
baseValue = isChecked baseValue = isChecked
} }
@ -142,10 +138,10 @@
} }
if let checked = try typeContainer.decodeIfPresent(Bool.self, forKey: .checked) { 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) { if let animated = try typeContainer.decodeIfPresent(Bool.self, forKey: .animated) {
self.animated = animated self.animated = animated
@ -179,7 +175,7 @@
try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(borderColor, forKey: .borderColor) try container.encodeIfPresent(borderColor, forKey: .borderColor)
try container.encode(borderWidth, forKey: .borderWidth) 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.encode(inverted, forKey: .inverted)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeIfPresent(checkColor, forKey: .checkColor) try container.encodeIfPresent(checkColor, forKey: .checkColor)

View File

@ -98,7 +98,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
self.constrainKnob() self.constrainKnob()
} }
toggleModel?.state = isOn toggleModel?.selected = isOn
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff")
setNeedsLayout() setNeedsLayout()
@ -381,7 +381,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
containerTintColor.off = model.offTintColor.uiColor containerTintColor.off = model.offTintColor.uiColor
knobTintColor.on = model.onKnobTintColor.uiColor knobTintColor.on = model.onKnobTintColor.uiColor
knobTintColor.off = model.offKnobTintColor.uiColor knobTintColor.off = model.offKnobTintColor.uiColor
isOn = model.state isOn = model.selected
changeStateNoAnimation(isOn) changeStateNoAnimation(isOn)
isAnimated = model.animated isAnimated = model.animated
isEnabled = model.enabled isEnabled = model.enabled

View File

@ -7,7 +7,7 @@
// //
public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol, SelectableMoleculeModel { public class ToggleModel: NSObject, MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol, SelectableMoleculeModel {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -15,7 +15,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
public static var identifier: String = "toggle" public static var identifier: String = "toggle"
public var accessibilityIdentifier: String? public var accessibilityIdentifier: String?
public var backgroundColor: Color? public var backgroundColor: Color?
public var state: Bool = false public dynamic var selected: Bool = false
public var animated: Bool = true public var animated: Bool = true
public var enabled: Bool = true public var enabled: Bool = true
public var action: ActionModelProtocol? public var action: ActionModelProtocol?
@ -56,18 +56,14 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
// MARK: - Form Valdiation // MARK: - Form Valdiation
//-------------------------------------------------- //--------------------------------------------------
public func formFieldValue() -> AnyHashable? { state } public func formFieldValue() -> AnyHashable? { selected }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Selectable Protocol // MARK: - Selectable Protocol
//-------------------------------------------------- //--------------------------------------------------
public func select(as isSelected: Bool) { public func select(as isSelected: Bool) {
state = isSelected selected = isSelected
}
public var selectedValue: Bool {
state
} }
//-------------------------------------------------- //--------------------------------------------------
@ -75,7 +71,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
//-------------------------------------------------- //--------------------------------------------------
public init(_ state: Bool) { public init(_ state: Bool) {
self.state = state self.selected = state
baseValue = state baseValue = state
} }
@ -87,7 +83,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { 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) { 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) accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
baseValue = state baseValue = selected
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName self.groupName = groupName
@ -135,7 +131,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(action, forKey: .action)
try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction)
try container.encode(moleculeName, forKey: .moleculeName) 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(animated, forKey: .animated)
try container.encode(enabled, forKey: .enabled) try container.encode(enabled, forKey: .enabled)
try container.encode(onTintColor, forKey: .onTintColor) try container.encode(onTintColor, forKey: .onTintColor)

View File

@ -9,8 +9,8 @@
/// Protocol to apply to any model of a UI Control with a binary on/off nature. /// Protocol to apply to any model of a UI Control with a binary on/off nature.
/// ///
/// Example classes: Checkbox or Switch. /// Example classes: Checkbox or Switch.
public protocol SelectableMoleculeModel { @objc public protocol SelectableMoleculeModel: AnyObject {
var selectedValue: Bool { get } @objc dynamic var selected: Bool { get set }
func select(as isSelected: Bool) func select(as isSelected: Bool)
} }
@ -84,31 +84,33 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior, PageMolecu
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject) { public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject) {
let selectableModels: [SelectableMoleculeModel] = rootMolecules.allMoleculesOfType() let selectableModels: [(NSObject & SelectableMoleculeModel)] = rootMolecules.allMoleculesOfType()
guard !selectableModels.isEmpty else { return } guard !selectableModels.isEmpty else { return }
for model in selectableModels { 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<T>(_ 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 } else if self.deselectAllIsMisaligned() {
self.realignPageBehavior(asSelectAll: false)
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)
}
}
} }
} }
} }
@ -201,13 +203,13 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior, PageMolecu
/// - Parameter model: A model object assined to the SelectableModel protocol /// - Parameter model: A model object assined to the SelectableModel protocol
/// - Returns: Boolean determining if the passed model should be selected. /// - Returns: Boolean determining if the passed model should be selected.
func toSelect(model: SelectableMoleculeModel) -> Bool { 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. /// 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 /// - Parameter model: A model object assined to the SelectableModel protocol
/// - Returns: Boolean determining if the passed model should be deselected. /// - Returns: Boolean determining if the passed model should be deselected.
func toDeselect(model: SelectableMoleculeModel) -> Bool { func toDeselect(model: SelectableMoleculeModel) -> Bool {
!didSelectAllState && model.selectedValue !didSelectAllState && model.selected
} }
} }