Merge branch 'feature/vds-toggle' into 'develop'

vds toggle import

### Summary
added VDS and extra framework needed in the project
update Toggle/ToggleModel

### JIRA Ticket
https://onejira.verizon.com/browse/ONEAPP-2802

Co-authored-by: Matt Bruce <matt.bruce@verizon.com>

See merge request https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui/-/merge_requests/901
This commit is contained in:
Hedden, Kyle Matthew 2022-10-25 20:09:29 +00:00
commit edfb2c1f36
5 changed files with 145 additions and 455 deletions

View File

@ -598,6 +598,8 @@
EAA0CFAF275E7D8000D65EB0 /* FormFieldEffectProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFAE275E7D8000D65EB0 /* FormFieldEffectProtocol.swift */; };
EAA0CFB1275E823A00D65EB0 /* HideFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFB0275E823A00D65EB0 /* HideFormFieldEffectModel.swift */; };
EAA0CFB3275E831E00D65EB0 /* DisableFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFB2275E831E00D65EB0 /* DisableFormFieldEffectModel.swift */; };
EAA5EEF828F5D079003B3210 /* VDS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEF628F5D074003B3210 /* VDS.framework */; };
EAA78020290081320057DFDF /* VDSMoleculeViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA7801F290081320057DFDF /* VDSMoleculeViewProtocol.swift */; };
EAB14BC127D935F00012AB2C /* RuleCompareModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB14BC027D935F00012AB2C /* RuleCompareModelProtocol.swift */; };
EAB14BC327D9378D0012AB2C /* RuleAnyModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */; };
EABFC1412763BB8D00E78B40 /* FormLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC1402763BB8D00E78B40 /* FormLabel.swift */; };
@ -1200,6 +1202,8 @@
EAA0CFAE275E7D8000D65EB0 /* FormFieldEffectProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormFieldEffectProtocol.swift; sourceTree = "<group>"; };
EAA0CFB0275E823A00D65EB0 /* HideFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideFormFieldEffectModel.swift; sourceTree = "<group>"; };
EAA0CFB2275E831E00D65EB0 /* DisableFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableFormFieldEffectModel.swift; sourceTree = "<group>"; };
EAA5EEF628F5D074003B3210 /* VDS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = VDS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
EAA7801F290081320057DFDF /* VDSMoleculeViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDSMoleculeViewProtocol.swift; sourceTree = "<group>"; };
EAB14BC027D935F00012AB2C /* RuleCompareModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleCompareModelProtocol.swift; sourceTree = "<group>"; };
EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyModelProtocol.swift; sourceTree = "<group>"; };
EABFC1402763BB8D00E78B40 /* FormLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabel.swift; sourceTree = "<group>"; };
@ -1214,6 +1218,7 @@
files = (
D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */,
AFE4A1D127DFB5EE00C458D0 /* VDSColorTokens.xcframework in Frameworks */,
EAA5EEF828F5D079003B3210 /* VDS.framework in Frameworks */,
187FEB2A2844D2A600BF29C2 /* VDSFormControlsTokens.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -2058,6 +2063,7 @@
D29DF0E421E4F3C7003B2FB9 /* Frameworks */ = {
isa = PBXGroup;
children = (
EAA5EEF628F5D074003B3210 /* VDS.framework */,
187FEB292844D2A600BF29C2 /* VDSFormControlsTokens.xcframework */,
AFE4A1D027DFB5EE00C458D0 /* VDSColorTokens.xcframework */,
D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */,
@ -2452,6 +2458,7 @@
D2B9D0E3265EEE9D0084735C /* MoleculeListProtocol.swift */,
011B58EE23A2AA850085F53C /* ModelProtocols */,
27559EFB27D691D3000836C1 /* ViewMaskingProtocol.swift */,
EAA7801F290081320057DFDF /* VDSMoleculeViewProtocol.swift */,
);
path = Protocols;
sourceTree = "<group>";
@ -2778,6 +2785,7 @@
01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */,
D2A92884241ACB25004E01C6 /* ProgrammaticScrollViewController.swift in Sources */,
D23A90002612347A007E14CE /* PageBehaviorHandlerModelProtocol.swift in Sources */,
EAA78020290081320057DFDF /* VDSMoleculeViewProtocol.swift in Sources */,
0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */,
D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */,
D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */,

View File

@ -8,8 +8,8 @@
import MVMCore
import UIKit
public typealias ActionBlockConfirmation = () -> (Bool)
import VDS
import Combine
/**
A custom implementation of Apple's UISwitch.
@ -19,402 +19,123 @@ 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 {
open class Toggle: ToggleBase, VDSMoleculeViewProtocol {
//--------------------------------------------------
// 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?
/// 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 {
didSet { isUserInteractionEnabled = !isLocked }
}
/// The state on the toggle. Default value: false.
open var isOn: Bool = false {
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()
public var viewModel: ToggleModel!
public var delegateObject: MVMCoreUIDelegateObject?
public var additionalData: [AnyHashable: Any]?
public var valueChangedSubscription: AnyCancellable? {
willSet{
if let current = valueChangedSubscription {
current.cancel()
}
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
}
//--------------------------------------------------
// 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
//--------------------------------------------------
public override init(frame: CGRect) {
super.init(frame: frame)
}
public convenience override init() {
self.init(frame: .zero)
}
public convenience init(isOn: Bool) {
self.init(frame: .zero)
self.isOn = isOn
}
/// - parameter isOn: Bool to set the state of the toggle.
/// - 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.didToggleAction = didToggleAction
}
/// - parameter shouldToggleAction: Takes a closure that returns a boolean.
/// - parameter didToggleAction: A closure which is executed after the toggle changes states.
public convenience init(shouldToggleAction: ActionBlockConfirmation?, didToggleAction: ActionBlock?) {
self.init(frame: .zero)
self.didToggleAction = didToggleAction
self.shouldToggleAction = shouldToggleAction
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("Toggle does not support xib.")
}
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
public override func updateView(_ size: CGFloat) {
super.updateView(size)
open override func initialSetup() {
super.initialSetup()
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
heightConstraint = heightAnchor.constraint(equalToConstant: Self.containerSize.height)
heightConstraint?.isActive = true
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()
}
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() {
if let result = shouldToggleAction?(), result {
isOn.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<UITouch>, with event: UIEvent?) {
UIView.animate(withDuration: 0.1, animations: {
self.knobWidthConstraint?.constant += PaddingOne
self.layoutIfNeeded()
})
}
public override func touchesEnded(_ touches: Set<UITouch>, 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<UITouch>, 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
guard let model = model as? ToggleModel else { return }
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 {
accessibilityLabel = accessibileString
}
if model.action != nil || model.alternateAction != nil {
didToggleAction = { [weak self] in
publisher(for: .touchUpInside)
.sink {[weak self] toggle in
guard let self = self else { return }
if self.isOn {
if let action = model.action {
MVMCoreUIActionHandler.performActionUnstructured(with: action, sourceModel: model, additionalData: additionalData, delegateObject: delegateObject)
}
} else {
if let action = model.alternateAction ?? model.action {
MVMCoreUIActionHandler.performActionUnstructured(with: action, sourceModel: model, additionalData: additionalData, delegateObject: delegateObject)
}
}
self.toggle()
}.store(in: &subscribers)
//this is logic that will always need to be run
//and is added into the array of Set<AnyCancellables>
publisher(for: .valueChanged)
.sink {[weak self] _ in
guard let self = self, let viewModel = self.viewModel else { return }
//sync the value on the viewModel
viewModel.selected = self.isOn
//tell the form you changed
_ = FormValidator.validate(delegate: self.delegateObject?.formHolderDelegate)
}.store(in: &subscribers)
//register the defaultActionExecuter
//this can then be overwritten by a subclass
valueChangedSubscription = publisher(for: .valueChanged)
.sink {[weak self] _ in
guard let self = self else { return }
self.executeDefaultAction()
}
accessibilityLabelEnabled = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel")
accessibilityLabelDisabled = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel")
accessibilityHintEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint")
accessibilityHintDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccDisabled")
accessibilityValueEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOn")
accessibilityValueDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOff")
}
open func updateView(_ size: CGFloat) {}
open override func updateView() {
super.updateView()
//we want to overwrite the VDS color that is set in the ToggleBase
//for surface since the Atomic controls doesn't look at
//surface today for its views. We just want to show whatever
//the current parent's background color.
backgroundColor = .clear
}
open func viewModelDidUpdate() {
guard let viewModel else { return }
//set properties from the viewModel change that came in
if let accessibilityText = viewModel.accessibilityText {
accessibilityLabelEnabled = accessibilityText
accessibilityLabelDisabled = accessibilityText
}
isAnimated = viewModel.animated
//send toggle.model to the Form
FormValidator.setupValidation(for: viewModel, delegate: delegateObject?.formHolderDelegate)
}
public func executeDefaultAction() {
guard let viewModel else { return }
if viewModel.action != nil || viewModel.alternateAction != nil {
var action: ActionModelProtocol?
if isOn {
action = viewModel.action
} else {
action = viewModel.alternateAction ?? viewModel.action
}
if let action {
MVMCoreUIActionHandler.performActionUnstructured(with: action,
sourceModel: viewModel,
additionalData: additionalData,
delegateObject: delegateObject)
}
}
}
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
Self.getContainerHeight()
//Return the same height as the internal ToggleBase.toggleContainerSize.height
//since this is a class func, we can't reference it directly
public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return 44
}
}
// MARK: - MVMCoreUIViewConstrainingProtocol
extension Toggle {
extension Toggle: MVMCoreUIViewConstrainingProtocol {
public func needsToBeConstrained() -> Bool { true }
public func horizontalAlignment() -> UIStackView.Alignment { .trailing }
}

View File

@ -13,20 +13,15 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol {
//--------------------------------------------------
public static var identifier: String = "toggle"
public var accessibilityIdentifier: String?
public var backgroundColor: Color?
public var backgroundColor: Color? //not used
public var selected: Bool = false
public var animated: Bool = true
public var enabled: Bool = true
public var readOnly: Bool = false
public var animated: Bool = true
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 fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable?
@ -34,22 +29,16 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol {
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case state
case animated
case enabled
case readOnly
case animated
case action
case backgroundColor
case accessibilityIdentifier
case alternateAction
case accessibilityText
case onTintColor
case offTintColor
case onKnobTintColor
case offKnobTintColor
case fieldKey
case groupName
}
@ -75,7 +64,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol {
//--------------------------------------------------
public init(_ state: Bool) {
self.selected = state
selected = state
baseValue = state
}
@ -87,62 +76,31 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) {
self.selected = state
selected = state
}
if let animated = try typeContainer.decodeIfPresent(Bool.self, forKey: .animated) {
self.animated = animated
}
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
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) ?? FormValidator.defaultGroupName
enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true
readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false
animated = try typeContainer.decodeIfPresent(Bool.self, forKey: .animated) ?? true
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeModelIfPresent(action, forKey: .action)
try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction)
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
try container.encode(moleculeName, forKey: .moleculeName)
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(animated, forKey: .animated)
}
}

View File

@ -8,6 +8,7 @@
//
import MVMCore
import VDS
public typealias ActionBlock = () -> ()
@ -737,36 +738,6 @@ public typealias ActionBlock = () -> ()
clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1))
}
/**
Provides a text container and layout manager of how the text would appear on screen.
They are used in tandem to derive low-level TextKit results of the label.
*/
public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager, NSTextStorage)? {
// Must configure the attributed string to translate what would appear on screen to accurately analyze.
guard let attributedText = attributedText else { return nil }
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = textAlignment
let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText)
stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count))
let textStorage = NSTextStorage(attributedString: stagedAttributedString)
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: .zero)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = lineBreakMode
textContainer.maximumNumberOfLines = numberOfLines
textContainer.size = bounds.size
return (textContainer, layoutManager, textStorage)
}
public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect {
guard let abstractContainer = label.abstractTextContainer() else { return CGRect() }

View File

@ -0,0 +1,32 @@
//
// VDSMoleculeViewProtocol.swift
// MVMCoreUI
//
// Created by Matt Bruce on 10/19/22.
// Copyright © 2022 Verizon Wireless. All rights reserved.
//
import Foundation
import MVMCore
///-----------------------------------------------------------------------------
///MARK: -- VDSMoleculeViewProtocol (Contract between VDS -> Atomic)
///-----------------------------------------------------------------------------
public protocol VDSMoleculeViewProtocol: MoleculeViewProtocol, MVMCoreViewProtocol {
associatedtype ViewModel: MoleculeModelProtocol
var viewModel: ViewModel! { get set }
var delegateObject: MVMCoreUIDelegateObject? { get set }
var additionalData: [AnyHashable: Any]? { get set }
func viewModelDidUpdate()
}
extension VDSMoleculeViewProtocol {
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let castedModel = model as? ViewModel else { return }
self.delegateObject = delegateObject
self.additionalData = additionalData
viewModel = castedModel
viewModelDidUpdate()
}
}