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
//--------------------------------------------------
@ -15,7 +15,7 @@
public static var identifier: String = "checkbox"
public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var checked: Bool = false
public dynamic var checked: Bool = false
public var enabled: Bool = true
public var animated: Bool = true
public var inverted: Bool = false

View File

@ -24,7 +24,7 @@ public class SelectAllBoxesBehaviorModel: PageBehaviorModelProtocol {
//--------------------------------------------------
// MARK: - Codable
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case selectAllTitle
case deselectAllTitle
@ -41,7 +41,7 @@ public class SelectAllBoxesBehaviorModel: PageBehaviorModelProtocol {
self.deselectAllTitle = deselectAllTitle
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(selectAllTitle, forKey: .selectAllTitle)
@ -50,16 +50,23 @@ public class SelectAllBoxesBehaviorModel: PageBehaviorModelProtocol {
}
/// Selects all the control models presented on a page.
public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior {
public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior, PageMoleculeTransformationBehavior {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
/// 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
/// 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
//--------------------------------------------------
@ -75,6 +82,45 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior {
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
//--------------------------------------------------
@ -91,25 +137,45 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior {
// Verify we have the correct action type and necessary values.
guard actionType == "selectAllBoxes",
let selectableModels: [SelectableMoleculeModel] = delegate?.moleculeDelegate?.getRootMolecules().allMoleculesOfType(),
!selectableModels.isEmpty,
let model = model as? SelectAllBoxesBehaviorModel
!selectableModels.isEmpty
else { return false }
// Flip the selected state of the behavior.
selectAllState.toggle()
didSelectAllState.toggle()
// Iterate through selectable molecules.
for selectableModel in selectableModels {
if toSelect(model: selectableModel) || toDeselect(model: selectableModel) {
selectableModel.select(as: selectAllState)
selectableModel.select(as: didSelectAllState)
}
}
// Get title to update the nav button title.
let navButtonTitle: String? = selectAllState ? model.deselectAllTitle : model.selectAllTitle
updatePageNavigationUI()
return true
}
//--------------------------------------------------
// 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() {
MVMCoreDispatchUtility.performBlock(onMainThread: {
guard let controller = self.delegate?.moleculeDelegate as? ViewController else { return }
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()
if MVMCoreUIUtility.getCurrentVisibleController() == controller {
@ -117,21 +183,31 @@ public class SelectAllBoxesBehavior: PageCustomActionHandlerBehavior {
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.
/// - 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
didSelectAllState && !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
!didSelectAllState && model.selectedValue
}
}