aligns behavior for user intiaited corner cases.

This commit is contained in:
Kevin G Christiano 2021-06-22 13:10:06 -04:00
parent cf8d0f967e
commit be196a289e
2 changed files with 94 additions and 18 deletions

View File

@ -7,7 +7,7 @@
// //
@objcMembers public class CheckboxModel: MoleculeModelProtocol, SelectableMoleculeModel, FormFieldProtocol { @objcMembers public class CheckboxModel: NSObject, MoleculeModelProtocol, SelectableMoleculeModel, FormFieldProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -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 var checked: Bool = false public dynamic var checked: 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

View File

@ -50,16 +50,23 @@ public class SelectAllBoxesBehaviorModel: PageBehaviorModelProtocol {
} }
/// Selects all the control models presented on a page. /// Selects all the control models presented on a page.
public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior { public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior, PageMoleculeTransformationBehavior {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
/// Status of the select all behavior. Initially false as the action has not been enaged. /// Status of the select all behavior. Initially false as the action has not been enaged.
var selectAllState = false var didSelectAllState = false
/// Reference to the general PageBehaviorModel.
var model: PageBehaviorModelProtocol var model: PageBehaviorModelProtocol
/// Dictionary of KVOs to observing the selected property of each `SelectableMoleculeModel`.
private var observers = [String: NSKeyValueObservation?]()
/// A store representing the values of the `SelectableMoleculeModel`.
private var valuesMirror = [String: Bool]()
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Delegate // MARK: - Delegate
//-------------------------------------------------- //--------------------------------------------------
@ -75,6 +82,45 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior {
self.delegate = delegateObject self.delegate = delegateObject
} }
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject) {
let selectableModels: [SelectableMoleculeModel] = rootMolecules.allMoleculesOfType()
guard !selectableModels.isEmpty else { return }
for model in selectableModels {
if let checkboxModel = model as? CheckboxModel, let key = checkboxModel.fieldKey {
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)
}
}
}
}
}
//--------------------------------------------------
// MARK: - Deinit
//--------------------------------------------------
deinit {
observers.values.forEach { $0?.invalidate() }
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Custom Action // MARK: - Custom Action
//-------------------------------------------------- //--------------------------------------------------
@ -91,25 +137,45 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior {
// Verify we have the correct action type and necessary values. // Verify we have the correct action type and necessary values.
guard actionType == "selectAllBoxes", guard actionType == "selectAllBoxes",
let selectableModels: [SelectableMoleculeModel] = delegate?.moleculeDelegate?.getRootMolecules().allMoleculesOfType(), let selectableModels: [SelectableMoleculeModel] = delegate?.moleculeDelegate?.getRootMolecules().allMoleculesOfType(),
!selectableModels.isEmpty, !selectableModels.isEmpty
let model = model as? SelectAllBoxesBehaviorModel
else { return false } else { return false }
// Flip the selected state of the behavior. // Flip the selected state of the behavior.
selectAllState.toggle() didSelectAllState.toggle()
// Iterate through selectable molecules. // Iterate through selectable molecules.
for selectableModel in selectableModels { for selectableModel in selectableModels {
if toSelect(model: selectableModel) || toDeselect(model: selectableModel) { if toSelect(model: selectableModel) || toDeselect(model: selectableModel) {
selectableModel.select(as: selectAllState) selectableModel.select(as: didSelectAllState)
} }
} }
// Get title to update the nav button title. updatePageNavigationUI()
let navButtonTitle: String? = selectAllState ? model.deselectAllTitle : model.selectAllTitle return true
}
MVMCoreDispatchUtility.performBlock(onMainThread: { //--------------------------------------------------
guard let controller = self.delegate?.moleculeDelegate as? ViewController else { return } // MARK: - Methods
//--------------------------------------------------
/// 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 didSelectAllState ought to be.
func realignPageBehavior(asSelectAll: Bool) {
didSelectAllState = asSelectAll
updatePageNavigationUI()
}
/// Updates the navigation UI to correctly reflect the behavior's state.
func updatePageNavigationUI() {
guard let model = model as? SelectAllBoxesBehaviorModel else { return }
let navButtonTitle: String? = didSelectAllState ? model.deselectAllTitle : model.selectAllTitle
MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in
guard let controller = self?.delegate?.moleculeDelegate as? ViewController else { return }
controller.handleNewDataAndUpdateUI() controller.handleNewDataAndUpdateUI()
if MVMCoreUIUtility.getCurrentVisibleController() == controller { if MVMCoreUIUtility.getCurrentVisibleController() == controller {
@ -117,21 +183,31 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior {
controller.manager?.refreshNavigationUI() controller.manager?.refreshNavigationUI()
} }
}) })
}
return true /// Convenience function for readability to confirmt he state of the behavior.
/// - Returns: Boolean indicating that the behavior's `didSelectAllState` is false while all model values are true (selected).
func selectAllIsMisaligned() -> Bool {
!didSelectAllState && valuesMirror.values.allSatisfy { $0 == true }
}
/// Convenience function for readability to confirmt he state of the behavior.
/// - Returns: Boolean indicating that the behavior's `didSelectAllState` is true while all model values are false (deselected).
func deselectAllIsMisaligned() -> Bool {
didSelectAllState && valuesMirror.values.allSatisfy { $0 == false }
} }
/// 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 selected. /// - Returns: Boolean determining if the passed model should be selected.
func toSelect(model: SelectableMoleculeModel) -> Bool { func toSelect(model: SelectableMoleculeModel) -> Bool {
selectAllState && !model.selectedValue didSelectAllState && !model.selectedValue
} }
/// 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 {
!selectAllState && model.selectedValue !didSelectAllState && model.selectedValue
} }
} }