From 11a023d92aa27f352df64f84f80f37d0a40d61f9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 28 Jun 2024 14:57:15 -0500 Subject: [PATCH 01/10] initial cut for the checkbox Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Selectors/Checkbox.swift | 377 +++--------------- .../Atoms/Selectors/CheckboxModel.swift | 81 +--- .../Atomic/Atoms/Views/CheckboxLabel.swift | 13 +- ...tLeftVariableCheckboxAllTextAndLinks.swift | 4 +- .../ListLeftVariableCheckboxBodyText.swift | 4 +- 5 files changed, 70 insertions(+), 409 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index f771dddf..d6e65968 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -7,113 +7,44 @@ // import MVMCore +import VDS /** This class expects its height and width to be equal. */ -@objcMembers open class Checkbox: Control, MVMCoreUIViewConstrainingProtocol { - //-------------------------------------------------- +@objcMembers open class Checkbox: VDS.Checkbox, VDSMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { + //------------------------------------------------------ // MARK: - Properties - //-------------------------------------------------- - - public var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0) - + //------------------------------------------------------ + open var viewModel: CheckboxModel! + open var delegateObject: MVMCoreUIDelegateObject? + open var additionalData: [AnyHashable : Any]? + // Form Validation var fieldKey: String? var fieldValue: JSONValue? var groupName: String? - var delegateObject: MVMCoreUIDelegateObject? public var checkboxModel: CheckboxModel? { - model as? CheckboxModel + viewModel } - - public static let defaultHeightWidth: CGFloat = 18.0 - - /// If true the border of this checkbox will be circular. - public var isRound: Bool = false - - /// Determined if the checkbox's UI should animated when selected. - public var isAnimated: Bool = true - + /// Disables all selection logic when setting the value of isSelected, reducing it to a stored property. public var updateSelectionOnly: Bool = false - - /// The color of the background when checked. - public var checkedBackgroundColor: UIColor = .clear { - didSet { - if isSelected { - backgroundColor = checkedBackgroundColor - } - } - } - - /// The color of the background when unChecked. - public var unCheckedBackgroundColor: UIColor = .clear { - didSet { - if !isSelected { - backgroundColor = unCheckedBackgroundColor - } - } - } - - /// Retrieves ideeal radius value to curve square into a circle. - public var cornerRadiusValue: CGFloat { - bounds.size.height / 2 - } - + /// Action Block called when the switch is selected. - public var actionBlock: ActionBlock? - - /// Manages the appearance of the checkbox. - private var shapeLayer: CAShapeLayer? - - /// Width of the check mark. - public var checkWidth: CGFloat = 2 { - didSet { - if let shapeLayer = shapeLayer { - CATransaction.withDisabledAnimations { - shapeLayer.lineWidth = checkWidth + public var actionBlock: ActionBlock? { + get { nil } + set { + if let action = newValue { + onChange = { _ in + action() } - } - } - } - - open override var isEnabled: Bool { - didSet { - - isUserInteractionEnabled = isEnabled - - if isEnabled { - layer.borderColor = borderColor.cgColor - backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor - setShapeLayerStrokeColor(checkColor) } else { - layer.borderColor = disabledBorderColor.cgColor - backgroundColor = disabledBackgroundColor - setShapeLayerStrokeColor(disabledCheckColor) + onChange = nil } } } - - public var disabledBackgroundColor: UIColor = .clear - public var disabledBorderColor: UIColor = .mvmCoolGray3 - public var disabledCheckColor: UIColor = .mvmCoolGray3 - - /// Color of the check mark. - public var checkColor: UIColor = .mvmBlack { - didSet { setShapeLayerStrokeColor(checkColor) } - } - - /// Border width of the checkbox - public var borderWidth: CGFloat = 1 { - didSet { layer.borderWidth = borderWidth } - } - - /// border color of the Checkbox - public var borderColor: UIColor = .mvmBlack { - didSet { layer.borderColor = borderColor.cgColor } - } /** The represented state of the Checkbox. @@ -123,42 +54,18 @@ import MVMCore override open var isSelected: Bool { didSet { if !updateSelectionOnly { - layoutIfNeeded() - (model as? CheckboxModel)?.selected = isSelected - shapeLayer?.removeAllAnimations() - updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) + viewModel.selected = isSelected _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) - updateAccessibilityLabel() } } } - //-------------------------------------------------- - // MARK: - Constraints - //-------------------------------------------------- - - public var heightConstraint: NSLayoutConstraint? - public var widthConstraint: NSLayoutConstraint? - - /// Updates the height and width anchors of the Checkbox with the assigned value. - public var heigthWidthConstant: CGFloat = Checkbox.defaultHeightWidth { - didSet { - heightConstraint?.constant = heigthWidthConstant - widthConstraint?.constant = heigthWidthConstant - } - } - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- override public init(frame: CGRect) { super.init(frame: frame) - - isAccessibilityElement = true - accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") - accessibilityTraits = .button - updateAccessibilityLabel() } /// There is currently no intention on using xib files. @@ -167,122 +74,61 @@ import MVMCore fatalError("xib file is not implemented for Checkbox.") } - public convenience override init() { + public convenience required init() { self.init(frame:.zero) } public convenience init(isChecked: Bool) { self.init(frame: .zero) - checkAndBypassAnimations(selected: isChecked) + isSelected = isChecked } public convenience init(checkedBackgroundColor: UIColor, unCheckedBackgroundColor: UIColor, isChecked: Bool = false) { self.init(frame: .zero) - checkAndBypassAnimations(selected: isChecked) - self.checkedBackgroundColor = checkedBackgroundColor - self.unCheckedBackgroundColor = unCheckedBackgroundColor + isSelected = isChecked } //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - override open func layoutSubviews() { - super.layoutSubviews() + open override func setup() { + super.setup() + bridge_accessibilityLabelBlock = { [weak self] in + // Attention: This needs to be addressed with the accessibility team. + // NOTE: Currently emptying description part of MVMCoreUICheckBox accessibility label to avoid crashing! + guard let self, + let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") + else { return nil } + + return String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@%@", "", state) + } - drawShapeLayer() - layer.cornerRadius = isRound ? cornerRadiusValue : 0 - } - - open override func setupView() { - super.setupView() - - isUserInteractionEnabled = true - translatesAutoresizingMaskIntoConstraints = false - backgroundColor = .clear - - widthConstraint = widthAnchor.constraint(equalToConstant: Checkbox.defaultHeightWidth) - heightConstraint = heightAnchor.constraint(equalToConstant: Checkbox.defaultHeightWidth) - heightWidthIsActive(true) + bridge_accessibilityHintBlock = { + MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") + } } //-------------------------------------------------- // MARK: - Actions //-------------------------------------------------- - open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { - super.sendAction(action, to: target, for: event) - toggleAndAction() - } - - open override func sendActions(for controlEvents: UIControl.Event) { - super.sendActions(for: controlEvents) - toggleAndAction() - } - /// This will toggle the state of the Checkbox and execute the actionBlock if provided. public func toggleAndAction() { - isSelected.toggle() - actionBlock?() + toggle() } - + //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- - - /// Creates the check mark layer. - private func drawShapeLayer() { - - if shapeLayer == nil { - - let shapeLayer = CAShapeLayer() - self.shapeLayer = shapeLayer - shapeLayer.frame = bounds - layer.addSublayer(shapeLayer) - shapeLayer.strokeColor = isEnabled ? checkColor.cgColor : disabledCheckColor.cgColor - shapeLayer.fillColor = UIColor.clear.cgColor - shapeLayer.path = checkMarkPath() - shapeLayer.lineJoin = .miter - shapeLayer.lineWidth = checkWidth - - CATransaction.withDisabledAnimations { - shapeLayer.strokeEnd = isSelected ? 1 : 0 - } - } - } - - /// - returns: The CGPath of a UIBezierPath detailing the path of a checkmark - func checkMarkPath() -> CGPath { - - let length = max(bounds.size.height, bounds.size.width) - let xInsetLeft = length * 0.25 - let yInsetTop = length * 0.3 - let innerWidth = length - (xInsetLeft + length * 0.25) // + Right X Inset - let innerHeight = length - (yInsetTop + length * 0.35) // + Bottom Y Inset - - let startPoint = CGPoint(x: xInsetLeft, y: yInsetTop + (innerHeight / 2)) - let pivotOffSet = CGPoint(x: xInsetLeft + (innerWidth * 0.33), y: yInsetTop + innerHeight) - let endOffset = CGPoint(x: xInsetLeft + innerWidth, y: yInsetTop) - - let bezierPath = UIBezierPath() - bezierPath.move(to: startPoint) - bezierPath.addLine(to: pivotOffSet) - bezierPath.addLine(to: endOffset) - - return bezierPath.cgPath - } - /// Programmatic means to check/uncheck the box. /// - parameter selected: state of the check box: true = checked OR false = unchecked. /// - parameter animated: allows the state of the checkbox to change with or without animation. public func updateSelection(to selected: Bool, animated: Bool) { DispatchQueue.main.async { - - self.checkAndBypassAnimations(selected: selected) - self.drawShapeLayer() - self.shapeLayer?.removeAllAnimations() - self.updateCheckboxUI(isSelected: selected, isAnimated: animated) + self.isAnimated = animated + self.isSelected = selected } } @@ -291,154 +137,59 @@ import MVMCore /// - parameter isAnimated: determines of the changes should animate or immediately refelect. public func updateCheckboxUI(isSelected: Bool, isAnimated: Bool) { - if isAnimated { - let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") - animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) - animateStrokeEnd.duration = 0.3 - animateStrokeEnd.fillMode = .both - animateStrokeEnd.isRemovedOnCompletion = false - animateStrokeEnd.fromValue = !isSelected ? 1 : 0 - animateStrokeEnd.toValue = isSelected ? 1 : 0 - self.shapeLayer?.add(animateStrokeEnd, forKey: "strokeEnd") - - UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { - self.backgroundColor = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor - }) - } else { - CATransaction.withDisabledAnimations { - self.shapeLayer?.strokeEnd = isSelected ? 1 : 0 - } - - backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor + DispatchQueue.main.async { + self.isAnimated = isAnimated + self.isSelected = isSelected } } - - /// Adjust accessibility label based on state of Checkbox. - public func updateAccessibilityLabel() { - // Attention: This needs to be addressed with the accessibility team. - // NOTE: Currently emptying description part of MVMCoreUICheckBox accessibility label to avoid crashing! - if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") { - accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@%@", "", state) - } - } - - private func setShapeLayerStrokeColor(_ color: UIColor) { - if let shapeLayer = shapeLayer { - CATransaction.withDisabledAnimations { - shapeLayer.strokeColor = color.cgColor - } - } - } - - public func heightWidthIsActive(_ isActive: Bool) { - - heightConstraint?.isActive = isActive - widthConstraint?.isActive = isActive - } - - private func checkAndBypassAnimations(selected: Bool) { - updateSelectionOnly = true - isSelected = selected - updateSelectionOnly = false - } - - //-------------------------------------------------- - // MARK: - UITouch - //-------------------------------------------------- - - open override func touchesEnded(_ touches: Set, with event: UIEvent?) { - - sendActions(for: .touchUpInside) - } - - override open func accessibilityActivate() -> Bool { - guard isEnabled else { return false } - sendActions(for: .touchUpInside) - return true - } - //-------------------------------------------------- // MARK: - Molecular //-------------------------------------------------- open func needsToBeConstrained() -> Bool { true } - - open override func reset() { - super.reset() - isEnabled = true - shapeLayer?.removeAllAnimations() - shapeLayer?.removeFromSuperlayer() - shapeLayer = nil - backgroundColor = .clear - borderColor = .mvmBlack - borderWidth = 1 - checkColor = .mvmBlack - checkWidth = 2 - checkAndBypassAnimations(selected: false) - } - - public override func updateView(_ size: CGFloat) { - super.updateView(size) - - if let dimension = sizeObject?.getValueBased(onSize: size) { - widthConstraint?.constant = dimension - heightConstraint?.constant = dimension - } - } + public func horizontalAlignment() -> UIStackView.Alignment { .leading } + + public func updateView(_ size: CGFloat) {} private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { MVMCoreUIActionHandler.performActionUnstructured(with: actionModel, sourceModel: checkboxModel, additionalData: additionalData, delegateObject: delegateObject) } - public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - super.set(with: model, delegateObject, additionalData) - self.delegateObject = delegateObject + + public func viewModelDidUpdate() { + FormValidator.setupValidation(for: viewModel, delegate: delegateObject?.formHolderDelegate) - guard let model = model as? CheckboxModel else { return } - - FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) - - if let fieldKey = model.fieldKey { + if let fieldKey = viewModel.fieldKey { self.fieldKey = fieldKey } - borderColor = (model.inverted ? model.invertedColor : model.borderColor).uiColor - borderWidth = model.borderWidth - - checkColor = (model.inverted ? model.invertedColor : model.checkColor).uiColor - unCheckedBackgroundColor = (model.inverted ? model.invertedBackgroundColor : model.unCheckedBackgroundColor).uiColor - checkedBackgroundColor = (model.inverted ? model.invertedBackgroundColor : model.checkedBackgroundColor).uiColor - disabledCheckColor = (model.inverted ? model.invertedColor : model.disabledCheckColor).uiColor - disabledBorderColor = (model.inverted ? model.invertedColor : model.disabledBorderColor).uiColor - disabledBackgroundColor = (model.inverted ? model.invertedColor : model.disabledBackgroundColor).uiColor - - isAnimated = model.animated - isRound = model.round - - if model.selected { - checkAndBypassAnimations(selected: model.selected) + isAnimated = viewModel.animated + if viewModel.selected { + updateSelectionOnly = true + isSelected = viewModel.selected + updateSelectionOnly = false } - model.updateUI = { [weak self] in - MVMCoreDispatchUtility.performBlock(onMainThread: { + viewModel.updateUI = { + MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in guard let self = self else { return } - self.isEnabled = model.enabled + isEnabled = viewModel.enabled }) } - isEnabled = model.enabled && !model.readOnly + isEnabled = viewModel.enabled && !viewModel.readOnly - if (model.action != nil || model.offAction != nil) { + if (viewModel.action != nil || viewModel.offAction != nil) { actionBlock = { [weak self] in guard let self = self else { return } - if let offAction = model.offAction, !self.isSelected { - self.performCheckboxAction(with: offAction, delegateObject: delegateObject, additionalData: additionalData) + if let offAction = viewModel.offAction, !isSelected { + performCheckboxAction(with: offAction, delegateObject: delegateObject, additionalData: additionalData) - } else if let action = model.action { - self.performCheckboxAction(with: action, delegateObject: delegateObject, additionalData: additionalData) + } else if let action = viewModel.action { + performCheckboxAction(with: action, delegateObject: delegateObject, additionalData: additionalData) } } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index 604a8a8d..98e0cbbd 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -5,6 +5,7 @@ // Created by Chintakrinda, Arun Kumar (Arun) on 21/01/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // +import VDS /// Protocol to apply to any model of a UI Control with a binary on/off nature. /// @@ -28,20 +29,10 @@ public var readOnly: Bool = false public var animated: Bool = true public var inverted: Bool = false - public var round: Bool = false - public var borderWidth: CGFloat = 1 - public var borderColor: Color = Color(uiColor: .mvmBlack) - public var checkColor: Color = Color(uiColor: .mvmBlack) - public var unCheckedBackgroundColor: Color = Color(uiColor: .clear) - public var checkedBackgroundColor: Color = Color(uiColor: .clear) - public var disabledBackgroundColor: Color = Color(uiColor: .clear) - public var disabledBorderColor: Color = Color(uiColor: .mvmCoolGray3) - public var disabledCheckColor: Color = Color(uiColor: .mvmCoolGray3) - public var invertedColor: Color = Color(uiColor: .mvmWhite) - public var invertedBackgroundColor: Color = Color(uiColor: .mvmBlack) public var action: ActionModelProtocol? public var offAction: ActionModelProtocol? + public var surface: Surface { inverted ? .dark : .light } public var fieldKey: String? public var groupName: String = FormValidator.defaultGroupName public var baseValue: AnyHashable? @@ -60,17 +51,6 @@ case readOnly case inverted case animated - case round - case borderWidth - case borderColor - case checkColor - case invertedColor - case invertedBackgroundColor - case unCheckedBackgroundColor - case checkedBackgroundColor - case disabledBackgroundColor - case disabledCheckColor - case disabledBorderColor case action case fieldKey case groupName @@ -113,46 +93,6 @@ accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) - if let borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) { - self.borderWidth = borderWidth - } - - if let borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) { - self.borderColor = borderColor - } - - if let checkColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkColor) { - self.checkColor = checkColor - } - - if let unCheckedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .unCheckedBackgroundColor) { - self.unCheckedBackgroundColor = unCheckedBackgroundColor - } - - if let checkedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkedBackgroundColor) { - self.checkedBackgroundColor = checkedBackgroundColor - } - - if let disabledBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBackgroundColor) { - self.disabledBackgroundColor = disabledBackgroundColor - } - - if let disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor) { - self.disabledBorderColor = disabledBorderColor - } - - if let disabledCheckColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledCheckColor) { - self.disabledCheckColor = disabledCheckColor - } - - if let invertedColor = try typeContainer.decodeIfPresent(Color.self, forKey: .invertedColor) { - self.invertedColor = invertedColor - } - - if let invertedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .invertedBackgroundColor) { - self.invertedBackgroundColor = invertedBackgroundColor - } - if let checked = try typeContainer.decodeIfPresent(Bool.self, forKey: .checked) { self.selected = checked } @@ -162,11 +102,7 @@ if let animated = try typeContainer.decodeIfPresent(Bool.self, forKey: .animated) { self.animated = animated } - - if let round = try typeContainer.decodeIfPresent(Bool.self, forKey: .round) { - self.round = round - } - + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { self.inverted = inverted } @@ -188,21 +124,10 @@ try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(groupName, forKey: .groupName) try container.encodeIfPresent(fieldKey, forKey: .fieldKey) - try container.encodeIfPresent(borderColor, forKey: .borderColor) - try container.encode(borderWidth, forKey: .borderWidth) try container.encode(selected, forKey: .checked) try container.encode(inverted, forKey: .inverted) try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) - try container.encodeIfPresent(checkColor, forKey: .checkColor) - try container.encodeIfPresent(invertedColor, forKey: .invertedColor) - try container.encodeIfPresent(invertedBackgroundColor, forKey: .invertedBackgroundColor) - try container.encodeIfPresent(unCheckedBackgroundColor, forKey: .unCheckedBackgroundColor) - try container.encodeIfPresent(checkedBackgroundColor, forKey: .checkedBackgroundColor) - try container.encodeIfPresent(disabledBorderColor, forKey: .disabledBorderColor) - try container.encodeIfPresent(disabledBackgroundColor, forKey: .disabledBackgroundColor) - try container.encodeIfPresent(disabledCheckColor, forKey: .disabledCheckColor) try container.encodeIfPresent(animated, forKey: .animated) - try container.encodeIfPresent(round, forKey: .round) try container.encode(enabled, forKey: .enabled) try container.encode(readOnly, forKey: .readOnly) try container.encodeModelIfPresent(action, forKey: .action) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift index 2ce1254b..a10abbac 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift @@ -36,7 +36,7 @@ override open func setupView() { super.setupView() - + guard subviews.isEmpty else { return } addSubview(checkbox) @@ -66,9 +66,6 @@ alignCheckbox(.center) isAccessibilityElement = false accessibilityElements = [checkbox, label] - observation = observe(\.checkbox.isSelected, options: [.new]) { [weak self] _, _ in - self?.updateAccessibilityLabel() - } } @objc override open func updateView(_ size: CGFloat) { @@ -117,7 +114,6 @@ checkbox.set(with: checkBoxWithLabelModel.checkbox, delegateObject, additionalData) label.set(with: checkBoxWithLabelModel.label, delegateObject, additionalData) - updateAccessibilityLabel() } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { @@ -135,11 +131,4 @@ override open func accessibilityActivate() -> Bool { checkbox.accessibilityActivate() } - - open func updateAccessibilityLabel() { - checkbox.updateAccessibilityLabel() - if let text = label.text { - checkbox.accessibilityLabel?.append(", \(text)") - } - } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift index 55ae3e02..cbeca37b 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift @@ -83,9 +83,7 @@ func updateAccessibilityLabel() { var message = "" - - checkbox.updateAccessibilityLabel() - + if let checkboxLabel = checkbox.accessibilityLabel, !checkboxLabel.isEmpty { message += checkboxLabel + ", " } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyText.swift index 1d173406..42bbb2d1 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyText.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyText.swift @@ -88,9 +88,7 @@ func updateAccessibilityLabel() { var message = "" - - checkbox.updateAccessibilityLabel() - + if let checkboxLabel = checkbox.accessibilityLabel { message += checkboxLabel + ", " } From 847daee32ff4f737a6cc160fbe64f9b201b452b2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 1 Jul 2024 14:55:25 -0500 Subject: [PATCH 02/10] - refactored more code - CheckboxLabel to VDS.CheckboxItem Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Selectors/Checkbox.swift | 9 +- .../Atomic/Atoms/Views/CheckboxLabel.swift | 193 ++++++++---------- .../Atoms/Views/CheckboxLabelModel.swift | 8 +- 3 files changed, 95 insertions(+), 115 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index d6e65968..1b8c9a84 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -159,12 +159,15 @@ import VDS public func viewModelDidUpdate() { + //forms FormValidator.setupValidation(for: viewModel, delegate: delegateObject?.formHolderDelegate) - + groupName = viewModel.groupName if let fieldKey = viewModel.fieldKey { self.fieldKey = fieldKey } + //properties + isEnabled = viewModel.enabled && !viewModel.readOnly isAnimated = viewModel.animated if viewModel.selected { updateSelectionOnly = true @@ -172,6 +175,7 @@ import VDS updateSelectionOnly = false } + //events viewModel.updateUI = { MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in guard let self = self else { return } @@ -179,8 +183,7 @@ import VDS }) } - isEnabled = viewModel.enabled && !viewModel.readOnly - + //onChange if (viewModel.action != nil || viewModel.offAction != nil) { actionBlock = { [weak self] in guard let self = self else { return } diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift index a10abbac..3a1722e0 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift @@ -5,130 +5,103 @@ // Created by Kevin Christiano on 9/13/19. // Copyright © 2019 Verizon Wireless. All rights reserved. // +import VDS - -@objcMembers open class CheckboxLabel: View { - //-------------------------------------------------- - // MARK: - Outlets - //-------------------------------------------------- - - public let checkbox = Checkbox() - public let label = Label(fontStyle: .RegularBodySmall) - private var observation: NSKeyValueObservation? = nil - - //-------------------------------------------------- +@objcMembers open class CheckboxLabel: VDS.CheckboxItem, VDSMoleculeViewProtocol { + //------------------------------------------------------ // MARK: - Properties - //-------------------------------------------------- - - public var checkboxPosition: CheckboxPosition = .center - - //-------------------------------------------------- - // MARK: - Constraints - //-------------------------------------------------- + //------------------------------------------------------ + open var viewModel: CheckboxLabelModel! + open var delegateObject: MVMCoreUIDelegateObject? + open var additionalData: [AnyHashable : Any]? + + // Form Validation + var fieldKey: String? + var fieldValue: JSONValue? + var groupName: String? + + private var updateSelectionOnly: Bool = false + override open var isSelected: Bool { + didSet { + if !updateSelectionOnly { + viewModel.checkbox.selected = isSelected + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + } + } + } - public var checkboxTopConstraint: NSLayoutConstraint? - public var checkboxBottomConstraint: NSLayoutConstraint? - public var checkboxCenterYConstraint: NSLayoutConstraint? - //-------------------------------------------------- // MARK: - Life Cycle //-------------------------------------------------- - - override open func setupView() { - super.setupView() - - guard subviews.isEmpty else { return } - - addSubview(checkbox) - addSubview(label) - - label.text = "" - - checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true - - checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor) - checkboxBottomConstraint?.isActive = true - - checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) - checkboxTopConstraint?.isActive = true - - checkboxCenterYConstraint = checkbox.centerYAnchor.constraint(equalTo: centerYAnchor) - - label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true - layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true - label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true - - layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true - let bottomLabelConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor) - bottomLabelConstraint.priority = .defaultLow - bottomLabelConstraint.isActive = true - - alignCheckbox(.center) - isAccessibilityElement = false - accessibilityElements = [checkbox, label] - } - - @objc override open func updateView(_ size: CGFloat) { - super.updateView(size) - - label.updateView(size) - checkbox.updateView(size) - layoutIfNeeded() - } - - //-------------------------------------------------- - // MARK: - Methods - //-------------------------------------------------- - - /// Aligns Checkbox and Label relative to the desired position of the Checkbox. - private func alignCheckbox(_ position: CheckboxPosition) { - checkboxPosition = position - - switch position { - case .center: - checkboxBottomConstraint?.isActive = false - checkboxTopConstraint?.isActive = false - checkboxCenterYConstraint?.isActive = true - - case .top: - checkboxBottomConstraint?.isActive = false - checkboxTopConstraint?.isActive = true - checkboxCenterYConstraint?.isActive = false - - case .bottom: - checkboxBottomConstraint?.isActive = true - checkboxTopConstraint?.isActive = false - checkboxCenterYConstraint?.isActive = false - } - } + @objc open func updateView(_ size: CGFloat) {} //-------------------------------------------------- // MARK: - Atomic //-------------------------------------------------- - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - guard let checkBoxWithLabelModel = model as? CheckboxLabelModel else { return } - - if let checkboxAlignment = checkBoxWithLabelModel.checkboxAlignment { - alignCheckbox(checkboxAlignment) + public func viewModelDidUpdate() { + updateCheckbox() + + //primary label + labelText = viewModel.label.text + if let attributes = viewModel.label.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) { + labelTextAttributes = attributes } - checkbox.set(with: checkBoxWithLabelModel.checkbox, delegateObject, additionalData) - label.set(with: checkBoxWithLabelModel.label, delegateObject, additionalData) + //secondary label + if let subtitleModel = viewModel.subtitle { + childText = subtitleModel.text + if let attributes = subtitleModel.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) { + childTextAttributes = attributes + } + } } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + MVMCoreUIActionHandler.performActionUnstructured(with: actionModel, sourceModel: viewModel.checkbox, additionalData: additionalData, delegateObject: delegateObject) + } + + public func updateCheckbox() { + //forms + FormValidator.setupValidation(for: viewModel.checkbox, delegate: delegateObject?.formHolderDelegate) + groupName = viewModel.checkbox.groupName + if let fieldKey = viewModel.checkbox.fieldKey { + self.fieldKey = fieldKey + } + + //properties + isAnimated = viewModel.checkbox.animated + isEnabled = viewModel.checkbox.enabled && !viewModel.checkbox.readOnly + if viewModel.checkbox.selected { + updateSelectionOnly = false + isSelected = viewModel.checkbox.selected + updateSelectionOnly = true + } + + //events + viewModel.checkbox.updateUI = { + MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in + guard let self = self else { return } + isEnabled = viewModel.checkbox.enabled + }) + } + + //onChange + if (viewModel.checkbox.action != nil || viewModel.checkbox.offAction != nil) { + onChange = { [weak self] control in + guard let self = self else { return } + + if let offAction = viewModel.checkbox.offAction, !isSelected { + performCheckboxAction(with: offAction, delegateObject: delegateObject, additionalData: additionalData) + + } else if let action = viewModel.checkbox.action { + performCheckboxAction(with: action, delegateObject: delegateObject, additionalData: additionalData) + } + } + } + + } + + open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 200 } - - open override func reset() { - super.reset() - - label.text = "" - checkbox.reset() - alignCheckbox(.center) - } - - override open func accessibilityActivate() -> Bool { - checkbox.accessibilityActivate() - } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift index 9618cfab..31cdf46d 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift @@ -21,11 +21,15 @@ public enum CheckboxPosition: String, Codable { @DecodableDefault.UUIDString public var id: String public var backgroundColor: Color? - public var checkboxAlignment: CheckboxPosition? public var checkbox: CheckboxModel public var label: LabelModel + public var subtitle: LabelModel? + + public var children: [MoleculeModelProtocol] { + guard let subtitle else { return [checkbox, label] } + return [checkbox, label, subtitle] + } - public var children: [MoleculeModelProtocol] { [checkbox, label] } //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- From 568ab2e5e57f44d472c4619f222eee5efc1f457e Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 2 Jul 2024 10:56:40 -0500 Subject: [PATCH 03/10] added surface Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift | 2 ++ MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift index 3a1722e0..e21f9895 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift @@ -39,6 +39,8 @@ import VDS // MARK: - Atomic //-------------------------------------------------- public func viewModelDidUpdate() { + surface = viewModel.surface + updateCheckbox() //primary label diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift index 31cdf46d..1592bd02 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift @@ -8,6 +8,7 @@ import Foundation import MVMCore +import VDS public enum CheckboxPosition: String, Codable { case center @@ -24,6 +25,8 @@ public enum CheckboxPosition: String, Codable { public var checkbox: CheckboxModel public var label: LabelModel public var subtitle: LabelModel? + public var inverted: Bool = false + public var surface: Surface { inverted ? .dark : .light } public var children: [MoleculeModelProtocol] { guard let subtitle else { return [checkbox, label] } @@ -34,9 +37,10 @@ public enum CheckboxPosition: String, Codable { // MARK: - Initializer //-------------------------------------------------- - public init(checkbox: CheckboxModel, label: LabelModel) { + public init(checkbox: CheckboxModel, label: LabelModel, subtitle: LabelModel?) { self.checkbox = checkbox self.label = label + self.subtitle = subtitle } } From a84f502a0ec70422abd8b475f90306f7d2cd1c00 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 3 Jul 2024 09:37:00 -0500 Subject: [PATCH 04/10] updated checkbox/label Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Selectors/Checkbox.swift | 5 +- .../Atoms/Selectors/CheckboxModel.swift | 65 +++++-------------- .../Atomic/Atoms/Views/CheckboxLabel.swift | 3 + .../Atoms/Views/CheckboxLabelModel.swift | 6 +- 4 files changed, 27 insertions(+), 52 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index 1b8c9a84..ed89af67 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -142,7 +142,7 @@ import VDS self.isSelected = isSelected } } - + //-------------------------------------------------- // MARK: - Molecular //-------------------------------------------------- @@ -179,7 +179,10 @@ import VDS viewModel.updateUI = { MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in guard let self = self else { return } + let isValid = viewModel.isValid ?? true + showError = !isValid isEnabled = viewModel.enabled + }) } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index 98e0cbbd..7c3d3ee0 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -14,46 +14,29 @@ import VDS var selected: Bool { get set } } -@objcMembers public class CheckboxModel: MoleculeModelProtocol, SelectableMoleculeModelProtocol, FormFieldProtocol, UIUpdatableModelProtocol { +@objcMembers public class CheckboxModel: FormFieldModel, SelectableMoleculeModelProtocol{ //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - - public static var identifier: String = "checkbox" - public var id: String = UUID().uuidString - public var backgroundColor: Color? - public var accessibilityIdentifier: String? + public static override var identifier: String { "checkbox" } public var selected: Bool = false - public var enabled: Bool = true - public var readOnly: Bool = false public var animated: Bool = true public var inverted: Bool = false public var action: ActionModelProtocol? public var offAction: ActionModelProtocol? public var surface: Surface { inverted ? .dark : .light } - public var fieldKey: String? - public var groupName: String = FormValidator.defaultGroupName - public var baseValue: AnyHashable? - public var updateUI: ActionBlock? - + //-------------------------------------------------- // MARK: - Keys //-------------------------------------------------- private enum CodingKeys: String, CodingKey { - case id - case moleculeName - case accessibilityIdentifier case checked - case enabled - case readOnly case inverted case animated case action - case fieldKey - case groupName case offAction } @@ -61,16 +44,17 @@ import VDS // MARK: - Form Validation //-------------------------------------------------- - public func formFieldValue() -> AnyHashable? { + open override func formFieldValue() -> AnyHashable? { guard enabled else { return nil } return selected } - //-------------------------------------------------- - // MARK: - Server Value - //-------------------------------------------------- - open func formFieldServerValue() -> AnyHashable? { - return formFieldValue() + open override func setValidity(_ valid: Bool, errorMessage: String?) { + if let ruleErrorMessage = errorMessage, fieldKey != nil { + self.errorMessage = ruleErrorMessage + } + isValid = valid + updateUI?() } //-------------------------------------------------- @@ -78,7 +62,8 @@ import VDS //-------------------------------------------------- public init(isChecked: Bool = false) { - self.selected = isChecked + super.init() + selected = isChecked baseValue = isChecked } @@ -87,12 +72,9 @@ import VDS //-------------------------------------------------- required public init(from decoder: Decoder) throws { + try super.init(from: decoder) + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - - id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString - - accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) - if let checked = try typeContainer.decodeIfPresent(Bool.self, forKey: .checked) { self.selected = checked } @@ -107,31 +89,18 @@ import VDS self.inverted = inverted } - enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true - readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false action = try typeContainer.decodeModelIfPresent(codingKey: .action) - fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) - - if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { - self.groupName = groupName - } offAction = try typeContainer.decodeModelIfPresent(codingKey: .offAction) + } - public func encode(to encoder: Encoder) throws { + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(id, forKey: .id) - try container.encode(moleculeName, forKey: .moleculeName) - try container.encodeIfPresent(groupName, forKey: .groupName) - try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encode(selected, forKey: .checked) try container.encode(inverted, forKey: .inverted) - try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(animated, forKey: .animated) - try container.encode(enabled, forKey: .enabled) - try container.encode(readOnly, forKey: .readOnly) try container.encodeModelIfPresent(action, forKey: .action) - try container.encodeIfPresent(groupName, forKey: .groupName) try container.encodeModelIfPresent(offAction, forKey: .offAction) } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift index e21f9895..d4c25889 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift @@ -83,6 +83,9 @@ import VDS viewModel.checkbox.updateUI = { MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in guard let self = self else { return } + let isValid = viewModel.checkbox.isValid ?? true + showError = !isValid + errorText = viewModel.checkbox.errorMessage isEnabled = viewModel.checkbox.enabled }) } diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift index 1592bd02..ea78a174 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift @@ -25,9 +25,9 @@ public enum CheckboxPosition: String, Codable { public var checkbox: CheckboxModel public var label: LabelModel public var subtitle: LabelModel? - public var inverted: Bool = false - public var surface: Surface { inverted ? .dark : .light } - + public var inverted: Bool? = false + public var surface: Surface { inverted ?? false ? .dark : .light } + public var children: [MoleculeModelProtocol] { guard let subtitle else { return [checkbox, label] } return [checkbox, label, subtitle] From fcd4f4e0ecd15a2c59bd65422da605589819faf6 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 3 Jul 2024 15:57:22 -0500 Subject: [PATCH 05/10] refactored name to subTitle Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift | 8 ++------ MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift | 6 +++--- .../Atomic/Atoms/Views/CheckboxLabelModel.swift | 16 +++++----------- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index ed89af67..473e56df 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -24,11 +24,7 @@ import VDS var fieldKey: String? var fieldValue: JSONValue? var groupName: String? - - public var checkboxModel: CheckboxModel? { - viewModel - } - + /// Disables all selection logic when setting the value of isSelected, reducing it to a stored property. public var updateSelectionOnly: Bool = false @@ -154,7 +150,7 @@ import VDS public func updateView(_ size: CGFloat) {} private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - MVMCoreUIActionHandler.performActionUnstructured(with: actionModel, sourceModel: checkboxModel, additionalData: additionalData, delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: actionModel, sourceModel: viewModel, additionalData: additionalData, delegateObject: delegateObject) } diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift index d4c25889..b1454a97 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift @@ -50,9 +50,9 @@ import VDS } //secondary label - if let subtitleModel = viewModel.subtitle { - childText = subtitleModel.text - if let attributes = subtitleModel.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) { + if let subTitleModel = viewModel.subTitle { + childText = subTitleModel.text + if let attributes = subTitleModel.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) { childTextAttributes = attributes } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift index ea78a174..faa13e60 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift @@ -10,12 +10,6 @@ import Foundation import MVMCore import VDS -public enum CheckboxPosition: String, Codable { - case center - case top - case bottom -} - @objcMembers open class CheckboxLabelModel: MoleculeModelProtocol, ParentMoleculeModelProtocol { open class var identifier: String { "checkboxLabel" } public var moleculeName: String = CheckboxLabelModel.identifier @@ -24,23 +18,23 @@ public enum CheckboxPosition: String, Codable { public var backgroundColor: Color? public var checkbox: CheckboxModel public var label: LabelModel - public var subtitle: LabelModel? + public var subTitle: LabelModel? public var inverted: Bool? = false public var surface: Surface { inverted ?? false ? .dark : .light } public var children: [MoleculeModelProtocol] { - guard let subtitle else { return [checkbox, label] } - return [checkbox, label, subtitle] + guard let subTitle else { return [checkbox, label] } + return [checkbox, label, subTitle] } //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- - public init(checkbox: CheckboxModel, label: LabelModel, subtitle: LabelModel?) { + public init(checkbox: CheckboxModel, label: LabelModel, subTitle: LabelModel?) { self.checkbox = checkbox self.label = label - self.subtitle = subtitle + self.subTitle = subTitle } } From efd98cc887a0adc4d5ce0ec6de14e4ff36ad6018 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 19 Jul 2024 16:00:43 -0500 Subject: [PATCH 06/10] removed un-needed properties Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index 7c3d3ee0..00554c2e 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -22,11 +22,8 @@ import VDS public static override var identifier: String { "checkbox" } public var selected: Bool = false public var animated: Bool = true - public var inverted: Bool = false public var action: ActionModelProtocol? public var offAction: ActionModelProtocol? - - public var surface: Surface { inverted ? .dark : .light } //-------------------------------------------------- // MARK: - Keys @@ -34,7 +31,6 @@ import VDS private enum CodingKeys: String, CodingKey { case checked - case inverted case animated case action case offAction @@ -85,10 +81,6 @@ import VDS self.animated = animated } - if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { - self.inverted = inverted - } - action = try typeContainer.decodeModelIfPresent(codingKey: .action) offAction = try typeContainer.decodeModelIfPresent(codingKey: .offAction) @@ -98,7 +90,6 @@ import VDS try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(selected, forKey: .checked) - try container.encode(inverted, forKey: .inverted) try container.encodeIfPresent(animated, forKey: .animated) try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(offAction, forKey: .offAction) From f82ef2e51c3063a498204a0ccacf81b2b8aea792 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 19 Jul 2024 16:12:12 -0500 Subject: [PATCH 07/10] add open Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index 473e56df..99dc419e 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -26,10 +26,10 @@ import VDS var groupName: String? /// Disables all selection logic when setting the value of isSelected, reducing it to a stored property. - public var updateSelectionOnly: Bool = false + open var updateSelectionOnly: Bool = false /// Action Block called when the switch is selected. - public var actionBlock: ActionBlock? { + open var actionBlock: ActionBlock? { get { nil } set { if let action = newValue { @@ -110,7 +110,7 @@ import VDS //-------------------------------------------------- /// This will toggle the state of the Checkbox and execute the actionBlock if provided. - public func toggleAndAction() { + open func toggleAndAction() { toggle() } @@ -120,7 +120,7 @@ import VDS /// Programmatic means to check/uncheck the box. /// - parameter selected: state of the check box: true = checked OR false = unchecked. /// - parameter animated: allows the state of the checkbox to change with or without animation. - public func updateSelection(to selected: Bool, animated: Bool) { + open func updateSelection(to selected: Bool, animated: Bool) { DispatchQueue.main.async { self.isAnimated = animated @@ -131,7 +131,7 @@ import VDS /// updates the visuals of the check mark and background. /// - parameter isSelected: the check state of the checkbox. /// - parameter isAnimated: determines of the changes should animate or immediately refelect. - public func updateCheckboxUI(isSelected: Bool, isAnimated: Bool) { + open func updateCheckboxUI(isSelected: Bool, isAnimated: Bool) { DispatchQueue.main.async { self.isAnimated = isAnimated @@ -145,9 +145,9 @@ import VDS open func needsToBeConstrained() -> Bool { true } - public func horizontalAlignment() -> UIStackView.Alignment { .leading } + open func horizontalAlignment() -> UIStackView.Alignment { .leading } - public func updateView(_ size: CGFloat) {} + open func updateView(_ size: CGFloat) {} private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { MVMCoreUIActionHandler.performActionUnstructured(with: actionModel, sourceModel: viewModel, additionalData: additionalData, delegateObject: delegateObject) From c47d21bdf1c50e2006c2be45b11d519ba5c50122 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 19 Jul 2024 16:32:46 -0500 Subject: [PATCH 08/10] updated with open Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift index b1454a97..9473a324 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift @@ -38,7 +38,7 @@ import VDS //-------------------------------------------------- // MARK: - Atomic //-------------------------------------------------- - public func viewModelDidUpdate() { + open func viewModelDidUpdate() { surface = viewModel.surface updateCheckbox() @@ -62,7 +62,7 @@ import VDS MVMCoreUIActionHandler.performActionUnstructured(with: actionModel, sourceModel: viewModel.checkbox, additionalData: additionalData, delegateObject: delegateObject) } - public func updateCheckbox() { + open func updateCheckbox() { //forms FormValidator.setupValidation(for: viewModel.checkbox, delegate: delegateObject?.formHolderDelegate) groupName = viewModel.checkbox.groupName From 2675488ab3510ecb5007903eab4c3065068f54dd Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 30 Jul 2024 08:03:26 -0500 Subject: [PATCH 09/10] removed accessibility so we can use the VDS version Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Selectors/Checkbox.swift | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index 473e56df..ddf84d7d 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -83,28 +83,7 @@ import VDS self.init(frame: .zero) isSelected = isChecked } - - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - - open override func setup() { - super.setup() - bridge_accessibilityLabelBlock = { [weak self] in - // Attention: This needs to be addressed with the accessibility team. - // NOTE: Currently emptying description part of MVMCoreUICheckBox accessibility label to avoid crashing! - guard let self, - let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") - else { return nil } - - return String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@%@", "", state) - } - bridge_accessibilityHintBlock = { - MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") - } - } - //-------------------------------------------------- // MARK: - Actions //-------------------------------------------------- From ac23b0dc41a869b732777e7f7d109f1ba154770c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 30 Jul 2024 16:35:46 -0500 Subject: [PATCH 10/10] refactored checkbox action to didSet Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Selectors/Checkbox.swift | 38 ++++++------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index 13fdcb19..e864caa1 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -24,17 +24,13 @@ import VDS var fieldKey: String? var fieldValue: JSONValue? var groupName: String? - - /// Disables all selection logic when setting the value of isSelected, reducing it to a stored property. - open var updateSelectionOnly: Bool = false - + /// Action Block called when the switch is selected. open var actionBlock: ActionBlock? { - get { nil } - set { - if let action = newValue { + didSet { + if let actionBlock { onChange = { _ in - action() + actionBlock() } } else { onChange = nil @@ -42,20 +38,6 @@ import VDS } } - /** - The represented state of the Checkbox. - - Setting updateSelectionOnly to true bypasses the animation logic inherent with setting this property. - */ - override open var isSelected: Bool { - didSet { - if !updateSelectionOnly { - viewModel.selected = isSelected - _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) - } - } - } - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -92,6 +74,12 @@ import VDS open func toggleAndAction() { toggle() } + + open override func toggle() { + super.toggle() + viewModel.selected = isSelected + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + } //-------------------------------------------------- // MARK: - Methods @@ -144,11 +132,7 @@ import VDS //properties isEnabled = viewModel.enabled && !viewModel.readOnly isAnimated = viewModel.animated - if viewModel.selected { - updateSelectionOnly = true - isSelected = viewModel.selected - updateSelectionOnly = false - } + isSelected = viewModel.selected //events viewModel.updateUI = {