From 66255d9dc5329a4d2b482529ee20c00fe3a8b7ac Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 27 Jun 2024 15:49:06 -0500 Subject: [PATCH 1/6] implemented initial vds toggle Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift | 343 +++--------------- .../Atomic/Atoms/Selectors/ToggleModel.swift | 60 +-- .../Atomic/Extensions/VDS-Enums+Codable.swift | 3 + 3 files changed, 91 insertions(+), 315 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift index 4341614e..dba501ca 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift @@ -8,6 +8,7 @@ import MVMCore import UIKit +import VDS public typealias ActionBlockConfirmation = () -> (Bool) @@ -19,53 +20,35 @@ public typealias ActionBlockConfirmation = () -> (Bool) Container: The background of the toggle control. Knob: The circular indicator that slides on the container. */ -@objcMembers open class Toggle: Control, MVMCoreUIViewConstrainingProtocol { - //-------------------------------------------------- +@objcMembers open class Toggle: VDS.Toggle, VDSMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { + //------------------------------------------------------ // MARK: - Properties - //-------------------------------------------------- - - /// Holds the on and off colors for the container. - public var containerTintColor: (on: UIColor, off: UIColor) = (on: .mvmGreen, off: .mvmBlack) - - /// Holds the on and off colors for the knob. - public var knobTintColor: (on: UIColor, off: UIColor) = (on: .mvmWhite, off: .mvmWhite) - - /// Holds the on and off colors for the disabled state.. - public var disabledTintColor: (container: UIColor, knob: UIColor) = (container: .mvmCoolGray3, knob: .mvmWhite) - - /// Set this flag to false if you do not want to animate state changes. - public var isAnimated = true - - public var didToggleAction: ActionBlock? + //------------------------------------------------------ + open var viewModel: ToggleModel! + open var delegateObject: MVMCoreUIDelegateObject? + open var additionalData: [AnyHashable : Any]? + + public var didToggleAction: ActionBlock? { + get { nil } + set { + if let action = newValue { + onChange = { _ in + action() + } + } else { + onChange = nil + } + } + } /// Executes logic before state change. If false, then toggle state will not change and the didToggleAction will not execute. public var shouldToggleAction: ActionBlockConfirmation? = { return { true } }() - // Sizes are from InVision design specs. - static let containerSize = CGSize(width: 51, height: 31) - static let knobSize = CGSize(width: 28, height: 28) - - private var knobView: View = { - let view = View() - view.backgroundColor = .white - view.layer.cornerRadius = Toggle.getKnobHeight() / 2.0 - return view - }() - //-------------------------------------------------- // MARK: - Computed Properties //-------------------------------------------------- - - open override var isEnabled: Bool { - didSet { - isUserInteractionEnabled = isEnabled - changeStateNoAnimation(isEnabled ? isOn : false) - setToggleAppearanceFromState() - accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: isEnabled ? "AccToggleHint" : "AccDisabled") - } - } /// Simple means to prevent user interaction with the toggle. public var isLocked: Bool = false { @@ -73,83 +56,16 @@ public typealias ActionBlockConfirmation = () -> (Bool) } /// The state on the toggle. Default value: false. - open var isOn: Bool = false { + open override var isOn: Bool { didSet { - if isAnimated { - UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: { - if self.isOn { - self.knobView.backgroundColor = self.knobTintColor.on - self.backgroundColor = self.containerTintColor.on - - } else { - self.knobView.backgroundColor = self.knobTintColor.off - self.backgroundColor = self.containerTintColor.off - } - }, completion: nil) - - UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.2, options: [], animations: { - self.constrainKnob() - self.knobWidthConstraint?.constant = Self.getKnobWidth() - self.layoutIfNeeded() - }, completion: nil) - - } else { - setToggleAppearanceFromState() - self.constrainKnob() - } - toggleModel?.selected = isOn _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) - accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") - setNeedsLayout() - layoutIfNeeded() } } public var toggleModel: ToggleModel? { - model as? ToggleModel + viewModel } - - //-------------------------------------------------- - // MARK: - Delegate - //-------------------------------------------------- - - private var delegateObject: MVMCoreUIDelegateObject? - - //-------------------------------------------------- - // MARK: - Constraints - //-------------------------------------------------- - - private var knobLeadingConstraint: NSLayoutConstraint? - private var knobTrailingConstraint: NSLayoutConstraint? - private var knobHeightConstraint: NSLayoutConstraint? - private var knobWidthConstraint: NSLayoutConstraint? - private var heightConstraint: NSLayoutConstraint? - private var widthConstraint: NSLayoutConstraint? - - private func constrainKnob() { - - knobLeadingConstraint?.isActive = false - knobTrailingConstraint?.isActive = false - - _ = isOn ? constrainKnobOn() : constrainKnobOff() - - knobTrailingConstraint?.isActive = true - knobLeadingConstraint?.isActive = true - } - - private func constrainKnobOn() { - - knobTrailingConstraint = trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 2) - knobLeadingConstraint = knobView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor) - } - - private func constrainKnobOff() { - - knobTrailingConstraint = trailingAnchor.constraint(greaterThanOrEqualTo: knobView.trailingAnchor) - knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 2) - } - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -158,7 +74,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) super.init(frame: frame) } - public convenience override init() { + public convenience required init() { self.init(frame: .zero) } @@ -171,7 +87,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) /// - parameter didToggleAction: A closure which is executed after the toggle changes states. public convenience init(isOn: Bool = false, didToggleAction: ActionBlock?) { self.init(frame: .zero) - changeStateNoAnimation(isOn) + self.isOn = isOn self.didToggleAction = didToggleAction } @@ -191,214 +107,71 @@ public typealias ActionBlockConfirmation = () -> (Bool) //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - - public override func updateView(_ size: CGFloat) { - super.updateView(size) - - heightConstraint?.constant = Self.getContainerHeight() - widthConstraint?.constant = Self.getContainerWidth() - - knobHeightConstraint?.constant = Self.getKnobHeight() - knobWidthConstraint?.constant = Self.getKnobWidth() - - layer.cornerRadius = Self.getContainerHeight() / 2.0 - knobView.layer.cornerRadius = Self.getKnobHeight() / 2.0 - - changeStateNoAnimation(isOn) - } - - public override func setupView() { - super.setupView() - - isAccessibilityElement = true - accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint") - accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") - accessibilityTraits = .button + public func updateView(_ size: CGFloat) { } - heightConstraint = heightAnchor.constraint(equalToConstant: Self.containerSize.height) - heightConstraint?.isActive = true + public override func setup() { + super.setup() + + bridge_accessibilityHintBlock = { [weak self] in + guard let self else { return nil } + return MVMCoreUIUtility.hardcodedString(withKey: isEnabled ? "AccToggleHint" : "AccDisabled") + } - widthConstraint = widthAnchor.constraint(equalToConstant: Self.containerSize.width) - widthConstraint?.isActive = true - - layer.cornerRadius = Self.getContainerHeight() / 2.0 - backgroundColor = containerTintColor.off - - addSubview(knobView) - - knobHeightConstraint = knobView.heightAnchor.constraint(equalToConstant: Self.knobSize.height) - knobHeightConstraint?.isActive = true - knobWidthConstraint = knobView.widthAnchor.constraint(equalToConstant: Self.knobSize.width) - knobWidthConstraint?.isActive = true - knobView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - knobView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true - bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true - - constrainKnobOff() + bridge_accessibilityValueBlock = { [weak self] in + guard let self else { return nil } + return isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") + } } public override func reset() { super.reset() - - backgroundColor = containerTintColor.off - knobView.backgroundColor = knobTintColor.off accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") - isAnimated = true didToggleAction = nil shouldToggleAction = { return true } } - class func getContainerWidth() -> CGFloat { - let containerWidth = Self.containerSize.width - return (MFSizeObject(standardSize: containerWidth, standardiPadPortraitSize: CGFloat(Self.containerSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerWidth - } - - class func getContainerHeight() -> CGFloat { - let containerHeight = Self.containerSize.height - return (MFSizeObject(standardSize: containerHeight, standardiPadPortraitSize: CGFloat(Self.containerSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerHeight - } - - class func getKnobWidth() -> CGFloat { - let knobWidth = Self.knobSize.width - return (MFSizeObject(standardSize: knobWidth, standardiPadPortraitSize: CGFloat(Self.knobSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? knobWidth - } - - class func getKnobHeight() -> CGFloat { - let knobHeight = Self.knobSize.width - return (MFSizeObject(standardSize: knobHeight, standardiPadPortraitSize: CGFloat(Self.knobSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? knobHeight - } - //-------------------------------------------------- // 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 Toggle and execute the actionBlock if provided. public func toggleAndAction() { - + toggle() + } + + open override func toggle() { if let result = shouldToggleAction?(), result { - isOn.toggle() + super.toggle() didToggleAction?() } } - - private func changeStateNoAnimation(_ state: Bool) { - - // Hold state in case User wanted isAnimated to remain off. - let isAnimatedState = isAnimated - - isAnimated = false - isOn = state - isAnimated = isAnimatedState - } - - override open func accessibilityActivate() -> Bool { - // Hold state in case User wanted isAnimated to remain off. - guard isUserInteractionEnabled else { return false } - let isAnimatedState = isAnimated - isAnimated = false - sendActions(for: .touchUpInside) - isAnimated = isAnimatedState - return true - } - - //-------------------------------------------------- - // MARK: - UIResponder - //-------------------------------------------------- - - open override func touchesBegan(_ touches: Set, with event: UIEvent?) { - - UIView.animate(withDuration: 0.1, animations: { - self.knobWidthConstraint?.constant += PaddingOne - self.layoutIfNeeded() - }) - } - - public override func touchesEnded(_ touches: Set, with event: UIEvent?) { - - knobReformAnimation() - - // Action only occurs of the user lifts up from withing acceptable region of the toggle. - guard let coordinates = touches.first?.location(in: self), - coordinates.x > -20, - coordinates.x < bounds.width + 20, - coordinates.y > -20, - coordinates.y < bounds.height + 20 - else { return } - - sendActions(for: .touchUpInside) - } - - public func touchesCancelled(_ touches: Set, with event: UIEvent) { - - knobReformAnimation() - sendActions(for: .touchCancel) - } - - //-------------------------------------------------- - // MARK: - Animations - //-------------------------------------------------- - - public func setToggleAppearanceFromState() { - - backgroundColor = isEnabled ? isOn ? containerTintColor.on : containerTintColor.off : disabledTintColor.container - knobView.backgroundColor = isEnabled ? isOn ? knobTintColor.on : knobTintColor.off : disabledTintColor.knob - } - - public func knobReformAnimation() { - - if isAnimated { - UIView.animate(withDuration: 0.1, animations: { - self.knobWidthConstraint?.constant = Self.getKnobWidth() - self.layoutIfNeeded() - }, completion: nil) - - } else { - knobWidthConstraint?.constant = Self.getKnobWidth() - layoutIfNeeded() - } - } // MARK:- MoleculeViewProtocol - 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? ToggleModel else { return } + isOn = viewModel.selected + isAnimated = viewModel.animated + isEnabled = viewModel.enabled && !viewModel.readOnly + showText = viewModel.showText + onText = viewModel.onText + offText = viewModel.offText + textSize = viewModel.textSize + textWeight = viewModel.textWeight + textPosition = viewModel.textPosition - FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) - - containerTintColor.on = model.onTintColor.uiColor - containerTintColor.off = model.offTintColor.uiColor - knobTintColor.on = model.onKnobTintColor.uiColor - knobTintColor.off = model.offKnobTintColor.uiColor - isOn = model.selected - changeStateNoAnimation(isOn) - isAnimated = model.animated - isEnabled = model.enabled && !model.readOnly - - if let accessibileString = model.accessibilityText { + if let accessibileString = viewModel.accessibilityText { accessibilityLabel = accessibileString } - if model.action != nil || model.alternateAction != nil { + if viewModel.action != nil || viewModel.alternateAction != nil { didToggleAction = { [weak self] in guard let self = self else { return } if self.isOn { - if let action = model.action { + if let action = viewModel.action { MVMCoreUIActionHandler.performActionUnstructured(with: action, sourceModel: model, additionalData: additionalData, delegateObject: delegateObject) } } else { - if let action = model.alternateAction ?? model.action { + if let action = viewModel.alternateAction ?? viewModel.action { MVMCoreUIActionHandler.performActionUnstructured(with: action, sourceModel: model, additionalData: additionalData, delegateObject: delegateObject) } } @@ -406,8 +179,8 @@ public typealias ActionBlockConfirmation = () -> (Bool) } } - public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - Self.getContainerHeight() + public class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return Self().intrinsicContentSize.height } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift index f4ce9234..a96bddb8 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift @@ -5,7 +5,7 @@ // Created by Scott Pfeil on 1/14/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // - +import VDS public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { //-------------------------------------------------- @@ -24,10 +24,13 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { public var action: ActionModelProtocol? public var alternateAction: ActionModelProtocol? public var accessibilityText: String? - public var onTintColor: Color = Color(uiColor: .mvmGreen) - public var offTintColor: Color = Color(uiColor: .mvmBlack) - public var onKnobTintColor: Color = Color(uiColor: .mvmWhite) - public var offKnobTintColor: Color = Color(uiColor: .mvmWhite) + + public var showText: Bool = false + public var onText: String = "On" + public var offText: String = "Off" + public var textSize: VDS.Toggle.TextSize = .small + public var textWeight: VDS.Toggle.TextWeight = .regular + public var textPosition: VDS.Toggle.TextPosition = .left public var fieldKey: String? public var groupName: String = FormValidator.defaultGroupName @@ -49,10 +52,14 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { case accessibilityIdentifier case alternateAction case accessibilityText - case onTintColor - case offTintColor - case onKnobTintColor - case offKnobTintColor + + case showText + case onText + case offText + case textSize + case textWeight + case textPosition + case fieldKey case groupName } @@ -102,25 +109,8 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { action = try typeContainer.decodeModelIfPresent(codingKey: .action) alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction) - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) - if let onTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .onTintColor) { - self.onTintColor = onTintColor - } - - if let offTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .offTintColor) { - self.offTintColor = offTintColor - } - - if let onKnobTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .onKnobTintColor) { - self.onKnobTintColor = onKnobTintColor - } - - if let offKnobTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .offKnobTintColor) { - self.offKnobTintColor = offKnobTintColor - } - accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) baseValue = selected @@ -130,6 +120,13 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { } enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false + + showText = try typeContainer.decodeIfPresent(Bool.self, forKey: .showText) ?? false + onText = try typeContainer.decodeIfPresent(String.self, forKey: .onText) ?? "On" + offText = try typeContainer.decodeIfPresent(String.self, forKey: .offText) ?? "Off" + textSize = try typeContainer.decodeIfPresent(VDS.Toggle.TextSize.self, forKey: .textSize) ?? .small + textWeight = try typeContainer.decodeIfPresent(VDS.Toggle.TextWeight.self, forKey: .textWeight) ?? .regular + textPosition = try typeContainer.decodeIfPresent(VDS.Toggle.TextPosition.self, forKey: .textPosition) ?? .left } public func encode(to encoder: Encoder) throws { @@ -143,13 +140,16 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { try container.encode(selected, forKey: .state) try container.encode(animated, forKey: .animated) try container.encode(enabled, forKey: .enabled) - try container.encode(onTintColor, forKey: .onTintColor) - try container.encode(onKnobTintColor, forKey: .onKnobTintColor) - try container.encode(onKnobTintColor, forKey: .onKnobTintColor) - try container.encode(offKnobTintColor, forKey: .offKnobTintColor) try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(groupName, forKey: .groupName) try container.encode(readOnly, forKey: .readOnly) + + try container.encode(showText, forKey: .showText) + try container.encode(onText, forKey: .onText) + try container.encode(offText, forKey: .offText) + try container.encode(textSize, forKey: .textSize) + try container.encode(textWeight, forKey: .textWeight) + try container.encode(textPosition, forKey: .textPosition) } } diff --git a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift index f4eb5500..1ae838e0 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift @@ -36,6 +36,9 @@ extension VDS.TextLinkCaret.IconPosition: Codable {} extension VDS.TileContainerBase.AspectRatio: Codable {} extension VDS.Tilelet.Padding: Codable {} extension VDS.TitleLockup.TextAlignment: Codable {} +extension VDS.Toggle.TextSize: Codable {} +extension VDS.Toggle.TextWeight: Codable {} +extension VDS.Toggle.TextPosition: Codable {} extension VDS.Tooltip.FillColor: Codable {} extension VDS.Tooltip.Size: Codable {} extension VDS.Line.Style: Codable {} From 04e11ee1fb28d7c8087b7dfdfaa5e99e05f066c8 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 28 Jun 2024 14:54:47 -0500 Subject: [PATCH 2/6] a few updates Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift | 21 ++++++++++++------- .../Atomic/Atoms/Selectors/ToggleModel.swift | 5 +++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift index dba501ca..30bcd85a 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift @@ -27,6 +27,9 @@ public typealias ActionBlockConfirmation = () -> (Bool) open var viewModel: ToggleModel! open var delegateObject: MVMCoreUIDelegateObject? open var additionalData: [AnyHashable : Any]? + + /// Disables all selection logic when setting the value of isOn, reducing it to a stored property. + public var updateSelectionOnly: Bool = false public var didToggleAction: ActionBlock? { get { nil } @@ -58,14 +61,13 @@ public typealias ActionBlockConfirmation = () -> (Bool) /// The state on the toggle. Default value: false. open override var isOn: Bool { didSet { - toggleModel?.selected = isOn - _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + if !updateSelectionOnly { + viewModel.selected = isOn + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + } } } - public var toggleModel: ToggleModel? { - viewModel - } //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -141,7 +143,6 @@ public typealias ActionBlockConfirmation = () -> (Bool) open override func toggle() { if let result = shouldToggleAction?(), result { super.toggle() - didToggleAction?() } } @@ -149,7 +150,13 @@ public typealias ActionBlockConfirmation = () -> (Bool) public func viewModelDidUpdate() { FormValidator.setupValidation(for: viewModel, delegate: delegateObject?.formHolderDelegate) - isOn = viewModel.selected + if viewModel.selected { + updateSelectionOnly = false + isOn = viewModel.selected + updateSelectionOnly = true + } + + surface = viewModel.surface isAnimated = viewModel.animated isEnabled = viewModel.enabled && !viewModel.readOnly showText = viewModel.showText diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift index a96bddb8..398ba42c 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift @@ -25,6 +25,8 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { public var alternateAction: ActionModelProtocol? public var accessibilityText: String? + public var surface: Surface { inverted ? .dark : .light } + public var inverted: Bool = false public var showText: Bool = false public var onText: String = "On" public var offText: String = "Off" @@ -53,6 +55,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { case alternateAction case accessibilityText + case inverted case showText case onText case offText @@ -121,6 +124,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false + inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) ?? false showText = try typeContainer.decodeIfPresent(Bool.self, forKey: .showText) ?? false onText = try typeContainer.decodeIfPresent(String.self, forKey: .onText) ?? "On" offText = try typeContainer.decodeIfPresent(String.self, forKey: .offText) ?? "Off" @@ -145,6 +149,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { try container.encodeIfPresent(groupName, forKey: .groupName) try container.encode(readOnly, forKey: .readOnly) + try container.encode(inverted, forKey: .inverted) try container.encode(showText, forKey: .showText) try container.encode(onText, forKey: .onText) try container.encode(offText, forKey: .offText) From 3689b163393f916e61e2332775ab148038c570f4 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 25 Jul 2024 10:19:55 -0500 Subject: [PATCH 3/6] refactored into VDS.Label extension Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Views/Label/Label.swift | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 605f2e8a..82928c0f 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -34,11 +34,7 @@ public typealias ActionBlock = () -> () /// A specific text index to use as a unique marker. public var hero: Int? - - public var getRange: NSRange { - NSRange(location: 0, length: text?.count ?? 0) - } - + public var shouldMaskWhileRecording: Bool = false public var hasText: Bool { @@ -378,19 +374,24 @@ extension Label { // MARK: - Atomization extension Label { - + public func needsToBeConstrained() -> Bool { true } public func horizontalAlignment() -> UIStackView.Alignment { .leading } public func copyBackgroundColor() -> Bool { true } + } // MARK: - Multi-Link Functionality -extension Label { +extension VDS.Label { + + public var getRange: NSRange { + NSRange(location: 0, length: text?.count ?? 0) + } /// Underlines the tappable region and stores the tap logic for interation. - private func setTextLinkState(range: NSRange, actionBlock: @escaping ActionBlock) { + internal func setTextLinkState(range: NSRange, actionBlock: @escaping ActionBlock) { guard let text, text.isValid(range: range) else { return } var textLink = ActionLabelAttribute(location: range.location, length: range.length) @@ -417,8 +418,16 @@ extension Label { return { [weak self] in guard let self = self else { return } - if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + if let button = self as? MFButtonProtocol { + if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(button, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, + additionalData: additionalData, + delegateObject: delegateObject) + } + } else { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, + additionalData: additionalData, + delegateObject: delegateObject) } } } From 62a74e8c34156ff71222d726a693fe58c38c2acc Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 30 Jul 2024 08:09:44 -0500 Subject: [PATCH 4/6] removed accessibility settings to use VDS and rearranged Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift | 51 ++++++++----------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift index 30bcd85a..c268158c 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift @@ -109,22 +109,6 @@ public typealias ActionBlockConfirmation = () -> (Bool) //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- - public func updateView(_ size: CGFloat) { } - - public override func setup() { - super.setup() - - bridge_accessibilityHintBlock = { [weak self] in - guard let self else { return nil } - return MVMCoreUIUtility.hardcodedString(withKey: isEnabled ? "AccToggleHint" : "AccDisabled") - } - - bridge_accessibilityValueBlock = { [weak self] in - guard let self else { return nil } - return isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") - } - } - public override func reset() { super.reset() accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") @@ -132,21 +116,6 @@ public typealias ActionBlockConfirmation = () -> (Bool) shouldToggleAction = { return true } } - //-------------------------------------------------- - // MARK: - Actions - //-------------------------------------------------- - /// This will toggle the state of the Toggle and execute the actionBlock if provided. - public func toggleAndAction() { - toggle() - } - - open override func toggle() { - if let result = shouldToggleAction?(), result { - super.toggle() - } - } - - // MARK:- MoleculeViewProtocol public func viewModelDidUpdate() { FormValidator.setupValidation(for: viewModel, delegate: delegateObject?.formHolderDelegate) @@ -185,7 +154,27 @@ public typealias ActionBlockConfirmation = () -> (Bool) } } } + + //-------------------------------------------------- + // MARK: - Actions + //-------------------------------------------------- + /// This will toggle the state of the Toggle and execute the actionBlock if provided. + public func toggleAndAction() { + toggle() + } + open override func toggle() { + if let result = shouldToggleAction?(), result { + super.toggle() + } + } + + //-------------------------------------------------- + // MARK:- MoleculeViewProtocol + //-------------------------------------------------- + + public func updateView(_ size: CGFloat) {} + public class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return Self().intrinsicContentSize.height } From eab6fb5f3b52509f331c448226c5bfbac6a08080 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 30 Jul 2024 16:13:17 -0500 Subject: [PATCH 5/6] refactored model to remove default text Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift index 398ba42c..c310e47c 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/ToggleModel.swift @@ -28,8 +28,8 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { public var surface: Surface { inverted ? .dark : .light } public var inverted: Bool = false public var showText: Bool = false - public var onText: String = "On" - public var offText: String = "Off" + public var onText: String? + public var offText: String? public var textSize: VDS.Toggle.TextSize = .small public var textWeight: VDS.Toggle.TextWeight = .regular public var textPosition: VDS.Toggle.TextPosition = .left @@ -126,8 +126,8 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) ?? false showText = try typeContainer.decodeIfPresent(Bool.self, forKey: .showText) ?? false - onText = try typeContainer.decodeIfPresent(String.self, forKey: .onText) ?? "On" - offText = try typeContainer.decodeIfPresent(String.self, forKey: .offText) ?? "Off" + onText = try typeContainer.decodeIfPresent(String.self, forKey: .onText) + offText = try typeContainer.decodeIfPresent(String.self, forKey: .offText) textSize = try typeContainer.decodeIfPresent(VDS.Toggle.TextSize.self, forKey: .textSize) ?? .small textWeight = try typeContainer.decodeIfPresent(VDS.Toggle.TextWeight.self, forKey: .textWeight) ?? .regular textPosition = try typeContainer.decodeIfPresent(VDS.Toggle.TextPosition.self, forKey: .textPosition) ?? .left @@ -151,8 +151,8 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { try container.encode(inverted, forKey: .inverted) try container.encode(showText, forKey: .showText) - try container.encode(onText, forKey: .onText) - try container.encode(offText, forKey: .offText) + try container.encodeIfPresent(onText, forKey: .onText) + try container.encodeIfPresent(offText, forKey: .offText) try container.encode(textSize, forKey: .textSize) try container.encode(textWeight, forKey: .textWeight) try container.encode(textPosition, forKey: .textPosition) From fe66380d7212cee41bbe52cc239012aa3f0e4321 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 30 Jul 2024 16:13:35 -0500 Subject: [PATCH 6/6] refactored setting of isON on viewModelDidUpdate Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift index c268158c..b229d12e 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift @@ -28,15 +28,11 @@ public typealias ActionBlockConfirmation = () -> (Bool) open var delegateObject: MVMCoreUIDelegateObject? open var additionalData: [AnyHashable : Any]? - /// Disables all selection logic when setting the value of isOn, reducing it to a stored property. - public var updateSelectionOnly: Bool = false - public var didToggleAction: ActionBlock? { - get { nil } - set { - if let action = newValue { + didSet { + if let didToggleAction { onChange = { _ in - action() + didToggleAction() } } else { onChange = nil @@ -58,16 +54,6 @@ public typealias ActionBlockConfirmation = () -> (Bool) didSet { isUserInteractionEnabled = !isLocked } } - /// The state on the toggle. Default value: false. - open override var isOn: Bool { - didSet { - if !updateSelectionOnly { - viewModel.selected = isOn - _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) - } - } - } - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -119,18 +105,17 @@ public typealias ActionBlockConfirmation = () -> (Bool) public func viewModelDidUpdate() { FormValidator.setupValidation(for: viewModel, delegate: delegateObject?.formHolderDelegate) - if viewModel.selected { - updateSelectionOnly = false - isOn = viewModel.selected - updateSelectionOnly = true - } - + isOn = viewModel.selected surface = viewModel.surface isAnimated = viewModel.animated isEnabled = viewModel.enabled && !viewModel.readOnly showText = viewModel.showText - onText = viewModel.onText - offText = viewModel.offText + if let onText = viewModel.onText { + self.onText = onText + } + if let offText = viewModel.offText { + self.offText = offText + } textSize = viewModel.textSize textWeight = viewModel.textWeight textPosition = viewModel.textPosition @@ -166,6 +151,8 @@ public typealias ActionBlockConfirmation = () -> (Bool) open override func toggle() { if let result = shouldToggleAction?(), result { super.toggle() + viewModel?.selected = isOn + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) } }