From 8a796467e66ea0d3c90bbe0236e7e200b1d10602 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 4 Dec 2019 15:50:08 -0500 Subject: [PATCH 01/54] Initial state of Swiftified Switch. --- MVMCoreUI.xcodeproj/project.pbxproj | 4 + MVMCoreUI/Atoms/Views/Switch.swift | 376 ++++++++++++++++++++++++++++ 2 files changed, 380 insertions(+) create mode 100644 MVMCoreUI/Atoms/Views/Switch.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 900edec2..33529648 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; }; 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; + 0AA33B3A2398524F0067DD0F /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Switch.swift */; }; 943784F5236B77BB006A1E82 /* GraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F3236B77BB006A1E82 /* GraphView.swift */; }; 943784F6236B77BB006A1E82 /* GraphViewAnimationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */; }; 9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */; }; @@ -226,6 +227,7 @@ 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = ""; }; 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; + 0AA33B392398524F0067DD0F /* Switch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Switch.swift; sourceTree = ""; }; 943784F3236B77BB006A1E82 /* GraphView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphView.swift; sourceTree = ""; }; 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphViewAnimationHandler.swift; sourceTree = ""; }; 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = ""; }; @@ -779,6 +781,7 @@ 01004F2F22721C3800991ECC /* RadioButton.swift */, 943784F3236B77BB006A1E82 /* GraphView.swift */, 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */, + 0AA33B392398524F0067DD0F /* Switch.swift */, ); path = Views; sourceTree = ""; @@ -1082,6 +1085,7 @@ D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, D29DF25321E6A177003B2FB9 /* MFDigitTextField.m in Sources */, D2B18B7F2360913400A9AEDC /* Control.swift in Sources */, + 0AA33B3A2398524F0067DD0F /* Switch.swift in Sources */, D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */, DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */, D224798C231450C8003FCCF9 /* HeadlineBodySwitch.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Switch.swift new file mode 100644 index 00000000..d7e9b25e --- /dev/null +++ b/MVMCoreUI/Atoms/Views/Switch.swift @@ -0,0 +1,376 @@ +// +// Switch.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 12/4/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import MVMCore +import MVMCoreUI +import UIKit + +typealias ValueChangeBlock = () -> Void + +open class Switch: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol { + var on = false + var onTintColor: UIColor? + var offTintColor: UIColor? + var onKnobTintColor: UIColor? + var offKnobTintColor: UIColor? + var shouldTouchToSwitch = false + var valueChangedBlock: ValueChangeBlock? + var actionBlock: ValueChangeBlock? + + let SwitchWidth: CGFloat = 42 + let SwitchHeight: CGFloat = 22 + let SwitchKnobWidth: CGFloat = 20 + let SwitchKnobHeight: CGFloat = 20 + let SwitchShakeIntensity: CGFloat = 2 + + private weak var baseView: UIView? + private weak var knobView: UIView? + private var knobLeftConstraint: NSLayoutConstraint? + private var knobRightConstraint: NSLayoutConstraint? + private var knobHeightConstraint: NSLayoutConstraint? + private var knobWidthConstraint: NSLayoutConstraint? + private weak var height: NSLayoutConstraint? + private weak var width: NSLayoutConstraint? + private var valueShouldChange = false + private var canChangeValue = false + private var json: [AnyHashable: Any]? + private var delegate: DelegateObject? + + public func updateView(_ size: CGFloat) { + height?.constant = MVMCoreUISwitch.getHeight() + width?.constant = MVMCoreUISwitch.getWidth() + baseView?.layer.cornerRadius = MVMCoreUISwitch.getHeight() / 2.0 + knobView?.layer.cornerRadius = MVMCoreUISwitch.getKnobHeight() * 0.5 + knobHeightConstraint?.constant = MVMCoreUISwitch.getKnobHeight() + knobWidthConstraint?.constant = MVMCoreUISwitch.getKnobWidth() + } + + public func setupView() { + onTintColor = UIColor.mfSwitchOnTint() + offTintColor = UIColor.mfSwitchOffTint() + canChangeValue = true + shouldTouchToSwitch = true + setUpViewWithState(on) + accessibilityLabel() = MVMCoreUIUtility.hardcodedString(withKey: "MVMCoreUISwitch_buttonlabel") + } + + public required init?(coder: NSCoder) { + if super.init(coder: coder) != nil { + setupView() + } + } + + public init(frame: CGRect) { + if super.init(frame: frame) != nil { + setupView() + } + } + + public init() { + if super.init() { + setupView() + } + } + + public class func mvmSwitchDefault() -> Self { + let mySwitch = self.init(frame: CGRect.zero) + mySwitch?.translatesAutoresizingMaskIntoConstraints = false + return mySwitch + } + + public class func mvmSwitchDefault(with block: ValueChangeBlock?) -> Self { + let mySwitch = self.init(frame: CGRect.zero) + mySwitch?.valueChangedBlock = block + return mySwitch + } + + public func setUpViewWithState(_ on: Bool) { + if !self.baseView { + isUserInteractionEnabled = true + valueShouldChange = true + + let baseView = MVMCoreUICommonViewsUtility.commonView() + if let baseView = baseView { + addSubview(baseView) + } + NSLayoutConstraint.constraintPinSubview(toSuperview: baseView) + let constraints = NSLayoutConstraint.constraintPinView(baseView, heightConstraint: true, heightConstant: MVMCoreUISwitch.getHeight(), widthConstraint: true, widthConstant: MVMCoreUISwitch.getWidth()) + self.height = constraints.object(forKey: ConstraintHeight, ofType: NSLayoutConstraint.self) + self.width = constraints.object(forKey: ConstraintWidth, ofType: NSLayoutConstraint.self) + baseView?.layer.cornerRadius = MVMCoreUISwitch.getHeight() / 2.0 + + onKnobTintColor = UIColor.white + offKnobTintColor = UIColor.white + + let knobView = MVMCoreUICommonViewsUtility.commonView() + knobView?.backgroundColor = UIColor.white + knobView?.layer.cornerRadius = MVMCoreUISwitch.getKnobHeight() * 0.5 + if let knobView = knobView { + baseView?.addSubview(knobView) + } + let heightWidth = NSLayoutConstraint.constraintPinView(knobView, heightConstraint: true, heightConstant: MVMCoreUISwitch.getKnobHeight(), widthConstraint: true, widthConstant: MVMCoreUISwitch.getKnobWidth()) + let height = heightWidth.object(forKey: ConstraintHeight, ofType: NSLayoutConstraint.self) + let width = heightWidth.object(forKey: ConstraintWidth, ofType: NSLayoutConstraint.self) + knobHeightConstraint = height + knobWidthConstraint = width + let leadingTrailingDic = NSLayoutConstraint.constraintPinSubview(knobView, pinTop: false, topConstant: 0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 1, pinRight: true, rightConstant: 1) + + let leadingTrailingDic = NSLayoutConstraint.constraintPinSubview(knobView, pinTop: false, topConstant: 0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 1, pinRight: true, rightConstant: 1) + let `left` = leadingTrailingDic.object(forKey: ConstraintLeading, ofType: NSLayoutConstraint.self) + let `right` = leadingTrailingDic.object(forKey: ConstraintTrailing, ofType: NSLayoutConstraint.self) + NSLayoutConstraint.constraintPinSubview(knobView, pinCenterX: false, centerXConstant: 0, pinCenterY: true, centerYConstant: 0) + `right`?.constant = 15 + knobLeftConstraint = `left` + knobRightConstraint = `right` + baseView.bringSubviewToFront(knobView) + + //baseView = baseView // Skipping redundant initializing to itself + //knobView = knobView // Skipping redundant initializing to itself + + baseView.isUserInteractionEnabled = false + knobView.isUserInteractionEnabled = false + shouldTouchToSwitch = false + setState(on, animated: false) + shouldTouchToSwitch = true + } + + // MARK: - MVMCoreUIMoleculeViewProtocol + public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + self.json = json + delegate = delegateObject + + FormValidator.setupValidation(withMolecule: self, delegate: delegateObject?.formValidationProtocol) + + var color = json?.string("onTintColor") + if color != nil { + onTintColor = UIColor.mfGet(forHex: color) + } + + color = json?.string("offTintColor") + if color != nil { + offTintColor = UIColor.mfGet(forHex: color) + } + + color = json?.string("onKnobTintColor") + if color != nil { + onKnobTintColor = UIColor.mfGet(forHex: color) + } + + color = json?.string("offKnobTintColor") + if color != nil { + offKnobTintColor = UIColor.mfGet(forHex: color) + } + + setState(json?.bool(forKey: "state"), animated: false) + + let actionMap = json?.dict("actionMap") + if actionMap != nil { + addTarget(self, action: #selector(addCustomAction), for: .touchUpInside) + } + } + + public @objc func addCustomAction() { + if actionBlock { + actionBlock() + } + } + + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return self.getSwitchHeight() + } + + // MARK: - MVMCoreUIMoleculeViewProtocol + public func needsToBeConstrained() -> Bool { + return true + } + + public func alignment() -> UIStackView.Alignment { + return .trailing + } + + // MARK: - UIResponder overide + public func touchesBegan(_ touches: Set, with event: UIEvent) { + knobEnlargeAnimation() + sendActions(for: .touchDown) + } + + public func touchesEnded(_ touches: Set, with event: UIEvent) { + if shouldTouchToSwitch { + if valueShouldChange { + changeValue() + } + } + sendActions(for: .touchUpInside) + knobMoveAnitmationTo(on: isOn) + knobShakeAnitmationTo(on: isOn) + knobReformAnimation(true) + valueShouldChange = true + } + + public func touchesMoved(_ touches: Set, with event: UIEvent) { + if shouldTouchToSwitch { + if touchMoves(toLeft: touches) { + knobMoveAnitmationTo(on: false) + knobShakeAnitmationTo(on: false) + knobReformAnimation(true) + } else { + knobMoveAnitmationTo(on: true) + knobShakeAnitmationTo(on: true) + knobReformAnimation(true) + } + } + if touchIsOutSide(touches) { + sendActions(for: .touchDragOutside) + if !shouldTouchToSwitch { + knobReformAnimation(true) + } + } else { + sendActions(for: .touchDragInside) + } + } + + public func touchesCancelled(_ touches: Set, with event: UIEvent) { + if shouldTouchToSwitch { + knobReformAnimation(true) + canChangeValue = true + } + sendActions(for: .touchCancel) + } + + // MARK: - animation + public func knobEnlargeAnimation() { + UIView.animate(withDuration: 0.1, animations: { + self.knobWidthConstraint.constant += PaddingOne + self.layoutIfNeeded() + }) + } + + public func knobReformAnimation(_ animated: Bool) { + if animated { + UIView.animate(withDuration: 0.1, animations: { + self.knobWidthConstraint.constant = MVMCoreUISwitch.getKnobWidth() + self.layoutIfNeeded() + }) { finished in + } + } else { + knobWidthConstraint.constant = MVMCoreUISwitch.getKnobWidth() + layoutIfNeeded() + } + } + + public func knobMoveAnitmationTo(on toOn: Bool) { + UIView.animate(withDuration: 0.1, animations: { + if toOn { + self.knobLeftConstraint.priority = 1 + self.knobRightConstraint.priority = 999 + self.knobRightConstraint.constant = 1 + } else { + self.knobLeftConstraint.priority = 999 + self.knobRightConstraint.priority = 1 + self.knobLeftConstraint.constant = 1 + } + self.setBaseColorToOn(toOn, animated: true) + self.knobWidthConstraint.constant = MVMCoreUISwitch.getKnobWidth() + PaddingOne + self.valueShouldChange = toOn != self.on + self.layoutIfNeeded() + }) + } + + public func setBaseColorToOn(_ toOn: Bool, animated: Bool) { + if animated { + UIView.beginAnimations(nil, context: nil) + UIView.setAnimationDuration(0.2) + UIView.setAnimationCurve(.easeIn) + if toOn { + knobView.backgroundColor = onKnobTintColor + baseView.backgroundColor = onTintColor + } else { + knobView.backgroundColor = offKnobTintColor + baseView.backgroundColor = offTintColor + } + UIView.commitAnimations() + } else if on { + knobView.backgroundColor = onKnobTintColor + baseView.backgroundColor = onTintColor + } else { + knobView.backgroundColor = offKnobTintColor + baseView.backgroundColor = offTintColor + } + } + + //used after knob moving to match the behavior of default uiswitch + public func knobShakeAnitmationTo(on toOn: Bool) { + UIView.animate(withDuration: 0.1, delay: 0.1, options: .curveEaseIn, animations: { + if toOn { + self.knobRightConstraint.constant = SwitchShakeIntensity + } else { + self.knobLeftConstraint.constant = SwitchShakeIntensity + } + self.layoutIfNeeded() + }) { finished in + } + UIView.animate(withDuration: 0.2, delay: 0.1, options: [], animations: { + if toOn { + self.knobRightConstraint.constant = 1 + } else { + self.knobLeftConstraint.constant = 1 + } + self.layoutIfNeeded() + }) { finished in + self.valueShouldChange = true + } + } + + // MARK: - switch logic + public func setState(_ state: Bool, animated: Bool) { + setState(state, withoutBlockAnimated: animated) + if valueChangedBlock { + valueChangedBlock() + } + + if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { + let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator + formValidator?.enableByValidation() + } + } + + public func setState(_ state: Bool, withoutBlockAnimated animated: Bool) { + on = state + if !shouldTouchToSwitch { + knobEnlargeAnimation() + knobMoveAnitmationTo(on: on) + knobShakeAnitmationTo(on: on) + } + if on { + knobLeftConstraint.priority = 1 + knobRightConstraint.priority = 999 + knobRightConstraint.constant = 1 + } else { + knobRightConstraint.priority = 1 + knobLeftConstraint.priority = 999 + knobLeftConstraint.constant = 1 + } + + setBaseColorToOn(on, animated: animated) + knobReformAnimation(animated) + accessibilityValue = state ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") + setNeedsLayout() + layoutIfNeeded() + } + + public func changeValue() -> Bool { + on ^= true + shouldTouchToSwitch = false + setState(on, animated: true) + shouldTouchToSwitch = true + sendActions(for: .valueChanged) + return on + } + } +} + From a00fdbad6041289fef25710bb3f2b2535d7fed7a Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 5 Dec 2019 15:16:46 -0500 Subject: [PATCH 02/54] Grooming process in converting objc to swift with Switch. --- MVMCoreUI/Atoms/Views/Switch.swift | 709 ++++++++++++++++------------ MVMCoreUI/BaseClasses/Control.swift | 5 +- 2 files changed, 406 insertions(+), 308 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Switch.swift index d7e9b25e..e12fd890 100644 --- a/MVMCoreUI/Atoms/Views/Switch.swift +++ b/MVMCoreUI/Atoms/Views/Switch.swift @@ -10,367 +10,464 @@ import MVMCore import MVMCoreUI import UIKit -typealias ValueChangeBlock = () -> Void +typealias ValueChangeBlock = () -> () -open class Switch: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol { - var on = false - var onTintColor: UIColor? - var offTintColor: UIColor? - var onKnobTintColor: UIColor? - var offKnobTintColor: UIColor? +open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public var isOn = false + public var trackTintColor: (on: UIColor?, off: UIColor?)? + public var thumbTintColor: (on: UIColor?, off: UIColor?)? + var shouldTouchToSwitch = false var valueChangedBlock: ValueChangeBlock? var actionBlock: ValueChangeBlock? - let SwitchWidth: CGFloat = 42 - let SwitchHeight: CGFloat = 22 - let SwitchKnobWidth: CGFloat = 20 - let SwitchKnobHeight: CGFloat = 20 - let SwitchShakeIntensity: CGFloat = 2 + static let width: CGFloat = 42 + static let height: CGFloat = 22 + static let thumbWidth: CGFloat = 20 + static let thumbHeight: CGFloat = 20 + static let shakeIntensity: CGFloat = 2 private weak var baseView: UIView? - private weak var knobView: UIView? - private var knobLeftConstraint: NSLayoutConstraint? - private var knobRightConstraint: NSLayoutConstraint? - private var knobHeightConstraint: NSLayoutConstraint? - private var knobWidthConstraint: NSLayoutConstraint? - private weak var height: NSLayoutConstraint? - private weak var width: NSLayoutConstraint? + private weak var thumbView: UIView? + private var valueShouldChange = false private var canChangeValue = false - private var json: [AnyHashable: Any]? + + //-------------------------------------------------- + // MARK: - Delegate + //-------------------------------------------------- + private var delegate: DelegateObject? + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + + private var thumbLeftConstraint: NSLayoutConstraint? + private var thumbRightConstraint: NSLayoutConstraint? + private var thumbHeightConstraint: NSLayoutConstraint? + private var thumbWidthConstraint: NSLayoutConstraint? + private weak var height: NSLayoutConstraint? + private weak var width: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + public func updateView(_ size: CGFloat) { + height?.constant = MVMCoreUISwitch.getHeight() width?.constant = MVMCoreUISwitch.getWidth() baseView?.layer.cornerRadius = MVMCoreUISwitch.getHeight() / 2.0 - knobView?.layer.cornerRadius = MVMCoreUISwitch.getKnobHeight() * 0.5 - knobHeightConstraint?.constant = MVMCoreUISwitch.getKnobHeight() - knobWidthConstraint?.constant = MVMCoreUISwitch.getKnobWidth() + thumbView?.layer.cornerRadius = MVMCoreUISwitch.getthumbHeight() * 0.5 + thumbHeightConstraint?.constant = MVMCoreUISwitch.getthumbHeight() + thumbWidthConstraint?.constant = MVMCoreUISwitch.getthumbWidth() } public func setupView() { - onTintColor = UIColor.mfSwitchOnTint() - offTintColor = UIColor.mfSwitchOffTint() + + trackTintColor?.on = .mfSwitchOnTint() + trackTintColor?.off = .mfSwitchOffTint() canChangeValue = true shouldTouchToSwitch = true - setUpViewWithState(on) - accessibilityLabel() = MVMCoreUIUtility.hardcodedString(withKey: "MVMCoreUISwitch_buttonlabel") + setUpViewWithState(isOn) + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "MVMCoreUISwitch_buttonlabel") + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + public override init(frame: CGRect) { + super.init(frame: frame) + setupView() + } + + public convenience init() { + self.init(frame: frame) } public required init?(coder: NSCoder) { - if super.init(coder: coder) != nil { - setupView() - } + super.init(coder: coder) + fatalError("Switch does not support xib") } - public init(frame: CGRect) { - if super.init(frame: frame) != nil { - setupView() - } - } - - public init() { - if super.init() { - setupView() - } - } + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- public class func mvmSwitchDefault() -> Self { - let mySwitch = self.init(frame: CGRect.zero) + + let mySwitch = self.init(frame: .zero) mySwitch?.translatesAutoresizingMaskIntoConstraints = false return mySwitch } public class func mvmSwitchDefault(with block: ValueChangeBlock?) -> Self { - let mySwitch = self.init(frame: CGRect.zero) + + let mySwitch = self.init(frame: .zero) mySwitch?.valueChangedBlock = block return mySwitch } public func setUpViewWithState(_ on: Bool) { - if !self.baseView { - isUserInteractionEnabled = true - valueShouldChange = true - - let baseView = MVMCoreUICommonViewsUtility.commonView() - if let baseView = baseView { - addSubview(baseView) - } - NSLayoutConstraint.constraintPinSubview(toSuperview: baseView) - let constraints = NSLayoutConstraint.constraintPinView(baseView, heightConstraint: true, heightConstant: MVMCoreUISwitch.getHeight(), widthConstraint: true, widthConstant: MVMCoreUISwitch.getWidth()) - self.height = constraints.object(forKey: ConstraintHeight, ofType: NSLayoutConstraint.self) - self.width = constraints.object(forKey: ConstraintWidth, ofType: NSLayoutConstraint.self) - baseView?.layer.cornerRadius = MVMCoreUISwitch.getHeight() / 2.0 - - onKnobTintColor = UIColor.white - offKnobTintColor = UIColor.white - - let knobView = MVMCoreUICommonViewsUtility.commonView() - knobView?.backgroundColor = UIColor.white - knobView?.layer.cornerRadius = MVMCoreUISwitch.getKnobHeight() * 0.5 - if let knobView = knobView { - baseView?.addSubview(knobView) - } - let heightWidth = NSLayoutConstraint.constraintPinView(knobView, heightConstraint: true, heightConstant: MVMCoreUISwitch.getKnobHeight(), widthConstraint: true, widthConstant: MVMCoreUISwitch.getKnobWidth()) - let height = heightWidth.object(forKey: ConstraintHeight, ofType: NSLayoutConstraint.self) - let width = heightWidth.object(forKey: ConstraintWidth, ofType: NSLayoutConstraint.self) - knobHeightConstraint = height - knobWidthConstraint = width - let leadingTrailingDic = NSLayoutConstraint.constraintPinSubview(knobView, pinTop: false, topConstant: 0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 1, pinRight: true, rightConstant: 1) - - let leadingTrailingDic = NSLayoutConstraint.constraintPinSubview(knobView, pinTop: false, topConstant: 0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 1, pinRight: true, rightConstant: 1) - let `left` = leadingTrailingDic.object(forKey: ConstraintLeading, ofType: NSLayoutConstraint.self) - let `right` = leadingTrailingDic.object(forKey: ConstraintTrailing, ofType: NSLayoutConstraint.self) - NSLayoutConstraint.constraintPinSubview(knobView, pinCenterX: false, centerXConstant: 0, pinCenterY: true, centerYConstant: 0) - `right`?.constant = 15 - knobLeftConstraint = `left` - knobRightConstraint = `right` - baseView.bringSubviewToFront(knobView) - - //baseView = baseView // Skipping redundant initializing to itself - //knobView = knobView // Skipping redundant initializing to itself - - baseView.isUserInteractionEnabled = false - knobView.isUserInteractionEnabled = false - shouldTouchToSwitch = false - setState(on, animated: false) - shouldTouchToSwitch = true + + guard baseView == nil else { return } + + isUserInteractionEnabled = true + valueShouldChange = true + + let baseView = MVMCoreUICommonViewsUtility.commonView() + + if let baseView = baseView { + addSubview(baseView) } - // MARK: - MVMCoreUIMoleculeViewProtocol - public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - self.json = json - delegate = delegateObject - - FormValidator.setupValidation(withMolecule: self, delegate: delegateObject?.formValidationProtocol) - - var color = json?.string("onTintColor") - if color != nil { - onTintColor = UIColor.mfGet(forHex: color) - } - - color = json?.string("offTintColor") - if color != nil { - offTintColor = UIColor.mfGet(forHex: color) - } - - color = json?.string("onKnobTintColor") - if color != nil { - onKnobTintColor = UIColor.mfGet(forHex: color) - } - - color = json?.string("offKnobTintColor") - if color != nil { - offKnobTintColor = UIColor.mfGet(forHex: color) - } - - setState(json?.bool(forKey: "state"), animated: false) - - let actionMap = json?.dict("actionMap") - if actionMap != nil { - addTarget(self, action: #selector(addCustomAction), for: .touchUpInside) - } + NSLayoutConstraint.constraintPinSubview(toSuperview: baseView) + let constraints = NSLayoutConstraint.constraintPinView(baseView, heightConstraint: true, heightConstant: MVMCoreUISwitch.getHeight(), widthConstraint: true, widthConstant: MVMCoreUISwitch.getWidth()) + self.height = constraints.object(forKey: ConstraintHeight, ofType: NSLayoutConstraint.self) + self.width = constraints.object(forKey: ConstraintWidth, ofType: NSLayoutConstraint.self) + baseView?.layer.cornerRadius = MVMCoreUISwitch.getHeight() / 2.0 + + onthumbTintColor = UIColor.white + offthumbTintColor = UIColor.white + + let thumbView = MVMCoreUICommonViewsUtility.commonView() + thumbView?.backgroundColor = UIColor.white + thumbView?.layer.cornerRadius = MVMCoreUISwitch.getthumbHeight() * 0.5 + + if let thumbView = thumbView { + baseView?.addSubview(thumbView) } - public @objc func addCustomAction() { - if actionBlock { - actionBlock() - } + let heightWidth = NSLayoutConstraint.constraintPinView(thumbView, heightConstraint: true, heightConstant: MVMCoreUISwitch.getthumbHeight(), widthConstraint: true, widthConstant: MVMCoreUISwitch.getthumbWidth()) + let height = heightWidth.object(forKey: ConstraintHeight, ofType: NSLayoutConstraint.self) + let width = heightWidth.object(forKey: ConstraintWidth, ofType: NSLayoutConstraint.self) + thumbHeightConstraint = height + thumbWidthConstraint = width + let leadingTrailingDic = NSLayoutConstraint.constraintPinSubview(thumbView, pinTop: false, topConstant: 0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 1, pinRight: true, rightConstant: 1) + + let leadingTrailingDic = NSLayoutConstraint.constraintPinSubview(thumbView, pinTop: false, topConstant: 0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 1, pinRight: true, rightConstant: 1) + let `left` = leadingTrailingDic.object(forKey: ConstraintLeading, ofType: NSLayoutConstraint.self) + let `right` = leadingTrailingDic.object(forKey: ConstraintTrailing, ofType: NSLayoutConstraint.self) + NSLayoutConstraint.constraintPinSubview(thumbView, pinCenterX: false, centerXConstant: 0, pinCenterY: true, centerYConstant: 0) + `right`?.constant = 15 + thumbLeftConstraint = `left` + thumbRightConstraint = `right` + baseView.bringSubviewToFront(thumbView) + + //baseView = baseView // Skipping redundant initializing to itself + //thumbView = thumbView // Skipping redundant initializing to itself + + baseView.isUserInteractionEnabled = false + thumbView.isUserInteractionEnabled = false + shouldTouchToSwitch = false + setState(on, animated: false) + shouldTouchToSwitch = true + } + + @objc func addCustomAction() { + + actionBlock?() + } + + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + + return Switch.getSwitchHeight() + } + + // MARK: - UIResponder overide + public func touchesBegan(_ touches: Set, with event: UIEvent) { + + thumbEnlargeAnimation() + sendActions(for: .touchDown) + } + + public func touchesEnded(_ touches: Set, with event: UIEvent) { + + if shouldTouchToSwitch && valueShouldChange{ + changeValue() } - public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return self.getSwitchHeight() - } + sendActions(for: .touchUpInside) + thumbMoveAnitmationTo(on: isOn) + thumbShakeAnitmationTo(on: isOn) + thumbReformAnimation(true) + valueShouldChange = true + } + + public func touchesMoved(_ touches: Set, with event: UIEvent) { - // MARK: - MVMCoreUIMoleculeViewProtocol - public func needsToBeConstrained() -> Bool { - return true - } - - public func alignment() -> UIStackView.Alignment { - return .trailing - } - - // MARK: - UIResponder overide - public func touchesBegan(_ touches: Set, with event: UIEvent) { - knobEnlargeAnimation() - sendActions(for: .touchDown) - } - - public func touchesEnded(_ touches: Set, with event: UIEvent) { - if shouldTouchToSwitch { - if valueShouldChange { - changeValue() - } - } - sendActions(for: .touchUpInside) - knobMoveAnitmationTo(on: isOn) - knobShakeAnitmationTo(on: isOn) - knobReformAnimation(true) - valueShouldChange = true - } - - public func touchesMoved(_ touches: Set, with event: UIEvent) { - if shouldTouchToSwitch { - if touchMoves(toLeft: touches) { - knobMoveAnitmationTo(on: false) - knobShakeAnitmationTo(on: false) - knobReformAnimation(true) - } else { - knobMoveAnitmationTo(on: true) - knobShakeAnitmationTo(on: true) - knobReformAnimation(true) - } - } - if touchIsOutSide(touches) { - sendActions(for: .touchDragOutside) - if !shouldTouchToSwitch { - knobReformAnimation(true) - } + if shouldTouchToSwitch { + if touchMoves(toLeft: touches) { + thumbMoveAnitmationTo(on: false) + thumbShakeAnitmationTo(on: false) + thumbReformAnimation(true) } else { - sendActions(for: .touchDragInside) + thumbMoveAnitmationTo(on: true) + thumbShakeAnitmationTo(on: true) + thumbReformAnimation(true) } } - public func touchesCancelled(_ touches: Set, with event: UIEvent) { - if shouldTouchToSwitch { - knobReformAnimation(true) - canChangeValue = true - } - sendActions(for: .touchCancel) - } - - // MARK: - animation - public func knobEnlargeAnimation() { - UIView.animate(withDuration: 0.1, animations: { - self.knobWidthConstraint.constant += PaddingOne - self.layoutIfNeeded() - }) - } - - public func knobReformAnimation(_ animated: Bool) { - if animated { - UIView.animate(withDuration: 0.1, animations: { - self.knobWidthConstraint.constant = MVMCoreUISwitch.getKnobWidth() - self.layoutIfNeeded() - }) { finished in - } - } else { - knobWidthConstraint.constant = MVMCoreUISwitch.getKnobWidth() - layoutIfNeeded() - } - } - - public func knobMoveAnitmationTo(on toOn: Bool) { - UIView.animate(withDuration: 0.1, animations: { - if toOn { - self.knobLeftConstraint.priority = 1 - self.knobRightConstraint.priority = 999 - self.knobRightConstraint.constant = 1 - } else { - self.knobLeftConstraint.priority = 999 - self.knobRightConstraint.priority = 1 - self.knobLeftConstraint.constant = 1 - } - self.setBaseColorToOn(toOn, animated: true) - self.knobWidthConstraint.constant = MVMCoreUISwitch.getKnobWidth() + PaddingOne - self.valueShouldChange = toOn != self.on - self.layoutIfNeeded() - }) - } - - public func setBaseColorToOn(_ toOn: Bool, animated: Bool) { - if animated { - UIView.beginAnimations(nil, context: nil) - UIView.setAnimationDuration(0.2) - UIView.setAnimationCurve(.easeIn) - if toOn { - knobView.backgroundColor = onKnobTintColor - baseView.backgroundColor = onTintColor - } else { - knobView.backgroundColor = offKnobTintColor - baseView.backgroundColor = offTintColor - } - UIView.commitAnimations() - } else if on { - knobView.backgroundColor = onKnobTintColor - baseView.backgroundColor = onTintColor - } else { - knobView.backgroundColor = offKnobTintColor - baseView.backgroundColor = offTintColor - } - } - - //used after knob moving to match the behavior of default uiswitch - public func knobShakeAnitmationTo(on toOn: Bool) { - UIView.animate(withDuration: 0.1, delay: 0.1, options: .curveEaseIn, animations: { - if toOn { - self.knobRightConstraint.constant = SwitchShakeIntensity - } else { - self.knobLeftConstraint.constant = SwitchShakeIntensity - } - self.layoutIfNeeded() - }) { finished in - } - UIView.animate(withDuration: 0.2, delay: 0.1, options: [], animations: { - if toOn { - self.knobRightConstraint.constant = 1 - } else { - self.knobLeftConstraint.constant = 1 - } - self.layoutIfNeeded() - }) { finished in - self.valueShouldChange = true - } - } - - // MARK: - switch logic - public func setState(_ state: Bool, animated: Bool) { - setState(state, withoutBlockAnimated: animated) - if valueChangedBlock { - valueChangedBlock() - } - - if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { - let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator - formValidator?.enableByValidation() - } - } - - public func setState(_ state: Bool, withoutBlockAnimated animated: Bool) { - on = state + if touchIsOutSide(touches) { + sendActions(for: .touchDragOutside) if !shouldTouchToSwitch { - knobEnlargeAnimation() - knobMoveAnitmationTo(on: on) - knobShakeAnitmationTo(on: on) + thumbReformAnimation(true) } - if on { - knobLeftConstraint.priority = 1 - knobRightConstraint.priority = 999 - knobRightConstraint.constant = 1 - } else { - knobRightConstraint.priority = 1 - knobLeftConstraint.priority = 999 - knobLeftConstraint.constant = 1 + } else { + sendActions(for: .touchDragInside) + } + } + + public func touchesCancelled(_ touches: Set, with event: UIEvent) { + + if shouldTouchToSwitch { + thumbReformAnimation(true) + canChangeValue = true + } + + sendActions(for: .touchCancel) + } + + // MARK: - animation + public func thumbEnlargeAnimation() { + + UIView.animate(withDuration: 0.1, animations: { + self.thumbWidthConstraint?.constant += PaddingOne + self.layoutIfNeeded() + }) + } + + public func thumbReformAnimation(_ animated: Bool) { + + if animated { + UIView.animate(withDuration: 0.1, animations: { + self.thumbWidthConstraint?.constant = MVMCoreUISwitch.getthumbWidth() + self.layoutIfNeeded() + }) { finished in } - - setBaseColorToOn(on, animated: animated) - knobReformAnimation(animated) - accessibilityValue = state ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") - setNeedsLayout() + } else { + thumbWidthConstraint?.constant = MVMCoreUISwitch.getthumbWidth() layoutIfNeeded() } + } + + public func thumbMoveAnitmationTo(on toOn: Bool) { - public func changeValue() -> Bool { - on ^= true - shouldTouchToSwitch = false - setState(on, animated: true) - shouldTouchToSwitch = true - sendActions(for: .valueChanged) - return on + UIView.animate(withDuration: 0.1, animations: { + if toOn { + self.thumbLeftConstraint?.priority = UILayoutPriority(1) + self.thumbRightConstraint?.priority = UILayoutPriority(999) + self.thumbRightConstraint?.constant = 1 + } else { + self.thumbLeftConstraint?.priority = UILayoutPriority(999) + self.thumbRightConstraint?.priority = UILayoutPriority(1) + self.thumbLeftConstraint?.constant = 1 + } + self.setBaseColorToOn(toOn, animated: true) + self.thumbWidthConstraint?.constant = MVMCoreUISwitch.getThumbWidth() + PaddingOne + self.valueShouldChange = toOn != self.isOn + self.layoutIfNeeded() + }) + } + + public func setBaseColorToOn(_ toOn: Bool, animated: Bool) { + + if animated { + UIView.beginAnimations(nil, context: nil) + UIView.setAnimationDuration(0.2) + UIView.setAnimationCurve(.easeIn) + + if toOn { + thumbView?.backgroundColor = thumbTintColor!.on + baseView?.backgroundColor = trackTintColor!.on + } else { + thumbView?.backgroundColor = thumbTintColor!.off + baseView?.backgroundColor = trackTintColor!.off + } + UIView.commitAnimations() + } else if isOn { + thumbView?.backgroundColor = thumbTintColor!.on + baseView?.backgroundColor = trackTintColor!.on + } else { + thumbView?.backgroundColor = thumbTintColor!.off + baseView?.backgroundColor = trackTintColor!.off } } + + //used after thumb moving to match the behavior of default uiswitch + public func thumbShakeAnitmationTo(on toOn: Bool) { + UIView.animate(withDuration: 0.1, delay: 0.1, options: .curveEaseIn, animations: { + if toOn { + self.thumbRightConstraint?.constant = Switch.shakeIntensity + } else { + self.thumbLeftConstraint?.constant = Switch.shakeIntensity + } + self.layoutIfNeeded() + }) { finished in + } + UIView.animate(withDuration: 0.2, delay: 0.1, options: [], animations: { + if toOn { + self.thumbRightConstraint?.constant = 1 + } else { + self.thumbLeftConstraint?.constant = 1 + } + self.layoutIfNeeded() + }) { finished in + self.valueShouldChange = true + } + } + + public func setState(_ state: Bool, animated: Bool) { + setState(state, withoutBlockAnimated: animated) + + valueChangedBlock?() + + if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { + let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator + formValidator?.enableByValidation() + } + } + + public func setState(_ state: Bool, withoutBlockAnimated animated: Bool) { + + isOn = state + + if !shouldTouchToSwitch { + thumbEnlargeAnimation() + thumbMoveAnitmationTo(on: isOn) + thumbShakeAnitmationTo(on: isOn) + } + + if isOn { + thumbLeftConstraint?.priority = UILayoutPriority(1) + thumbRightConstraint?.priority = UILayoutPriority(999) + thumbRightConstraint?.constant = 1 + } else { + thumbRightConstraint?.priority = UILayoutPriority(1) + thumbLeftConstraint?.priority = UILayoutPriority(999) + thumbLeftConstraint?.constant = 1 + } + + setBaseColorToOn(isOn, animated: animated) + thumbReformAnimation(animated) + accessibilityValue = state ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") + setNeedsLayout() + layoutIfNeeded() + } + + public func changeValue() -> Bool { + isOn ^= true + shouldTouchToSwitch = false + setState(isOn, animated: true) + shouldTouchToSwitch = true + sendActions(for: .valueChanged) + return isOn + } + + + // MARK: - helper + func touchIsOutSide(_ touches: Set?) -> Bool { + + let location = touches?.first?.location(in: self) + let x = Float(location?.x ?? 0.0) + let y = Float(location?.y ?? 0.0) + return x < 0 || x > frame.size.width || y < 0 || y > frame.size.height + } + + func touchMoves(toLeft touches: Set?) -> Bool { + + let location = touches?.first?.location(in: self) + let x = Float(location?.x ?? 0.0) + return x < frame.size.width / 2.0 + } + + class func getSwitchWidth() -> CGFloat { + return (MFSizeObject(standardSize: Switch.width, standardiPadPortraitSize: Double(SwitchWidth) * 1.5)).getValueBasedOnApplicationWidth() + } + + class func getSwitchHeight() -> CGFloat { + return (MFSizeObject(standardSize: Switch.height, standardiPadPortraitSize: Double(SwitchHeight) * 1.5)).getValueBasedOnApplicationWidth() + } + + class func getSwitchthumbWidth() -> CGFloat { + return (MFSizeObject(standardSize: Switch.thumbWidth, standardiPadPortraitSize: Double(SwitchthumbWidth) * 1.5)).getValueBasedOnApplicationWidth() + } + + class func getSwitchthumbHeight() -> CGFloat { + return (MFSizeObject(standardSize: Switch.thumbHeight, standardiPadPortraitSize: Double(SwitchthumbHeight) * 1.5)).getValueBasedOnApplicationWidth() + } } +// MARK: - Accessibility +extension Switch { + + func formFieldGroupName() -> String? { + return json.string("groupName") + } +} + +// MARK: FormValidationProtocol +extension Switch { + + func isValidField() -> Bool { + return isOn && json.bool(forKey: "required") + } + + func formFieldName() -> String? { + return json.string(KeyFieldKey) + } + + func formFieldValue() -> Any? { + return NSNumber(value: isOn) + } +} + +// MARK: - MVMCoreUIMoleculeViewProtocol +extension Switch: MVMCoreUIMoleculeViewProtocol { + + public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + self.json = json + delegate = delegateObject + + guard let dictionary = json else { return } + + FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) + + if let color = dictionary["onTintColor"] as? String { + trackTintColor?.on = UIColor.mfGet(forHex: color) + } + + if let color = dictionary["offTintColor"] as? String { + trackTintColor?.off = UIColor.mfGet(forHex: color) + } + + if let color = dictionary["onthumbTintColor"] as? String { + thumbTintColor?.on = UIColor.mfGet(forHex: color) + } + + if let color = dictionary["offthumbTintColor"] as? String { + thumbTintColor?.off = UIColor.mfGet(forHex: color) + } + + setState(json?.bool(forKey: "state"), animated: false) + + let actionMap = json?.dict("actionMap") + if actionMap != nil { + addTarget(self, action: #selector(addCustomAction), for: .touchUpInside) + } + } + + public func needsToBeConstrained() -> Bool { + return true + } + + public func alignment() -> UIStackView.Alignment { + return .trailing + } +} diff --git a/MVMCoreUI/BaseClasses/Control.swift b/MVMCoreUI/BaseClasses/Control.swift index fdf8204f..e1690306 100644 --- a/MVMCoreUI/BaseClasses/Control.swift +++ b/MVMCoreUI/BaseClasses/Control.swift @@ -9,6 +9,7 @@ import UIKit public class Control: UIControl { + var json: [AnyHashable: Any]? private var initialSetupPerformed = false @@ -37,8 +38,8 @@ public class Control: UIControl { } extension Control: MVMCoreViewProtocol { - public func updateView(_ size: CGFloat) { - } + + public func updateView(_ size: CGFloat) { } /// Will be called only once. public func setupView() { From cbb016406a5b6463119fc5ef90e185fee2400845 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 9 Dec 2019 13:37:33 -0500 Subject: [PATCH 03/54] WIP, putting Switch together. --- MVMCoreUI/Atoms/Views/Switch.swift | 377 +++++++++--------- MVMCoreUI/BaseClasses/Control.swift | 19 +- .../MVMCoreUIMoleculeMappingObject.m | 2 +- 3 files changed, 212 insertions(+), 186 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Switch.swift index e12fd890..025c7b64 100644 --- a/MVMCoreUI/Atoms/Views/Switch.swift +++ b/MVMCoreUI/Atoms/Views/Switch.swift @@ -7,17 +7,16 @@ // import MVMCore -import MVMCoreUI import UIKit -typealias ValueChangeBlock = () -> () +public typealias ValueChangeBlock = () -> () -open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol { + +@objcMembers open class Switch: Control, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - public var isOn = false public var trackTintColor: (on: UIColor?, off: UIColor?)? public var thumbTintColor: (on: UIColor?, off: UIColor?)? @@ -25,18 +24,40 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc var valueChangedBlock: ValueChangeBlock? var actionBlock: ValueChangeBlock? - static let width: CGFloat = 42 - static let height: CGFloat = 22 - static let thumbWidth: CGFloat = 20 - static let thumbHeight: CGFloat = 20 + static let trackSize = CGSize(width: 46, height: 24) + static let thumbSize = CGSize(width: 22, height: 22) + static let shakeIntensity: CGFloat = 2 - private weak var baseView: UIView? - private weak var thumbView: UIView? + private var thumbView = View() private var valueShouldChange = false private var canChangeValue = false + //-------------------------------------------------- + // MARK: - Computed Properties + //-------------------------------------------------- + + open override var isEnabled: Bool { + didSet { + if isEnabled { + + } else { + + } + } + } + + open var isOn: Bool = false { + didSet { + if isOn { + + } else { + + } + } + } + //-------------------------------------------------- // MARK: - Delegate //-------------------------------------------------- @@ -47,36 +68,12 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc // MARK: - Constraints //-------------------------------------------------- - private var thumbLeftConstraint: NSLayoutConstraint? - private var thumbRightConstraint: NSLayoutConstraint? + private var thumbLeadingConstraint: NSLayoutConstraint? + private var thumbTrailingConstraint: NSLayoutConstraint? private var thumbHeightConstraint: NSLayoutConstraint? private var thumbWidthConstraint: NSLayoutConstraint? - private weak var height: NSLayoutConstraint? - private weak var width: NSLayoutConstraint? - - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - - public func updateView(_ size: CGFloat) { - - height?.constant = MVMCoreUISwitch.getHeight() - width?.constant = MVMCoreUISwitch.getWidth() - baseView?.layer.cornerRadius = MVMCoreUISwitch.getHeight() / 2.0 - thumbView?.layer.cornerRadius = MVMCoreUISwitch.getthumbHeight() * 0.5 - thumbHeightConstraint?.constant = MVMCoreUISwitch.getthumbHeight() - thumbWidthConstraint?.constant = MVMCoreUISwitch.getthumbWidth() - } - - public func setupView() { - - trackTintColor?.on = .mfSwitchOnTint() - trackTintColor?.off = .mfSwitchOffTint() - canChangeValue = true - shouldTouchToSwitch = true - setUpViewWithState(isOn) - accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "MVMCoreUISwitch_buttonlabel") - } + private var heightConstraint: NSLayoutConstraint? + private var widthConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Initializers @@ -87,100 +84,101 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc setupView() } - public convenience init() { + public convenience override init() { self.init(frame: frame) } + public convenience init(isOn: Bool, changeBlock: ValueChangeBlock?) { + self.init(frame: frame) + self.isOn = isOn + valueChangedBlock = changeBlock + } + + public convenience init(changeBlock: ValueChangeBlock?) { + self.init(frame: frame) + valueChangedBlock = changeBlock + } + public required init?(coder: NSCoder) { super.init(coder: coder) - fatalError("Switch does not support xib") + fatalError("Switch does not support xib.") + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + public override func updateView(_ size: CGFloat) { + super.updateView(size) + + heightConstraint?.constant = Switch.getTrackHeight() + widthConstraint?.constant = Switch.getTrackWidth() + + layer.cornerRadius = Switch.getTrackHeight() / 2.0 + thumbView.layer.cornerRadius = Switch.getThumbHeight() / 2.0 + + thumbHeightConstraint?.constant = Switch.getThumbHeight() + thumbWidthConstraint?.constant = Switch.getThumbWidth() + } + + public override func setupView() { + super.setupView() + + guard !subviews.isEmpty else { return } + + trackTintColor?.on = .mfSwitchOnTint() // Green + trackTintColor?.off = .mfSwitchOffTint() // Black + + thumbTintColor?.on = .white + thumbTintColor?.off = .white + + canChangeValue = true + shouldTouchToSwitch = true + valueShouldChange = true + + heightConstraint = heightAnchor.constraint(equalToConstant: Switch.trackSize.height) + heightConstraint?.isActive = true + + widthConstraint = widthAnchor.constraint(equalToConstant: Switch.trackSize.width) + widthConstraint?.isActive = true + + layer.cornerRadius = Switch.trackSize.height / 2.0 + + let thumbView = MVMCoreUICommonViewsUtility.commonView() + thumbView.backgroundColor = .white + thumbView.layer.cornerRadius = Switch.getThumbHeight() / 2.0 + + addSubview(thumbView) + + thumbView.heightAnchor.constraint(equalToConstant: Switch.thumbSize.height).isActive = true + thumbView.widthAnchor.constraint(equalToConstant: Switch.thumbSize.width).isActive = true + thumbView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + thumbTrailingConstraint = trailingAnchor.constraint(equalTo: thumbView.trailingAnchor, constant: 1) + thumbLeadingConstraint = thumbView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) + thumbLeadingConstraint?.isActive = true + + shouldTouchToSwitch = false + setState(isOn, animated: false) + shouldTouchToSwitch = true + + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Switch_buttonlabel") } //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- - public class func mvmSwitchDefault() -> Self { - - let mySwitch = self.init(frame: .zero) - mySwitch?.translatesAutoresizingMaskIntoConstraints = false - return mySwitch - } - - public class func mvmSwitchDefault(with block: ValueChangeBlock?) -> Self { - - let mySwitch = self.init(frame: .zero) - mySwitch?.valueChangedBlock = block - return mySwitch - } - - public func setUpViewWithState(_ on: Bool) { - - guard baseView == nil else { return } - - isUserInteractionEnabled = true - valueShouldChange = true - - let baseView = MVMCoreUICommonViewsUtility.commonView() - - if let baseView = baseView { - addSubview(baseView) - } - - NSLayoutConstraint.constraintPinSubview(toSuperview: baseView) - let constraints = NSLayoutConstraint.constraintPinView(baseView, heightConstraint: true, heightConstant: MVMCoreUISwitch.getHeight(), widthConstraint: true, widthConstant: MVMCoreUISwitch.getWidth()) - self.height = constraints.object(forKey: ConstraintHeight, ofType: NSLayoutConstraint.self) - self.width = constraints.object(forKey: ConstraintWidth, ofType: NSLayoutConstraint.self) - baseView?.layer.cornerRadius = MVMCoreUISwitch.getHeight() / 2.0 - - onthumbTintColor = UIColor.white - offthumbTintColor = UIColor.white - - let thumbView = MVMCoreUICommonViewsUtility.commonView() - thumbView?.backgroundColor = UIColor.white - thumbView?.layer.cornerRadius = MVMCoreUISwitch.getthumbHeight() * 0.5 - - if let thumbView = thumbView { - baseView?.addSubview(thumbView) - } - - let heightWidth = NSLayoutConstraint.constraintPinView(thumbView, heightConstraint: true, heightConstant: MVMCoreUISwitch.getthumbHeight(), widthConstraint: true, widthConstant: MVMCoreUISwitch.getthumbWidth()) - let height = heightWidth.object(forKey: ConstraintHeight, ofType: NSLayoutConstraint.self) - let width = heightWidth.object(forKey: ConstraintWidth, ofType: NSLayoutConstraint.self) - thumbHeightConstraint = height - thumbWidthConstraint = width - let leadingTrailingDic = NSLayoutConstraint.constraintPinSubview(thumbView, pinTop: false, topConstant: 0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 1, pinRight: true, rightConstant: 1) - - let leadingTrailingDic = NSLayoutConstraint.constraintPinSubview(thumbView, pinTop: false, topConstant: 0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 1, pinRight: true, rightConstant: 1) - let `left` = leadingTrailingDic.object(forKey: ConstraintLeading, ofType: NSLayoutConstraint.self) - let `right` = leadingTrailingDic.object(forKey: ConstraintTrailing, ofType: NSLayoutConstraint.self) - NSLayoutConstraint.constraintPinSubview(thumbView, pinCenterX: false, centerXConstant: 0, pinCenterY: true, centerYConstant: 0) - `right`?.constant = 15 - thumbLeftConstraint = `left` - thumbRightConstraint = `right` - baseView.bringSubviewToFront(thumbView) - - //baseView = baseView // Skipping redundant initializing to itself - //thumbView = thumbView // Skipping redundant initializing to itself - - baseView.isUserInteractionEnabled = false - thumbView.isUserInteractionEnabled = false - shouldTouchToSwitch = false - setState(on, animated: false) - shouldTouchToSwitch = true - } - @objc func addCustomAction() { actionBlock?() } - public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - - return Switch.getSwitchHeight() - } - // MARK: - UIResponder overide + //-------------------------------------------------- + // MARK: - UIResponder + //-------------------------------------------------- + public func touchesBegan(_ touches: Set, with event: UIEvent) { thumbEnlargeAnimation() @@ -189,7 +187,7 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc public func touchesEnded(_ touches: Set, with event: UIEvent) { - if shouldTouchToSwitch && valueShouldChange{ + if shouldTouchToSwitch && valueShouldChange { changeValue() } @@ -247,12 +245,11 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc if animated { UIView.animate(withDuration: 0.1, animations: { - self.thumbWidthConstraint?.constant = MVMCoreUISwitch.getthumbWidth() + self.thumbWidthConstraint?.constant = Switch.getThumbWidth() self.layoutIfNeeded() - }) { finished in - } + }) { finished in } } else { - thumbWidthConstraint?.constant = MVMCoreUISwitch.getthumbWidth() + thumbWidthConstraint?.constant = Switch.getThumbWidth() layoutIfNeeded() } } @@ -261,16 +258,18 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc UIView.animate(withDuration: 0.1, animations: { if toOn { - self.thumbLeftConstraint?.priority = UILayoutPriority(1) - self.thumbRightConstraint?.priority = UILayoutPriority(999) - self.thumbRightConstraint?.constant = 1 + self.thumbLeadingConstraint?.priority = UILayoutPriority(1) + self.thumbTrailingConstraint?.priority = UILayoutPriority(999) + self.thumbTrailingConstraint?.constant = 1 + } else { - self.thumbLeftConstraint?.priority = UILayoutPriority(999) - self.thumbRightConstraint?.priority = UILayoutPriority(1) - self.thumbLeftConstraint?.constant = 1 + self.thumbLeadingConstraint?.priority = UILayoutPriority(999) + self.thumbTrailingConstraint?.priority = UILayoutPriority(1) + self.thumbLeadingConstraint?.constant = 1 } + self.setBaseColorToOn(toOn, animated: true) - self.thumbWidthConstraint?.constant = MVMCoreUISwitch.getThumbWidth() + PaddingOne + self.thumbWidthConstraint?.constant = Switch.getThumbWidth() + PaddingOne self.valueShouldChange = toOn != self.isOn self.layoutIfNeeded() }) @@ -284,38 +283,41 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc UIView.setAnimationCurve(.easeIn) if toOn { - thumbView?.backgroundColor = thumbTintColor!.on - baseView?.backgroundColor = trackTintColor!.on + thumbView.backgroundColor = thumbTintColor?.on + backgroundColor = trackTintColor?.on } else { - thumbView?.backgroundColor = thumbTintColor!.off - baseView?.backgroundColor = trackTintColor!.off + thumbView.backgroundColor = thumbTintColor?.off + backgroundColor = trackTintColor?.off } UIView.commitAnimations() + } else if isOn { - thumbView?.backgroundColor = thumbTintColor!.on - baseView?.backgroundColor = trackTintColor!.on + thumbView.backgroundColor = thumbTintColor?.on + backgroundColor = trackTintColor?.on + } else { - thumbView?.backgroundColor = thumbTintColor!.off - baseView?.backgroundColor = trackTintColor!.off + thumbView.backgroundColor = thumbTintColor?.off + backgroundColor = trackTintColor?.off } } //used after thumb moving to match the behavior of default uiswitch public func thumbShakeAnitmationTo(on toOn: Bool) { + UIView.animate(withDuration: 0.1, delay: 0.1, options: .curveEaseIn, animations: { if toOn { - self.thumbRightConstraint?.constant = Switch.shakeIntensity + self.thumbTrailingConstraint?.constant = Switch.shakeIntensity } else { - self.thumbLeftConstraint?.constant = Switch.shakeIntensity + self.thumbLeadingConstraint?.constant = Switch.shakeIntensity } self.layoutIfNeeded() - }) { finished in - } + }, completion: nil) + UIView.animate(withDuration: 0.2, delay: 0.1, options: [], animations: { if toOn { - self.thumbRightConstraint?.constant = 1 + self.thumbTrailingConstraint?.constant = 1 } else { - self.thumbLeftConstraint?.constant = 1 + self.thumbLeadingConstraint?.constant = 1 } self.layoutIfNeeded() }) { finished in @@ -328,10 +330,10 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc valueChangedBlock?() - if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { - let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator - formValidator?.enableByValidation() - } + // if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { + let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator + formValidator?.enableByValidation() + // } } public func setState(_ state: Bool, withoutBlockAnimated animated: Bool) { @@ -345,13 +347,13 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc } if isOn { - thumbLeftConstraint?.priority = UILayoutPriority(1) - thumbRightConstraint?.priority = UILayoutPriority(999) - thumbRightConstraint?.constant = 1 + thumbLeadingConstraint?.priority = UILayoutPriority(1) + thumbTrailingConstraint?.priority = UILayoutPriority(999) + thumbTrailingConstraint?.constant = 1 } else { - thumbRightConstraint?.priority = UILayoutPriority(1) - thumbLeftConstraint?.priority = UILayoutPriority(999) - thumbLeftConstraint?.constant = 1 + thumbTrailingConstraint?.priority = UILayoutPriority(1) + thumbLeadingConstraint?.priority = UILayoutPriority(999) + thumbLeadingConstraint?.constant = 1 } setBaseColorToOn(isOn, animated: animated) @@ -361,77 +363,82 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc layoutIfNeeded() } + @discardableResult public func changeValue() -> Bool { - isOn ^= true + + isOn.toggle() shouldTouchToSwitch = false setState(isOn, animated: true) shouldTouchToSwitch = true sendActions(for: .valueChanged) - return isOn - } - - - // MARK: - helper - func touchIsOutSide(_ touches: Set?) -> Bool { - let location = touches?.first?.location(in: self) - let x = Float(location?.x ?? 0.0) - let y = Float(location?.y ?? 0.0) - return x < 0 || x > frame.size.width || y < 0 || y > frame.size.height + return isOn } func touchMoves(toLeft touches: Set?) -> Bool { let location = touches?.first?.location(in: self) - let x = Float(location?.x ?? 0.0) + let x = CGFloat(location?.x ?? 0.0) return x < frame.size.width / 2.0 } - class func getSwitchWidth() -> CGFloat { - return (MFSizeObject(standardSize: Switch.width, standardiPadPortraitSize: Double(SwitchWidth) * 1.5)).getValueBasedOnApplicationWidth() + override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + + let faultTolerance: CGFloat = 20.0 + let area = bounds.insetBy(dx: -faultTolerance, dy: -faultTolerance) + return area.contains(point) } - class func getSwitchHeight() -> CGFloat { - return (MFSizeObject(standardSize: Switch.height, standardiPadPortraitSize: Double(SwitchHeight) * 1.5)).getValueBasedOnApplicationWidth() + open override func touchesEnded(_ touches: Set, with event: UIEvent?) { + + sendActions(for: .touchUpInside) } - class func getSwitchthumbWidth() -> CGFloat { - return (MFSizeObject(standardSize: Switch.thumbWidth, standardiPadPortraitSize: Double(SwitchthumbWidth) * 1.5)).getValueBasedOnApplicationWidth() + class func getTrackWidth() -> CGFloat { + return (MFSizeObject(standardSize: Switch.trackSize.width, standardiPadPortraitSize: CGFloat(Switch.trackSize.width * 1.5)))!.getValueBasedOnApplicationWidth() } - class func getSwitchthumbHeight() -> CGFloat { - return (MFSizeObject(standardSize: Switch.thumbHeight, standardiPadPortraitSize: Double(SwitchthumbHeight) * 1.5)).getValueBasedOnApplicationWidth() + class func getTrackHeight() -> CGFloat { + return (MFSizeObject(standardSize: Switch.trackSize.height, standardiPadPortraitSize: CGFloat(Switch.trackSize.height * 1.5) ))!.getValueBasedOnApplicationWidth() + } + + class func getThumbWidth() -> CGFloat { + return (MFSizeObject(standardSize: Switch.thumbSize.width, standardiPadPortraitSize: CGFloat(Switch.thumbSize.width * 1.5)))!.getValueBasedOnApplicationWidth() + } + + class func getThumbHeight() -> CGFloat { + return (MFSizeObject(standardSize: Switch.thumbSize.height, standardiPadPortraitSize: CGFloat(Switch.thumbSize.height * 1.5)))!.getValueBasedOnApplicationWidth() } } // MARK: - Accessibility extension Switch { - func formFieldGroupName() -> String? { - return json.string("groupName") + public func formFieldGroupName() -> String? { + return json?["groupName"] as? String } } // MARK: FormValidationProtocol extension Switch { - func isValidField() -> Bool { - return isOn && json.bool(forKey: "required") + public func isValidField() -> Bool { + return isOn && json?["required"] as? Bool ?? false } - func formFieldName() -> String? { - return json.string(KeyFieldKey) + public func formFieldName() -> String? { + return json?[KeyFieldKey] as? String ?? "" } - func formFieldValue() -> Any? { + public func formFieldValue() -> Any? { return NSNumber(value: isOn) } } // MARK: - MVMCoreUIMoleculeViewProtocol -extension Switch: MVMCoreUIMoleculeViewProtocol { +extension Switch { - public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { self.json = json delegate = delegateObject @@ -455,14 +462,18 @@ extension Switch: MVMCoreUIMoleculeViewProtocol { thumbTintColor?.off = UIColor.mfGet(forHex: color) } - setState(json?.bool(forKey: "state"), animated: false) + setState(dictionary["state"] as? Bool ?? false, animated: false) - let actionMap = json?.dict("actionMap") - if actionMap != nil { + if let _ = dictionary["actionMap"] as? [String: Any] { addTarget(self, action: #selector(addCustomAction), for: .touchUpInside) } } + public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + + return Switch.getTrackHeight() + } + public func needsToBeConstrained() -> Bool { return true } diff --git a/MVMCoreUI/BaseClasses/Control.swift b/MVMCoreUI/BaseClasses/Control.swift index e1690306..34785ce1 100644 --- a/MVMCoreUI/BaseClasses/Control.swift +++ b/MVMCoreUI/BaseClasses/Control.swift @@ -8,12 +8,19 @@ import UIKit -public class Control: UIControl { +open class Control: UIControl { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- var json: [AnyHashable: Any]? private var initialSetupPerformed = false + //-------------------------------------------------- + // MARK: - Initialiers + //-------------------------------------------------- + public override init(frame: CGRect) { super.init(frame: .zero) initialSetup() @@ -29,7 +36,12 @@ public class Control: UIControl { initialSetup() } + //-------------------------------------------------- + // MARK: - Setup + //-------------------------------------------------- + public func initialSetup() { + if !initialSetupPerformed { initialSetupPerformed = true setupView() @@ -37,6 +49,7 @@ public class Control: UIControl { } } +// MARK: MVMCoreViewProtocol extension Control: MVMCoreViewProtocol { public func updateView(_ size: CGFloat) { } @@ -48,8 +61,10 @@ extension Control: MVMCoreViewProtocol { } } +// MARK: MVMCoreUIMoleculeViewProtocol extension Control: MVMCoreUIMoleculeViewProtocol { - public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + + public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { self.json = json if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 51a07225..4923f21d 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -48,7 +48,7 @@ @"radioButtonLabel": RadioButtonLabel.class, @"listItem": MoleculeTableViewCell.class, @"accordionListItem": AccordionMoleculeTableViewCell.class, - @"switch": MVMCoreUISwitch.class, + @"switch": Switch.class, @"leftRightLabelView": LeftRightLabelView.class, @"actionDetailWithImage": ActionDetailWithImage.class, @"image": MFLoadImageView.class, From 1668fb8c3235ada04fa4d2c3f2156cb299f08921 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 9 Dec 2019 15:03:53 -0500 Subject: [PATCH 04/54] More Switch development. --- MVMCoreUI/Atoms/Views/Switch.swift | 227 ++++++++++++++--------------- 1 file changed, 111 insertions(+), 116 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Switch.swift index 025c7b64..03724267 100644 --- a/MVMCoreUI/Atoms/Views/Switch.swift +++ b/MVMCoreUI/Atoms/Views/Switch.swift @@ -16,9 +16,10 @@ public typealias ValueChangeBlock = () -> () //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - - public var trackTintColor: (on: UIColor?, off: UIColor?)? - public var thumbTintColor: (on: UIColor?, off: UIColor?)? + + public var trackTintColor: (on: UIColor?, off: UIColor?)? = (on: .mfShamrock(), off: .black) + public var thumbTintColor: (on: UIColor?, off: UIColor?)? = (on: .white, off: .white) + public var disabledTintColor: (track: UIColor?, thumb: UIColor?)? = (track: .mfSilver(), thumb: .white) var shouldTouchToSwitch = false var valueChangedBlock: ValueChangeBlock? @@ -40,21 +41,16 @@ public typealias ValueChangeBlock = () -> () open override var isEnabled: Bool { didSet { - if isEnabled { - - } else { - - } + isUserInteractionEnabled = isEnabled + backgroundColor = isEnabled ? trackTintColor?.on : disabledTintColor?.track + thumbView.backgroundColor = isEnabled ? thumbTintColor?.on : disabledTintColor?.thumb } } open var isOn: Bool = false { didSet { - if isOn { - - } else { - - } + backgroundColor = isOn ? trackTintColor?.on : trackTintColor?.off + thumbView.backgroundColor = isOn ? thumbTintColor?.on : thumbTintColor?.off } } @@ -62,7 +58,7 @@ public typealias ValueChangeBlock = () -> () // MARK: - Delegate //-------------------------------------------------- - private var delegate: DelegateObject? + private var delegateObject: MVMCoreUIDelegateObject? //-------------------------------------------------- // MARK: - Constraints @@ -85,17 +81,17 @@ public typealias ValueChangeBlock = () -> () } public convenience override init() { - self.init(frame: frame) + self.init(frame: .zero) } public convenience init(isOn: Bool, changeBlock: ValueChangeBlock?) { - self.init(frame: frame) + self.init(frame: .zero) self.isOn = isOn valueChangedBlock = changeBlock } public convenience init(changeBlock: ValueChangeBlock?) { - self.init(frame: frame) + self.init(frame: .zero) valueChangedBlock = changeBlock } @@ -126,12 +122,6 @@ public typealias ValueChangeBlock = () -> () guard !subviews.isEmpty else { return } - trackTintColor?.on = .mfSwitchOnTint() // Green - trackTintColor?.off = .mfSwitchOffTint() // Black - - thumbTintColor?.on = .white - thumbTintColor?.off = .white - canChangeValue = true shouldTouchToSwitch = true valueShouldChange = true @@ -165,6 +155,22 @@ public typealias ValueChangeBlock = () -> () accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Switch_buttonlabel") } + class func getTrackWidth() -> CGFloat { + return (MFSizeObject(standardSize: Switch.trackSize.width, standardiPadPortraitSize: CGFloat(Switch.trackSize.width * 1.5)))!.getValueBasedOnApplicationWidth() + } + + class func getTrackHeight() -> CGFloat { + return (MFSizeObject(standardSize: Switch.trackSize.height, standardiPadPortraitSize: CGFloat(Switch.trackSize.height * 1.5) ))!.getValueBasedOnApplicationWidth() + } + + class func getThumbWidth() -> CGFloat { + return (MFSizeObject(standardSize: Switch.thumbSize.width, standardiPadPortraitSize: CGFloat(Switch.thumbSize.width * 1.5)))!.getValueBasedOnApplicationWidth() + } + + class func getThumbHeight() -> CGFloat { + return (MFSizeObject(standardSize: Switch.thumbSize.height, standardiPadPortraitSize: CGFloat(Switch.thumbSize.height * 1.5)))!.getValueBasedOnApplicationWidth() + } + //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- @@ -174,11 +180,64 @@ public typealias ValueChangeBlock = () -> () actionBlock?() } + public func setState(_ state: Bool, animated: Bool) { + + setState(state, withoutBlockAnimated: animated) + valueChangedBlock?() + FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) + } + + public func setState(_ state: Bool, withoutBlockAnimated animated: Bool) { + + isOn = state + + if !shouldTouchToSwitch { + thumbEnlargeAnimation() + thumbMoveAnitmationTo(on: isOn) + thumbShakeAnitmationTo(on: isOn) + } + + if isOn { + thumbLeadingConstraint?.priority = UILayoutPriority(1) + thumbTrailingConstraint?.priority = UILayoutPriority(999) + thumbTrailingConstraint?.constant = 1 + + } else { + thumbTrailingConstraint?.priority = UILayoutPriority(1) + thumbLeadingConstraint?.priority = UILayoutPriority(999) + thumbLeadingConstraint?.constant = 1 + } + + setBaseColorToOn(isOn, animated: animated) + thumbReformAnimation(animated) + accessibilityValue = state ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") + setNeedsLayout() + layoutIfNeeded() + } + + @discardableResult + public func changeValue() -> Bool { + + isOn.toggle() + shouldTouchToSwitch = false + setState(isOn, animated: true) + shouldTouchToSwitch = true + sendActions(for: .valueChanged) + + return isOn + } //-------------------------------------------------- // MARK: - UIResponder //-------------------------------------------------- + override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + + let faultTolerance: CGFloat = 20.0 + let area = bounds.insetBy(dx: -faultTolerance, dy: -faultTolerance) + return area.contains(point) + } + public func touchesBegan(_ touches: Set, with event: UIEvent) { thumbEnlargeAnimation() @@ -198,6 +257,11 @@ public typealias ValueChangeBlock = () -> () valueShouldChange = true } + open override func touchesEnded(_ touches: Set, with event: UIEvent?) { + + sendActions(for: .touchUpInside) + } + public func touchesMoved(_ touches: Set, with event: UIEvent) { if shouldTouchToSwitch { @@ -211,7 +275,7 @@ public typealias ValueChangeBlock = () -> () thumbReformAnimation(true) } } - + /* if touchIsOutSide(touches) { sendActions(for: .touchDragOutside) if !shouldTouchToSwitch { @@ -220,6 +284,14 @@ public typealias ValueChangeBlock = () -> () } else { sendActions(for: .touchDragInside) } + */ + } + + func touchMoves(toLeft touches: Set?) -> Bool { + + let location = touches?.first?.location(in: self) + let x = CGFloat(location?.x ?? 0.0) + return x < frame.size.width / 2.0 } public func touchesCancelled(_ touches: Set, with event: UIEvent) { @@ -232,7 +304,10 @@ public typealias ValueChangeBlock = () -> () sendActions(for: .touchCancel) } - // MARK: - animation + //-------------------------------------------------- + // MARK: - Animation + //-------------------------------------------------- + public func thumbEnlargeAnimation() { UIView.animate(withDuration: 0.1, animations: { @@ -247,7 +322,7 @@ public typealias ValueChangeBlock = () -> () UIView.animate(withDuration: 0.1, animations: { self.thumbWidthConstraint?.constant = Switch.getThumbWidth() self.layoutIfNeeded() - }) { finished in } + }, completion: nil) } else { thumbWidthConstraint?.constant = Switch.getThumbWidth() layoutIfNeeded() @@ -285,6 +360,7 @@ public typealias ValueChangeBlock = () -> () if toOn { thumbView.backgroundColor = thumbTintColor?.on backgroundColor = trackTintColor?.on + } else { thumbView.backgroundColor = thumbTintColor?.off backgroundColor = trackTintColor?.off @@ -310,7 +386,9 @@ public typealias ValueChangeBlock = () -> () } else { self.thumbLeadingConstraint?.constant = Switch.shakeIntensity } + self.layoutIfNeeded() + }, completion: nil) UIView.animate(withDuration: 0.2, delay: 0.1, options: [], animations: { @@ -319,96 +397,13 @@ public typealias ValueChangeBlock = () -> () } else { self.thumbLeadingConstraint?.constant = 1 } + self.layoutIfNeeded() + }) { finished in self.valueShouldChange = true } } - - public func setState(_ state: Bool, animated: Bool) { - setState(state, withoutBlockAnimated: animated) - - valueChangedBlock?() - - // if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { - let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator - formValidator?.enableByValidation() - // } - } - - public func setState(_ state: Bool, withoutBlockAnimated animated: Bool) { - - isOn = state - - if !shouldTouchToSwitch { - thumbEnlargeAnimation() - thumbMoveAnitmationTo(on: isOn) - thumbShakeAnitmationTo(on: isOn) - } - - if isOn { - thumbLeadingConstraint?.priority = UILayoutPriority(1) - thumbTrailingConstraint?.priority = UILayoutPriority(999) - thumbTrailingConstraint?.constant = 1 - } else { - thumbTrailingConstraint?.priority = UILayoutPriority(1) - thumbLeadingConstraint?.priority = UILayoutPriority(999) - thumbLeadingConstraint?.constant = 1 - } - - setBaseColorToOn(isOn, animated: animated) - thumbReformAnimation(animated) - accessibilityValue = state ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") - setNeedsLayout() - layoutIfNeeded() - } - - @discardableResult - public func changeValue() -> Bool { - - isOn.toggle() - shouldTouchToSwitch = false - setState(isOn, animated: true) - shouldTouchToSwitch = true - sendActions(for: .valueChanged) - - return isOn - } - - func touchMoves(toLeft touches: Set?) -> Bool { - - let location = touches?.first?.location(in: self) - let x = CGFloat(location?.x ?? 0.0) - return x < frame.size.width / 2.0 - } - - override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - - let faultTolerance: CGFloat = 20.0 - let area = bounds.insetBy(dx: -faultTolerance, dy: -faultTolerance) - return area.contains(point) - } - - open override func touchesEnded(_ touches: Set, with event: UIEvent?) { - - sendActions(for: .touchUpInside) - } - - class func getTrackWidth() -> CGFloat { - return (MFSizeObject(standardSize: Switch.trackSize.width, standardiPadPortraitSize: CGFloat(Switch.trackSize.width * 1.5)))!.getValueBasedOnApplicationWidth() - } - - class func getTrackHeight() -> CGFloat { - return (MFSizeObject(standardSize: Switch.trackSize.height, standardiPadPortraitSize: CGFloat(Switch.trackSize.height * 1.5) ))!.getValueBasedOnApplicationWidth() - } - - class func getThumbWidth() -> CGFloat { - return (MFSizeObject(standardSize: Switch.thumbSize.width, standardiPadPortraitSize: CGFloat(Switch.thumbSize.width * 1.5)))!.getValueBasedOnApplicationWidth() - } - - class func getThumbHeight() -> CGFloat { - return (MFSizeObject(standardSize: Switch.thumbSize.height, standardiPadPortraitSize: CGFloat(Switch.thumbSize.height * 1.5)))!.getValueBasedOnApplicationWidth() - } } // MARK: - Accessibility @@ -438,9 +433,9 @@ extension Switch { // MARK: - MVMCoreUIMoleculeViewProtocol extension Switch { - public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - self.json = json - delegate = delegateObject + public override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + self.delegateObject = delegateObject guard let dictionary = json else { return } @@ -454,11 +449,11 @@ extension Switch { trackTintColor?.off = UIColor.mfGet(forHex: color) } - if let color = dictionary["onthumbTintColor"] as? String { + if let color = dictionary["onThumbTintColor"] as? String { thumbTintColor?.on = UIColor.mfGet(forHex: color) } - if let color = dictionary["offthumbTintColor"] as? String { + if let color = dictionary["offThumbTintColor"] as? String { thumbTintColor?.off = UIColor.mfGet(forHex: color) } From dd4770706493771679ae1066888a1cb077292284 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 10 Dec 2019 08:54:01 -0500 Subject: [PATCH 05/54] the latest switch. --- MVMCoreUI/Atoms/Views/Switch.swift | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Switch.swift index 03724267..8ea86227 100644 --- a/MVMCoreUI/Atoms/Views/Switch.swift +++ b/MVMCoreUI/Atoms/Views/Switch.swift @@ -11,7 +11,12 @@ import UIKit public typealias ValueChangeBlock = () -> () - +/** + A custom implementation of Apple's UISwitch. + + Track: The background of the switch control. + Thumb: The circular indicator that slides on the track. + */ @objcMembers open class Switch: Control, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol { //-------------------------------------------------- // MARK: - Properties @@ -21,10 +26,14 @@ public typealias ValueChangeBlock = () -> () public var thumbTintColor: (on: UIColor?, off: UIColor?)? = (on: .white, off: .white) public var disabledTintColor: (track: UIColor?, thumb: UIColor?)? = (track: .mfSilver(), thumb: .white) - var shouldTouchToSwitch = false - var valueChangedBlock: ValueChangeBlock? - var actionBlock: ValueChangeBlock? + private var valueShouldChange = false + private var canChangeValue = false + private var shouldTouchToSwitch = false + private var valueChangedBlock: ValueChangeBlock? + private var actionBlock: ValueChangeBlock? + + // Sizes are from InVision design specs. static let trackSize = CGSize(width: 46, height: 24) static let thumbSize = CGSize(width: 22, height: 22) @@ -32,9 +41,6 @@ public typealias ValueChangeBlock = () -> () private var thumbView = View() - private var valueShouldChange = false - private var canChangeValue = false - //-------------------------------------------------- // MARK: - Computed Properties //-------------------------------------------------- From 83370956c1b43cdec3a389e23d59d078e43f1993 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 10 Dec 2019 10:34:08 -0500 Subject: [PATCH 06/54] movemt,. --- MVMCoreUI/Atoms/Views/Switch.swift | 34 ++++++++++++------------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Switch.swift index 8ea86227..da2c13bf 100644 --- a/MVMCoreUI/Atoms/Views/Switch.swift +++ b/MVMCoreUI/Atoms/Views/Switch.swift @@ -9,8 +9,6 @@ import MVMCore import UIKit -public typealias ValueChangeBlock = () -> () - /** A custom implementation of Apple's UISwitch. @@ -27,11 +25,11 @@ public typealias ValueChangeBlock = () -> () public var disabledTintColor: (track: UIColor?, thumb: UIColor?)? = (track: .mfSilver(), thumb: .white) private var valueShouldChange = false - private var canChangeValue = false +// private var canChangeValue = false private var shouldTouchToSwitch = false - private var valueChangedBlock: ValueChangeBlock? - private var actionBlock: ValueChangeBlock? + private var valueChangedBlock: ActionBlock? + private var actionBlock: ActionBlock? // Sizes are from InVision design specs. static let trackSize = CGSize(width: 46, height: 24) @@ -90,13 +88,13 @@ public typealias ValueChangeBlock = () -> () self.init(frame: .zero) } - public convenience init(isOn: Bool, changeBlock: ValueChangeBlock?) { + public convenience init(isOn: Bool, changeBlock: ActionBlock?) { self.init(frame: .zero) self.isOn = isOn valueChangedBlock = changeBlock } - public convenience init(changeBlock: ValueChangeBlock?) { + public convenience init(changeBlock: ActionBlock?) { self.init(frame: .zero) valueChangedBlock = changeBlock } @@ -116,19 +114,19 @@ public typealias ValueChangeBlock = () -> () heightConstraint?.constant = Switch.getTrackHeight() widthConstraint?.constant = Switch.getTrackWidth() - layer.cornerRadius = Switch.getTrackHeight() / 2.0 - thumbView.layer.cornerRadius = Switch.getThumbHeight() / 2.0 - thumbHeightConstraint?.constant = Switch.getThumbHeight() thumbWidthConstraint?.constant = Switch.getThumbWidth() + + layer.cornerRadius = Switch.getTrackHeight() / 2.0 + thumbView.layer.cornerRadius = Switch.getThumbHeight() / 2.0 } public override func setupView() { super.setupView() - guard !subviews.isEmpty else { return } + guard subviews.isEmpty else { return } - canChangeValue = true +// canChangeValue = true shouldTouchToSwitch = true valueShouldChange = true @@ -263,11 +261,6 @@ public typealias ValueChangeBlock = () -> () valueShouldChange = true } - open override func touchesEnded(_ touches: Set, with event: UIEvent?) { - - sendActions(for: .touchUpInside) - } - public func touchesMoved(_ touches: Set, with event: UIEvent) { if shouldTouchToSwitch { @@ -304,14 +297,13 @@ public typealias ValueChangeBlock = () -> () if shouldTouchToSwitch { thumbReformAnimation(true) - canChangeValue = true } sendActions(for: .touchCancel) } //-------------------------------------------------- - // MARK: - Animation + // MARK: - Animations //-------------------------------------------------- public func thumbEnlargeAnimation() { @@ -443,10 +435,10 @@ extension Switch { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) self.delegateObject = delegateObject - guard let dictionary = json else { return } - FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) + guard let dictionary = json else { return } + if let color = dictionary["onTintColor"] as? String { trackTintColor?.on = UIColor.mfGet(forHex: color) } From ab0ff64677e39bdb30f5906cc847c247bc0e33fb Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 10 Dec 2019 14:38:32 -0500 Subject: [PATCH 07/54] General Working order. --- MVMCoreUI/Atoms/Views/Switch.swift | 311 ++++++++++------------------- 1 file changed, 102 insertions(+), 209 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Switch.swift index da2c13bf..332bacc5 100644 --- a/MVMCoreUI/Atoms/Views/Switch.swift +++ b/MVMCoreUI/Atoms/Views/Switch.swift @@ -10,25 +10,25 @@ import MVMCore import UIKit /** - A custom implementation of Apple's UISwitch. - - Track: The background of the switch control. - Thumb: The circular indicator that slides on the track. + A custom implementation of Apple's UISwitch. + + Track: The background of the switch control. + Thumb: The circular indicator that slides on the track. */ @objcMembers open class Switch: Control, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - + public var trackTintColor: (on: UIColor?, off: UIColor?)? = (on: .mfShamrock(), off: .black) public var thumbTintColor: (on: UIColor?, off: UIColor?)? = (on: .white, off: .white) public var disabledTintColor: (track: UIColor?, thumb: UIColor?)? = (track: .mfSilver(), thumb: .white) - private var valueShouldChange = false -// private var canChangeValue = false - private var shouldTouchToSwitch = false + public var isAnimated = true + + private var valueShouldChange = true + private var shouldTouchToSwitch = true - private var valueChangedBlock: ActionBlock? private var actionBlock: ActionBlock? // Sizes are from InVision design specs. @@ -37,7 +37,12 @@ import UIKit static let shakeIntensity: CGFloat = 2 - private var thumbView = View() + private var thumbView: View = { + let view = View() + view.backgroundColor = .white + view.layer.cornerRadius = Switch.getThumbHeight() / 2.0 + return view + }() //-------------------------------------------------- // MARK: - Computed Properties @@ -53,8 +58,49 @@ import UIKit open var isOn: Bool = false { didSet { - backgroundColor = isOn ? trackTintColor?.on : trackTintColor?.off - thumbView.backgroundColor = isOn ? thumbTintColor?.on : thumbTintColor?.off + isSelected = isOn + + if isAnimated { + UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: { + if self.isOn { + self.thumbView.backgroundColor = self.thumbTintColor?.on + self.backgroundColor = self.trackTintColor?.on + + } else { + self.thumbView.backgroundColor = self.thumbTintColor?.off + self.backgroundColor = self.trackTintColor?.off + } + + }, completion: nil) + + UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.3, options: [], animations: { + if self.isOn { + self.thumbLeadingConstraint?.isActive = true + self.thumbTrailingConstraint?.isActive = false + self.relativeThumbLeadingConstraint?.isActive = false + self.relativeThumbTrailingConstraint?.isActive = true + + } else { + self.thumbLeadingConstraint?.isActive = false + self.thumbTrailingConstraint?.isActive = true + self.relativeThumbLeadingConstraint?.isActive = true + self.relativeThumbTrailingConstraint?.isActive = false + } + + self.thumbWidthConstraint?.constant = Switch.getThumbWidth() + PaddingOne + self.layoutIfNeeded() + + }, completion: nil) + + } else { + backgroundColor = isOn ? trackTintColor?.on : trackTintColor?.off + thumbView.backgroundColor = isOn ? thumbTintColor?.on : thumbTintColor?.off + } + + FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) + accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") + setNeedsLayout() + layoutIfNeeded() } } @@ -70,6 +116,9 @@ import UIKit private var thumbLeadingConstraint: NSLayoutConstraint? private var thumbTrailingConstraint: NSLayoutConstraint? + private var relativeThumbLeadingConstraint: NSLayoutConstraint? + private var relativeThumbTrailingConstraint: NSLayoutConstraint? + private var thumbHeightConstraint: NSLayoutConstraint? private var thumbWidthConstraint: NSLayoutConstraint? private var heightConstraint: NSLayoutConstraint? @@ -88,15 +137,15 @@ import UIKit self.init(frame: .zero) } - public convenience init(isOn: Bool, changeBlock: ActionBlock?) { + public convenience init(isOn: Bool, actionBlock: ActionBlock?) { self.init(frame: .zero) self.isOn = isOn - valueChangedBlock = changeBlock + self.actionBlock = actionBlock } - public convenience init(changeBlock: ActionBlock?) { + public convenience init(actionBlock: ActionBlock?) { self.init(frame: .zero) - valueChangedBlock = changeBlock + self.actionBlock = actionBlock } public required init?(coder: NSCoder) { @@ -126,10 +175,6 @@ import UIKit guard subviews.isEmpty else { return } -// canChangeValue = true - shouldTouchToSwitch = true - valueShouldChange = true - heightConstraint = heightAnchor.constraint(equalToConstant: Switch.trackSize.height) heightConstraint?.isActive = true @@ -137,10 +182,7 @@ import UIKit widthConstraint?.isActive = true layer.cornerRadius = Switch.trackSize.height / 2.0 - - let thumbView = MVMCoreUICommonViewsUtility.commonView() - thumbView.backgroundColor = .white - thumbView.layer.cornerRadius = Switch.getThumbHeight() / 2.0 + backgroundColor = trackTintColor?.off addSubview(thumbView) @@ -149,13 +191,13 @@ import UIKit thumbView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true thumbTrailingConstraint = trailingAnchor.constraint(equalTo: thumbView.trailingAnchor, constant: 1) + relativeThumbTrailingConstraint = trailingAnchor.constraint(greaterThanOrEqualTo: thumbView.trailingAnchor) + relativeThumbTrailingConstraint?.isActive = true + + relativeThumbLeadingConstraint = thumbView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor) thumbLeadingConstraint = thumbView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) thumbLeadingConstraint?.isActive = true - shouldTouchToSwitch = false - setState(isOn, animated: false) - shouldTouchToSwitch = true - accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Switch_buttonlabel") } @@ -176,59 +218,23 @@ import UIKit } //-------------------------------------------------- - // MARK: - Methods + // MARK: - Actions //-------------------------------------------------- - @objc func addCustomAction() { - - actionBlock?() + open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { + super.sendAction(action, to: target, for: event) + toggleAndAction() } - public func setState(_ state: Bool, animated: Bool) { - - setState(state, withoutBlockAnimated: animated) - valueChangedBlock?() - FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) + open override func sendActions(for controlEvents: UIControl.Event) { + super.sendActions(for: controlEvents) + toggleAndAction() } - public func setState(_ state: Bool, withoutBlockAnimated animated: Bool) { - - isOn = state - - if !shouldTouchToSwitch { - thumbEnlargeAnimation() - thumbMoveAnitmationTo(on: isOn) - thumbShakeAnitmationTo(on: isOn) - } - - if isOn { - thumbLeadingConstraint?.priority = UILayoutPriority(1) - thumbTrailingConstraint?.priority = UILayoutPriority(999) - thumbTrailingConstraint?.constant = 1 - - } else { - thumbTrailingConstraint?.priority = UILayoutPriority(1) - thumbLeadingConstraint?.priority = UILayoutPriority(999) - thumbLeadingConstraint?.constant = 1 - } - - setBaseColorToOn(isOn, animated: animated) - thumbReformAnimation(animated) - accessibilityValue = state ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") - setNeedsLayout() - layoutIfNeeded() - } - - @discardableResult - public func changeValue() -> Bool { - + /// This will toggle the state of the Switch and execute the actionBlock if provided. + public func toggleAndAction() { isOn.toggle() - shouldTouchToSwitch = false - setState(isOn, animated: true) - shouldTouchToSwitch = true - sendActions(for: .valueChanged) - - return isOn + actionBlock?() } //-------------------------------------------------- @@ -244,61 +250,24 @@ import UIKit public func touchesBegan(_ touches: Set, with event: UIEvent) { - thumbEnlargeAnimation() + UIView.animate(withDuration: 0.1, animations: { + self.thumbWidthConstraint?.constant += PaddingOne + self.layoutIfNeeded() + }) + sendActions(for: .touchDown) } - public func touchesEnded(_ touches: Set, with event: UIEvent) { + public override func touchesEnded(_ touches: Set, with event: UIEvent?) { - if shouldTouchToSwitch && valueShouldChange { - changeValue() - } - - sendActions(for: .touchUpInside) - thumbMoveAnitmationTo(on: isOn) - thumbShakeAnitmationTo(on: isOn) - thumbReformAnimation(true) + thumbReformAnimation() valueShouldChange = true - } - - public func touchesMoved(_ touches: Set, with event: UIEvent) { - - if shouldTouchToSwitch { - if touchMoves(toLeft: touches) { - thumbMoveAnitmationTo(on: false) - thumbShakeAnitmationTo(on: false) - thumbReformAnimation(true) - } else { - thumbMoveAnitmationTo(on: true) - thumbShakeAnitmationTo(on: true) - thumbReformAnimation(true) - } - } - /* - if touchIsOutSide(touches) { - sendActions(for: .touchDragOutside) - if !shouldTouchToSwitch { - thumbReformAnimation(true) - } - } else { - sendActions(for: .touchDragInside) - } - */ - } - - func touchMoves(toLeft touches: Set?) -> Bool { - - let location = touches?.first?.location(in: self) - let x = CGFloat(location?.x ?? 0.0) - return x < frame.size.width / 2.0 + sendActions(for: .touchUpInside) } public func touchesCancelled(_ touches: Set, with event: UIEvent) { - if shouldTouchToSwitch { - thumbReformAnimation(true) - } - + thumbReformAnimation() sendActions(for: .touchCancel) } @@ -306,17 +275,9 @@ import UIKit // MARK: - Animations //-------------------------------------------------- - public func thumbEnlargeAnimation() { + public func thumbReformAnimation() { - UIView.animate(withDuration: 0.1, animations: { - self.thumbWidthConstraint?.constant += PaddingOne - self.layoutIfNeeded() - }) - } - - public func thumbReformAnimation(_ animated: Bool) { - - if animated { + if isAnimated { UIView.animate(withDuration: 0.1, animations: { self.thumbWidthConstraint?.constant = Switch.getThumbWidth() self.layoutIfNeeded() @@ -326,82 +287,6 @@ import UIKit layoutIfNeeded() } } - - public func thumbMoveAnitmationTo(on toOn: Bool) { - - UIView.animate(withDuration: 0.1, animations: { - if toOn { - self.thumbLeadingConstraint?.priority = UILayoutPriority(1) - self.thumbTrailingConstraint?.priority = UILayoutPriority(999) - self.thumbTrailingConstraint?.constant = 1 - - } else { - self.thumbLeadingConstraint?.priority = UILayoutPriority(999) - self.thumbTrailingConstraint?.priority = UILayoutPriority(1) - self.thumbLeadingConstraint?.constant = 1 - } - - self.setBaseColorToOn(toOn, animated: true) - self.thumbWidthConstraint?.constant = Switch.getThumbWidth() + PaddingOne - self.valueShouldChange = toOn != self.isOn - self.layoutIfNeeded() - }) - } - - public func setBaseColorToOn(_ toOn: Bool, animated: Bool) { - - if animated { - UIView.beginAnimations(nil, context: nil) - UIView.setAnimationDuration(0.2) - UIView.setAnimationCurve(.easeIn) - - if toOn { - thumbView.backgroundColor = thumbTintColor?.on - backgroundColor = trackTintColor?.on - - } else { - thumbView.backgroundColor = thumbTintColor?.off - backgroundColor = trackTintColor?.off - } - UIView.commitAnimations() - - } else if isOn { - thumbView.backgroundColor = thumbTintColor?.on - backgroundColor = trackTintColor?.on - - } else { - thumbView.backgroundColor = thumbTintColor?.off - backgroundColor = trackTintColor?.off - } - } - - //used after thumb moving to match the behavior of default uiswitch - public func thumbShakeAnitmationTo(on toOn: Bool) { - - UIView.animate(withDuration: 0.1, delay: 0.1, options: .curveEaseIn, animations: { - if toOn { - self.thumbTrailingConstraint?.constant = Switch.shakeIntensity - } else { - self.thumbLeadingConstraint?.constant = Switch.shakeIntensity - } - - self.layoutIfNeeded() - - }, completion: nil) - - UIView.animate(withDuration: 0.2, delay: 0.1, options: [], animations: { - if toOn { - self.thumbTrailingConstraint?.constant = 1 - } else { - self.thumbLeadingConstraint?.constant = 1 - } - - self.layoutIfNeeded() - - }) { finished in - self.valueShouldChange = true - } - } } // MARK: - Accessibility @@ -455,10 +340,18 @@ extension Switch { thumbTintColor?.off = UIColor.mfGet(forHex: color) } - setState(dictionary["state"] as? Bool ?? false, animated: false) + if let state = dictionary["state"] as? Bool { + isAnimated = false + isOn = state + isAnimated = true + } - if let _ = dictionary["actionMap"] as? [String: Any] { - addTarget(self, action: #selector(addCustomAction), for: .touchUpInside) + if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { +// actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } + } + + if let isAnimated = dictionary["state"] as? Bool { + self.isAnimated = isAnimated } } From c32a0d71b0a081359fde5c7fd4fe327722e4b18b Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 10 Dec 2019 15:58:59 -0500 Subject: [PATCH 08/54] good working state. --- MVMCoreUI/Atoms/Views/Switch.swift | 111 +++++++++++++++++------------ 1 file changed, 67 insertions(+), 44 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Switch.swift index 332bacc5..ee257ce8 100644 --- a/MVMCoreUI/Atoms/Views/Switch.swift +++ b/MVMCoreUI/Atoms/Views/Switch.swift @@ -26,17 +26,12 @@ import UIKit public var isAnimated = true - private var valueShouldChange = true - private var shouldTouchToSwitch = true - private var actionBlock: ActionBlock? // Sizes are from InVision design specs. static let trackSize = CGSize(width: 46, height: 24) static let thumbSize = CGSize(width: 22, height: 22) - static let shakeIntensity: CGFloat = 2 - private var thumbView: View = { let view = View() view.backgroundColor = .white @@ -56,6 +51,12 @@ import UIKit } } + public var isLocked: Bool = false { + didSet { + isUserInteractionEnabled = !isLocked + } + } + open var isOn: Bool = false { didSet { isSelected = isOn @@ -73,21 +74,10 @@ import UIKit }, completion: nil) - UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.3, options: [], animations: { - if self.isOn { - self.thumbLeadingConstraint?.isActive = true - self.thumbTrailingConstraint?.isActive = false - self.relativeThumbLeadingConstraint?.isActive = false - self.relativeThumbTrailingConstraint?.isActive = true - - } else { - self.thumbLeadingConstraint?.isActive = false - self.thumbTrailingConstraint?.isActive = true - self.relativeThumbLeadingConstraint?.isActive = true - self.relativeThumbTrailingConstraint?.isActive = false - } + UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.2, options: [], animations: { - self.thumbWidthConstraint?.constant = Switch.getThumbWidth() + PaddingOne + self.constrainThumb() + self.thumbWidthConstraint?.constant = Switch.getThumbWidth() self.layoutIfNeeded() }, completion: nil) @@ -95,6 +85,7 @@ import UIKit } else { backgroundColor = isOn ? trackTintColor?.on : trackTintColor?.off thumbView.backgroundColor = isOn ? thumbTintColor?.on : thumbTintColor?.off + self.constrainThumb() } FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) @@ -116,14 +107,17 @@ import UIKit private var thumbLeadingConstraint: NSLayoutConstraint? private var thumbTrailingConstraint: NSLayoutConstraint? - private var relativeThumbLeadingConstraint: NSLayoutConstraint? - private var relativeThumbTrailingConstraint: NSLayoutConstraint? - private var thumbHeightConstraint: NSLayoutConstraint? private var thumbWidthConstraint: NSLayoutConstraint? private var heightConstraint: NSLayoutConstraint? private var widthConstraint: NSLayoutConstraint? + private func constrainThumb() { + + self.thumbLeadingConstraint?.isActive = isOn + self.thumbTrailingConstraint?.isActive = !isOn + } + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -186,35 +180,51 @@ import UIKit addSubview(thumbView) - thumbView.heightAnchor.constraint(equalToConstant: Switch.thumbSize.height).isActive = true - thumbView.widthAnchor.constraint(equalToConstant: Switch.thumbSize.width).isActive = true + thumbHeightConstraint = thumbView.heightAnchor.constraint(equalToConstant: Switch.thumbSize.height) + thumbHeightConstraint?.isActive = true + thumbWidthConstraint = thumbView.widthAnchor.constraint(equalToConstant: Switch.thumbSize.width) + thumbWidthConstraint?.isActive = true + thumbView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + thumbView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: thumbView.bottomAnchor).isActive = true thumbTrailingConstraint = trailingAnchor.constraint(equalTo: thumbView.trailingAnchor, constant: 1) - relativeThumbTrailingConstraint = trailingAnchor.constraint(greaterThanOrEqualTo: thumbView.trailingAnchor) - relativeThumbTrailingConstraint?.isActive = true - - relativeThumbLeadingConstraint = thumbView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor) thumbLeadingConstraint = thumbView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) thumbLeadingConstraint?.isActive = true + let longPress = UILongPressGestureRecognizer(target: self, action: #selector(stretchThumb)) + addGestureRecognizer(longPress) + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Switch_buttonlabel") } + @objc private func stretchThumb() { + + UIView.animate(withDuration: 0.1, animations: { + self.thumbWidthConstraint?.constant = 26 + self.layoutIfNeeded() + }) + } + class func getTrackWidth() -> CGFloat { - return (MFSizeObject(standardSize: Switch.trackSize.width, standardiPadPortraitSize: CGFloat(Switch.trackSize.width * 1.5)))!.getValueBasedOnApplicationWidth() + let trackWidth = Switch.trackSize.width + return (MFSizeObject(standardSize: trackWidth, standardiPadPortraitSize: CGFloat(Switch.trackSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? trackWidth } class func getTrackHeight() -> CGFloat { - return (MFSizeObject(standardSize: Switch.trackSize.height, standardiPadPortraitSize: CGFloat(Switch.trackSize.height * 1.5) ))!.getValueBasedOnApplicationWidth() + let trackHeight = Switch.trackSize.height + return (MFSizeObject(standardSize: trackHeight, standardiPadPortraitSize: CGFloat(Switch.trackSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? trackHeight } class func getThumbWidth() -> CGFloat { - return (MFSizeObject(standardSize: Switch.thumbSize.width, standardiPadPortraitSize: CGFloat(Switch.thumbSize.width * 1.5)))!.getValueBasedOnApplicationWidth() + let thumbWidth = Switch.thumbSize.width + return (MFSizeObject(standardSize: thumbWidth, standardiPadPortraitSize: CGFloat(Switch.thumbSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? thumbWidth } class func getThumbHeight() -> CGFloat { - return (MFSizeObject(standardSize: Switch.thumbSize.height, standardiPadPortraitSize: CGFloat(Switch.thumbSize.height * 1.5)))!.getValueBasedOnApplicationWidth() + let thumbHeight = Switch.thumbSize.width + return (MFSizeObject(standardSize: thumbHeight, standardiPadPortraitSize: CGFloat(Switch.thumbSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? thumbHeight } //-------------------------------------------------- @@ -248,22 +258,35 @@ import UIKit return area.contains(point) } - public func touchesBegan(_ touches: Set, with event: UIEvent) { - - UIView.animate(withDuration: 0.1, animations: { - self.thumbWidthConstraint?.constant += PaddingOne - self.layoutIfNeeded() - }) - - sendActions(for: .touchDown) - } - public override func touchesEnded(_ touches: Set, with event: UIEvent?) { thumbReformAnimation() - valueShouldChange = true sendActions(for: .touchUpInside) } + /* + open override func touchesMoved(_ touches: Set, with event: UIEvent?) { + + if shouldTouchToSwitch { + if touchMoves(toLeft: touches) { + thumbMoveAnitmationTo(on: false) + thumbShakeAnitmationTo(on: false) + thumbReformAnimation(true) + } else { + thumbMoveAnitmationTo(on: true) + thumbShakeAnitmationTo(on: true) + thumbReformAnimation(true) + } + } + + if touchIsOutSide(touches) { + sendActions(for: .touchDragOutside) + if !shouldTouchToSwitch { + thumbReformAnimation(true) + } + } else { + sendActions(for: .touchDragInside) + } + }*/ public func touchesCancelled(_ touches: Set, with event: UIEvent) { @@ -347,7 +370,7 @@ extension Switch { } if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { -// actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } + actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } if let isAnimated = dictionary["state"] as? Bool { From d1ee242ca7cadbec92edd495b4bc8e4e39c2f232 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 11 Dec 2019 10:47:52 -0500 Subject: [PATCH 09/54] should did action. --- MVMCoreUI/Atoms/Views/Switch.swift | 84 ++++++++++++++---------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Switch.swift index ee257ce8..3fbe1055 100644 --- a/MVMCoreUI/Atoms/Views/Switch.swift +++ b/MVMCoreUI/Atoms/Views/Switch.swift @@ -9,6 +9,8 @@ import MVMCore import UIKit +public typealias ActionBlockConfirmation = () -> (Bool) + /** A custom implementation of Apple's UISwitch. @@ -26,7 +28,10 @@ import UIKit public var isAnimated = true - private var actionBlock: ActionBlock? + public var didSwitchAction: ActionBlock? + public var shouldSwitchAction: ActionBlockConfirmation? = { + return { return true } + }() // Sizes are from InVision design specs. static let trackSize = CGSize(width: 46, height: 24) @@ -131,15 +136,21 @@ import UIKit self.init(frame: .zero) } - public convenience init(isOn: Bool, actionBlock: ActionBlock?) { + public convenience init(isOn: Bool, didSwitchAction: ActionBlock?) { self.init(frame: .zero) self.isOn = isOn - self.actionBlock = actionBlock + self.didSwitchAction = didSwitchAction } - public convenience init(actionBlock: ActionBlock?) { + public convenience init(didSwitchAction: ActionBlock?) { self.init(frame: .zero) - self.actionBlock = actionBlock + self.didSwitchAction = didSwitchAction + } + + public convenience init(shouldSwitchAction: ActionBlockConfirmation?, didSwitchAction: ActionBlock?) { + self.init(frame: .zero) + self.didSwitchAction = didSwitchAction + self.shouldSwitchAction = shouldSwitchAction } public required init?(coder: NSCoder) { @@ -184,7 +195,6 @@ import UIKit thumbHeightConstraint?.isActive = true thumbWidthConstraint = thumbView.widthAnchor.constraint(equalToConstant: Switch.thumbSize.width) thumbWidthConstraint?.isActive = true - thumbView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true thumbView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true bottomAnchor.constraint(greaterThanOrEqualTo: thumbView.bottomAnchor).isActive = true @@ -193,20 +203,9 @@ import UIKit thumbLeadingConstraint = thumbView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) thumbLeadingConstraint?.isActive = true - let longPress = UILongPressGestureRecognizer(target: self, action: #selector(stretchThumb)) - addGestureRecognizer(longPress) - accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Switch_buttonlabel") } - @objc private func stretchThumb() { - - UIView.animate(withDuration: 0.1, animations: { - self.thumbWidthConstraint?.constant = 26 - self.layoutIfNeeded() - }) - } - class func getTrackWidth() -> CGFloat { let trackWidth = Switch.trackSize.width return (MFSizeObject(standardSize: trackWidth, standardiPadPortraitSize: CGFloat(Switch.trackSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? trackWidth @@ -243,8 +242,11 @@ import UIKit /// This will toggle the state of the Switch and execute the actionBlock if provided. public func toggleAndAction() { - isOn.toggle() - actionBlock?() + + if let result = shouldSwitchAction?(), result { + isOn.toggle() + didSwitchAction?() + } } //-------------------------------------------------- @@ -258,35 +260,27 @@ import UIKit return area.contains(point) } + open override func touchesBegan(_ touches: Set, with event: UIEvent?) { + + UIView.animate(withDuration: 0.1, animations: { + self.thumbWidthConstraint?.constant += PaddingOne + self.layoutIfNeeded() + }) + } + public override func touchesEnded(_ touches: Set, with event: UIEvent?) { thumbReformAnimation() + + guard let coordinates: CGPoint = 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) } - /* - open override func touchesMoved(_ touches: Set, with event: UIEvent?) { - - if shouldTouchToSwitch { - if touchMoves(toLeft: touches) { - thumbMoveAnitmationTo(on: false) - thumbShakeAnitmationTo(on: false) - thumbReformAnimation(true) - } else { - thumbMoveAnitmationTo(on: true) - thumbShakeAnitmationTo(on: true) - thumbReformAnimation(true) - } - } - - if touchIsOutSide(touches) { - sendActions(for: .touchDragOutside) - if !shouldTouchToSwitch { - thumbReformAnimation(true) - } - } else { - sendActions(for: .touchDragInside) - } - }*/ public func touchesCancelled(_ touches: Set, with event: UIEvent) { @@ -320,7 +314,7 @@ extension Switch { } } -// MARK: FormValidationProtocol +// MARK: - FormValidationProtocol extension Switch { public func isValidField() -> Bool { @@ -370,7 +364,7 @@ extension Switch { } if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { - actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } + actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } if let isAnimated = dictionary["state"] as? Bool { From eab910011ecabb3c4b13a28954450cbfec7a7ccb Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 11 Dec 2019 15:10:26 -0500 Subject: [PATCH 10/54] General working order of the Toggle. --- MVMCoreUI.xcodeproj/project.pbxproj | 8 +- .../Views/{Switch.swift => Toggle.swift} | 196 +++++++++--------- .../MVMCoreUIMoleculeMappingObject.m | 2 +- 3 files changed, 102 insertions(+), 104 deletions(-) rename MVMCoreUI/Atoms/Views/{Switch.swift => Toggle.swift} (55%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 33529648..880427d0 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -28,7 +28,7 @@ 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; }; 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; - 0AA33B3A2398524F0067DD0F /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Switch.swift */; }; + 0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Toggle.swift */; }; 943784F5236B77BB006A1E82 /* GraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F3236B77BB006A1E82 /* GraphView.swift */; }; 943784F6236B77BB006A1E82 /* GraphViewAnimationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */; }; 9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */; }; @@ -227,7 +227,7 @@ 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = ""; }; 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; - 0AA33B392398524F0067DD0F /* Switch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Switch.swift; sourceTree = ""; }; + 0AA33B392398524F0067DD0F /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = ""; }; 943784F3236B77BB006A1E82 /* GraphView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphView.swift; sourceTree = ""; }; 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphViewAnimationHandler.swift; sourceTree = ""; }; 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = ""; }; @@ -781,7 +781,7 @@ 01004F2F22721C3800991ECC /* RadioButton.swift */, 943784F3236B77BB006A1E82 /* GraphView.swift */, 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */, - 0AA33B392398524F0067DD0F /* Switch.swift */, + 0AA33B392398524F0067DD0F /* Toggle.swift */, ); path = Views; sourceTree = ""; @@ -1085,7 +1085,7 @@ D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, D29DF25321E6A177003B2FB9 /* MFDigitTextField.m in Sources */, D2B18B7F2360913400A9AEDC /* Control.swift in Sources */, - 0AA33B3A2398524F0067DD0F /* Switch.swift in Sources */, + 0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */, D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */, DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */, D224798C231450C8003FCCF9 /* HeadlineBodySwitch.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Toggle.swift similarity index 55% rename from MVMCoreUI/Atoms/Views/Switch.swift rename to MVMCoreUI/Atoms/Views/Toggle.swift index 3fbe1055..0ee42937 100644 --- a/MVMCoreUI/Atoms/Views/Switch.swift +++ b/MVMCoreUI/Atoms/Views/Toggle.swift @@ -1,5 +1,5 @@ // -// Switch.swift +// Toggle.swift // MVMCoreUI // // Created by Kevin Christiano on 12/4/19. @@ -14,33 +14,33 @@ public typealias ActionBlockConfirmation = () -> (Bool) /** A custom implementation of Apple's UISwitch. - Track: The background of the switch control. - Thumb: The circular indicator that slides on the track. + Container: The background of the toggle control. + Knob: The circular indicator that slides on the container. */ -@objcMembers open class Switch: Control, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol { +@objcMembers open class Toggle: Control, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - public var trackTintColor: (on: UIColor?, off: UIColor?)? = (on: .mfShamrock(), off: .black) - public var thumbTintColor: (on: UIColor?, off: UIColor?)? = (on: .white, off: .white) - public var disabledTintColor: (track: UIColor?, thumb: UIColor?)? = (track: .mfSilver(), thumb: .white) + public var containerTintColor: (on: UIColor?, off: UIColor?)? = (on: .mfShamrock(), off: .black) + public var knobTintColor: (on: UIColor?, off: UIColor?)? = (on: .white, off: .white) + public var disabledTintColor: (container: UIColor?, knob: UIColor?)? = (container: .mfSilver(), knob: .white) public var isAnimated = true - public var didSwitchAction: ActionBlock? - public var shouldSwitchAction: ActionBlockConfirmation? = { + public var didToggleAction: ActionBlock? + public var shouldToggleAction: ActionBlockConfirmation? = { return { return true } }() // Sizes are from InVision design specs. - static let trackSize = CGSize(width: 46, height: 24) - static let thumbSize = CGSize(width: 22, height: 22) + static let containerSize = CGSize(width: 46, height: 24) + static let knobSize = CGSize(width: 22, height: 22) - private var thumbView: View = { + private var knobView: View = { let view = View() view.backgroundColor = .white - view.layer.cornerRadius = Switch.getThumbHeight() / 2.0 + view.layer.cornerRadius = Toggle.getKnobHeight() / 2.0 return view }() @@ -51,8 +51,8 @@ public typealias ActionBlockConfirmation = () -> (Bool) open override var isEnabled: Bool { didSet { isUserInteractionEnabled = isEnabled - backgroundColor = isEnabled ? trackTintColor?.on : disabledTintColor?.track - thumbView.backgroundColor = isEnabled ? thumbTintColor?.on : disabledTintColor?.thumb + backgroundColor = isEnabled ? containerTintColor?.on : disabledTintColor?.container + knobView.backgroundColor = isEnabled ? knobTintColor?.on : disabledTintColor?.knob } } @@ -69,28 +69,25 @@ public typealias ActionBlockConfirmation = () -> (Bool) if isAnimated { UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: { if self.isOn { - self.thumbView.backgroundColor = self.thumbTintColor?.on - self.backgroundColor = self.trackTintColor?.on + self.knobView.backgroundColor = self.knobTintColor?.on + self.backgroundColor = self.containerTintColor?.on } else { - self.thumbView.backgroundColor = self.thumbTintColor?.off - self.backgroundColor = self.trackTintColor?.off + 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.constrainThumb() - self.thumbWidthConstraint?.constant = Switch.getThumbWidth() + self.constrainKnob() + self.knobWidthConstraint?.constant = Toggle.getKnobWidth() self.layoutIfNeeded() - }, completion: nil) } else { - backgroundColor = isOn ? trackTintColor?.on : trackTintColor?.off - thumbView.backgroundColor = isOn ? thumbTintColor?.on : thumbTintColor?.off - self.constrainThumb() + backgroundColor = isOn ? containerTintColor?.on : containerTintColor?.off + knobView.backgroundColor = isOn ? knobTintColor?.on : knobTintColor?.off + self.constrainKnob() } FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) @@ -110,17 +107,17 @@ public typealias ActionBlockConfirmation = () -> (Bool) // MARK: - Constraints //-------------------------------------------------- - private var thumbLeadingConstraint: NSLayoutConstraint? - private var thumbTrailingConstraint: NSLayoutConstraint? - private var thumbHeightConstraint: NSLayoutConstraint? - private var thumbWidthConstraint: NSLayoutConstraint? + 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 constrainThumb() { + private func constrainKnob() { - self.thumbLeadingConstraint?.isActive = isOn - self.thumbTrailingConstraint?.isActive = !isOn + self.knobLeadingConstraint?.isActive = !isOn + self.knobTrailingConstraint?.isActive = isOn } //-------------------------------------------------- @@ -136,26 +133,26 @@ public typealias ActionBlockConfirmation = () -> (Bool) self.init(frame: .zero) } - public convenience init(isOn: Bool, didSwitchAction: ActionBlock?) { + public convenience init(isOn: Bool, didToggleAction: ActionBlock?) { self.init(frame: .zero) self.isOn = isOn - self.didSwitchAction = didSwitchAction + self.didToggleAction = didToggleAction } - public convenience init(didSwitchAction: ActionBlock?) { + public convenience init(didToggleAction: ActionBlock?) { self.init(frame: .zero) - self.didSwitchAction = didSwitchAction + self.didToggleAction = didToggleAction } - public convenience init(shouldSwitchAction: ActionBlockConfirmation?, didSwitchAction: ActionBlock?) { + public convenience init(shouldToggleAction: ActionBlockConfirmation?, didToggleAction: ActionBlock?) { self.init(frame: .zero) - self.didSwitchAction = didSwitchAction - self.shouldSwitchAction = shouldSwitchAction + self.didToggleAction = didToggleAction + self.shouldToggleAction = shouldToggleAction } public required init?(coder: NSCoder) { super.init(coder: coder) - fatalError("Switch does not support xib.") + fatalError("Toggle does not support xib.") } //-------------------------------------------------- @@ -165,14 +162,14 @@ public typealias ActionBlockConfirmation = () -> (Bool) public override func updateView(_ size: CGFloat) { super.updateView(size) - heightConstraint?.constant = Switch.getTrackHeight() - widthConstraint?.constant = Switch.getTrackWidth() + heightConstraint?.constant = Toggle.getContainerHeight() + widthConstraint?.constant = Toggle.getContainerWidth() - thumbHeightConstraint?.constant = Switch.getThumbHeight() - thumbWidthConstraint?.constant = Switch.getThumbWidth() + knobHeightConstraint?.constant = Toggle.getKnobHeight() + knobWidthConstraint?.constant = Toggle.getKnobWidth() - layer.cornerRadius = Switch.getTrackHeight() / 2.0 - thumbView.layer.cornerRadius = Switch.getThumbHeight() / 2.0 + layer.cornerRadius = Toggle.getContainerHeight() / 2.0 + knobView.layer.cornerRadius = Toggle.getKnobHeight() / 2.0 } public override func setupView() { @@ -180,50 +177,50 @@ public typealias ActionBlockConfirmation = () -> (Bool) guard subviews.isEmpty else { return } - heightConstraint = heightAnchor.constraint(equalToConstant: Switch.trackSize.height) + heightConstraint = heightAnchor.constraint(equalToConstant: Toggle.containerSize.height) heightConstraint?.isActive = true - widthConstraint = widthAnchor.constraint(equalToConstant: Switch.trackSize.width) + widthConstraint = widthAnchor.constraint(equalToConstant: Toggle.containerSize.width) widthConstraint?.isActive = true - layer.cornerRadius = Switch.trackSize.height / 2.0 - backgroundColor = trackTintColor?.off + layer.cornerRadius = Toggle.containerSize.height / 2.0 + backgroundColor = containerTintColor?.off - addSubview(thumbView) + addSubview(knobView) - thumbHeightConstraint = thumbView.heightAnchor.constraint(equalToConstant: Switch.thumbSize.height) - thumbHeightConstraint?.isActive = true - thumbWidthConstraint = thumbView.widthAnchor.constraint(equalToConstant: Switch.thumbSize.width) - thumbWidthConstraint?.isActive = true - thumbView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - thumbView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true - bottomAnchor.constraint(greaterThanOrEqualTo: thumbView.bottomAnchor).isActive = true + knobHeightConstraint = knobView.heightAnchor.constraint(equalToConstant: Toggle.knobSize.height) + knobHeightConstraint?.isActive = true + knobWidthConstraint = knobView.widthAnchor.constraint(equalToConstant: Toggle.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 - thumbTrailingConstraint = trailingAnchor.constraint(equalTo: thumbView.trailingAnchor, constant: 1) - thumbLeadingConstraint = thumbView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) - thumbLeadingConstraint?.isActive = true + knobTrailingConstraint = trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 1) + knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) + knobLeadingConstraint?.isActive = true - accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Switch_buttonlabel") + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") } - class func getTrackWidth() -> CGFloat { - let trackWidth = Switch.trackSize.width - return (MFSizeObject(standardSize: trackWidth, standardiPadPortraitSize: CGFloat(Switch.trackSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? trackWidth + class func getContainerWidth() -> CGFloat { + let containerWidth = Toggle.containerSize.width + return (MFSizeObject(standardSize: containerWidth, standardiPadPortraitSize: CGFloat(Toggle.containerSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerWidth } - class func getTrackHeight() -> CGFloat { - let trackHeight = Switch.trackSize.height - return (MFSizeObject(standardSize: trackHeight, standardiPadPortraitSize: CGFloat(Switch.trackSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? trackHeight + class func getContainerHeight() -> CGFloat { + let containerHeight = Toggle.containerSize.height + return (MFSizeObject(standardSize: containerHeight, standardiPadPortraitSize: CGFloat(Toggle.containerSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerHeight } - class func getThumbWidth() -> CGFloat { - let thumbWidth = Switch.thumbSize.width - return (MFSizeObject(standardSize: thumbWidth, standardiPadPortraitSize: CGFloat(Switch.thumbSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? thumbWidth + class func getKnobWidth() -> CGFloat { + let knobWidth = Toggle.knobSize.width + return (MFSizeObject(standardSize: knobWidth, standardiPadPortraitSize: CGFloat(Toggle.knobSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? knobWidth } - class func getThumbHeight() -> CGFloat { - let thumbHeight = Switch.thumbSize.width - return (MFSizeObject(standardSize: thumbHeight, standardiPadPortraitSize: CGFloat(Switch.thumbSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? thumbHeight + class func getKnobHeight() -> CGFloat { + let knobHeight = Toggle.knobSize.width + return (MFSizeObject(standardSize: knobHeight, standardiPadPortraitSize: CGFloat(Toggle.knobSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? knobHeight } //-------------------------------------------------- @@ -240,12 +237,12 @@ public typealias ActionBlockConfirmation = () -> (Bool) toggleAndAction() } - /// This will toggle the state of the Switch and execute the actionBlock if provided. + /// This will toggle the state of the Toggle and execute the actionBlock if provided. public func toggleAndAction() { - if let result = shouldSwitchAction?(), result { + if let result = shouldToggleAction?(), result { isOn.toggle() - didSwitchAction?() + didToggleAction?() } } @@ -263,16 +260,17 @@ public typealias ActionBlockConfirmation = () -> (Bool) open override func touchesBegan(_ touches: Set, with event: UIEvent?) { UIView.animate(withDuration: 0.1, animations: { - self.thumbWidthConstraint?.constant += PaddingOne + self.knobWidthConstraint?.constant += PaddingOne self.layoutIfNeeded() }) } public override func touchesEnded(_ touches: Set, with event: UIEvent?) { - thumbReformAnimation() + knobReformAnimation() - guard let coordinates: CGPoint = touches.first?.location(in: self), + // 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, @@ -284,7 +282,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) public func touchesCancelled(_ touches: Set, with event: UIEvent) { - thumbReformAnimation() + knobReformAnimation() sendActions(for: .touchCancel) } @@ -292,22 +290,23 @@ public typealias ActionBlockConfirmation = () -> (Bool) // MARK: - Animations //-------------------------------------------------- - public func thumbReformAnimation() { + public func knobReformAnimation() { if isAnimated { UIView.animate(withDuration: 0.1, animations: { - self.thumbWidthConstraint?.constant = Switch.getThumbWidth() + self.knobWidthConstraint?.constant = Toggle.getKnobWidth() self.layoutIfNeeded() }, completion: nil) + } else { - thumbWidthConstraint?.constant = Switch.getThumbWidth() + knobWidthConstraint?.constant = Toggle.getKnobWidth() layoutIfNeeded() } } } // MARK: - Accessibility -extension Switch { +extension Toggle { public func formFieldGroupName() -> String? { return json?["groupName"] as? String @@ -315,7 +314,7 @@ extension Switch { } // MARK: - FormValidationProtocol -extension Switch { +extension Toggle { public func isValidField() -> Bool { return isOn && json?["required"] as? Bool ?? false @@ -331,7 +330,7 @@ extension Switch { } // MARK: - MVMCoreUIMoleculeViewProtocol -extension Switch { +extension Toggle { public override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) @@ -342,19 +341,19 @@ extension Switch { guard let dictionary = json else { return } if let color = dictionary["onTintColor"] as? String { - trackTintColor?.on = UIColor.mfGet(forHex: color) + containerTintColor?.on = UIColor.mfGet(forHex: color) } if let color = dictionary["offTintColor"] as? String { - trackTintColor?.off = UIColor.mfGet(forHex: color) + containerTintColor?.off = UIColor.mfGet(forHex: color) } - if let color = dictionary["onThumbTintColor"] as? String { - thumbTintColor?.on = UIColor.mfGet(forHex: color) + if let color = dictionary["onKnobTintColor"] as? String { + knobTintColor?.on = UIColor.mfGet(forHex: color) } - if let color = dictionary["offThumbTintColor"] as? String { - thumbTintColor?.off = UIColor.mfGet(forHex: color) + if let color = dictionary["offKnobTintColor"] as? String { + knobTintColor?.off = UIColor.mfGet(forHex: color) } if let state = dictionary["state"] as? Bool { @@ -364,17 +363,16 @@ extension Switch { } if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { - actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } + didToggleAction = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } - if let isAnimated = dictionary["state"] as? Bool { + if let isAnimated = dictionary["isAnimated"] as? Bool { self.isAnimated = isAnimated } } public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - - return Switch.getTrackHeight() + return Toggle.getContainerHeight() } public func needsToBeConstrained() -> Bool { diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 4923f21d..98df5822 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -48,7 +48,7 @@ @"radioButtonLabel": RadioButtonLabel.class, @"listItem": MoleculeTableViewCell.class, @"accordionListItem": AccordionMoleculeTableViewCell.class, - @"switch": Switch.class, + @"toggle": Toggle.class, @"leftRightLabelView": LeftRightLabelView.class, @"actionDetailWithImage": ActionDetailWithImage.class, @"image": MFLoadImageView.class, From 98997bfa8046cc86d9958e5d8923570c862a28b5 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 11 Dec 2019 15:58:18 -0500 Subject: [PATCH 11/54] Comments, logical updates, refactoring. --- MVMCoreUI/Atoms/Views/Toggle.swift | 34 +++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Toggle.swift b/MVMCoreUI/Atoms/Views/Toggle.swift index 0ee42937..b49104c5 100644 --- a/MVMCoreUI/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atoms/Views/Toggle.swift @@ -14,6 +14,8 @@ public typealias ActionBlockConfirmation = () -> (Bool) /** A custom implementation of Apple's UISwitch. + By default this class begins in the off state. + Container: The background of the toggle control. Knob: The circular indicator that slides on the container. */ @@ -29,6 +31,8 @@ public typealias ActionBlockConfirmation = () -> (Bool) 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 { return true } }() @@ -51,8 +55,9 @@ public typealias ActionBlockConfirmation = () -> (Bool) open override var isEnabled: Bool { didSet { isUserInteractionEnabled = isEnabled - backgroundColor = isEnabled ? containerTintColor?.on : disabledTintColor?.container - knobView.backgroundColor = isEnabled ? knobTintColor?.on : disabledTintColor?.knob + changeStateNoAnimation(isEnabled) + backgroundColor = isEnabled ? containerTintColor?.off : disabledTintColor?.container + knobView.backgroundColor = isEnabled ? knobTintColor?.off : disabledTintColor?.knob } } @@ -116,8 +121,8 @@ public typealias ActionBlockConfirmation = () -> (Bool) private func constrainKnob() { - self.knobLeadingConstraint?.isActive = !isOn - self.knobTrailingConstraint?.isActive = isOn + knobLeadingConstraint?.isActive = !isOn + knobTrailingConstraint?.isActive = isOn } //-------------------------------------------------- @@ -133,17 +138,21 @@ public typealias ActionBlockConfirmation = () -> (Bool) self.init(frame: .zero) } - public convenience init(isOn: Bool, didToggleAction: ActionBlock?) { + public convenience init(isOn: Bool) { self.init(frame: .zero) self.isOn = isOn - self.didToggleAction = didToggleAction } - public convenience init(didToggleAction: ActionBlock?) { + /// - 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 @@ -246,6 +255,13 @@ public typealias ActionBlockConfirmation = () -> (Bool) } } + private func changeStateNoAnimation(_ state: Bool) { + + isAnimated = false + isOn = state + isAnimated = true + } + //-------------------------------------------------- // MARK: - UIResponder //-------------------------------------------------- @@ -357,9 +373,7 @@ extension Toggle { } if let state = dictionary["state"] as? Bool { - isAnimated = false - isOn = state - isAnimated = true + changeStateNoAnimation(state) } if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { From 639330ad5bd56755a03c35cb232c006c02910869 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 11 Dec 2019 16:03:01 -0500 Subject: [PATCH 12/54] More comments. --- MVMCoreUI/Atoms/Views/Toggle.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MVMCoreUI/Atoms/Views/Toggle.swift b/MVMCoreUI/Atoms/Views/Toggle.swift index b49104c5..2d056d24 100644 --- a/MVMCoreUI/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atoms/Views/Toggle.swift @@ -24,10 +24,16 @@ public typealias ActionBlockConfirmation = () -> (Bool) // MARK: - Properties //-------------------------------------------------- + /// Holds the on and off colors for the container. public var containerTintColor: (on: UIColor?, off: UIColor?)? = (on: .mfShamrock(), off: .black) + + /// Holds the on and off colors for the knob. public var knobTintColor: (on: UIColor?, off: UIColor?)? = (on: .white, off: .white) + + /// Holds the on and off colors for the disabled state.. public var disabledTintColor: (container: UIColor?, knob: UIColor?)? = (container: .mfSilver(), knob: .white) + /// Set this flag to false if you do not want to animate state changes. public var isAnimated = true public var didToggleAction: ActionBlock? @@ -61,12 +67,14 @@ public typealias ActionBlockConfirmation = () -> (Bool) } } + /// 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 { isSelected = isOn From 39b752d01b556b9fd0581c7fe2dd728b0920c4f0 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 12 Dec 2019 13:37:18 -0500 Subject: [PATCH 13/54] Included reset function. accounted for isAnimated state when changing isOn without animations. --- MVMCoreUI/Atoms/Views/Toggle.swift | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Toggle.swift b/MVMCoreUI/Atoms/Views/Toggle.swift index 2d056d24..aff1abb3 100644 --- a/MVMCoreUI/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atoms/Views/Toggle.swift @@ -220,6 +220,18 @@ public typealias ActionBlockConfirmation = () -> (Bool) accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") } + public override func reset() { + super.reset() + + backgroundColor = containerTintColor?.off + knobView.backgroundColor = knobTintColor?.off + isAnimated = false + isOn = false + constrainKnob() + didToggleAction = nil + shouldToggleAction = { return true } + } + class func getContainerWidth() -> CGFloat { let containerWidth = Toggle.containerSize.width return (MFSizeObject(standardSize: containerWidth, standardiPadPortraitSize: CGFloat(Toggle.containerSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerWidth @@ -265,9 +277,12 @@ public typealias ActionBlockConfirmation = () -> (Bool) private func changeStateNoAnimation(_ state: Bool) { + // Hold state in case User wanted isAnimated to remain off. + let isAnimatedState = isAnimated + isAnimated = false isOn = state - isAnimated = true + isAnimated = isAnimatedState } //-------------------------------------------------- From 4a49c40b19068747cbfd6e1c3918dd50753458aa Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 8 Jan 2020 10:28:37 -0500 Subject: [PATCH 14/54] Using pointInside from Control. --- MVMCoreUI/Atoms/Views/Toggle.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Toggle.swift b/MVMCoreUI/Atoms/Views/Toggle.swift index aff1abb3..d8b73837 100644 --- a/MVMCoreUI/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atoms/Views/Toggle.swift @@ -289,13 +289,6 @@ public typealias ActionBlockConfirmation = () -> (Bool) // MARK: - UIResponder //-------------------------------------------------- - override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - - let faultTolerance: CGFloat = 20.0 - let area = bounds.insetBy(dx: -faultTolerance, dy: -faultTolerance) - return area.contains(point) - } - open override func touchesBegan(_ touches: Set, with event: UIEvent?) { UIView.animate(withDuration: 0.1, animations: { From 14f49882b1f1c485f492ef9292b478b190a1b0cc Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 8 Jan 2020 10:44:36 -0500 Subject: [PATCH 15/54] Big Self. --- MVMCoreUI/Atoms/Views/Toggle.swift | 47 ++++++++++++++--------------- MVMCoreUI/BaseClasses/Control.swift | 1 + 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Toggle.swift b/MVMCoreUI/Atoms/Views/Toggle.swift index d8b73837..1a2341c2 100644 --- a/MVMCoreUI/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atoms/Views/Toggle.swift @@ -93,7 +93,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.2, options: [], animations: { self.constrainKnob() - self.knobWidthConstraint?.constant = Toggle.getKnobWidth() + self.knobWidthConstraint?.constant = Self.getKnobWidth() self.layoutIfNeeded() }, completion: nil) @@ -128,7 +128,6 @@ public typealias ActionBlockConfirmation = () -> (Bool) private var widthConstraint: NSLayoutConstraint? private func constrainKnob() { - knobLeadingConstraint?.isActive = !isOn knobTrailingConstraint?.isActive = isOn } @@ -179,14 +178,14 @@ public typealias ActionBlockConfirmation = () -> (Bool) public override func updateView(_ size: CGFloat) { super.updateView(size) - heightConstraint?.constant = Toggle.getContainerHeight() - widthConstraint?.constant = Toggle.getContainerWidth() + heightConstraint?.constant = Self.getContainerHeight() + widthConstraint?.constant = Self.getContainerWidth() - knobHeightConstraint?.constant = Toggle.getKnobHeight() - knobWidthConstraint?.constant = Toggle.getKnobWidth() + knobHeightConstraint?.constant = Self.getKnobHeight() + knobWidthConstraint?.constant = Self.getKnobWidth() - layer.cornerRadius = Toggle.getContainerHeight() / 2.0 - knobView.layer.cornerRadius = Toggle.getKnobHeight() / 2.0 + layer.cornerRadius = Self.getContainerHeight() / 2.0 + knobView.layer.cornerRadius = Self.getKnobHeight() / 2.0 } public override func setupView() { @@ -194,20 +193,20 @@ public typealias ActionBlockConfirmation = () -> (Bool) guard subviews.isEmpty else { return } - heightConstraint = heightAnchor.constraint(equalToConstant: Toggle.containerSize.height) + heightConstraint = heightAnchor.constraint(equalToConstant: Self.containerSize.height) heightConstraint?.isActive = true - widthConstraint = widthAnchor.constraint(equalToConstant: Toggle.containerSize.width) + widthConstraint = widthAnchor.constraint(equalToConstant: Self.containerSize.width) widthConstraint?.isActive = true - layer.cornerRadius = Toggle.containerSize.height / 2.0 + layer.cornerRadius = Self.containerSize.height / 2.0 backgroundColor = containerTintColor?.off addSubview(knobView) - knobHeightConstraint = knobView.heightAnchor.constraint(equalToConstant: Toggle.knobSize.height) + knobHeightConstraint = knobView.heightAnchor.constraint(equalToConstant: Self.knobSize.height) knobHeightConstraint?.isActive = true - knobWidthConstraint = knobView.widthAnchor.constraint(equalToConstant: Toggle.knobSize.width) + 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 @@ -233,23 +232,23 @@ public typealias ActionBlockConfirmation = () -> (Bool) } class func getContainerWidth() -> CGFloat { - let containerWidth = Toggle.containerSize.width - return (MFSizeObject(standardSize: containerWidth, standardiPadPortraitSize: CGFloat(Toggle.containerSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerWidth + let containerWidth = Self.containerSize.width + return (MFSizeObject(standardSize: containerWidth, standardiPadPortraitSize: CGFloat(Self.containerSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerWidth } class func getContainerHeight() -> CGFloat { - let containerHeight = Toggle.containerSize.height - return (MFSizeObject(standardSize: containerHeight, standardiPadPortraitSize: CGFloat(Toggle.containerSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerHeight + let containerHeight = Self.containerSize.height + return (MFSizeObject(standardSize: containerHeight, standardiPadPortraitSize: CGFloat(Self.containerSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerHeight } class func getKnobWidth() -> CGFloat { - let knobWidth = Toggle.knobSize.width - return (MFSizeObject(standardSize: knobWidth, standardiPadPortraitSize: CGFloat(Toggle.knobSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? knobWidth + let knobWidth = Self.knobSize.width + return (MFSizeObject(standardSize: knobWidth, standardiPadPortraitSize: CGFloat(Self.knobSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? knobWidth } class func getKnobHeight() -> CGFloat { - let knobHeight = Toggle.knobSize.width - return (MFSizeObject(standardSize: knobHeight, standardiPadPortraitSize: CGFloat(Toggle.knobSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? knobHeight + let knobHeight = Self.knobSize.width + return (MFSizeObject(standardSize: knobHeight, standardiPadPortraitSize: CGFloat(Self.knobSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? knobHeight } //-------------------------------------------------- @@ -326,12 +325,12 @@ public typealias ActionBlockConfirmation = () -> (Bool) if isAnimated { UIView.animate(withDuration: 0.1, animations: { - self.knobWidthConstraint?.constant = Toggle.getKnobWidth() + self.knobWidthConstraint?.constant = Self.getKnobWidth() self.layoutIfNeeded() }, completion: nil) } else { - knobWidthConstraint?.constant = Toggle.getKnobWidth() + knobWidthConstraint?.constant = Self.getKnobWidth() layoutIfNeeded() } } @@ -402,7 +401,7 @@ extension Toggle { } public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return Toggle.getContainerHeight() + return Self.getContainerHeight() } public func needsToBeConstrained() -> Bool { diff --git a/MVMCoreUI/BaseClasses/Control.swift b/MVMCoreUI/BaseClasses/Control.swift index 0b03ba27..5bc80003 100644 --- a/MVMCoreUI/BaseClasses/Control.swift +++ b/MVMCoreUI/BaseClasses/Control.swift @@ -72,6 +72,7 @@ extension Control: MVMCoreViewProtocol { // MARK: - MVMCoreUIMoleculeViewProtocol extension Control: MVMCoreUIMoleculeViewProtocol { + public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { self.json = json From dfa2fa334356f3f655e9fb41e7ddb4e81dc9bd70 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 8 Jan 2020 11:03:44 -0500 Subject: [PATCH 16/54] Ensure that isEnabled only triggers when false. Preventing isEnabled from changing isON. --- MVMCoreUI/Atoms/Views/Toggle.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Toggle.swift b/MVMCoreUI/Atoms/Views/Toggle.swift index 1a2341c2..22956a98 100644 --- a/MVMCoreUI/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atoms/Views/Toggle.swift @@ -61,7 +61,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) open override var isEnabled: Bool { didSet { isUserInteractionEnabled = isEnabled - changeStateNoAnimation(isEnabled) + changeStateNoAnimation(isEnabled ? isOn : false) backgroundColor = isEnabled ? containerTintColor?.off : disabledTintColor?.container knobView.backgroundColor = isEnabled ? knobTintColor?.off : disabledTintColor?.knob } @@ -398,6 +398,10 @@ extension Toggle { if let isAnimated = dictionary["isAnimated"] as? Bool { self.isAnimated = isAnimated } + + if let isEnabled = dictionary["isEnabled"] as? Bool{//}, !isEnabled { + self.isEnabled = isEnabled + } } public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { From a99dd7f0a6169184991aca0518fb3b58bd12de84 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 8 Jan 2020 14:46:40 -0500 Subject: [PATCH 17/54] removed isSelected. --- MVMCoreUI/Atoms/Views/Toggle.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Toggle.swift b/MVMCoreUI/Atoms/Views/Toggle.swift index 22956a98..9d97f5a6 100644 --- a/MVMCoreUI/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atoms/Views/Toggle.swift @@ -77,8 +77,6 @@ public typealias ActionBlockConfirmation = () -> (Bool) /// The state on the toggle. Default value: false. open var isOn: Bool = false { didSet { - isSelected = isOn - if isAnimated { UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: { if self.isOn { From efc163ab38d2f955fd411d3fb7929a634fc445b7 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 8 Jan 2020 15:08:31 -0500 Subject: [PATCH 18/54] no com --- MVMCoreUI/Atoms/Views/Toggle.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Toggle.swift b/MVMCoreUI/Atoms/Views/Toggle.swift index 9d97f5a6..17d286ac 100644 --- a/MVMCoreUI/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atoms/Views/Toggle.swift @@ -397,7 +397,7 @@ extension Toggle { self.isAnimated = isAnimated } - if let isEnabled = dictionary["isEnabled"] as? Bool{//}, !isEnabled { + if let isEnabled = dictionary["isEnabled"] as? Bool{ self.isEnabled = isEnabled } } From 396c22f2434b0c5eb94c42add833adf30b978f2c Mon Sep 17 00:00:00 2001 From: "Murugan, Vimal" Date: Sat, 11 Jan 2020 16:06:25 +0530 Subject: [PATCH 19/54] doughnut chart model added doughnut chart view added doughnut chart added --- MVMCoreUI.xcodeproj/project.pbxproj | 28 ++- .../Models/Molecules/DoughnutChartModel.swift | 85 +++++++++ MVMCoreUI/Molecules/DoughnutChart.swift | 179 ++++++++++++++++++ MVMCoreUI/Molecules/DoughnutChartView.swift | 179 ++++++++++++++++++ .../MVMCoreUIMoleculeMappingObject.m | 3 +- .../OtherHandlers/MoleculeObjectMapping.swift | 1 + 6 files changed, 466 insertions(+), 9 deletions(-) create mode 100644 MVMCoreUI/Models/Molecules/DoughnutChartModel.swift create mode 100644 MVMCoreUI/Molecules/DoughnutChart.swift create mode 100644 MVMCoreUI/Molecules/DoughnutChartView.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index dbee02e2..8efde844 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -67,6 +67,9 @@ 94C2D9A723872DA90006CF46 /* LabelAttributeColorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9A623872DA90006CF46 /* LabelAttributeColorModel.swift */; }; 94C2D9A923872E5E0006CF46 /* LabelAttributeImageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9A823872E5E0006CF46 /* LabelAttributeImageModel.swift */; }; 94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */; }; + C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69323C9909000BFB94E /* DoughnutChartModel.swift */; }; + C695A69623C990BC00BFB94E /* DoughnutChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69523C990BC00BFB94E /* DoughnutChart.swift */; }; + C695A69823C990C200BFB94E /* DoughnutChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69723C990C200BFB94E /* DoughnutChartView.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */; }; @@ -307,6 +310,9 @@ 94C2D9A623872DA90006CF46 /* LabelAttributeColorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeColorModel.swift; sourceTree = ""; }; 94C2D9A823872E5E0006CF46 /* LabelAttributeImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeImageModel.swift; sourceTree = ""; }; 94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeActionModel.swift; sourceTree = ""; }; + C695A69323C9909000BFB94E /* DoughnutChartModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartModel.swift; sourceTree = ""; }; + C695A69523C990BC00BFB94E /* DoughnutChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChart.swift; sourceTree = ""; }; + C695A69723C990C200BFB94E /* DoughnutChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartView.swift; sourceTree = ""; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLayoutConstraintAxis+Extension.swift"; sourceTree = ""; }; @@ -530,6 +536,7 @@ 01EB368723609801006832FA /* Molecules */ = { isa = PBXGroup; children = ( + C695A69323C9909000BFB94E /* DoughnutChartModel.swift */, 01EB368923609801006832FA /* ListItemModel.swift */, 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */, 01EB368B23609801006832FA /* MoleculeStackModel.swift */, @@ -542,6 +549,14 @@ path = Molecules; sourceTree = ""; }; + 0A5D59C323AD488600EFD9E9 /* Protocols */ = { + isa = PBXGroup; + children = ( + 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */, + ); + path = Protocols; + sourceTree = ""; + }; 0AA33B322398134B0067DD0F /* Primitive Models */ = { isa = PBXGroup; children = ( @@ -580,14 +595,6 @@ name = "Recovered References"; sourceTree = ""; }; - 0A5D59C323AD488600EFD9E9 /* Protocols */ = { - isa = PBXGroup; - children = ( - 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */, - ); - path = Protocols; - sourceTree = ""; - }; D213347423842FE3008E41B3 /* Controllers */ = { isa = PBXGroup; children = ( @@ -785,6 +792,8 @@ D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */, 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, 017BEB49236235BA0024EF95 /* ModelMoleculeViewProtocol.swift */, + C695A69523C990BC00BFB94E /* DoughnutChart.swift */, + C695A69723C990C200BFB94E /* DoughnutChartView.swift */, ); path = Molecules; sourceTree = ""; @@ -1296,6 +1305,7 @@ D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */, D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */, D29DF24D21E6A177003B2FB9 /* MFTextField.m in Sources */, + C695A69623C990BC00BFB94E /* DoughnutChart.swift in Sources */, 94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */, 017BEB4023620A230024EF95 /* TextFieldModel.swift in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, @@ -1396,6 +1406,7 @@ D2B18B922361E65A00A9AEDC /* CoreUIObject.swift in Sources */, D29DF2BE21E7BEA4003B2FB9 /* TopTabbar.m in Sources */, D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */, + C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */, D29DF32421ED0DA2003B2FB9 /* TextButtonView.m in Sources */, D29DF29E21E7AE3B003B2FB9 /* MFStyler.m in Sources */, D2A514592211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m in Sources */, @@ -1404,6 +1415,7 @@ 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */, 01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */, D22D1F1B220341F60077CEC0 /* MVMCoreUICheckBox.m in Sources */, + C695A69823C990C200BFB94E /* DoughnutChartView.swift in Sources */, D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */, 946EE1BA237B66D80036751F /* ModelHelper.swift in Sources */, 01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */, diff --git a/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift b/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift new file mode 100644 index 00000000..f6a1efb8 --- /dev/null +++ b/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift @@ -0,0 +1,85 @@ +// +// DoughnutChartModel.swift +// MVMCoreUI +// +// Created by Murugan, Vimal on 10/01/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class DoughnutChartModel: MoleculeProtocol { + + public var backgroundColor: String? + public static var identifier: String = "doughnutChart" + public var title: LabelModel? + public var subtitle: LabelModel? + public var sections: [ChartItemModel] + public var lineWidth: CGFloat = 30.0 + public var lineGap: CGFloat = 0.05 //For 3px gap + public var spaceRequired = true + + enum CodingKeys: String, CodingKey { + case list + case title + case subtitle + case sections + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + self.title = try typeContainer.decodeMoleculeIfPresent(codingKey: .title) as? LabelModel + self.subtitle = try typeContainer.decodeMoleculeIfPresent(codingKey: .subtitle) as? LabelModel + //self.sections = try typeContainer.decodeMolecules(codingKey: .sections) as! [ChartItemModel] + + //TODO: Hasve to check with scott + self.sections = [] + guard var container = try? typeContainer.nestedUnkeyedContainer(forKey: .sections) else { + return + } + var sections = [ChartItemModel]() + while !container.isAtEnd { + if let element = try container + .decodeIfPresent(ChartItemModel.self) { + sections.append(element) + } + } + self.sections = sections + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeModelIfPresent(title, forKey: .title) + try container.encodeModelIfPresent(subtitle, forKey: .subtitle) + try container.encodeModels(sections as [Model], forKey: .sections) + } +} + + +@objcMembers public class ChartItemModel: MoleculeProtocol { + public var backgroundColor: String? + public var label: LabelModel + public var percent: CGFloat + public var color: String + public static var identifier: String = "chartItem" + + enum CodingKeys: String, CodingKey { + case label + case percent + case color + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + self.label = try typeContainer.decodeMolecule(codingKey: .label) as! LabelModel + self.percent = try typeContainer.decode(CGFloat.self, forKey: .percent) + self.color = try typeContainer.decode(String.self, forKey: .color) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeModel(label, forKey: .label) + try container.encode(percent, forKey: .percent) + try container.encode(color, forKey: .color) + } +} diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/DoughnutChart.swift new file mode 100644 index 00000000..08bdefb1 --- /dev/null +++ b/MVMCoreUI/Molecules/DoughnutChart.swift @@ -0,0 +1,179 @@ +// +// DoughnutChart.swift +// MVMCoreUI +// +// Created by Murugan, Vimal on 07/01/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { + + var containerView = MVMCoreUICommonViewsUtility.commonView() + var containerLayer = CALayer() + var titleLabel = Label.commonLabelH3(true) + var subTitleLabel = Label.commonLabelB2(true) + var doughnutChartModel: DoughnutChartModel? { + get { return model as? DoughnutChartModel } + } + var labelContainer = ViewConstrainingView.empty() + var heightConstraint: NSLayoutConstraint? + + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 150)! + //private var lineSizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 30)! + + var height: CGFloat = 150 { + didSet { + if height != oldValue { + sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)! + updateContainer() + drawGraph() + } + } + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + titleLabel.updateView(size) + subTitleLabel.updateView(size) + updateContainer() + drawGraph() + } + + open override func reset() { + super.reset() + titleLabel.reset() + subTitleLabel.reset() + clearLayers() + } + + public func setAsMolecule() { + titleLabel.setAsMolecule() + subTitleLabel.setAsMolecule() + } + + open override func setupView() { + super.setupView() + guard containerView.superview == nil else { + return + } + addSubview(containerView) + addSubview(labelContainer) + + labelContainer.addSubview(titleLabel) + labelContainer.addSubview(subTitleLabel) + titleLabel.textAlignment = .center + subTitleLabel.textAlignment = .center + + //Make label font size to adjust width if label content is high + titleLabel.numberOfLines = 1 + titleLabel.adjustsFontSizeToFitWidth = true + + containerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + containerView.topAnchor.constraint(equalTo: topAnchor).isActive = true + bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true + trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true + containerView.layer.addSublayer(containerLayer) + heightConstraint = containerView.heightAnchor.constraint(equalToConstant: + sizeObject.getValueBasedOnApplicationWidth()) + heightConstraint?.isActive = true + containerView.widthAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true + + labelContainer.leftPin = labelContainer.leftAnchor.constraint(greaterThanOrEqualTo: containerView.leftAnchor, constant: lineWidth()) + labelContainer.leftPin?.isActive = true + labelContainer.topPin = labelContainer.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor, constant: lineWidth()) + labelContainer.topPin?.isActive = true + labelContainer.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true + labelContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true + + NSLayoutConstraint.constraintPinSubview(titleLabel, pinTop: true, pinBottom: false, pinLeft: true, pinRight: true) + NSLayoutConstraint.constraintPinSubview(subTitleLabel, pinTop: false, pinBottom: true, pinLeft: true, pinRight: true) + _ = NSLayoutConstraint(pinFirstView: titleLabel, toSecondView: subTitleLabel, withConstant: 0, directionVertical: true) + //Rotate view for initial draw + containerLayer.transform = CATransform3DMakeRotation(1 * .pi, 0.0, 0.0, 1.0) + + } + + open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + super.setWithModel(model, delegateObject, additionalData) + clearLayers() + guard let doughnutChartModel = model as? DoughnutChartModel else { + return + } + titleLabel.setWithModel(doughnutChartModel.title, delegateObject, additionalData) + subTitleLabel.setWithModel(doughnutChartModel.subtitle, delegateObject, additionalData) + titleLabel.textAlignment = .center + subTitleLabel.textAlignment = .center + //lineSizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: doughnutChartModel.lineWidth)! + updateLabelContainer() + drawGraph() + } + + func drawGraph() { + clearLayers() + let widthHeight = sizeObject.getValueBasedOnApplicationWidth() + containerLayer.frame = CGRect(x: 0, y: 0, + width: widthHeight, + height: widthHeight) + if let doughnutChart = doughnutChartModel { + var prevPercent: CGFloat = 0.0 + //If Single item in array. We don't need gap + doughnutChartModel?.lineGap = (doughnutChart.sections.count == 1) ? 0.0 : doughnutChart.lineGap + for model in doughnutChart.sections { + prevPercent += drawBar(model, prevPercent, doughnutChart.spaceRequired, doughnutChart.lineGap) + } + } + } + + func drawBar(_ chartModel: ChartItemModel, _ prevPercent: CGFloat, _ spaceRequired: Bool, _ lineGap: CGFloat) -> CGFloat { + + let shapeLayer = CAShapeLayer() + shapeLayer.frame = containerLayer.frame + shapeLayer.lineWidth = lineWidth() + //lineSizeObject.getValueBasedOnApplicationWidth() + shapeLayer.fillColor = nil + shapeLayer.strokeColor = UIColor.mfGet(forHex: chartModel.color).cgColor + + let arcCenter = shapeLayer.position + let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth()/2.0 + //lineSizeObject.getValueBasedOnApplicationWidth() / 2.0 + let clockwise = true + + let value: CGFloat = chartModel.percent + let gap: CGFloat = spaceRequired ? lineGap : 0.0 + let startAngle = ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi) + let endAngle = ((value / 100.0) * 2 * .pi) + startAngle - gap + let circlePath = UIBezierPath(arcCenter: arcCenter, + radius: radius, + startAngle: startAngle, + endAngle: endAngle, + clockwise: clockwise) + + shapeLayer.path = circlePath.cgPath + containerLayer.addSublayer(shapeLayer) + return value + } + + func clearLayers() { + containerLayer.sublayers?.forEach({ $0.removeFromSuperlayer() }) + } + + func updateContainer() { + heightConstraint?.constant = sizeObject.getValueBasedOnApplicationWidth() + updateLabelContainer() + setNeedsDisplay() + } + + func lineWidth() -> CGFloat { + return doughnutChartModel?.lineWidth ?? 30 + //lineSizeObject.getValueBasedOnApplicationWidth() + 5 + } + + func updateLabelContainer() { + labelContainer.leftPin?.constant = lineWidth() + labelContainer.topPin?.constant = lineWidth() + labelContainer.setNeedsDisplay() + } + +} diff --git a/MVMCoreUI/Molecules/DoughnutChartView.swift b/MVMCoreUI/Molecules/DoughnutChartView.swift new file mode 100644 index 00000000..ebe77200 --- /dev/null +++ b/MVMCoreUI/Molecules/DoughnutChartView.swift @@ -0,0 +1,179 @@ +// +// DoughnutChartView.swift +// MVMCoreUI +// +// Created by Murugan, Vimal on 26/12/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class DoughnutChartView: View { + + var doughnutChart = DoughnutChart(frame: CGRect.zero) + var colorLablesStack = ColorViewLabelsStack() + var container = MVMCoreUICommonViewsUtility.commonView() + var doughnutChartModel: DoughnutChartModel? { + get { return model as? DoughnutChartModel } + } + + open override func setupView() { + super.setupView() + guard container.superview == nil else { + return + } + + addSubview(container) + doughnutChart.translatesAutoresizingMaskIntoConstraints = false + container.addSubview(doughnutChart) + colorLablesStack.translatesAutoresizingMaskIntoConstraints = false + container.addSubview(colorLablesStack) + + NSLayoutConstraint.constraintPinSubview(container, pinTop: true, pinBottom: true, pinLeft: true, pinRight: true) + doughnutChart.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true + doughnutChart.topAnchor.constraint(equalTo: container.topAnchor, constant: PaddingFour).isActive = true + container.bottomAnchor.constraint(greaterThanOrEqualTo: doughnutChart.bottomAnchor).isActive = true + + let containerBottomAnchor = container.bottomAnchor.constraint(equalTo: doughnutChart.bottomAnchor) + containerBottomAnchor.priority = UILayoutPriority(rawValue: 200) + containerBottomAnchor.isActive = true + + let colorLablesBottomAnchor = container.bottomAnchor.constraint(equalTo: colorLablesStack.bottomAnchor) + colorLablesBottomAnchor.priority = UILayoutPriority(rawValue: 204) + colorLablesBottomAnchor.isActive = true + + let colorLablesTopAnchor = colorLablesStack.topAnchor.constraint(equalTo: doughnutChart.topAnchor) + colorLablesTopAnchor.priority = UILayoutPriority(rawValue: 204) + colorLablesTopAnchor.isActive = true + + colorLablesStack.topAnchor.constraint(greaterThanOrEqualTo: doughnutChart.topAnchor).isActive = true + container.bottomAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.bottomAnchor).isActive = true + container.trailingAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.trailingAnchor).isActive = true + + let centerY = colorLablesStack.centerYAnchor.constraint(equalTo: doughnutChart.centerYAnchor) + centerY.priority = .defaultLow + centerY.isActive = true + + colorLablesStack.leadingAnchor.constraint(equalTo: doughnutChart.trailingAnchor, constant: PaddingThree).isActive = true + colorLablesStack.backgroundColor = UIColor.clear + + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + doughnutChart.updateView(size) + colorLablesStack.updateView(size) + } + + open override func reset() { + super.reset() + colorLablesStack.reset() + } + + open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + super.setWithModel(model, delegateObject, additionalData) + doughnutChart.clearLayers() + colorLablesStack.removeAllItemViews() + doughnutChart.setWithModel(model, delegateObject, additionalData) + colorLablesStack.setWithModel(model, delegateObject, additionalData) + } + + open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + if model == nil { + let data = try! JSONSerialization.data(withJSONObject: json!) + let decoder = JSONDecoder() + let model = try! decoder.decode(DoughnutChartModel.self, from: data) + setWithModel(model, delegateObject, additionalData as? [String : AnyHashable]) + } else { + setWithModel(model, delegateObject, additionalData as? [String : AnyHashable]) + } + } + +} + +class ColorViewLabelsStack: MoleculeStackView { + + var dougnutChartModel: DoughnutChartModel? + + override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + let previousModel = self.dougnutChartModel + removeAllItemViews() + guard let dougnutChartModel = model as? DoughnutChartModel else { + return + } + var items: [StackItem]? + if previousModel?.sections.count == dougnutChartModel.sections.count { + items = stackItems + } + stackItems = [] + self.model = MoleculeStackModel(molecules: []) + for (index, chartItemModel) in dougnutChartModel.sections.enumerated() { + let colorViewWithLabel = items?[index].view as? ColorViewWithLabel ?? ColorViewWithLabel() + colorViewWithLabel.setWithModel(chartItemModel, delegateObject, additionalData) + addView(colorViewWithLabel, lastItem: index == dougnutChartModel.sections.count - 1) + } + self.dougnutChartModel = dougnutChartModel + restack() + } +} + +class ColorViewWithLabel: View, MVMCoreUIViewConstrainingProtocol { + + var label = Label.commonLabelB2(true) + var colorView = MVMCoreUICommonViewsUtility.commonView() + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 8)! + var heightConstraint: NSLayoutConstraint? + + override func setupView() { + super.setupView() + guard colorView.superview == nil else { + return + } + translatesAutoresizingMaskIntoConstraints = false + addSubview(colorView) + addSubview(label) + + heightConstraint = colorView.heightAnchor.constraint(equalToConstant: sizeObject.getValueBasedOnApplicationWidth()) + heightConstraint?.isActive = true + colorView.widthAnchor.constraint(equalTo: colorView.heightAnchor).isActive = true + colorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + colorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + label.leadingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: PaddingOne).isActive = true + label.topAnchor.constraint(equalTo: topAnchor).isActive = true + trailingAnchor.constraint(greaterThanOrEqualTo: label.trailingAnchor).isActive = true + bottomAnchor.constraint(equalTo: label.bottomAnchor).isActive = true + + } + + override func updateView(_ size: CGFloat) { + super.updateView(size) + label.updateView(size) + heightConstraint?.constant = sizeObject.getValueBased(onSize: size) + setNeedsDisplay() + } + + override func reset() { + super.reset() + label.reset() + } + + func setAsMolecule() { + label.setAsMolecule() + } + + override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { + super.setWithModel(model, delegateObject, additionalData) + guard let chartItemModel = model as? ChartItemModel else { + return + } + label.setWithModel(chartItemModel.label, delegateObject, additionalData) + colorView.backgroundColor = UIColor.mfGet(forHex: chartItemModel.color) + } + + override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + Label.setUILabel(label, withJSON: json?.optionalDictionaryForKey("label"), delegate: delegateObject, additionalData: additionalData) + colorView.backgroundColor = UIColor.mfGet(forHex: json?.optionalStringForKey("color") ?? "#000000") + } + +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index c656ab2f..4b597288 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -68,7 +68,8 @@ @"dropDownListItem": DropDownFilterTableViewCell.class, @"headlineBodyButton": HeadlineBodyButton.class, @"eyebrowHeadlineBodyLink": EyebrowHeadlineBodyLink.class, - @"stackItem": StackItem.class + @"stackItem": StackItem.class, + @"doughnutChart": DoughnutChartView.class } mutableCopy]; }); return mapping; diff --git a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift index ac96c44f..651d18a7 100644 --- a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift +++ b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift @@ -29,5 +29,6 @@ import Foundation ModelRegistry.register(LabelAttributeUnderlineModel.self) ModelRegistry.register(LabelAttributeStrikeThroughModel.self) ModelRegistry.register(LabelAttributeActionModel.self) + ModelRegistry.register(DoughnutChartModel.self) } } From be094b3cf91879c7d654d3b25c97ce585e3cb2e0 Mon Sep 17 00:00:00 2001 From: "Murugan, Vimal" Date: Mon, 13 Jan 2020 22:49:51 +0530 Subject: [PATCH 20/54] review comments updated --- .../Models/Molecules/DoughnutChartModel.swift | 61 +------------------ MVMCoreUI/Molecules/DoughnutChart.swift | 21 ++++--- MVMCoreUI/Molecules/DoughnutChartView.swift | 5 -- 3 files changed, 14 insertions(+), 73 deletions(-) diff --git a/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift b/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift index f6a1efb8..15ecab85 100644 --- a/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift +++ b/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift @@ -9,50 +9,13 @@ import Foundation @objcMembers public class DoughnutChartModel: MoleculeProtocol { - public var backgroundColor: String? public static var identifier: String = "doughnutChart" public var title: LabelModel? public var subtitle: LabelModel? public var sections: [ChartItemModel] - public var lineWidth: CGFloat = 30.0 - public var lineGap: CGFloat = 0.05 //For 3px gap - public var spaceRequired = true - - enum CodingKeys: String, CodingKey { - case list - case title - case subtitle - case sections - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - self.title = try typeContainer.decodeMoleculeIfPresent(codingKey: .title) as? LabelModel - self.subtitle = try typeContainer.decodeMoleculeIfPresent(codingKey: .subtitle) as? LabelModel - //self.sections = try typeContainer.decodeMolecules(codingKey: .sections) as! [ChartItemModel] - - //TODO: Hasve to check with scott - self.sections = [] - guard var container = try? typeContainer.nestedUnkeyedContainer(forKey: .sections) else { - return - } - var sections = [ChartItemModel]() - while !container.isAtEnd { - if let element = try container - .decodeIfPresent(ChartItemModel.self) { - sections.append(element) - } - } - self.sections = sections - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeModelIfPresent(title, forKey: .title) - try container.encodeModelIfPresent(subtitle, forKey: .subtitle) - try container.encodeModels(sections as [Model], forKey: .sections) - } + public var lineWidth: CGFloat? + public var spaceRequired: Bool? } @@ -62,24 +25,4 @@ import Foundation public var percent: CGFloat public var color: String public static var identifier: String = "chartItem" - - enum CodingKeys: String, CodingKey { - case label - case percent - case color - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - self.label = try typeContainer.decodeMolecule(codingKey: .label) as! LabelModel - self.percent = try typeContainer.decode(CGFloat.self, forKey: .percent) - self.color = try typeContainer.decode(String.self, forKey: .color) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeModel(label, forKey: .label) - try container.encode(percent, forKey: .percent) - try container.encode(color, forKey: .color) - } } diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/DoughnutChart.swift index 08bdefb1..ac19af7d 100644 --- a/MVMCoreUI/Molecules/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/DoughnutChart.swift @@ -21,7 +21,6 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { var heightConstraint: NSLayoutConstraint? private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 150)! - //private var lineSizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 30)! var height: CGFloat = 150 { didSet { @@ -105,7 +104,6 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { subTitleLabel.setWithModel(doughnutChartModel.subtitle, delegateObject, additionalData) titleLabel.textAlignment = .center subTitleLabel.textAlignment = .center - //lineSizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: doughnutChartModel.lineWidth)! updateLabelContainer() drawGraph() } @@ -118,20 +116,17 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { height: widthHeight) if let doughnutChart = doughnutChartModel { var prevPercent: CGFloat = 0.0 - //If Single item in array. We don't need gap - doughnutChartModel?.lineGap = (doughnutChart.sections.count == 1) ? 0.0 : doughnutChart.lineGap for model in doughnutChart.sections { - prevPercent += drawBar(model, prevPercent, doughnutChart.spaceRequired, doughnutChart.lineGap) + prevPercent += drawBar(model, prevPercent) } } } - func drawBar(_ chartModel: ChartItemModel, _ prevPercent: CGFloat, _ spaceRequired: Bool, _ lineGap: CGFloat) -> CGFloat { + func drawBar(_ chartModel: ChartItemModel, _ prevPercent: CGFloat) -> CGFloat { let shapeLayer = CAShapeLayer() shapeLayer.frame = containerLayer.frame shapeLayer.lineWidth = lineWidth() - //lineSizeObject.getValueBasedOnApplicationWidth() shapeLayer.fillColor = nil shapeLayer.strokeColor = UIColor.mfGet(forHex: chartModel.color).cgColor @@ -141,7 +136,7 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { let clockwise = true let value: CGFloat = chartModel.percent - let gap: CGFloat = spaceRequired ? lineGap : 0.0 + let gap: CGFloat = spaceReuired() ? lineGap() : 0.0 let startAngle = ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi) let endAngle = ((value / 100.0) * 2 * .pi) + startAngle - gap let circlePath = UIBezierPath(arcCenter: arcCenter, @@ -167,7 +162,15 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { func lineWidth() -> CGFloat { return doughnutChartModel?.lineWidth ?? 30 - //lineSizeObject.getValueBasedOnApplicationWidth() + 5 + } + + func lineGap() -> CGFloat { + //If array is having the single item then no space required + return doughnutChartModel?.sections.count == 1 ? 0.0 : 0.05 + } + + func spaceReuired() -> Bool { + return doughnutChartModel?.spaceRequired ?? true } func updateLabelContainer() { diff --git a/MVMCoreUI/Molecules/DoughnutChartView.swift b/MVMCoreUI/Molecules/DoughnutChartView.swift index ebe77200..95e23440 100644 --- a/MVMCoreUI/Molecules/DoughnutChartView.swift +++ b/MVMCoreUI/Molecules/DoughnutChartView.swift @@ -171,9 +171,4 @@ class ColorViewWithLabel: View, MVMCoreUIViewConstrainingProtocol { colorView.backgroundColor = UIColor.mfGet(forHex: chartItemModel.color) } - override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - Label.setUILabel(label, withJSON: json?.optionalDictionaryForKey("label"), delegate: delegateObject, additionalData: additionalData) - colorView.backgroundColor = UIColor.mfGet(forHex: json?.optionalStringForKey("color") ?? "#000000") - } - } From 979718d4b8836e69323dcf24146b75dcc5d6cd79 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 11:56:50 -0500 Subject: [PATCH 21/54] Defaults, percent fix --- MVMCoreUI/Models/Molecules/FooterModel.swift | 22 +++++++++++++ MVMCoreUI/Models/Molecules/HeaderModel.swift | 29 ++++++++++++----- MVMCoreUI/Molecules/Items/ListItemModel.swift | 31 ++++++++++++++----- .../Molecules/Items/StackItemModel.swift | 8 ++--- MVMCoreUI/Organisms/MoleculeStackView.swift | 8 ++--- 5 files changed, 75 insertions(+), 23 deletions(-) diff --git a/MVMCoreUI/Models/Molecules/FooterModel.swift b/MVMCoreUI/Models/Molecules/FooterModel.swift index 757e2fd5..e31ec1fc 100644 --- a/MVMCoreUI/Models/Molecules/FooterModel.swift +++ b/MVMCoreUI/Models/Molecules/FooterModel.swift @@ -16,11 +16,33 @@ import Foundation enum FooterCodingKeys: String, CodingKey { case backgroundColor } + + /// Defaults to set + func setDefaults() { + if useHorizontalMargins == nil { + useHorizontalMargins = true + } + if useVerticalMargins == nil { + useVerticalMargins = true + } + if topMarginPadding == nil { + topMarginPadding = PaddingDefaultVerticalSpacing + } + if bottomMarginPadding == nil { + bottomMarginPadding = PaddingDefaultVerticalSpacing + } + } + + public override init(with moleculeModel: MoleculeProtocol) { + super.init(with: moleculeModel) + setDefaults() + } required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: FooterCodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) try super.init(from: decoder) + setDefaults() } public override func encode(to encoder: Encoder) throws { diff --git a/MVMCoreUI/Models/Molecules/HeaderModel.swift b/MVMCoreUI/Models/Molecules/HeaderModel.swift index 1e4ab9f2..4499d313 100644 --- a/MVMCoreUI/Models/Molecules/HeaderModel.swift +++ b/MVMCoreUI/Models/Molecules/HeaderModel.swift @@ -18,19 +18,34 @@ import Foundation case line case backgroundColor } - - required public init(from decoder: Decoder) throws { - try super.init(from: decoder) - let typeContainer = try decoder.container(keyedBy: HeaderCodingKeys.self) - line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) - - // Default Values + + /// Defaults to set + func setDefaults() { + if useHorizontalMargins == nil { + useHorizontalMargins = true + } + if useVerticalMargins == nil { + useVerticalMargins = true + } if topMarginPadding == nil { topMarginPadding = PaddingDefaultVerticalSpacing } if bottomMarginPadding == nil { bottomMarginPadding = PaddingDefaultVerticalSpacing } + line?.type = .heavy + } + + public override init(with moleculeModel: MoleculeProtocol) { + super.init(with: moleculeModel) + setDefaults() + } + + required public init(from decoder: Decoder) throws { + try super.init(from: decoder) + let typeContainer = try decoder.container(keyedBy: HeaderCodingKeys.self) + line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) + setDefaults() } public override func encode(to encoder: Encoder) throws { diff --git a/MVMCoreUI/Molecules/Items/ListItemModel.swift b/MVMCoreUI/Molecules/Items/ListItemModel.swift index 24cb05e0..cd0b1dc3 100644 --- a/MVMCoreUI/Molecules/Items/ListItemModel.swift +++ b/MVMCoreUI/Molecules/Items/ListItemModel.swift @@ -24,6 +24,28 @@ import MVMCore case line case style } + + /// Defaults to set + func setDefaults() { + style = "standard" + if useHorizontalMargins == nil { + useHorizontalMargins = true + } + if useVerticalMargins == nil { + useVerticalMargins = true + } + if topMarginPadding == nil { + topMarginPadding = 24 + } + if bottomMarginPadding == nil { + bottomMarginPadding = 24 + } + } + + public override init(with moleculeModel: MoleculeProtocol) { + super.init(with: moleculeModel) + setDefaults() + } required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: ListItemCodingKeys.self) @@ -34,15 +56,8 @@ import MVMCore if let style = try typeContainer.decodeIfPresent(String.self, forKey: .style) { self.style = style } - try super.init(from: decoder) - - if useHorizontalMargins == nil { - useHorizontalMargins = true - } - if useVerticalMargins == nil { - useVerticalMargins = true - } + setDefaults() } public override func encode(to encoder: Encoder) throws { diff --git a/MVMCoreUI/Molecules/Items/StackItemModel.swift b/MVMCoreUI/Molecules/Items/StackItemModel.swift index a5128f1b..d19f2401 100644 --- a/MVMCoreUI/Molecules/Items/StackItemModel.swift +++ b/MVMCoreUI/Molecules/Items/StackItemModel.swift @@ -12,12 +12,12 @@ import Foundation public static var identifier: String = "stackItem" public var backgroundColor: Color? public var spacing: CGFloat? - public var percentage: Int? = 0 + public var percent: Int? public var gone: Bool = false enum MoleculeStackItemCodingKeys: String, CodingKey { case spacing - case percentage + case percent case gone } @@ -28,7 +28,7 @@ import Foundation required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: MoleculeStackItemCodingKeys.self) spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing) - percentage = try typeContainer.decodeIfPresent(Int.self, forKey: .percentage) + percent = try typeContainer.decodeIfPresent(Int.self, forKey: .percent) if let gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) { self.gone = gone } @@ -39,7 +39,7 @@ import Foundation try super.encode(to: encoder) var container = encoder.container(keyedBy: MoleculeStackItemCodingKeys.self) try container.encodeIfPresent(spacing, forKey: .spacing) - try container.encodeIfPresent(percentage, forKey: .percentage) + try container.encodeIfPresent(percent, forKey: .percent) try container.encode(gone, forKey: .gone) } } diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 5345549c..1783e19a 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -222,8 +222,8 @@ open class MoleculeStackView: Container { stackItem.translatesAutoresizingMaskIntoConstraints = false let spacing = model.spacing ?? stackModel.spacing - let verticalAlignment = model.verticalAlignment ?? (stackItem.view as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() ?? (model.percentage == nil && stackModel.axis == .vertical ? .fill : (stackModel.axis == .vertical ? .leading : .center)) - let horizontalAlignment = model.horizontalAlignment ?? (stackItem.view as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() ?? (stackModel.axis == .vertical || model.percentage == nil ? .fill : .leading) + let verticalAlignment = model.verticalAlignment ?? (stackItem.view as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() ?? (model.percent == nil && stackModel.axis == .vertical ? .fill : (stackModel.axis == .vertical ? .leading : .center)) + let horizontalAlignment = model.horizontalAlignment ?? (stackItem.view as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() ?? (stackModel.axis == .vertical || model.percent == nil ? .fill : .leading) stackItem.containerHelper.alignHorizontal(horizontalAlignment) stackItem.containerHelper.alignVertical(verticalAlignment) @@ -238,7 +238,7 @@ open class MoleculeStackView: Container { } pinView(stackItem, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: 0) pinView(contentView, toView: stackItem, attribute: .trailing, relation: .equal, priority: .required, constant: 0) - if let percent = model.percentage { + if let percent = model.percent { stackItem.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: CGFloat(percent)/100.0).isActive = true } if lastItem { @@ -255,7 +255,7 @@ open class MoleculeStackView: Container { } pinView(stackItem, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0) pinView(contentView, toView: stackItem, attribute: .bottom, relation: .equal, priority: .required, constant: 0) - if let percent = model.percentage { + if let percent = model.percent { stackItem.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: CGFloat(percent)/100.0).isActive = true } if lastItem { From a2046036164c05d895b1a127bbe59858aae7a580 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 12:19:26 -0500 Subject: [PATCH 22/54] temp corner labels fix --- MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift b/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift index bde3ab6f..5c0dbfcf 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift @@ -8,7 +8,7 @@ import UIKit -@objcMembers public class CornerLabels: ViewConstrainingView { +@objcMembers public class CornerLabels: ViewConstrainingView, ModelMoleculeViewProtocol { let topLeftLabel = Label.commonLabelB1(true) let topRightLabel = Label.commonLabelB1(true) @@ -177,9 +177,7 @@ import UIKit public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 34 } -} -extension CornerLabels: MoleculeViewProtocol { public func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let model = model as? CornerLabelsModel, let data = try? model.encode(using: JSONEncoder()), From 3d2fd02ac54a4d3011c4298735da6b7f78742d08 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 13:58:18 -0500 Subject: [PATCH 23/54] undo view constraining removal code fix molecule table cell fix corner labels --- MVMCoreUI/Atoms/Views/ViewConstrainingView.m | 39 +++++++++++ MVMCoreUI/BaseClasses/View.swift | 1 + .../Items/MoleculeTableViewCell.swift | 11 ++-- .../LeftRightViews/CornerLabels.swift | 66 ++++++++----------- MVMCoreUI/Molecules/MoleculeContainer.swift | 7 +- 5 files changed, 80 insertions(+), 44 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m index 54a12612..b98e72a5 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m @@ -352,6 +352,45 @@ } - (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { + // Only treated as a container if we are constraining a molecule. + if (!self.constrainedView) { + [super setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; + } + if (self.shouldSetupMoleculeFromJSON) { + NSDictionary *moleculeJSON = [json dict:KeyMolecule]; + if (self.molecule) { + [self.molecule setWithJSON:moleculeJSON delegateObject:delegateObject additionalData:additionalData]; + } else if (moleculeJSON) { + UIView *molecule = [[MVMCoreUIMoleculeMappingObject sharedMappingObject] createMoleculeForJSON:moleculeJSON delegateObject:delegateObject constrainIfNeeded:true]; + if (molecule) { + [self addMolecule:molecule]; + } + self.molecule = molecule; + [self setMoleculeAccessibility]; + } + } else { + [self.molecule setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; + } + + NSNumber *useHorizontalMargins = [json optionalNumberForKey:@"useHorizontalMargins"]; + if (useHorizontalMargins) { + self.updateViewHorizontalDefaults = [useHorizontalMargins boolValue]; + } + NSNumber *useVerticalMargins = [json optionalNumberForKey:@"useVerticalMargins"]; + if (useVerticalMargins) { + self.updateViewVerticalDefaults = [useVerticalMargins boolValue]; + } + + // Set the alignment for the stack in the containing view. The json driven value is for the axis direction alignment. + NSString *alignment = [json string:@"horizontalAlignment"]; + if (alignment) { + [self alignHorizontal:[ViewConstrainingView getAlignmentForString:alignment defaultAlignment:UIStackViewAlignmentFill]]; + } + alignment = [json string:@"verticalAlignment"]; + if (alignment) { + [self alignVertical:[ViewConstrainingView getAlignmentForString:alignment defaultAlignment:UIStackViewAlignmentFill]]; + } + if ([self.molecule respondsToSelector:@selector(copyBackgroundColor)] && [self.molecule performSelector:@selector(copyBackgroundColor)]) { self.backgroundColor = self.molecule.backgroundColor; } diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index 4691f8e7..d3843f43 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -61,6 +61,7 @@ extension View: MVMCoreViewProtocol { open func setupView() { translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false + MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0) } } diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 1fee9e20..2b4fa0a8 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -13,13 +13,12 @@ import UIKit // MARK: - MVMCoreUIMoleculeViewProtocol public override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.setWithModel(model, delegateObject, additionalData) - - guard let model = model, - let moleculeModel = (model as? ListItemModel)?.molecule, - let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(moleculeModel, delegateObject, true) else { - return + guard let moleculeModel = (model as? ListItemModel)?.molecule else { return } + if molecule != nil { + (molecule as? ModelMoleculeViewProtocol)?.setWithModel(moleculeModel, delegateObject, additionalData) + } else if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(moleculeModel, delegateObject, true) { + addMolecule(moleculeView) } - addMolecule(moleculeView) } public override class func name(forReuse molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> String? { diff --git a/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift b/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift index 5c0dbfcf..ac9fe240 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift @@ -8,8 +8,8 @@ import UIKit -@objcMembers public class CornerLabels: ViewConstrainingView, ModelMoleculeViewProtocol { - +@objcMembers public class CornerLabels: View { + var middleView: UIView? let topLeftLabel = Label.commonLabelB1(true) let topRightLabel = Label.commonLabelB1(true) let bottomLeftLabel = Label.commonLabelB3(true) @@ -38,18 +38,19 @@ import UIKit var topLabelToMoleculeConstraint: NSLayoutConstraint? var bottomLabelToMoleculeConstraint: NSLayoutConstraint? - public override func addMolecule(_ molecule: UIView) { - insertSubview(molecule, at: 0) + public func addMiddleView(_ view: UIView) { + insertSubview(view, at: 0) topLabelToMoleculeConstraint?.isActive = false bottomLabelToMoleculeConstraint?.isActive = false - molecule.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor).isActive = true - layoutMarginsGuide.rightAnchor.constraint(equalTo: molecule.rightAnchor).isActive = true + view.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor).isActive = true + layoutMarginsGuide.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true - topLabelToMoleculeConstraint = molecule.topAnchor.constraint(equalTo: topLabelsView.bottomAnchor, constant: spaceAboveMolecule) + topLabelToMoleculeConstraint = view.topAnchor.constraint(equalTo: topLabelsView.bottomAnchor, constant: spaceAboveMolecule) topLabelToMoleculeConstraint?.isActive = true - bottomLabelToMoleculeConstraint = bottomLabelsView.topAnchor.constraint(equalTo: molecule.bottomAnchor, constant: spaceBelowMolecule) + bottomLabelToMoleculeConstraint = bottomLabelsView.topAnchor.constraint(equalTo: view.bottomAnchor, constant: spaceBelowMolecule) bottomLabelToMoleculeConstraint?.isActive = true + self.middleView = view } // MARK: - MVMCoreViewProtocol @@ -59,12 +60,11 @@ import UIKit topRightLabel.updateView(size) bottomLeftLabel.updateView(size) bottomRightLabel.updateView(size) + (middleView as? MVMCoreViewProtocol)?.updateView(size) } public override func setupView() { super.setupView() - shouldSetupMoleculeFromJSON = true - guard topLeftLabel.superview == nil else { return } @@ -141,17 +141,6 @@ import UIKit } // MARK: - MVMCoreUIMoleculeViewProtocol - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - topLeftLabel.setWithJSON(json?.optionalDictionaryForKey("topLeftLabel"), delegateObject: delegateObject, additionalData: additionalData) - topRightLabel.setWithJSON(json?.optionalDictionaryForKey("topRightLabel"), delegateObject: delegateObject, additionalData: additionalData) - bottomLeftLabel.setWithJSON(json?.optionalDictionaryForKey("bottomLeftLabel"), delegateObject: delegateObject, additionalData: additionalData) - bottomRightLabel.setWithJSON(json?.optionalDictionaryForKey("bottomRightLabel"), delegateObject: delegateObject, additionalData: additionalData) - - topLabelToMoleculeConstraint?.constant = (molecule != nil && (topLeftLabel.hasText || topRightLabel.hasText)) ? spaceAboveMolecule : 0 - bottomLabelToMoleculeConstraint?.constant = (molecule != nil && (bottomLeftLabel.hasText || bottomRightLabel.hasText)) ? spaceBelowMolecule : 0 - } - public override func setAsMolecule() { super.setAsMolecule() styleDefault() @@ -163,8 +152,7 @@ import UIKit styleDefault() spaceAboveMolecule = 6.0 spaceBelowMolecule = 6.0 - - molecule?.reset?() + (middleView as? MoleculeViewProtocol)?.reset?() } func styleDefault() { @@ -174,23 +162,27 @@ import UIKit bottomRightLabel.styleB3(true) } - public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 34 } - public func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - guard let model = model as? CornerLabelsModel, - let data = try? model.encode(using: JSONEncoder()), - let json = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any] else { - return + public override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.setWithModel(model, delegateObject, additionalData) + guard let model = model as? CornerLabelsModel else { return } + if middleView != nil { + (middleView as? ModelMoleculeViewProtocol)?.setWithModel(model, delegateObject, additionalData) + } else { + if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model.molecule, delegateObject) { + addMiddleView(molecule) + } } - self.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) -// topLeftLabel.setWithModel(model.topLeftLabel, delegateObject, additionalData) -// topRightLabel.setWithModel(model.topRightLabel, delegateObject, additionalData) -// bottomLeftLabel.setWithModel(model.bottomLeftLabel, delegateObject, additionalData) -// bottomRightLabel.setWithModel(model.bottomRightLabel, delegateObject, additionalData) -// -// topLabelToMoleculeConstraint?.constant = (molecule != nil && (topLeftLabel.hasText || topRightLabel.hasText)) ? spaceAboveMolecule : 0 -// bottomLabelToMoleculeConstraint?.constant = (molecule != nil && (bottomLeftLabel.hasText || bottomRightLabel.hasText)) ? spaceBelowMolecule : 0 + + topLeftLabel.setWithModel(model.topLeftLabel, delegateObject, additionalData) + topRightLabel.setWithModel(model.topRightLabel, delegateObject, additionalData) + bottomLeftLabel.setWithModel(model.bottomLeftLabel, delegateObject, additionalData) + bottomRightLabel.setWithModel(model.bottomRightLabel, delegateObject, additionalData) + + topLabelToMoleculeConstraint?.constant = (middleView != nil && (topLeftLabel.hasText || topRightLabel.hasText)) ? spaceAboveMolecule : 0 + bottomLabelToMoleculeConstraint?.constant = (middleView != nil && (bottomLeftLabel.hasText || bottomRightLabel.hasText)) ? spaceBelowMolecule : 0 } } diff --git a/MVMCoreUI/Molecules/MoleculeContainer.swift b/MVMCoreUI/Molecules/MoleculeContainer.swift index f45ca482..e3646bcf 100644 --- a/MVMCoreUI/Molecules/MoleculeContainer.swift +++ b/MVMCoreUI/Molecules/MoleculeContainer.swift @@ -9,6 +9,11 @@ import UIKit open class MoleculeContainer: Container { + + /// Can be overriden to change how the molecule is added to the hierarchy. + public func addMolecule(_ molecule: UIView) { + addAndContain(molecule) + } override public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule) else { @@ -31,7 +36,7 @@ open class MoleculeContainer: Container { (view as? ModelMoleculeViewProtocol)?.setWithModel(casteModel.molecule, delegateObject, additionalData) } else { if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(casteModel.molecule, delegateObject) { - addAndContain(molecule) + addMolecule(molecule) } } } From 147ce53c90f6a0dd771cb1ddebbb25996f4d1d60 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 14 Jan 2020 15:50:46 -0500 Subject: [PATCH 24/54] name for class --- MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift | 10 +++++----- MVMCoreUI/Molecules/Items/TableViewCell.swift | 9 ++++++--- MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift | 5 ++--- MVMCoreUI/Molecules/ModuleMolecule.swift | 2 +- MVMCoreUI/Organisms/Carousel.swift | 2 +- MVMCoreUI/Organisms/MoleculeStackView.swift | 3 ++- MVMCoreUI/Templates/MoleculeListTemplate.swift | 2 +- 7 files changed, 18 insertions(+), 15 deletions(-) diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 1fee9e20..3733ea6e 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -22,12 +22,12 @@ import UIKit addMolecule(moleculeView) } - public override class func name(forReuse molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> String? { - guard let moleculeModel = (molecule as? ListItemModel)?.molecule else { + public override class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + guard let moleculeModel = (model as? ListItemModel)?.molecule else { return "\(self)<>" } - let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol - let moleculeName = className?.nameForReuse(molecule, delegateObject) ?? moleculeModel.moleculeName ?? "" + let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type + let moleculeName = className?.nameForReuse(model, delegateObject) ?? moleculeModel.moleculeName ?? "" return "\(self)<\(moleculeName)>" } @@ -39,7 +39,7 @@ import UIKit return theClass.requiredModules?(moleculeJSON, delegateObject: delegateObject, error: error) } - public static func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let moleculeModel = (molecule as? MoleculeContainerModel)?.molecule, let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type, let height = classType.estimatedHeight(forRow: moleculeModel, delegateObject: delegateObject) else { diff --git a/MVMCoreUI/Molecules/Items/TableViewCell.swift b/MVMCoreUI/Molecules/Items/TableViewCell.swift index 774f50d1..ffdcd85b 100644 --- a/MVMCoreUI/Molecules/Items/TableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/TableViewCell.swift @@ -179,10 +179,13 @@ import UIKit backgroundColor = .white } - public class func name(forReuse molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> String? { - return molecule?.moleculeName ?? "" + public class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + return model?.moleculeName } - + + public class func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return nil + } // MARK: - Arrow /// Adds the standard mvm style caret to the accessory view @objc public func addCaretViewAccessory() { diff --git a/MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift b/MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift index de8e4d4f..34f2c67b 100644 --- a/MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift +++ b/MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift @@ -10,13 +10,13 @@ import Foundation public protocol ModelMoleculeViewProtocol { func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) - func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? + static func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? static func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? static func requiredModules(_ molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? } extension ModelMoleculeViewProtocol { - public func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + public static func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { return nil } public static func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { @@ -25,7 +25,6 @@ extension ModelMoleculeViewProtocol { public static func requiredModules(_ molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { return nil } - // Temporary public static func decodeJSONToModel(json: [AnyHashable: Any], type: T.Type) throws -> T where T : Decodable { let data = try JSONSerialization.data(withJSONObject: json) diff --git a/MVMCoreUI/Molecules/ModuleMolecule.swift b/MVMCoreUI/Molecules/ModuleMolecule.swift index e2411ec0..0cb0e541 100644 --- a/MVMCoreUI/Molecules/ModuleMolecule.swift +++ b/MVMCoreUI/Molecules/ModuleMolecule.swift @@ -61,7 +61,7 @@ open class ModuleMolecule: Container { public override func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { guard let moduleMolecule = model as? ModuleMoleculeModel, let moduleModel = delegateObject?.moleculeDelegate?.getModuleWithName(moduleMolecule.moduleName), - let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moduleModel) as? ModelMoleculeViewProtocol, + let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moduleModel) as? ModelMoleculeViewProtocol.Type, let name = classType.nameForReuse(moduleModel, delegateObject) else { // Critical error return "moduleMolecule<>" diff --git a/MVMCoreUI/Organisms/Carousel.swift b/MVMCoreUI/Organisms/Carousel.swift index 181ae372..98f1e6a2 100644 --- a/MVMCoreUI/Organisms/Carousel.swift +++ b/MVMCoreUI/Organisms/Carousel.swift @@ -169,7 +169,7 @@ open class Carousel: ViewConstrainingView, ModelMoleculeViewProtocol { /// Returns the (identifier, class) of the molecule for the given map. func getMoleculeInfo(with molecule: MoleculeProtocol, delegateObject: MVMCoreUIDelegateObject?) -> (identifier: String, class: AnyClass, molecule: MoleculeProtocol)? { guard let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule) , - let moleculeName = (className as? ModelMoleculeViewProtocol)?.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName else { + let moleculeName = (className as? ModelMoleculeViewProtocol.Type)?.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName else { return nil } return (moleculeName, className, molecule) diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 5345549c..a09ec83d 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -119,7 +119,8 @@ open class MoleculeStackView: Container { var name = "stack<" for case let item in model.molecules { if let moleculeName = item.molecule.moleculeName { - if let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? ModelMoleculeViewProtocol, let nameForReuse = moleculeClass.nameForReuse(item.molecule, delegateObject) { + if let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? ModelMoleculeViewProtocol.Type, + let nameForReuse = moleculeClass.nameForReuse(item.molecule, delegateObject) { name.append(nameForReuse + ",") } else { name.append(moleculeName + ",") diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 3a551a79..5570bb1a 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -167,7 +167,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol func getMoleculeInfo(with listItem: ListItemModelProtocol?) -> (identifier: String, class: AnyClass, molecule: ListItemModelProtocol)? { guard let listItem = listItem, let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(listItem), - let moleculeName = (moleculeClass as? ModelMoleculeViewProtocol)?.nameForReuse(listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName else { + let moleculeName = (moleculeClass as? ModelMoleculeViewProtocol.Type)?.nameForReuse(listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName else { return nil } return (moleculeName, moleculeClass, listItem) From 27793931f5c1f56bccfb09ed832de3a2e10736cf Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 15:55:18 -0500 Subject: [PATCH 25/54] fix for static functions --- MVMCoreUI/BaseClasses/View.swift | 4 ---- .../Molecules/Items/MoleculeCollectionViewCell.swift | 8 +++++--- .../Molecules/Items/MoleculeTableViewCell.swift | 12 ++++++------ MVMCoreUI/Molecules/Items/TableViewCell.swift | 4 ---- MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift | 6 +++--- MVMCoreUI/Molecules/ModuleMolecule.swift | 4 ++-- MVMCoreUI/Organisms/Carousel.swift | 2 +- MVMCoreUI/Organisms/MoleculeStackView.swift | 6 +++--- MVMCoreUI/Templates/MoleculeListTemplate.swift | 2 +- 9 files changed, 21 insertions(+), 27 deletions(-) diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index d3843f43..4d6e247d 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -40,10 +40,6 @@ import UIKit } } - public func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - return model?.moleculeName - } - open func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { self.model = model if let backgroundColor = model?.backgroundColor { diff --git a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift index b396dde6..93f5e039 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift @@ -114,11 +114,13 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi backgroundColor = .white } - public class func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { - guard let molecule = molecule?.optionalDictionaryForKey(KeyMolecule) else { + public class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + guard let molecule = (model as? CarouselItemModel)?.molecule, + let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule) as? ModelMoleculeViewProtocol.Type, + let name = moleculeClass.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName else { return nil } - return MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.name?(forReuse: molecule, delegateObject: delegateObject) ?? molecule.optionalStringForKey(KeyMoleculeName) + return name } public func updateView(_ size: CGFloat) { diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 2b4fa0a8..6b57c0f5 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -20,16 +20,16 @@ import UIKit addMolecule(moleculeView) } } - - public override class func name(forReuse molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> String? { - guard let moleculeModel = (molecule as? ListItemModel)?.molecule else { + + public class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + guard let moleculeModel = (model as? ListItemModel)?.molecule else { return "\(self)<>" } - let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol - let moleculeName = className?.nameForReuse(molecule, delegateObject) ?? moleculeModel.moleculeName ?? "" + let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type + let moleculeName = className?.nameForReuse(moleculeModel, delegateObject) ?? moleculeModel.moleculeName ?? "" return "\(self)<\(moleculeName)>" } - + public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON) else { diff --git a/MVMCoreUI/Molecules/Items/TableViewCell.swift b/MVMCoreUI/Molecules/Items/TableViewCell.swift index 774f50d1..dfcf7252 100644 --- a/MVMCoreUI/Molecules/Items/TableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/TableViewCell.swift @@ -178,10 +178,6 @@ import UIKit styleStandard() backgroundColor = .white } - - public class func name(forReuse molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> String? { - return molecule?.moleculeName ?? "" - } // MARK: - Arrow /// Adds the standard mvm style caret to the accessory view diff --git a/MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift b/MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift index de8e4d4f..fcf03ccd 100644 --- a/MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift +++ b/MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift @@ -10,14 +10,14 @@ import Foundation public protocol ModelMoleculeViewProtocol { func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) - func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? + static func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? static func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? static func requiredModules(_ molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? } extension ModelMoleculeViewProtocol { - public func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - return nil + public static func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + return model?.moleculeName } public static func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return nil diff --git a/MVMCoreUI/Molecules/ModuleMolecule.swift b/MVMCoreUI/Molecules/ModuleMolecule.swift index e2411ec0..be13fc79 100644 --- a/MVMCoreUI/Molecules/ModuleMolecule.swift +++ b/MVMCoreUI/Molecules/ModuleMolecule.swift @@ -58,10 +58,10 @@ open class ModuleMolecule: Container { return height } - public override func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + public class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { guard let moduleMolecule = model as? ModuleMoleculeModel, let moduleModel = delegateObject?.moleculeDelegate?.getModuleWithName(moduleMolecule.moduleName), - let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moduleModel) as? ModelMoleculeViewProtocol, + let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moduleModel) as? ModelMoleculeViewProtocol.Type, let name = classType.nameForReuse(moduleModel, delegateObject) else { // Critical error return "moduleMolecule<>" diff --git a/MVMCoreUI/Organisms/Carousel.swift b/MVMCoreUI/Organisms/Carousel.swift index 181ae372..98f1e6a2 100644 --- a/MVMCoreUI/Organisms/Carousel.swift +++ b/MVMCoreUI/Organisms/Carousel.swift @@ -169,7 +169,7 @@ open class Carousel: ViewConstrainingView, ModelMoleculeViewProtocol { /// Returns the (identifier, class) of the molecule for the given map. func getMoleculeInfo(with molecule: MoleculeProtocol, delegateObject: MVMCoreUIDelegateObject?) -> (identifier: String, class: AnyClass, molecule: MoleculeProtocol)? { guard let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule) , - let moleculeName = (className as? ModelMoleculeViewProtocol)?.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName else { + let moleculeName = (className as? ModelMoleculeViewProtocol.Type)?.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName else { return nil } return (moleculeName, className, molecule) diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 1783e19a..60c6515f 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -97,7 +97,7 @@ open class MoleculeStackView: Container { removeAllItemViews() // If the items in the stack are different, clear them, create new ones. - if (previousModel == nil) || nameForReuse(previousModel, delegateObject) != nameForReuse(model, delegateObject) { + if (previousModel == nil) || MoleculeStackView.nameForReuse(previousModel, delegateObject) != MoleculeStackView.nameForReuse(model, delegateObject) { stackItems = [] createStackItemsFromModel(with: delegateObject) } else if let models = stackModel?.molecules { @@ -111,7 +111,7 @@ open class MoleculeStackView: Container { stackModel?.useVerticalMargins = moleculesShouldSetVerticalMargins } - public override func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + public class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { // This will aggregate names of molecules to make an id. guard let model = model as? MoleculeStackModel else { return "stack<>" @@ -119,7 +119,7 @@ open class MoleculeStackView: Container { var name = "stack<" for case let item in model.molecules { if let moleculeName = item.molecule.moleculeName { - if let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? ModelMoleculeViewProtocol, let nameForReuse = moleculeClass.nameForReuse(item.molecule, delegateObject) { + if let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? ModelMoleculeViewProtocol.Type, let nameForReuse = moleculeClass.nameForReuse(item.molecule, delegateObject) { name.append(nameForReuse + ",") } else { name.append(moleculeName + ",") diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 3a551a79..5570bb1a 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -167,7 +167,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol func getMoleculeInfo(with listItem: ListItemModelProtocol?) -> (identifier: String, class: AnyClass, molecule: ListItemModelProtocol)? { guard let listItem = listItem, let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(listItem), - let moleculeName = (moleculeClass as? ModelMoleculeViewProtocol)?.nameForReuse(listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName else { + let moleculeName = (moleculeClass as? ModelMoleculeViewProtocol.Type)?.nameForReuse(listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName else { return nil } return (moleculeName, moleculeClass, listItem) From fc2326e9a8706b81bf2c2c597ba9e138e56977dc Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 16:32:42 -0500 Subject: [PATCH 26/54] button size --- MVMCoreUI/Atoms/Buttons/ButtonModel.swift | 17 +++++++++++-- ...maryButton+MoleculeProtocolExtension.swift | 8 +++++++ MVMCoreUI/BaseClasses/View.swift | 12 ++++++++++ MVMCoreUI/Molecules/Items/ListItemModel.swift | 1 - .../SwitchMolecules/HeadlineBodySwitch.swift | 2 +- MVMCoreUI/Molecules/ModuleMolecule.swift | 6 ++--- .../EyebrowHeadlineBodyLink.swift | 2 +- MVMCoreUI/Organisms/MoleculeStackView.swift | 24 +++++++++---------- 8 files changed, 52 insertions(+), 20 deletions(-) diff --git a/MVMCoreUI/Atoms/Buttons/ButtonModel.swift b/MVMCoreUI/Atoms/Buttons/ButtonModel.swift index 03eb7fc3..4329ccff 100644 --- a/MVMCoreUI/Atoms/Buttons/ButtonModel.swift +++ b/MVMCoreUI/Atoms/Buttons/ButtonModel.swift @@ -13,12 +13,18 @@ public enum ButtonStyle: String, Codable { case secondary } +public enum ButtonSize: String, Codable { + case standard + case tiny +} + public class ButtonModel: MoleculeProtocol { public static var identifier: String = "button" public var backgroundColor: Color? public var title: String public var action: ActionProtocol - public var style: ButtonStyle? + public var style: ButtonStyle? = .primary + public var size: ButtonSize? = .standard init(with title: String, action: ActionProtocol) { self.title = title @@ -30,6 +36,7 @@ public class ButtonModel: MoleculeProtocol { case title case action case style + case size } required public init(from decoder: Decoder) throws { @@ -37,7 +44,12 @@ public class ButtonModel: MoleculeProtocol { backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) title = try typeContainer.decode(String.self, forKey: .title) action = try typeContainer.decodeModel(codingKey: .action, typeCodingKey: ActionCodingKey.actionType) - style = try typeContainer.decodeIfPresent(ButtonStyle.self, forKey: .style) + if let style = try typeContainer.decodeIfPresent(ButtonStyle.self, forKey: .style) { + self.style = style + } + if let size = try typeContainer.decodeIfPresent(ButtonSize.self, forKey: .size) { + self.size = size + } } public func encode(to encoder: Encoder) throws { @@ -46,5 +58,6 @@ public class ButtonModel: MoleculeProtocol { try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeModel(action, forKey: .action) try container.encodeIfPresent(style, forKey: .style) + try container.encodeIfPresent(size, forKey: .size) } } diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift b/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift index 6f285e19..2d7ecf40 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift @@ -22,6 +22,14 @@ extension PrimaryButton: ModelMoleculeViewProtocol { setAsSecondaryCustom() } } + if let size = model.size { + switch size { + case .standard: + setAsTiny(false) + case .tiny: + setAsTiny(true) + } + } set(with: model.action, delegateObject: delegateObject, additionalData: additionalData) } } diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index 4d6e247d..05cbef15 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -46,6 +46,18 @@ import UIKit self.backgroundColor = backgroundColor.uiColor } } + + public class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + return model?.moleculeName + } + + public class func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return nil + } + + public class func requiredModules(_ molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + return nil + } } // MARK:- MVMCoreViewProtocol diff --git a/MVMCoreUI/Molecules/Items/ListItemModel.swift b/MVMCoreUI/Molecules/Items/ListItemModel.swift index cd0b1dc3..5e54a2e9 100644 --- a/MVMCoreUI/Molecules/Items/ListItemModel.swift +++ b/MVMCoreUI/Molecules/Items/ListItemModel.swift @@ -27,7 +27,6 @@ import MVMCore /// Defaults to set func setDefaults() { - style = "standard" if useHorizontalMargins == nil { useHorizontalMargins = true } diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift index fee32b2f..1c937df1 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift @@ -41,7 +41,7 @@ import UIKit super.setWithModel(model, delegateObject, additionalData) } - public static func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + public class override func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 30 } diff --git a/MVMCoreUI/Molecules/ModuleMolecule.swift b/MVMCoreUI/Molecules/ModuleMolecule.swift index be13fc79..04caa9da 100644 --- a/MVMCoreUI/Molecules/ModuleMolecule.swift +++ b/MVMCoreUI/Molecules/ModuleMolecule.swift @@ -46,7 +46,7 @@ open class ModuleMolecule: Container { } } - public static func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + public override class func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { guard let moduleMolecule = molecule as? ModuleMoleculeModel, let moduleModel = delegateObject?.moleculeDelegate?.getModuleWithName(moduleMolecule.moduleName), @@ -58,7 +58,7 @@ open class ModuleMolecule: Container { return height } - public class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + public override class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { guard let moduleMolecule = model as? ModuleMoleculeModel, let moduleModel = delegateObject?.moleculeDelegate?.getModuleWithName(moduleMolecule.moduleName), let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moduleModel) as? ModelMoleculeViewProtocol.Type, @@ -69,7 +69,7 @@ open class ModuleMolecule: Container { return name } - public static func requiredModules(_ molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + public override class func requiredModules(_ molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { guard let moduleName = (molecule as? ModuleMoleculeModel)?.moduleName, let _ = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else { diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift index 1262994f..d5557eca 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift @@ -85,7 +85,7 @@ struct EyebrowHeadlineBodyLinkModel: MoleculeProtocol { body.styleB2(true) } - public static func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + public override class func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 65 } } diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index c3da98ef..9f2f526f 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -111,7 +111,18 @@ open class MoleculeStackView: Container { stackModel?.useVerticalMargins = moleculesShouldSetVerticalMargins } - public class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + if model == nil { + let data = try! JSONSerialization.data(withJSONObject: json!) + let decoder = JSONDecoder() + let model = try! decoder.decode(MoleculeStackModel.self, from: data) + setWithModel(model, delegateObject, additionalData) + } else { + setWithModel(model, delegateObject, additionalData) + } + } + + public override class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { // This will aggregate names of molecules to make an id. guard let model = model as? MoleculeStackModel else { return "stack<>" @@ -131,17 +142,6 @@ open class MoleculeStackView: Container { return name } - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - if model == nil { - let data = try! JSONSerialization.data(withJSONObject: json!) - let decoder = JSONDecoder() - let model = try! decoder.decode(MoleculeStackModel.self, from: data) - setWithModel(model, delegateObject, additionalData) - } else { - setWithModel(model, delegateObject, additionalData) - } - } - public class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { // This will aggregate names of molecules to make an id. guard let molecules = molecule?.optionalArrayForKey(KeyMolecules) else { From 7723ffb260bb1755d48879f8c41478e4687a232d Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 17:40:51 -0500 Subject: [PATCH 27/54] verbose errors --- MVMCoreUI/BaseControllers/MFViewController.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/BaseControllers/MFViewController.m b/MVMCoreUI/BaseControllers/MFViewController.m index d2ff7cda..09700268 100644 --- a/MVMCoreUI/BaseControllers/MFViewController.m +++ b/MVMCoreUI/BaseControllers/MFViewController.m @@ -101,7 +101,9 @@ [self parsePageJSONAndReturnError:&parseError]; if (parseError) { if (error) { - *error = [MVMCoreErrorObject createErrorObjectForNSError:parseError location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]; + MVMCoreErrorObject *errorObject = [MVMCoreErrorObject createErrorObjectForNSError:parseError location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]; + errorObject.messageToDisplay = [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess]; + *error = errorObject; } return false; } From e85270d0389857eaae0f67fe941bb731e0796fea Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 18:33:34 -0500 Subject: [PATCH 28/54] new molecules to model --- MVMCoreUI.xcodeproj/project.pbxproj | 4 ++++ .../HeadLineBodyCaretLinkImage.swift | 15 ++++++++++++--- .../HeadlineBodyCaretLinkImageModel.swift | 17 +++++++++++++++++ .../OtherHandlers/MoleculeObjectMapping.swift | 3 +++ 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 6580f328..9c93c8dc 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -150,6 +150,7 @@ D28A838D23CCDCC200DFE4FC /* PrimaryButton+MoleculeProtocolExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838C23CCDCC200DFE4FC /* PrimaryButton+MoleculeProtocolExtension.swift */; }; D28A838F23CCDEDE00DFE4FC /* TwoButtonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */; }; D28A839123CD4FD400DFE4FC /* CornerLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839023CD4FD400DFE4FC /* CornerLabelsModel.swift */; }; + D28A839323CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */; }; D296E14722A5984C0051EBE7 /* MVMCoreUIViewConstrainingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */; }; D29770C921F7C4AE00B2F0D0 /* TopLabelsView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29770C721F7C4AE00B2F0D0 /* TopLabelsView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -418,6 +419,7 @@ D28A838C23CCDCC200DFE4FC /* PrimaryButton+MoleculeProtocolExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrimaryButton+MoleculeProtocolExtension.swift"; sourceTree = ""; }; D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonViewModel.swift; sourceTree = ""; }; D28A839023CD4FD400DFE4FC /* CornerLabelsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerLabelsModel.swift; sourceTree = ""; }; + D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyCaretLinkImageModel.swift; sourceTree = ""; }; D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewConstrainingProtocol.h; sourceTree = ""; }; D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopLabelsView.m; sourceTree = ""; }; D29770C721F7C4AE00B2F0D0 /* TopLabelsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopLabelsView.h; sourceTree = ""; }; @@ -760,6 +762,7 @@ 01EB368D23609801006832FA /* HeadlineBodyModel.swift */, D22479952316AF6D003FCCF9 /* HeadlineBodyTextButton.swift */, D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */, + D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */, C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */, ); path = VerticalCombinationViews; @@ -1536,6 +1539,7 @@ 0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */, D243859923A16B1800332775 /* Container.swift in Sources */, D29DF29821E7ADB8003B2FB9 /* MFScrollingViewController.m in Sources */, + D28A839323CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift in Sources */, D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */, 01EB369323609801006832FA /* HeaderModel.swift in Sources */, D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */, diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadLineBodyCaretLinkImage.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadLineBodyCaretLinkImage.swift index 9c3dba8a..3b52e6bf 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadLineBodyCaretLinkImage.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadLineBodyCaretLinkImage.swift @@ -7,7 +7,7 @@ // import Foundation -@objcMembers public class HeadLineBodyCaretLinkImage: ViewConstrainingView { +@objcMembers public class HeadLineBodyCaretLinkImage: View { let headlineBody = HeadlineBody(frame: .zero) let caretButton = CaretButton(frame: .zero) @@ -34,7 +34,7 @@ import Foundation } let view = MVMCoreUICommonViewsUtility.commonView() addSubview(view) - pinView(toSuperView: view) + NSLayoutConstraint.constraintPinSubview(toSuperview: view) view.addSubview(headlineBody) view.addSubview(caretButton) @@ -78,7 +78,16 @@ import Foundation backgroundImageView.reset() } - public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 320 } + + public override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.setWithModel(model, delegateObject, additionalData) + guard let model = model as? HeadlineBodyCaretLinkImageModel else { return } + headlineBody.setWithModel(model.headlineBody, delegateObject, additionalData) + caretButton.setWithModel(model.caretLink, delegateObject, additionalData) + backgroundImageView.setWithModel(model.image, delegateObject, additionalData) + } } + diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift new file mode 100644 index 00000000..4bfa2740 --- /dev/null +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift @@ -0,0 +1,17 @@ +// +// headlineBodyCaretLinkImageModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/14/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +public class HeadlineBodyCaretLinkImageModel: MoleculeProtocol { + public static var identifier: String = "headlineBodyCaretLinkImageMolecule" + public var backgroundColor: Color? + public var caretLink: LinkModel? + public var headlineBody: HeadlineBodyModel + public var image: ImageViewModel +} diff --git a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift index 6a958061..e4c35777 100644 --- a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift +++ b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift @@ -25,10 +25,13 @@ import Foundation ModelRegistry.register(ScrollerModel.self) ModelRegistry.register(CornerLabelsModel.self) ModelRegistry.register(LineModel.self) + ModelRegistry.register(CircleProgressModel.self) + ModelRegistry.register(HeadlineBodyCaretLinkImageModel.self) // buttons ModelRegistry.register(ButtonModel.self) ModelRegistry.register(TwoButtonViewModel.self) ModelRegistry.register(LinkModel.self) + ModelRegistry.register(CaretLinkModel.self) // list items ModelRegistry.register(ListItemModel.self) ModelRegistry.register(DropDownListItemModel.self) From f87ddf34480333952e3847b1dd2b706cc405ba27 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 18:41:54 -0500 Subject: [PATCH 29/54] name update --- .../HeadlineBodyCaretLinkImageModel.swift | 2 +- MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift index 4bfa2740..8052a7db 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift @@ -9,7 +9,7 @@ import UIKit public class HeadlineBodyCaretLinkImageModel: MoleculeProtocol { - public static var identifier: String = "headlineBodyCaretLinkImageMolecule" + public static var identifier: String = "headlineBodyCaretLinkImage" public var backgroundColor: Color? public var caretLink: LinkModel? public var headlineBody: HeadlineBodyModel diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index b2590566..ad987229 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -73,7 +73,7 @@ @"headlineBodyButton": HeadlineBodyButton.class, @"stackItem": StackItem.class, @"eyebrowHeadlineBodyLink": EyebrowHeadlineBodyLink.class, - @"headLineBodyCaretLinkImage" : HeadLineBodyCaretLinkImage.class + @"headlineBodyCaretLinkImage" : HeadLineBodyCaretLinkImage.class } mutableCopy]; }); return mapping; From 76b14f6e8ed8b5d2b8612c2a83ac1270f19723ce Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 20:20:02 -0500 Subject: [PATCH 30/54] fix typo --- MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 4172e4dc..88531ceb 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -16,7 +16,7 @@ import UIKit guard let moleculeModel = (model as? ListItemModel)?.molecule else { return } if molecule != nil { (molecule as? ModelMoleculeViewProtocol)?.setWithModel(moleculeModel, delegateObject, additionalData) - } else if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(moleculeModel, delegateObject, true) { + } else if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(moleculeModel, delegateObject, false) { addMolecule(moleculeView) } } From a737b3b94c0e2dc4f20adcb419cf9645e9788e9a Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 20:23:14 -0500 Subject: [PATCH 31/54] Remove adding view constraining view --- MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift | 2 +- MVMCoreUI/Molecules/ModuleMolecule.swift | 2 +- MVMCoreUI/Organisms/Carousel.swift | 2 +- MVMCoreUI/Templates/MoleculeListTemplate.swift | 4 ++-- MVMCoreUI/Templates/MoleculeStackTemplate.swift | 4 ++-- MVMCoreUI/Templates/ThreeLayerTemplate.swift | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift index 93f5e039..976b6e60 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift @@ -92,7 +92,7 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi } if molecule == nil { - if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(collectionModel.molecule, delegateObject, true) { + if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(collectionModel.molecule, delegateObject, false) { contentView.insertSubview(moleculeView, at: 0) containerHelper.constrainView(moleculeView) molecule = moleculeView diff --git a/MVMCoreUI/Molecules/ModuleMolecule.swift b/MVMCoreUI/Molecules/ModuleMolecule.swift index 04caa9da..ba0801f3 100644 --- a/MVMCoreUI/Molecules/ModuleMolecule.swift +++ b/MVMCoreUI/Molecules/ModuleMolecule.swift @@ -29,7 +29,7 @@ open class ModuleMolecule: Container { } if moduleMolecule == nil { - if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(moduleModel, delegateObject, true) { + if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(moduleModel, delegateObject, false) { addSubview(moleculeView) NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: false).values)) moduleMolecule = moleculeView as? (UIView & MVMCoreUIMoleculeViewProtocol & ModelMoleculeViewProtocol) diff --git a/MVMCoreUI/Organisms/Carousel.swift b/MVMCoreUI/Organisms/Carousel.swift index 98f1e6a2..50d55c16 100644 --- a/MVMCoreUI/Organisms/Carousel.swift +++ b/MVMCoreUI/Organisms/Carousel.swift @@ -151,7 +151,7 @@ open class Carousel: ViewConstrainingView, ModelMoleculeViewProtocol { open func setupPagingMolecule(_ molecule: PagingMoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) { var pagingView: (UIView & MVMCoreUIPagingProtocol)? = nil if let molecule = molecule { - pagingView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(molecule, delegateObject, true) as? (UIView & MVMCoreUIPagingProtocol) + pagingView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(molecule, delegateObject, false) as? (UIView & MVMCoreUIPagingProtocol) } addPaging(view: pagingView, position: (CGFloat(molecule?.position ?? 20))) } diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 5570bb1a..092c8648 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -34,7 +34,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol open override func viewForTop() -> UIView { guard let headerModel = templateModel?.header, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, true) else { + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { return super.viewForTop() } return molecule @@ -42,7 +42,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol override open func viewForBottom() -> UIView { guard let footerModel = templateModel?.footer, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, true) else { + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { return super.viewForBottom() } return molecule diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Templates/MoleculeStackTemplate.swift index 74eae770..a4f69518 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackTemplate.swift @@ -36,7 +36,7 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol { open override func viewForTop() -> UIView? { guard let headerModel = templateModel?.header, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, true) else { + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { return nil } return molecule @@ -57,7 +57,7 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol { override open func viewForBottom() -> UIView? { guard let footerModel = templateModel?.footer, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, true) else { + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { return nil } return molecule diff --git a/MVMCoreUI/Templates/ThreeLayerTemplate.swift b/MVMCoreUI/Templates/ThreeLayerTemplate.swift index 472985e3..9c963693 100644 --- a/MVMCoreUI/Templates/ThreeLayerTemplate.swift +++ b/MVMCoreUI/Templates/ThreeLayerTemplate.swift @@ -29,7 +29,7 @@ import UIKit open override func viewForTop() -> UIView? { guard let headerModel = templateModel?.header, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, true) else { + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { return nil } return molecule @@ -37,7 +37,7 @@ import UIKit open override func viewForMiddle() -> UIView? { guard let middleModel = templateModel?.middle, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(middleModel, delegateObject() as? MVMCoreUIDelegateObject, true) else { + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(middleModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { return nil } return molecule @@ -45,7 +45,7 @@ import UIKit override open func viewForBottom() -> UIView? { guard let footerModel = templateModel?.footer, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, true) else { + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { return nil } return molecule From 8936f510b2b48571b8830011e075b6258efb2fb3 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 20:35:53 -0500 Subject: [PATCH 32/54] temporary change to match --- MVMCoreUI/Templates/MoleculeListTemplate.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 092c8648..61e1121c 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -37,6 +37,11 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { return super.viewForTop() } + + // Temporary, Default the horizontal padding + if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil { + container.useHorizontalMargins = true + } return molecule } From 952c3bdb53ee6c6571d723ce88d5faa0fe001dbd Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 14 Jan 2020 21:08:48 -0500 Subject: [PATCH 33/54] Toggle model temporary --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++++ .../Atoms/Views/MVMCoreUISwitch+Model.swift | 31 ++++++++++++++ MVMCoreUI/Atoms/Views/ToggleModel.swift | 41 +++++++++++++++++++ .../OtherHandlers/MoleculeObjectMapping.swift | 1 + 4 files changed, 81 insertions(+) create mode 100644 MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift create mode 100644 MVMCoreUI/Atoms/Views/ToggleModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 9c93c8dc..c5ac1374 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -123,6 +123,8 @@ D22D1F562204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D22D1F542204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; D22D1F572204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D22D1F552204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m */; }; D243859923A16B1800332775 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = D243859823A16B1800332775 /* Container.swift */; }; + D260105323CEA61600764D80 /* ToggleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105223CEA61600764D80 /* ToggleModel.swift */; }; + D260105523CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */; }; D260D7B122D65BDD007E7233 /* MVMCoreUIPageControl.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */; }; D260D7B622D68514007E7233 /* MVMCoreUIPagingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -393,6 +395,8 @@ D22D1F542204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIStackableViewController.h; sourceTree = ""; }; D22D1F552204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIStackableViewController.m; sourceTree = ""; }; D243859823A16B1800332775 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; + D260105223CEA61600764D80 /* ToggleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleModel.swift; sourceTree = ""; }; + D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUISwitch+Model.swift"; sourceTree = ""; }; D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPageControl.h; sourceTree = ""; }; D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIPageControl.m; sourceTree = ""; }; D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPagingProtocol.h; sourceTree = ""; }; @@ -1087,6 +1091,8 @@ D29DF32221ED0DA2003B2FB9 /* TextButtonView.m */, D29770FB21F7C77400B2F0D0 /* MVMCoreUITextFieldView.h */, D29770FA21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m */, + D260105223CEA61600764D80 /* ToggleModel.swift */, + D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */, D22D1F44220496A30077CEC0 /* MVMCoreUISwitch.h */, D22D1F45220496A30077CEC0 /* MVMCoreUISwitch.m */, DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */, @@ -1471,6 +1477,7 @@ DBEFFA04225A829700230692 /* Label.swift in Sources */, D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */, 01509D952327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift in Sources */, + D260105523CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift in Sources */, D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */, 0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */, 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */, @@ -1478,6 +1485,7 @@ D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */, D2B18B812360945C00A9AEDC /* View.swift in Sources */, D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */, + D260105323CEA61600764D80 /* ToggleModel.swift in Sources */, 014AA72523C501E2006F3E93 /* ContainerModel.swift in Sources */, D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift new file mode 100644 index 00000000..68482f09 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift @@ -0,0 +1,31 @@ +// +// MVMCoreUISwitch+Model.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/14/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +// temporary until link is finished +extension MVMCoreUISwitch: ModelMoleculeViewProtocol { + public func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + guard let model = model as? ToggleModel else { return } + + if let castSelf = self as? FormValidationProtocol { + FormValidator.setupValidation(molecule: castSelf, delegate: delegateObject?.formValidationProtocol) + } + + setState(model.on, animated: false) + + guard let action = model.action else { return } + actionBlock = { + if let data = try? action.encode(using: JSONEncoder()), + let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any] { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + + } + } + } +} diff --git a/MVMCoreUI/Atoms/Views/ToggleModel.swift b/MVMCoreUI/Atoms/Views/ToggleModel.swift new file mode 100644 index 00000000..df7d2ba8 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/ToggleModel.swift @@ -0,0 +1,41 @@ +// +// ToggleModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/14/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +public class ToggleModel: MoleculeProtocol { + public static var identifier: String = "toggle" + public var backgroundColor: Color? + public var on: Bool = true + public var action: ActionProtocol? + + enum CodingKeys: String, CodingKey { + case on + case action + case backgroundColor + } + + public init(_ on: Bool) { + self.on = on + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + if let on = try typeContainer.decodeIfPresent(Bool.self, forKey: .on) { + self.on = on + } + action = try typeContainer.decodeModelIfPresent(codingKey: .action, typeCodingKey: ActionCodingKey.actionType) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeModelIfPresent(action, forKey: .action) + } +} diff --git a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift index e4c35777..1a041e2c 100644 --- a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift +++ b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift @@ -27,6 +27,7 @@ import Foundation ModelRegistry.register(LineModel.self) ModelRegistry.register(CircleProgressModel.self) ModelRegistry.register(HeadlineBodyCaretLinkImageModel.self) + ModelRegistry.register(ToggleModel.self) // buttons ModelRegistry.register(ButtonModel.self) ModelRegistry.register(TwoButtonViewModel.self) From 703e0cd32bc06b1866fee7a95c5dbb411b3a699c Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 15 Jan 2020 12:38:31 -0500 Subject: [PATCH 34/54] Convenience to access cgColor. --- MVMCoreUI/Models/Primitive Models/Color.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MVMCoreUI/Models/Primitive Models/Color.swift b/MVMCoreUI/Models/Primitive Models/Color.swift index e4c0e4fa..d72e1af9 100644 --- a/MVMCoreUI/Models/Primitive Models/Color.swift +++ b/MVMCoreUI/Models/Primitive Models/Color.swift @@ -19,6 +19,11 @@ public final class Color: Codable { //-------------------------------------------------- public let uiColor: UIColor + + public var cgColor: CGColor { + return uiColor.cgColor + } + public private(set) var hex: String = "" public private(set) var name: String = "" From 388d88b47c4049cd78f2c532891e6b5ec028deeb Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 15 Jan 2020 13:13:15 -0500 Subject: [PATCH 35/54] autorResiszing exception. --- MVMCoreUI/Molecules/Items/TableViewCell.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/MVMCoreUI/Molecules/Items/TableViewCell.swift b/MVMCoreUI/Molecules/Items/TableViewCell.swift index 537af049..dc62ae65 100644 --- a/MVMCoreUI/Molecules/Items/TableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/TableViewCell.swift @@ -192,6 +192,7 @@ import UIKit @objc public func addCaretViewAccessory() { guard accessoryView == nil else { return } caretView = CaretView(lineWidth: 1) + caretView?.translatesAutoresizingMaskIntoConstraints = true caretView?.size = .small(.vertical) caretView?.setConstraints() From 9329f0ca15cfd8dac6a1c5979f7b8e36fbf348b2 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 15 Jan 2020 13:28:40 -0500 Subject: [PATCH 36/54] update left and right padding --- MVMCoreUI/Molecules/DoughnutChart.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/DoughnutChart.swift index d7f25ac6..d58dc7c4 100644 --- a/MVMCoreUI/Molecules/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/DoughnutChart.swift @@ -173,10 +173,15 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { return doughnutChartModel?.spaceRequired ?? true } - func updateLabelContainer() { - labelContainer.leftPin?.constant = lineWidth() - labelContainer.topPin?.constant = lineWidth() - labelContainer.setNeedsDisplay() - } + func updateLabelContainer() { + labelContainer.layoutIfNeeded() + let radius = sizeObject.getValueBasedOnApplicationWidth()/2 - lineWidth() + let labelheight = labelContainer.frame.height/2 + let padding = sizeObject.getValueBasedOnApplicationWidth()/2 - sqrt(pow(radius, 2) - pow(labelheight, 2)) + + labelContainer.leftPin?.constant = padding + labelContainer.topPin?.constant = padding + labelContainer.setNeedsDisplay() + } } From 00d78cfdedcdf238466c35c6fea4852cac6da925 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 15 Jan 2020 13:53:54 -0500 Subject: [PATCH 37/54] update label frame --- MVMCoreUI/Molecules/DoughnutChart.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/DoughnutChart.swift index d58dc7c4..e6a169f1 100644 --- a/MVMCoreUI/Molecules/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/DoughnutChart.swift @@ -20,9 +20,9 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { var labelContainer = ViewConstrainingView.empty() var heightConstraint: NSLayoutConstraint? - private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 150)! + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 100)! - var height: CGFloat = 150 { + var height: CGFloat = 100 { didSet { if height != oldValue { sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)! @@ -174,14 +174,14 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { } func updateLabelContainer() { + labelContainer.setNeedsDisplay() labelContainer.layoutIfNeeded() let radius = sizeObject.getValueBasedOnApplicationWidth()/2 - lineWidth() let labelheight = labelContainer.frame.height/2 let padding = sizeObject.getValueBasedOnApplicationWidth()/2 - sqrt(pow(radius, 2) - pow(labelheight, 2)) labelContainer.leftPin?.constant = padding - labelContainer.topPin?.constant = padding - labelContainer.setNeedsDisplay() + labelContainer.topPin?.constant = max(radius - labelheight, labelheight) } } From e5accc7611d5f836e2793c9e3205ca536822b574 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 15 Jan 2020 13:54:31 -0500 Subject: [PATCH 38/54] update height --- MVMCoreUI/Molecules/DoughnutChart.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/DoughnutChart.swift index e6a169f1..1175a563 100644 --- a/MVMCoreUI/Molecules/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/DoughnutChart.swift @@ -20,9 +20,9 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { var labelContainer = ViewConstrainingView.empty() var heightConstraint: NSLayoutConstraint? - private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 100)! + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 150)! - var height: CGFloat = 100 { + var height: CGFloat = 150 { didSet { if height != oldValue { sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)! From e95afdc2491ba00193122adc00ed02172f4e9d74 Mon Sep 17 00:00:00 2001 From: "Pan, Xinlei (Ryan)" Date: Wed, 15 Jan 2020 16:36:13 -0500 Subject: [PATCH 39/54] update color --- MVMCoreUI/Atoms/Views/GraphView.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/GraphView.swift b/MVMCoreUI/Atoms/Views/GraphView.swift index cda77660..1f6ea45b 100644 --- a/MVMCoreUI/Atoms/Views/GraphView.swift +++ b/MVMCoreUI/Atoms/Views/GraphView.swift @@ -112,7 +112,9 @@ import UIKit //if number of colors is even, need to display gradient layer, otherwise make top layer as solid color layer if graphObject.colors.count % 2 == 0 { leftColors.removeLast() - topLayer.colors = [leftColors.last!, rightColors.first!] + let firstColor = leftColors.last!.uiColor.cgColor + let secondColor = rightColors.first!.uiColor.cgColor + topLayer.colors = [firstColor, secondColor] } else { topLayer.backgroundColor = leftColors.last?.uiColor.cgColor } @@ -125,7 +127,9 @@ import UIKit //count of graidentLayer.colors must be bigger than 1, otherwise set backgroundColor if leftColors.count > 1 { - leftLayer.colors = Array(leftColors) + leftLayer.colors = leftColors.map({ (color) -> CGColor in + return color.uiColor.cgColor + }) } else { leftLayer.backgroundColor = leftColors.first?.uiColor.cgColor } @@ -136,7 +140,9 @@ import UIKit rightLayer.startPoint = CGPoint(x: 0, y: 0) rightLayer.endPoint = CGPoint(x: 0, y: 1) if rightColors.count > 1 { - rightLayer.colors = Array(rightColors) + rightLayer.colors = rightColors.map({ (color) -> CGColor in + return color.uiColor.cgColor + }) } else { rightLayer.backgroundColor = rightColors.first?.uiColor.cgColor } From d69dbc41d00229c4c7d57a29ac9682a047ae5511 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 15 Jan 2020 18:27:56 -0500 Subject: [PATCH 40/54] fixes --- MVMCoreUI.xcodeproj/project.pbxproj | 4 ++++ .../Atoms/Views/MVMCoreUISwitch+Model.swift | 2 +- MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m | 8 +++---- MVMCoreUI/Atoms/Views/Toggle.swift | 9 +++++++ MVMCoreUI/Atoms/Views/ToggleModel.swift | 24 ++++++++++++++----- .../TwoButtonView.swift | 4 ++-- .../SwitchMolecules/LabelSwitch.swift | 12 ++++++++-- .../SwitchMolecules/LabelToggleModel.swift | 16 +++++++++++++ .../OtherHandlers/MoleculeObjectMapping.swift | 2 ++ 9 files changed, 66 insertions(+), 15 deletions(-) create mode 100644 MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelToggleModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 022ed193..5d855ab3 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0198F79E225679870066C936 /* FormValidationProtocol.swift */; }; 0198F7A62256A80B0066C936 /* MFRadioButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 0198F7A02256A80A0066C936 /* MFRadioButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 0198F7A22256A80A0066C936 /* MFRadioButton.m */; }; + 01C851D323CF9E740021F976 /* LabelToggleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C851D223CF9E740021F976 /* LabelToggleModel.swift */; }; 01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 01EB3684236097C0006832FA /* MoleculeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB3683236097C0006832FA /* MoleculeProtocol.swift */; }; 01EB368F23609801006832FA /* LabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368823609801006832FA /* LabelModel.swift */; }; @@ -333,6 +334,7 @@ 0198F79E225679870066C936 /* FormValidationProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormValidationProtocol.swift; sourceTree = ""; }; 0198F7A02256A80A0066C936 /* MFRadioButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFRadioButton.h; sourceTree = ""; }; 0198F7A22256A80A0066C936 /* MFRadioButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFRadioButton.m; sourceTree = ""; }; + 01C851D223CF9E740021F976 /* LabelToggleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelToggleModel.swift; sourceTree = ""; }; 01EB3683236097C0006832FA /* MoleculeProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeProtocol.swift; sourceTree = ""; }; 01EB368823609801006832FA /* LabelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelModel.swift; sourceTree = ""; }; 01EB368923609801006832FA /* ListItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListItemModel.swift; sourceTree = ""; }; @@ -756,6 +758,7 @@ children = ( 01509D942327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift */, D22479892314445E003FCCF9 /* LabelSwitch.swift */, + 01C851D223CF9E740021F976 /* LabelToggleModel.swift */, D224798B231450C8003FCCF9 /* HeadlineBodySwitch.swift */, ); path = SwitchMolecules; @@ -1438,6 +1441,7 @@ 9445890C2385BCE300DE9FD4 /* ProgressBarModel.swift in Sources */, D29DF17C21E69E1F003B2FB9 /* MFTextButton.m in Sources */, 9445891F2385D2E900DE9FD4 /* CaretViewModel.swift in Sources */, + 01C851D323CF9E740021F976 /* LabelToggleModel.swift in Sources */, D29DF2C521E7BF57003B2FB9 /* MFTabBarSwipeAnimator.m in Sources */, 012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */, D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */, diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift index 68482f09..e62a8b94 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift @@ -17,7 +17,7 @@ extension MVMCoreUISwitch: ModelMoleculeViewProtocol { FormValidator.setupValidation(molecule: castSelf, delegate: delegateObject?.formValidationProtocol) } - setState(model.on, animated: false) + setState(model.state, animated: false) guard let action = model.action else { return } actionBlock = { diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m index 4b66d519..1c40bc5a 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m @@ -423,16 +423,16 @@ const CGFloat SwitchShakeIntensity = 2; return UIAccessibilityTraitButton; } -- (NSString * _Nullable)formFieldGroupName { - return [self.json string:@"groupName"]; -} - - (NSString *)accessibilityHint { return [MVMCoreUIUtility hardcodedStringWithKey:@"AccToggleHint"]; } #pragma mark FormValidationProtocol +- (NSString * _Nullable)formFieldGroupName { + return [self.json string:@"groupName"]; +} + - (BOOL)isValidField { return self.isOn && [self.json boolForKey:@"required"]; } diff --git a/MVMCoreUI/Atoms/Views/Toggle.swift b/MVMCoreUI/Atoms/Views/Toggle.swift index 17d286ac..4e855532 100644 --- a/MVMCoreUI/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atoms/Views/Toggle.swift @@ -332,6 +332,15 @@ public typealias ActionBlockConfirmation = () -> (Bool) layoutIfNeeded() } } + + public override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let toggleModel = model as? ToggleModel else { + return + } + + let toggleModelJSON = toggleModel.toJSON() + setWithJSON(toggleModelJSON, delegateObject: delegateObject, additionalData: additionalData) + } } // MARK: - Accessibility diff --git a/MVMCoreUI/Atoms/Views/ToggleModel.swift b/MVMCoreUI/Atoms/Views/ToggleModel.swift index df7d2ba8..81602c2a 100644 --- a/MVMCoreUI/Atoms/Views/ToggleModel.swift +++ b/MVMCoreUI/Atoms/Views/ToggleModel.swift @@ -10,32 +10,44 @@ import UIKit public class ToggleModel: MoleculeProtocol { public static var identifier: String = "toggle" + public var moleculeName: String? public var backgroundColor: Color? - public var on: Bool = true + public var state: Bool = true public var action: ActionProtocol? + public var required: Bool? + public var fieldKey: String? enum CodingKeys: String, CodingKey { - case on + case moleculeName + case state case action case backgroundColor + case required + case fieldKey } - public init(_ on: Bool) { - self.on = on + public init(_ state: Bool) { + self.state = state } required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - if let on = try typeContainer.decodeIfPresent(Bool.self, forKey: .on) { - self.on = on + if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { + self.state = state } action = try typeContainer.decodeModelIfPresent(codingKey: .action, typeCodingKey: ActionCodingKey.actionType) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + required = try typeContainer.decodeIfPresent(Bool.self, forKey: .required) + fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeModelIfPresent(action, forKey: .action) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(state, forKey: .state) + try container.encodeIfPresent(required, forKey: .required) + try container.encodeIfPresent(fieldKey, forKey: .fieldKey) } } diff --git a/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift b/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift index b2f43529..9a7bd952 100644 --- a/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift +++ b/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift @@ -297,8 +297,8 @@ extension TwoButtonView { } } -extension TwoButtonView: MoleculeViewProtocol { - func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { +extension TwoButtonView: ModelMoleculeViewProtocol { + public func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let model = model as? TwoButtonViewModel else { return } setupUI(primaryButtonShowing: model.primaryButton != nil, secondaryButtonShowing: model.secondaryButton != nil) setDefaultCustom() diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift index 50c19c1d..0713d40f 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift @@ -8,9 +8,9 @@ import UIKit -@objcMembers public class LabelSwitch: ViewConstrainingView { +@objcMembers public class LabelSwitch: ViewConstrainingView, ModelMoleculeViewProtocol { let label = Label.commonLabelB1(true) - let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault() + let mvmSwitch = Toggle() // MARK: - MVMCoreViewProtocol open override func updateView(_ size: CGFloat) { @@ -40,6 +40,14 @@ import UIKit label.setWithJSON(json?.optionalDictionaryForKey("label"), delegateObject: delegateObject, additionalData: additionalData) mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("toggle"), delegateObject: delegateObject, additionalData: additionalData) } + + public func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let labelToggleModel = model as? LabelToggleModel else { + return + } + label.setWithModel(labelToggleModel.label, delegateObject, additionalData) + mvmSwitch.setWithModel(labelToggleModel.toggle, delegateObject, additionalData) + } public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return MVMCoreUISwitch.estimatedHeight(forRow: json, delegateObject: delegateObject) diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelToggleModel.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelToggleModel.swift new file mode 100644 index 00000000..6ac592d6 --- /dev/null +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelToggleModel.swift @@ -0,0 +1,16 @@ +// +// LabelToggle.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 1/15/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class LabelToggleModel: MoleculeProtocol { + public static var identifier: String = "labelToggle" + public var backgroundColor: Color? + public var label: LabelModel + public var toggle: ToggleModel +} diff --git a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift index 1a041e2c..d84f48b7 100644 --- a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift +++ b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift @@ -12,6 +12,7 @@ import Foundation public static func registerObjects() { ModelRegistry.register(LabelModel.self) ModelRegistry.register(HeaderModel.self) + ModelRegistry.register(FooterModel.self) ModelRegistry.register(HeadlineBodyModel.self) ModelRegistry.register(MoleculeStackModel.self) ModelRegistry.register(StackItemModel.self) @@ -51,5 +52,6 @@ import Foundation ModelRegistry.register(LeftRightLabelModel.self) ModelRegistry.register(CaretViewModel.self) ModelRegistry.register(CaretLinkModel.self) + ModelRegistry.register(LabelToggleModel.self) } } From de27398c49de13fac16143703c90698db43fb865 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 15 Jan 2020 18:59:48 -0500 Subject: [PATCH 41/54] leftRightLabelView --- MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift index 5e091446..be703bef 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift @@ -9,7 +9,7 @@ import UIKit @objcMembers public class LeftRightLabelModel: MoleculeProtocol { - public static var identifier: String = "leftRightLabel" + public static var identifier: String = "leftRightLabelView" public var backgroundColor: Color? public var leftText: LabelModel public var rightText: LabelModel From 9bf9d711951dc078eab2b1268f2ae28e2f5fe2b1 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 16 Jan 2020 13:30:30 -0500 Subject: [PATCH 42/54] optional --- MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift | 2 +- .../VerticalCombinationViews/HeadlineBodyModel.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift index be703bef..73abb4d6 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift @@ -12,5 +12,5 @@ import UIKit public static var identifier: String = "leftRightLabelView" public var backgroundColor: Color? public var leftText: LabelModel - public var rightText: LabelModel + public var rightText: LabelModel? } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift index 6ce51d3e..bc63c12c 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift @@ -10,8 +10,8 @@ import Foundation @objcMembers public class HeadlineBodyModel: MoleculeProtocol { public static var identifier: String = "headlineBody" - public var headline: LabelModel - public var body: LabelModel + public var headline: LabelModel? + public var body: LabelModel? public var style: String? public var backgroundColor: Color? From 71efaa9026dd839c0af64a1c3e8220e4c4907354 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 16 Jan 2020 13:51:55 -0500 Subject: [PATCH 43/54] alignment --- MVMCoreUI/Organisms/MoleculeStackView.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 9f2f526f..aad48bd0 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -107,8 +107,6 @@ open class MoleculeStackView: Container { } restack() - stackModel?.useHorizontalMargins = moleculesShouldSetHorizontalMargins - stackModel?.useVerticalMargins = moleculesShouldSetVerticalMargins } open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { From b876d81922373bda1a1ecabe0ca8fea1aa3aa6bf Mon Sep 17 00:00:00 2001 From: "Pan, Xinlei (Ryan)" Date: Thu, 16 Jan 2020 14:29:30 -0500 Subject: [PATCH 44/54] two button view's internal spacing constraint change for a huge height bug --- .../TwoButtonView.swift | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift b/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift index 9a7bd952..10a41daa 100644 --- a/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift +++ b/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift @@ -84,17 +84,19 @@ import UIKit } open func setupConstraintsForViewWithButtons() { - guard let viewForButtons = viewForButtons, let primaryButton = primaryButton, let secondaryButton = secondaryButton else { - return - } + guard let viewForButtons = viewForButtons, + let primaryButton = primaryButton, + let secondaryButton = secondaryButton + else { return } + viewForButtons.addSubview(primaryButton) viewForButtons.addSubview(secondaryButton) secondaryButton.widthAnchor.constraint(equalTo: primaryButton.widthAnchor, multiplier: 1).isActive = true - secondaryButton.topAnchor.constraint(equalTo: viewForButtons.topAnchor).isActive = true - primaryButton.topAnchor.constraint(equalTo: viewForButtons.topAnchor).isActive = true - viewForButtons.bottomAnchor.constraint(equalTo: secondaryButton.bottomAnchor).isActive = true - viewForButtons.bottomAnchor.constraint(equalTo: primaryButton.bottomAnchor).isActive = true - NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[leftButton]-10-[rightButton]-0-|", options: NSLayoutConstraint.FormatOptions.alignAllCenterY, metrics: nil, views: ["leftButton": secondaryButton, "rightButton": primaryButton])) + NSLayoutConstraint.constraintPinSubview(primaryButton, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false) + NSLayoutConstraint.constraintPinSubview(secondaryButton, pinTop: false, pinBottom: false, pinLeft: false, pinRight: true) + let constraint = secondaryButton.leadingAnchor.constraint(equalTo: primaryButton.trailingAnchor, constant: 10) + constraint.priority = UILayoutPriority(900) + constraint.isActive = true } func setupWithTwoButtons() { @@ -107,7 +109,6 @@ import UIKit pinView(toSuperView: viewForButtons) alignCenterHorizontal() - createPrimaryButton() createSecondaryButton() setupConstraintsForViewWithButtons() From db0d9f43531d19da30b7991a081e738133735d51 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 16 Jan 2020 14:59:24 -0500 Subject: [PATCH 45/54] Container protocol Doughnut chart model Break stack into regular and dynamic components --- MVMCoreUI.xcodeproj/project.pbxproj | 65 +++-- MVMCoreUI/Containers/Container.swift | 15 +- MVMCoreUI/Containers/ContainerProtocol.swift | 15 ++ .../{ => Doughnut}/DoughnutChart.swift | 80 +++--- .../Doughnut}/DoughnutChartModel.swift | 22 +- .../{ => Doughnut}/DoughnutChartView.swift | 103 ++++--- .../Molecules/Items/MoleculeStackItem.swift | 16 ++ .../Items/MoleculeStackItemModel.swift | 45 ++++ MVMCoreUI/Molecules/Items/StackItem.swift | 8 +- .../Molecules/Items/StackItemModel.swift | 39 +-- .../Items/StackItemModelProtocol.swift | 15 ++ .../EyebrowHeadlineBodyLink.swift | 25 +- MVMCoreUI/Organisms/MoleculeStackModel.swift | 10 +- MVMCoreUI/Organisms/MoleculeStackView.swift | 252 +----------------- MVMCoreUI/Organisms/Stack.swift | 239 +++++++++++++++++ MVMCoreUI/Organisms/StackModel.swift | 22 ++ MVMCoreUI/Organisms/StackModelProtocol.swift | 18 ++ .../MVMCoreUIMoleculeMappingObject.m | 1 - .../OtherHandlers/MoleculeObjectMapping.swift | 4 +- .../Templates/MoleculeStackTemplate.swift | 4 +- .../NSLayoutConstraintAxis+Extension.swift | 31 +++ 21 files changed, 602 insertions(+), 427 deletions(-) create mode 100644 MVMCoreUI/Containers/ContainerProtocol.swift rename MVMCoreUI/Molecules/{ => Doughnut}/DoughnutChart.swift (65%) rename MVMCoreUI/{Models/Molecules => Molecules/Doughnut}/DoughnutChartModel.swift (50%) rename MVMCoreUI/Molecules/{ => Doughnut}/DoughnutChartView.swift (55%) create mode 100644 MVMCoreUI/Molecules/Items/MoleculeStackItem.swift create mode 100644 MVMCoreUI/Molecules/Items/MoleculeStackItemModel.swift create mode 100644 MVMCoreUI/Molecules/Items/StackItemModelProtocol.swift create mode 100644 MVMCoreUI/Organisms/Stack.swift create mode 100644 MVMCoreUI/Organisms/StackModel.swift create mode 100644 MVMCoreUI/Organisms/StackModelProtocol.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 32d9b7ba..e2e09a72 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -105,10 +105,10 @@ 94C661D923CCF4B400D9FE5B /* LeftRightLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9402C34F23A2CEA3004B974C /* LeftRightLabelModel.swift */; }; 94C661DA23CCF4FB00D9FE5B /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B33239813C50067DD0F /* UIColor+Extension.swift */; }; C003506123AA94CD00B6AC29 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = C003506023AA94CD00B6AC29 /* Button.swift */; }; - C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */; }; C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69323C9909000BFB94E /* DoughnutChartModel.swift */; }; C695A69623C990BC00BFB94E /* DoughnutChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69523C990BC00BFB94E /* DoughnutChart.swift */; }; C695A69823C990C200BFB94E /* DoughnutChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69723C990C200BFB94E /* DoughnutChartView.swift */; }; + C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */; }; @@ -128,10 +128,17 @@ D243859923A16B1800332775 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = D243859823A16B1800332775 /* Container.swift */; }; D260105323CEA61600764D80 /* ToggleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105223CEA61600764D80 /* ToggleModel.swift */; }; D260105523CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */; }; + D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105823D0A92900764D80 /* ContainerProtocol.swift */; }; + D260105B23D0BB7100764D80 /* StackModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105A23D0BB7100764D80 /* StackModelProtocol.swift */; }; + D260105D23D0BCD400764D80 /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105C23D0BCD400764D80 /* Stack.swift */; }; + D260105F23D0BFFC00764D80 /* StackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105E23D0BFFC00764D80 /* StackItem.swift */; }; + D260106123D0C02A00764D80 /* StackItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106023D0C02A00764D80 /* StackItemModelProtocol.swift */; }; + D260106323D0C05000764D80 /* StackItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106223D0C05000764D80 /* StackItemModel.swift */; }; + D260106523D0CEA700764D80 /* StackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106423D0CEA700764D80 /* StackModel.swift */; }; D260D7B122D65BDD007E7233 /* MVMCoreUIPageControl.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */; }; D260D7B622D68514007E7233 /* MVMCoreUIPagingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D268C70C2386DFFD007F2C1C /* StackItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368A23609801006832FA /* StackItemModel.swift */; }; + D268C70C2386DFFD007F2C1C /* MoleculeStackItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */; }; D268C70E238C22D7007F2C1C /* DropDownFilterTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */; }; D268C712238D6699007F2C1C /* DropDown.swift in Sources */ = {isa = PBXBuildFile; fileRef = D268C711238D6699007F2C1C /* DropDown.swift */; }; D274CA332236A78900B01B62 /* StandardFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D274CA322236A78900B01B62 /* StandardFooterView.swift */; }; @@ -282,7 +289,7 @@ D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; }; D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; }; D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; }; - D2FB151D23A40F1500C20E10 /* StackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* StackItem.swift */; }; + D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */; }; DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; }; DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; }; DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; }; @@ -338,7 +345,7 @@ 01EB3683236097C0006832FA /* MoleculeProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeProtocol.swift; sourceTree = ""; }; 01EB368823609801006832FA /* LabelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelModel.swift; sourceTree = ""; }; 01EB368923609801006832FA /* ListItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListItemModel.swift; sourceTree = ""; }; - 01EB368A23609801006832FA /* StackItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackItemModel.swift; sourceTree = ""; }; + 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackItemModel.swift; sourceTree = ""; }; 01EB368B23609801006832FA /* MoleculeStackModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackModel.swift; sourceTree = ""; }; 01EB368C23609801006832FA /* HeaderModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderModel.swift; sourceTree = ""; }; 01EB368D23609801006832FA /* HeadlineBodyModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeadlineBodyModel.swift; sourceTree = ""; }; @@ -380,10 +387,10 @@ 94C2D9A823872E5E0006CF46 /* LabelAttributeImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeImageModel.swift; sourceTree = ""; }; 94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeActionModel.swift; sourceTree = ""; }; C003506023AA94CD00B6AC29 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; - C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadLineBodyCaretLinkImage.swift; sourceTree = ""; }; C695A69323C9909000BFB94E /* DoughnutChartModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartModel.swift; sourceTree = ""; }; C695A69523C990BC00BFB94E /* DoughnutChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChart.swift; sourceTree = ""; }; C695A69723C990C200BFB94E /* DoughnutChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartView.swift; sourceTree = ""; }; + C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadLineBodyCaretLinkImage.swift; sourceTree = ""; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLayoutConstraintAxis+Extension.swift"; sourceTree = ""; }; @@ -403,6 +410,13 @@ D243859823A16B1800332775 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; D260105223CEA61600764D80 /* ToggleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleModel.swift; sourceTree = ""; }; D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUISwitch+Model.swift"; sourceTree = ""; }; + D260105823D0A92900764D80 /* ContainerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerProtocol.swift; sourceTree = ""; }; + D260105A23D0BB7100764D80 /* StackModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackModelProtocol.swift; sourceTree = ""; }; + D260105C23D0BCD400764D80 /* Stack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; + D260105E23D0BFFC00764D80 /* StackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackItem.swift; sourceTree = ""; }; + D260106023D0C02A00764D80 /* StackItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackItemModelProtocol.swift; sourceTree = ""; }; + D260106223D0C05000764D80 /* StackItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackItemModel.swift; sourceTree = ""; }; + D260106423D0CEA700764D80 /* StackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackModel.swift; sourceTree = ""; }; D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPageControl.h; sourceTree = ""; }; D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIPageControl.m; sourceTree = ""; }; D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPagingProtocol.h; sourceTree = ""; }; @@ -572,7 +586,7 @@ D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = ""; }; D2F4DDE52371A4CB00CD28BB /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.swift; sourceTree = ""; }; - D2FB151C23A40F1500C20E10 /* StackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackItem.swift; sourceTree = ""; }; + D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackItem.swift; sourceTree = ""; }; DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = ""; }; DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = ""; }; @@ -665,10 +679,6 @@ 01EB368723609801006832FA /* Molecules */ = { isa = PBXGroup; children = ( - C695A69323C9909000BFB94E /* DoughnutChartModel.swift */, - 01EB368923609801006832FA /* ListItemModel.swift */, - 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */, - 01EB368B23609801006832FA /* MoleculeStackModel.swift */, 011B58F323A2CCC80085F53C /* DropDownModel.swift */, 01EB368C23609801006832FA /* HeaderModel.swift */, 012A88EB238F084D00FE3DA1 /* FooterModel.swift */, @@ -806,6 +816,9 @@ D22479902316A9CB003FCCF9 /* Organisms */ = { isa = PBXGroup; children = ( + D260105A23D0BB7100764D80 /* StackModelProtocol.swift */, + D260106423D0CEA700764D80 /* StackModel.swift */, + D260105C23D0BCD400764D80 /* Stack.swift */, 01EB368B23609801006832FA /* MoleculeStackModel.swift */, D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */, D2A6390022CBB1820052ED1F /* Carousel.swift */, @@ -826,8 +839,11 @@ D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */, 011B58F123A2AE2C0085F53C /* DropDownListItemModel.swift */, D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */, - 01EB368A23609801006832FA /* StackItemModel.swift */, - D2FB151C23A40F1500C20E10 /* StackItem.swift */, + D260106023D0C02A00764D80 /* StackItemModelProtocol.swift */, + D260106223D0C05000764D80 /* StackItemModel.swift */, + D260105E23D0BFFC00764D80 /* StackItem.swift */, + 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */, + D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */, ); path = Items; sourceTree = ""; @@ -841,6 +857,16 @@ path = Legacy; sourceTree = ""; }; + D260105723CF9CC500764D80 /* Doughnut */ = { + isa = PBXGroup; + children = ( + C695A69323C9909000BFB94E /* DoughnutChartModel.swift */, + C695A69523C990BC00BFB94E /* DoughnutChart.swift */, + C695A69723C990C200BFB94E /* DoughnutChartView.swift */, + ); + path = Doughnut; + sourceTree = ""; + }; D29DF0C221E404D4003B2FB9 = { isa = PBXGroup; children = ( @@ -937,8 +963,7 @@ D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */, 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, 017BEB49236235BA0024EF95 /* ModelMoleculeViewProtocol.swift */, - C695A69523C990BC00BFB94E /* DoughnutChart.swift */, - C695A69723C990C200BFB94E /* DoughnutChartView.swift */, + D260105723CF9CC500764D80 /* Doughnut */, ); path = Molecules; sourceTree = ""; @@ -986,6 +1011,7 @@ D29DF2B721E7BE79003B2FB9 /* TabBarController */, D29DF2B621E7BE66003B2FB9 /* SplitViewController */, D2B18B93236214AD00A9AEDC /* NavigationController.swift */, + D260105823D0A92900764D80 /* ContainerProtocol.swift */, D243859823A16B1800332775 /* Container.swift */, ); path = Containers; @@ -1424,7 +1450,7 @@ 94C2D9A923872E5E0006CF46 /* LabelAttributeImageModel.swift in Sources */, DBC4391922442197001AB423 /* DashLine.swift in Sources */, 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, - D2FB151D23A40F1500C20E10 /* StackItem.swift in Sources */, + D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, 0116A4E5228B19640094F3ED /* RadioButtonModel.swift in Sources */, 017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */, @@ -1449,6 +1475,7 @@ D29DF2C521E7BF57003B2FB9 /* MFTabBarSwipeAnimator.m in Sources */, 012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */, D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */, + D260106323D0C05000764D80 /* StackItemModel.swift in Sources */, 01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */, 0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */, D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */, @@ -1465,6 +1492,7 @@ D28A838723CCCF6500DFE4FC /* MFTextButton+ModelExtension.swift in Sources */, C695A69623C990BC00BFB94E /* DoughnutChart.swift in Sources */, 014AA72D23C5059B006F3E93 /* StackPageTemplateModel.swift in Sources */, + D260106123D0C02A00764D80 /* StackItemModelProtocol.swift in Sources */, 012A88C4238D86E600FE3DA1 /* CollectionCellMoleculeProtocol.swift in Sources */, 94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */, 014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */, @@ -1486,7 +1514,7 @@ D2A514672213885800345BFB /* StandardHeaderView.swift in Sources */, 01EB369023609801006832FA /* ListItemModel.swift in Sources */, D28A838323CCBD3F00DFE4FC /* CircleProgressModel.swift in Sources */, - D268C70C2386DFFD007F2C1C /* StackItemModel.swift in Sources */, + D268C70C2386DFFD007F2C1C /* MoleculeStackItemModel.swift in Sources */, DBEFFA04225A829700230692 /* Label.swift in Sources */, D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */, 01509D952327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift in Sources */, @@ -1504,6 +1532,7 @@ D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */, D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, + D260105D23D0BCD400764D80 /* Stack.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, 01EB368F23609801006832FA /* LabelModel.swift in Sources */, 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, @@ -1559,9 +1588,11 @@ D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, 0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */, D243859923A16B1800332775 /* Container.swift in Sources */, + D260105B23D0BB7100764D80 /* StackModelProtocol.swift in Sources */, D29DF29821E7ADB8003B2FB9 /* MFScrollingViewController.m in Sources */, D28A839323CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift in Sources */, D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */, + D260105F23D0BFFC00764D80 /* StackItem.swift in Sources */, 01EB369323609801006832FA /* HeaderModel.swift in Sources */, D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */, 0A21DB83235DFBC500C160A2 /* MdnEntryField.swift in Sources */, @@ -1601,6 +1632,7 @@ 014AA72E23C5059B006F3E93 /* StackCenteredPageTemplateModel.swift in Sources */, 0ABD136B237B193A0081388D /* EntryFieldContainer.swift in Sources */, D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */, + D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */, C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */, D29DF32421ED0DA2003B2FB9 /* TextButtonView.m in Sources */, 012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */, @@ -1616,6 +1648,7 @@ D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */, 946EE1BA237B66D80036751F /* MoleculeModelHelper.swift in Sources */, 01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */, + D260106523D0CEA700764D80 /* StackModel.swift in Sources */, D29770F521F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/MVMCoreUI/Containers/Container.swift b/MVMCoreUI/Containers/Container.swift index ab495b81..43fd62c8 100644 --- a/MVMCoreUI/Containers/Container.swift +++ b/MVMCoreUI/Containers/Container.swift @@ -196,7 +196,7 @@ public class ContainerHelper: NSObject { } } -open class Container: View { +open class Container: View, ContainerProtocol { var view: UIView? let containerHelper = ContainerHelper() var containerModel: ContainerModelProtocol? { @@ -208,6 +208,19 @@ open class Container: View { guard let containerModel = model as? ContainerModelProtocol else { return } containerHelper.set(with: containerModel, for: view as? MVMCoreUIViewConstrainingProtocol) } + + // MARK:- ContainerProtocol + public func alignHorizontal(_ alignment: UIStackView.Alignment) { + containerHelper.alignHorizontal(alignment) + } + + public func alignVertical(_ alignment: UIStackView.Alignment) { + containerHelper.alignVertical(alignment) + } + + public func constrainView(_ view: UIView) { + containerHelper.constrainView(view) + } } // MARK: - MVMCoreViewProtocol diff --git a/MVMCoreUI/Containers/ContainerProtocol.swift b/MVMCoreUI/Containers/ContainerProtocol.swift new file mode 100644 index 00000000..8079ca24 --- /dev/null +++ b/MVMCoreUI/Containers/ContainerProtocol.swift @@ -0,0 +1,15 @@ +// +// ContainerProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol ContainerProtocol { + func alignHorizontal(_ alignment: UIStackView.Alignment) + func alignVertical(_ alignment: UIStackView.Alignment) + func constrainView(_ view: UIView) +} diff --git a/MVMCoreUI/Molecules/DoughnutChart.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift similarity index 65% rename from MVMCoreUI/Molecules/DoughnutChart.swift rename to MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift index d7f25ac6..5b0763e6 100644 --- a/MVMCoreUI/Molecules/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift @@ -8,21 +8,23 @@ import UIKit -open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { - - var containerView = MVMCoreUICommonViewsUtility.commonView() - var containerLayer = CALayer() +open class DoughnutChart: View { + var doughnutLayer = CALayer() var titleLabel = Label.commonLabelH3(true) var subTitleLabel = Label.commonLabelB2(true) var doughnutChartModel: DoughnutChartModel? { get { return model as? DoughnutChartModel } } - var labelContainer = ViewConstrainingView.empty() + var labelContainer = MVMCoreUICommonViewsUtility.commonView() + var labelContainerLeftConstraint: NSLayoutConstraint? + var labelContainerTopConstraint: NSLayoutConstraint? + var labelContainerBottomConstraint: NSLayoutConstraint? + var labelContainerRightConstraint: NSLayoutConstraint? var heightConstraint: NSLayoutConstraint? - private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 150)! - - var height: CGFloat = 150 { + static let heightConstant: CGFloat = 150 + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: heightConstant)! + var height: CGFloat = heightConstant { didSet { if height != oldValue { sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)! @@ -47,17 +49,16 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { clearLayers() } - public override func setAsMolecule() { + public override func setAsMolecule() { titleLabel.setAsMolecule() subTitleLabel.setAsMolecule() } open override func setupView() { super.setupView() - guard containerView.superview == nil else { + guard labelContainer.superview == nil else { return } - addSubview(containerView) addSubview(labelContainer) labelContainer.addSubview(titleLabel) @@ -68,36 +69,35 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { //Make label font size to adjust width if label content is high titleLabel.numberOfLines = 1 titleLabel.adjustsFontSizeToFitWidth = true - - containerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - containerView.topAnchor.constraint(equalTo: topAnchor).isActive = true - bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true - trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true - containerView.layer.addSublayer(containerLayer) - heightConstraint = containerView.heightAnchor.constraint(equalToConstant: + + layer.addSublayer(doughnutLayer) + heightConstraint = heightAnchor.constraint(equalToConstant: sizeObject.getValueBasedOnApplicationWidth()) heightConstraint?.isActive = true - containerView.widthAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true + widthAnchor.constraint(equalTo: heightAnchor).isActive = true - labelContainer.leftPin = labelContainer.leftAnchor.constraint(greaterThanOrEqualTo: containerView.leftAnchor, constant: lineWidth()) - labelContainer.leftPin?.isActive = true - labelContainer.topPin = labelContainer.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor, constant: lineWidth()) - labelContainer.topPin?.isActive = true - labelContainer.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true - labelContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true + labelContainerLeftConstraint = labelContainer.leftAnchor.constraint(greaterThanOrEqualTo: leftAnchor, constant: lineWidth()) + labelContainerLeftConstraint?.isActive = true + labelContainerTopConstraint = labelContainer.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: lineWidth()) + labelContainerTopConstraint?.isActive = true + labelContainerRightConstraint = rightAnchor.constraint(greaterThanOrEqualTo: labelContainer.rightAnchor, constant: lineWidth()) + labelContainerRightConstraint?.isActive = true + labelContainerBottomConstraint = bottomAnchor.constraint(greaterThanOrEqualTo: labelContainer.bottomAnchor, constant: lineWidth()) + labelContainerBottomConstraint?.isActive = true + labelContainer.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + labelContainer.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true NSLayoutConstraint.constraintPinSubview(titleLabel, pinTop: true, pinBottom: false, pinLeft: true, pinRight: true) NSLayoutConstraint.constraintPinSubview(subTitleLabel, pinTop: false, pinBottom: true, pinLeft: true, pinRight: true) _ = NSLayoutConstraint(pinFirstView: titleLabel, toSecondView: subTitleLabel, withConstant: 0, directionVertical: true) //Rotate view for initial draw - containerLayer.transform = CATransform3DMakeRotation(1 * .pi, 0.0, 0.0, 1.0) - + doughnutLayer.transform = CATransform3DMakeRotation(1 * .pi, 0.0, 0.0, 1.0) } open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.setWithModel(model, delegateObject, additionalData) clearLayers() - guard let doughnutChartModel = model as? DoughnutChartModel else { + guard let doughnutChartModel = doughnutChartModel else { return } titleLabel.setWithModel(doughnutChartModel.title, delegateObject, additionalData) @@ -111,7 +111,7 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { func drawGraph() { clearLayers() let widthHeight = sizeObject.getValueBasedOnApplicationWidth() - containerLayer.frame = CGRect(x: 0, y: 0, + doughnutLayer.frame = CGRect(x: 0, y: 0, width: widthHeight, height: widthHeight) if let doughnutChart = doughnutChartModel { @@ -122,17 +122,16 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { } } - func drawBar(_ chartModel: ChartItemModel, _ prevPercent: CGFloat) -> CGFloat { + func drawBar(_ chartModel: DoughnutChartItemModel, _ prevPercent: CGFloat) -> CGFloat { let shapeLayer = CAShapeLayer() - shapeLayer.frame = containerLayer.frame + shapeLayer.frame = doughnutLayer.frame shapeLayer.lineWidth = lineWidth() shapeLayer.fillColor = nil - shapeLayer.strokeColor = UIColor.mfGet(forHex: chartModel.color).cgColor + shapeLayer.strokeColor = chartModel.color.uiColor.cgColor let arcCenter = shapeLayer.position - let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth()/2.0 - //lineSizeObject.getValueBasedOnApplicationWidth() / 2.0 + let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth()/2.0 let clockwise = true let value: CGFloat = chartModel.percent @@ -146,12 +145,12 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { clockwise: clockwise) shapeLayer.path = circlePath.cgPath - containerLayer.addSublayer(shapeLayer) + doughnutLayer.addSublayer(shapeLayer) return value } func clearLayers() { - containerLayer.sublayers?.forEach({ $0.removeFromSuperlayer() }) + doughnutLayer.sublayers?.forEach({ $0.removeFromSuperlayer() }) } func updateContainer() { @@ -161,7 +160,7 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { } func lineWidth() -> CGFloat { - return doughnutChartModel?.lineWidth ?? 30 + return 30 } func lineGap() -> CGFloat { @@ -174,9 +173,10 @@ open class DoughnutChart: View, MVMCoreUIViewConstrainingProtocol { } func updateLabelContainer() { - labelContainer.leftPin?.constant = lineWidth() - labelContainer.topPin?.constant = lineWidth() + labelContainerLeftConstraint?.constant = lineWidth() + labelContainerTopConstraint?.constant = lineWidth() + labelContainerRightConstraint?.constant = lineWidth() + labelContainerBottomConstraint?.constant = lineWidth() labelContainer.setNeedsDisplay() } - } diff --git a/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChartModel.swift similarity index 50% rename from MVMCoreUI/Models/Molecules/DoughnutChartModel.swift rename to MVMCoreUI/Molecules/Doughnut/DoughnutChartModel.swift index 7aab35fd..1f4098e2 100644 --- a/MVMCoreUI/Models/Molecules/DoughnutChartModel.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChartModel.swift @@ -13,16 +13,24 @@ import Foundation public static var identifier: String = "doughnutChart" public var title: LabelModel? public var subtitle: LabelModel? - public var sections: [ChartItemModel] - public var lineWidth: CGFloat? + public var sections: [DoughnutChartItemModel] public var spaceRequired: Bool? + + public init(sections: [DoughnutChartItemModel]) { + self.sections = sections + } } - -@objcMembers public class ChartItemModel: MoleculeProtocol { +@objcMembers public class DoughnutChartItemModel: MoleculeProtocol { public var backgroundColor: Color? + public static var identifier: String = "doughnutChartItem" public var label: LabelModel - public var percent: CGFloat - public var color: String - public static var identifier: String = "chartItem" + @Percent public var percent: CGFloat + public var color: Color + + public init(percent: CGFloat, color: Color, label: LabelModel) { + self.percent = percent + self.color = color + self.label = label + } } diff --git a/MVMCoreUI/Molecules/DoughnutChartView.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift similarity index 55% rename from MVMCoreUI/Molecules/DoughnutChartView.swift rename to MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift index a54c04d4..4893edbc 100644 --- a/MVMCoreUI/Molecules/DoughnutChartView.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift @@ -9,54 +9,48 @@ import Foundation @objcMembers open class DoughnutChartView: View { - var doughnutChart = DoughnutChart(frame: CGRect.zero) var colorLablesStack = ColorViewLabelsStack() - var container = MVMCoreUICommonViewsUtility.commonView() var doughnutChartModel: DoughnutChartModel? { get { return model as? DoughnutChartModel } } open override func setupView() { super.setupView() - guard container.superview == nil else { + guard doughnutChart.superview == nil else { return } - - addSubview(container) doughnutChart.translatesAutoresizingMaskIntoConstraints = false - container.addSubview(doughnutChart) + addSubview(doughnutChart) colorLablesStack.translatesAutoresizingMaskIntoConstraints = false - container.addSubview(colorLablesStack) + addSubview(colorLablesStack) - NSLayoutConstraint.constraintPinSubview(container, pinTop: true, pinBottom: true, pinLeft: true, pinRight: true) - doughnutChart.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true - doughnutChart.topAnchor.constraint(equalTo: container.topAnchor, constant: PaddingFour).isActive = true - container.bottomAnchor.constraint(greaterThanOrEqualTo: doughnutChart.bottomAnchor).isActive = true + doughnutChart.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + doughnutChart.topAnchor.constraint(equalTo: topAnchor, constant: PaddingFour).isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: doughnutChart.bottomAnchor).isActive = true - let containerBottomAnchor = container.bottomAnchor.constraint(equalTo: doughnutChart.bottomAnchor) - containerBottomAnchor.priority = UILayoutPriority(rawValue: 200) - containerBottomAnchor.isActive = true + let doughnutBottomAnchor = bottomAnchor.constraint(equalTo: doughnutChart.bottomAnchor) + doughnutBottomAnchor.priority = UILayoutPriority(rawValue: 200) + doughnutBottomAnchor.isActive = true - let colorLablesBottomAnchor = container.bottomAnchor.constraint(equalTo: colorLablesStack.bottomAnchor) + let colorLablesBottomAnchor = bottomAnchor.constraint(equalTo: colorLablesStack.bottomAnchor) colorLablesBottomAnchor.priority = UILayoutPriority(rawValue: 204) colorLablesBottomAnchor.isActive = true let colorLablesTopAnchor = colorLablesStack.topAnchor.constraint(equalTo: doughnutChart.topAnchor) - colorLablesTopAnchor.priority = UILayoutPriority(rawValue: 204) + colorLablesTopAnchor.priority = .defaultLow colorLablesTopAnchor.isActive = true colorLablesStack.topAnchor.constraint(greaterThanOrEqualTo: doughnutChart.topAnchor).isActive = true - container.bottomAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.bottomAnchor).isActive = true - container.trailingAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.trailingAnchor).isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.bottomAnchor).isActive = true + trailingAnchor.constraint(equalTo: colorLablesStack.trailingAnchor).isActive = true let centerY = colorLablesStack.centerYAnchor.constraint(equalTo: doughnutChart.centerYAnchor) - centerY.priority = .defaultLow + centerY.priority = UILayoutPriority(rawValue: 500) centerY.isActive = true colorLablesStack.leadingAnchor.constraint(equalTo: doughnutChart.trailingAnchor, constant: PaddingThree).isActive = true colorLablesStack.backgroundColor = UIColor.clear - } open override func updateView(_ size: CGFloat) { @@ -67,56 +61,51 @@ import Foundation open override func reset() { super.reset() + doughnutChart.reset() colorLablesStack.reset() } open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.setWithModel(model, delegateObject, additionalData) - doughnutChart.clearLayers() - colorLablesStack.removeAllItemViews() + + guard let model = doughnutChartModel else { return } doughnutChart.setWithModel(model, delegateObject, additionalData) - colorLablesStack.setWithModel(model, delegateObject, additionalData) + + // Create the stack model + var stackItems: [MoleculeStackItemModel] = [] + for item in model.sections { + stackItems.append(MoleculeStackItemModel(with: item)) + } + let stack = MoleculeStackModel(molecules: stackItems) + stack.verticalAlignment = .fill + colorLablesStack.setWithModel(stack, delegateObject, additionalData) } open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - if model == nil { - let data = try! JSONSerialization.data(withJSONObject: json!) - let decoder = JSONDecoder() - let model = try! decoder.decode(DoughnutChartModel.self, from: data) - setWithModel(model, delegateObject, additionalData as? [String : AnyHashable]) - } else { - setWithModel(model, delegateObject, additionalData as? [String : AnyHashable]) - } + guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: DoughnutChartModel.self) else { return } + setWithModel(model, delegateObject, additionalData) + } +} + +extension DoughnutChartView: MVMCoreUIViewConstrainingProtocol { + open func horizontalAlignment() -> UIStackView.Alignment { + return .leading } - } class ColorViewLabelsStack: MoleculeStackView { - - var dougnutChartModel: DoughnutChartModel? - override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - let previousModel = self.dougnutChartModel - removeAllItemViews() - guard let dougnutChartModel = model as? DoughnutChartModel else { - return + override func createStackItemsFromModel(with delegate: MVMCoreUIDelegateObject?) { + guard let stackItemModels = stackModel?.molecules else { return } + for model in stackItemModels { + let view = ColorViewWithLabel() + let stackItem = MoleculeStackItem(andContain: view) + stackItem.setWithModel(model, delegate, nil) + stackItems.append(stackItem) } - var items: [StackItem]? - if previousModel?.sections.count == dougnutChartModel.sections.count { - items = stackItems - } - stackItems = [] - self.model = MoleculeStackModel(molecules: []) - for (index, chartItemModel) in dougnutChartModel.sections.enumerated() { - let colorViewWithLabel = items?[index].view as? ColorViewWithLabel ?? ColorViewWithLabel() - colorViewWithLabel.setWithModel(chartItemModel, delegateObject, additionalData) - addView(colorViewWithLabel, lastItem: index == dougnutChartModel.sections.count - 1) - } - self.dougnutChartModel = dougnutChartModel - restack() } } -class ColorViewWithLabel: View, MVMCoreUIViewConstrainingProtocol { +class ColorViewWithLabel: View { var label = Label.commonLabelB2(true) var colorView = MVMCoreUICommonViewsUtility.commonView() @@ -140,9 +129,8 @@ class ColorViewWithLabel: View, MVMCoreUIViewConstrainingProtocol { label.leadingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: PaddingOne).isActive = true label.topAnchor.constraint(equalTo: topAnchor).isActive = true - trailingAnchor.constraint(greaterThanOrEqualTo: label.trailingAnchor).isActive = true + trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true bottomAnchor.constraint(equalTo: label.bottomAnchor).isActive = true - } override func updateView(_ size: CGFloat) { @@ -163,11 +151,10 @@ class ColorViewWithLabel: View, MVMCoreUIViewConstrainingProtocol { override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.setWithModel(model, delegateObject, additionalData) - guard let chartItemModel = model as? ChartItemModel else { + guard let chartItemModel = model as? DoughnutChartItemModel else { return } label.setWithModel(chartItemModel.label, delegateObject, additionalData) - colorView.backgroundColor = UIColor.mfGet(forHex: chartItemModel.color) + colorView.backgroundColor = chartItemModel.color.uiColor } - } diff --git a/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift b/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift new file mode 100644 index 00000000..d883f93e --- /dev/null +++ b/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift @@ -0,0 +1,16 @@ +// +// StackItem.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 12/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// +// A list item that contains a molecule + +import UIKit + +open class MoleculeStackItem: MoleculeContainer { + var stackItemModel: StackItemModel? { + get { return model as? StackItemModel } + } +} diff --git a/MVMCoreUI/Molecules/Items/MoleculeStackItemModel.swift b/MVMCoreUI/Molecules/Items/MoleculeStackItemModel.swift new file mode 100644 index 00000000..e2ee337d --- /dev/null +++ b/MVMCoreUI/Molecules/Items/MoleculeStackItemModel.swift @@ -0,0 +1,45 @@ +// +// MoleculeStackItem.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 10/4/19. +// Copyright © 2019 Suresh, Kamlesh. All rights reserved. +// + +import Foundation + +@objcMembers public class MoleculeStackItemModel: MoleculeContainerModel, MoleculeProtocol, StackItemModelProtocol { + public static var identifier: String = "stackItem" + public var backgroundColor: Color? + public var spacing: CGFloat? + public var percent: Int? + public var gone: Bool = false + + enum MoleculeStackItemCodingKeys: String, CodingKey { + case spacing + case percent + case gone + } + + public override init(with moleculeModel: MoleculeProtocol) { + super.init(with: moleculeModel) + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: MoleculeStackItemCodingKeys.self) + spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing) + percent = try typeContainer.decodeIfPresent(Int.self, forKey: .percent) + if let gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) { + self.gone = gone + } + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: MoleculeStackItemCodingKeys.self) + try container.encodeIfPresent(spacing, forKey: .spacing) + try container.encodeIfPresent(percent, forKey: .percent) + try container.encode(gone, forKey: .gone) + } +} diff --git a/MVMCoreUI/Molecules/Items/StackItem.swift b/MVMCoreUI/Molecules/Items/StackItem.swift index 59c30962..07cae3d0 100644 --- a/MVMCoreUI/Molecules/Items/StackItem.swift +++ b/MVMCoreUI/Molecules/Items/StackItem.swift @@ -2,13 +2,13 @@ // StackItem.swift // MVMCoreUI // -// Created by Scott Pfeil on 12/13/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. // -import UIKit +import Foundation -open class StackItem: MoleculeContainer { +open class StackItem: Container { var stackItemModel: StackItemModel? { get { return model as? StackItemModel } } diff --git a/MVMCoreUI/Molecules/Items/StackItemModel.swift b/MVMCoreUI/Molecules/Items/StackItemModel.swift index d19f2401..9ab86687 100644 --- a/MVMCoreUI/Molecules/Items/StackItemModel.swift +++ b/MVMCoreUI/Molecules/Items/StackItemModel.swift @@ -1,45 +1,20 @@ // -// MoleculeStackItem.swift +// StackItemModel.swift // MVMCoreUI // -// Created by Suresh, Kamlesh on 10/4/19. -// Copyright © 2019 Suresh, Kamlesh. All rights reserved. +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. // import Foundation -@objcMembers public class StackItemModel: MoleculeContainerModel, MoleculeProtocol { - public static var identifier: String = "stackItem" - public var backgroundColor: Color? +@objcMembers public class StackItemModel: StackItemModelProtocol, Codable { public var spacing: CGFloat? public var percent: Int? public var gone: Bool = false - enum MoleculeStackItemCodingKeys: String, CodingKey { - case spacing - case percent - case gone - } - - public override init(with moleculeModel: MoleculeProtocol) { - super.init(with: moleculeModel) - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: MoleculeStackItemCodingKeys.self) - spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing) - percent = try typeContainer.decodeIfPresent(Int.self, forKey: .percent) - if let gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) { - self.gone = gone - } - try super.init(from: decoder) - } - - public override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: MoleculeStackItemCodingKeys.self) - try container.encodeIfPresent(spacing, forKey: .spacing) - try container.encodeIfPresent(percent, forKey: .percent) - try container.encode(gone, forKey: .gone) + public convenience init(gone: Bool) { + self.init() + self.gone = gone } } diff --git a/MVMCoreUI/Molecules/Items/StackItemModelProtocol.swift b/MVMCoreUI/Molecules/Items/StackItemModelProtocol.swift new file mode 100644 index 00000000..2223ed39 --- /dev/null +++ b/MVMCoreUI/Molecules/Items/StackItemModelProtocol.swift @@ -0,0 +1,15 @@ +// +// StackItemModelProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol StackItemModelProtocol { + var spacing: CGFloat? { get set } + var percent: Int? { get set } + var gone: Bool { get set } +} diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift index d5557eca..01844e07 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift @@ -19,7 +19,7 @@ struct EyebrowHeadlineBodyLinkModel: MoleculeProtocol { } @objcMembers open class EyebrowHeadlineBodyLink: Container { - let stack = MoleculeStackView(frame: .zero) + let stack = Stack(frame: .zero) let eyebrow = Label.commonLabelB3(true) let headline = Label.commonLabelB1(true) let body = Label.commonLabelB2(true) @@ -34,19 +34,7 @@ struct EyebrowHeadlineBodyLinkModel: MoleculeProtocol { guard stack.superview == nil else { return } - let eyebrowStackItem = StackItemModel(with: casteModel!.eyeBrow!) - let headlineStackItem = StackItemModel(with: casteModel!.headline!) - let bodyStackItem = StackItemModel(with: casteModel!.body!) - let linkStackItem = StackItemModel(with: casteModel!.link!) - - // To visually take into account the extra padding in the intrinsic content of a button. - linkStackItem.spacing = -6 - - let stackModel = MoleculeStackModel(molecules: [eyebrowStackItem,headlineStackItem,bodyStackItem,linkStackItem]) - stackModel.spacing = 0 - stack.model = stackModel stack.stackItems = [StackItem(andContain: eyebrow),StackItem(andContain: headline),StackItem(andContain: body),StackItem(andContain: link)] - addSubview(stack) NSLayoutConstraint.constraintPinSubview(toSuperview: stack) } @@ -64,13 +52,14 @@ struct EyebrowHeadlineBodyLinkModel: MoleculeProtocol { headline.setWithModel(casteModel?.headline, delegateObject, additionalData) body.setWithModel(casteModel?.body, delegateObject, additionalData) link.setWithModel(casteModel?.link, delegateObject, additionalData) - - (stack.stackItems[0].model as? StackItemModel)?.gone = !eyebrow.hasText - (stack.stackItems[1].model as? StackItemModel)?.gone = !headline.hasText - (stack.stackItems[2].model as? StackItemModel)?.gone = !body.hasText - (stack.stackItems[3].model as? StackItemModel)?.gone = ((link.titleLabel?.text?.count) ?? 0) == 0 + + // Create a stack model to use for the internal stack. + let stackModel = StackModel(molecules: [StackItemModel(gone: !eyebrow.hasText),StackItemModel(gone: !headline.hasText),StackItemModel(gone: !body.hasText),StackItemModel(gone: (link.titleLabel?.text?.count ?? 0) == 0)]) + stackModel.spacing = 0 + stack.model = stackModel stack.restack() } + open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: EyebrowHeadlineBodyLinkModel.self) else { return } setWithModel(model, delegateObject, additionalData) diff --git a/MVMCoreUI/Organisms/MoleculeStackModel.swift b/MVMCoreUI/Organisms/MoleculeStackModel.swift index 2a149e73..7136718c 100644 --- a/MVMCoreUI/Organisms/MoleculeStackModel.swift +++ b/MVMCoreUI/Organisms/MoleculeStackModel.swift @@ -5,17 +5,19 @@ // Created by Suresh, Kamlesh on 10/3/19. // Copyright © 2019 Suresh, Kamlesh. All rights reserved. // +// A stack that has a list molecule stack items. import Foundation -@objcMembers public class MoleculeStackModel: ContainerModel, MoleculeProtocol { +@objcMembers public class MoleculeStackModel: ContainerModel, MoleculeProtocol, StackModelProtocol { public static var identifier: String = "stack" public var backgroundColor: Color? - public var molecules: [StackItemModel] + public var molecules: [MoleculeStackItemModel] public var axis: NSLayoutConstraint.Axis = .vertical public var spacing: CGFloat = 16.0 + public var useStackSpacingBeforeFirstItem = false - public init(molecules: [StackItemModel]) { + public init(molecules: [MoleculeStackItemModel]) { self.molecules = molecules super.init() } @@ -29,7 +31,7 @@ import Foundation required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: StackCodingKeys.self) - molecules = try typeContainer.decode([StackItemModel].self, forKey: .molecules) + molecules = try typeContainer.decode([MoleculeStackItemModel].self, forKey: .molecules) if let axisString = try typeContainer.decodeIfPresent(String.self, forKey: .axis), let optionalAxis = NSLayoutConstraint.Axis(rawValue: axisString) { axis = optionalAxis } diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 9f2f526f..2674de0a 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -8,193 +8,22 @@ import UIKit -open class MoleculeStackView: Container { - var contentView: UIView = MVMCoreUICommonViewsUtility.commonView() - var useStackSpacingBeforeFirstItem = false - var stackModel: MoleculeStackModel? { +open class MoleculeStackView: Stack { + override var stackModel: MoleculeStackModel? { get { return model as? MoleculeStackModel } } - var stackItems: [StackItem] = [] - var moleculesShouldSetHorizontalMargins = false - var moleculesShouldSetVerticalMargins = false - - // MARK: - Helpers - public func pinView(_ view: UIView, toView: UIView, attribute: NSLayoutConstraint.Attribute, relation: NSLayoutConstraint.Relation, priority: UILayoutPriority, constant: CGFloat) { - let constraint = NSLayoutConstraint(item: view, attribute: attribute, relatedBy: relation, toItem: toView, attribute: attribute, multiplier: 1.0, constant: constant) - constraint.priority = priority - constraint.isActive = true - } - - /// Restacks the existing items. - func restack() { - removeAllItemViews() - let stackItems = self.stackItems - self.stackItems = [] - let lastItem = stackItems.last(where: { (item) -> Bool in - return !item.stackItemModel!.gone - }) - for item in stackItems { - addStackItem(item, lastItem: item === lastItem) - } - } - - /// Removes all stack items views from the view. - func removeAllItemViews() { - for item in stackItems { - item.removeFromSuperview() - } + /// Convenience function, adds a molecule to a MoleculeStackItem to the MoleculeStack + func addMolecule(_ view: View, lastItem: Bool) { + guard let model = view.model else { return } + let stackItemModel = MoleculeStackItemModel(with: model) + let stackItem = MoleculeStackItem(andContain: view) + addView(stackItem, stackItemModel, lastItem: lastItem) } - // MARK: - Inits - public override init(frame: CGRect) { - super.init(frame: frame) - } - - public init(withJSON json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - super.init(frame: CGRect.zero) - setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - } - - public required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - MFViewProtocol - public override func setupView() { - super.setupView() - guard contentView.superview == nil else { - return - } - MVMCoreUIUtility.setMarginsFor(contentView, leading: 0, top: 0, trailing: 0, bottom: 0) - translatesAutoresizingMaskIntoConstraints = false - backgroundColor = .clear - addSubview(contentView) - containerHelper.constrainView(contentView) - contentView.setContentHuggingPriority(.defaultHigh, for: .vertical) - contentView.setContentHuggingPriority(.defaultHigh, for: .horizontal) - } - - public override func updateView(_ size: CGFloat) { - super.updateView(size) - for item in stackItems { - item.updateView(size) - } - } - - // MARK: - MVMCoreUIMoleculeViewProtocol - public override func reset() { - super.reset() - backgroundColor = .clear - for item in stackItems { - item.reset() - } - } - - public override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - let previousModel = stackModel - super.setWithModel(model, delegateObject, additionalData) - removeAllItemViews() - - // If the items in the stack are different, clear them, create new ones. - if (previousModel == nil) || MoleculeStackView.nameForReuse(previousModel, delegateObject) != MoleculeStackView.nameForReuse(model, delegateObject) { - stackItems = [] - createStackItemsFromModel(with: delegateObject) - } else if let models = stackModel?.molecules { - for (index, element) in models.enumerated() { - stackItems[index].setWithModel(element, delegateObject, additionalData) - } - } - - restack() - stackModel?.useHorizontalMargins = moleculesShouldSetHorizontalMargins - stackModel?.useVerticalMargins = moleculesShouldSetVerticalMargins - } - - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - if model == nil { - let data = try! JSONSerialization.data(withJSONObject: json!) - let decoder = JSONDecoder() - let model = try! decoder.decode(MoleculeStackModel.self, from: data) - setWithModel(model, delegateObject, additionalData) - } else { - setWithModel(model, delegateObject, additionalData) - } - } - - public override class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - // This will aggregate names of molecules to make an id. - guard let model = model as? MoleculeStackModel else { - return "stack<>" - } - var name = "stack<" - for case let item in model.molecules { - if let moleculeName = item.molecule.moleculeName { - if let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? ModelMoleculeViewProtocol.Type, - let nameForReuse = moleculeClass.nameForReuse(item.molecule, delegateObject) { - name.append(nameForReuse + ",") - } else { - name.append(moleculeName + ",") - } - } - } - name.append(">") - return name - } - - public class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { - // This will aggregate names of molecules to make an id. - guard let molecules = molecule?.optionalArrayForKey(KeyMolecules) else { - return "stack<>" - } - var name = "stack<" - for case let item as [AnyHashable: Any] in molecules { - if let molecule = item.optionalDictionaryForKey(KeyMolecule), let moleculeName = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.name?(forReuse: molecule, delegateObject: delegateObject) ?? molecule.optionalStringForKey(KeyMoleculeName) { - name.append(moleculeName + ",") - } - } - name.append(">") - return name - } - - public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - guard let items = json?.optionalArrayForKey(KeyMolecules) else { - return 0 - } - let horizontal = json?.optionalStringForKey("axis") == "horizontal" - var estimatedHeight: CGFloat = 0 - for case let item as [AnyHashable: AnyHashable] in items { - if let molecule = item.optionalDictionaryForKey(KeyMolecule) { - let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.estimatedHeight?(forRow: molecule, delegateObject: delegateObject) - if !horizontal { - // Vertical stack aggregates the items - let spacing = item.optionalCGFloatForKey("spacing") ?? (estimatedHeight != 0 ? (json?.optionalCGFloatForKey("spacing") ?? 16) : 0) - estimatedHeight += ((height ?? 0) + spacing) - } else if let height = height { - // Horizontal stack takes the tallest item. - estimatedHeight = max(estimatedHeight, height) - } - } - } - return estimatedHeight - } - - public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - guard let items = json?.optionalArrayForKey(KeyMolecules) else { - return nil - } - var modules: [String] = [] - for case let item as [AnyHashable: AnyHashable] in items { - if let molecule = item.optionalDictionaryForKey(KeyMolecule), let modulesForMolecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.requiredModules?(molecule, delegateObject: delegateObject, error: error) { - modules += modulesForMolecule - } - } - return modules.count > 0 ? modules : nil - } - // MARK: - Adding to stack /// Creates all of the stackItems for the stackItemModels - func createStackItemsFromModel(with delegate: MVMCoreUIDelegateObject?) { + override func createStackItemsFromModel(with delegate: MVMCoreUIDelegateObject?) { guard let stackItemModels = stackModel?.molecules else { return } for model in stackItemModels { if let stackItem = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model, delegate) as? StackItem { @@ -202,67 +31,4 @@ open class MoleculeStackView: Container { } } } - - /// Adds the view to the stack. - func addView(_ view: View, lastItem: Bool) { - guard let model = view.model else { return } - let stackItem = StackItem(andContain: view) - stackItem.model = StackItemModel(with: model) - addStackItem(stackItem, lastItem: lastItem) - } - - /// Adds the stack item view - private func addStackItem(_ stackItem: StackItem, lastItem: Bool) { - let stackModel = self.stackModel! - let model = stackItem.stackItemModel! - guard !model.gone else { - // Gone views do not show - return - } - contentView.addSubview(stackItem) - stackItem.translatesAutoresizingMaskIntoConstraints = false - - let spacing = model.spacing ?? stackModel.spacing - let verticalAlignment = model.verticalAlignment ?? (stackItem.view as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() ?? (model.percent == nil && stackModel.axis == .vertical ? .fill : (stackModel.axis == .vertical ? .leading : .center)) - let horizontalAlignment = model.horizontalAlignment ?? (stackItem.view as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() ?? (stackModel.axis == .vertical || model.percent == nil ? .fill : .leading) - stackItem.containerHelper.alignHorizontal(horizontalAlignment) - stackItem.containerHelper.alignVertical(verticalAlignment) - - let first = stackItems.first { !($0.stackItemModel?.gone ?? false) } == nil - if stackModel.axis == .vertical { - if first { - pinView(stackItem, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: useStackSpacingBeforeFirstItem ? spacing : model.spacing ?? 0) - } else if let previousView = stackItems.last(where: { item in - return !item.stackItemModel!.gone - }) { - stackItem.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: spacing).isActive = true - } - pinView(stackItem, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: 0) - pinView(contentView, toView: stackItem, attribute: .trailing, relation: .equal, priority: .required, constant: 0) - if let percent = model.percent { - stackItem.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: CGFloat(percent)/100.0).isActive = true - } - if lastItem { - pinView(contentView, toView: stackItem, attribute: .bottom, relation: .equal, priority: .required, constant: 0) - } - } else { - if first { - // First horizontal item has no spacing by default unless told otherwise. - pinView(stackItem, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: useStackSpacingBeforeFirstItem ? spacing : model.spacing ?? 0) - } else if let previousView = stackItems.last(where: { item in - return !item.stackItemModel!.gone - }) { - stackItem.leftAnchor.constraint(equalTo: previousView.rightAnchor, constant: spacing).isActive = true - } - pinView(stackItem, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0) - pinView(contentView, toView: stackItem, attribute: .bottom, relation: .equal, priority: .required, constant: 0) - if let percent = model.percent { - stackItem.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: CGFloat(percent)/100.0).isActive = true - } - if lastItem { - pinView(contentView, toView: stackItem, attribute: .right, relation: .equal, priority: .required, constant: 0) - } - } - stackItems.append(stackItem) - } } diff --git a/MVMCoreUI/Organisms/Stack.swift b/MVMCoreUI/Organisms/Stack.swift new file mode 100644 index 00000000..29f9cd9f --- /dev/null +++ b/MVMCoreUI/Organisms/Stack.swift @@ -0,0 +1,239 @@ +// +// Stack.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class Stack: Container where T: StackModelProtocol { + var contentView: UIView = MVMCoreUICommonViewsUtility.commonView() + var stackModel: T? { + get { return model as? T } + } + var stackItems: [UIView] = [] + + // MARK: - Helpers + public func pinView(_ view: UIView, toView: UIView, attribute: NSLayoutConstraint.Attribute, relation: NSLayoutConstraint.Relation, priority: UILayoutPriority, constant: CGFloat) { + let constraint = NSLayoutConstraint(item: view, attribute: attribute, relatedBy: relation, toItem: toView, attribute: attribute, multiplier: 1.0, constant: constant) + constraint.priority = priority + constraint.isActive = true + } + + /// Restacks the existing items. + func restack() { + removeAllItemViews() + guard let stackModel = stackModel else { return } + let stackItems = self.stackItems + self.stackItems = [] + let lastItemIndex = stackModel.molecules.lastIndex(where: { (item) -> Bool in + return !item.gone + }) + for (index, view) in stackItems.enumerated() { + addView(view, stackModel.molecules[index], lastItem: lastItemIndex == index) + } + } + + /// Removes all stack items views from the view. + func removeAllItemViews() { + for item in stackItems { + item.removeFromSuperview() + } + } + + // MARK: - Inits + public override init(frame: CGRect) { + super.init(frame: frame) + } + + public init(withJSON json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + super.init(frame: CGRect.zero) + setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - MFViewProtocol + public override func setupView() { + super.setupView() + guard contentView.superview == nil else { + return + } + MVMCoreUIUtility.setMarginsFor(contentView, leading: 0, top: 0, trailing: 0, bottom: 0) + translatesAutoresizingMaskIntoConstraints = false + backgroundColor = .clear + addSubview(contentView) + containerHelper.constrainView(contentView) + contentView.setContentHuggingPriority(.defaultHigh, for: .vertical) + contentView.setContentHuggingPriority(.defaultHigh, for: .horizontal) + } + + public override func updateView(_ size: CGFloat) { + super.updateView(size) + for item in stackItems { + (item as? MVMCoreViewProtocol)?.updateView(size) + } + } + + // MARK: - MVMCoreUIMoleculeViewProtocol + public override func reset() { + super.reset() + backgroundColor = .clear + for item in stackItems { + (item as? MoleculeViewProtocol)?.reset?() + } + } + + public override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + let previousModel = self.model + super.setWithModel(model, delegateObject, additionalData) + removeAllItemViews() + + // If the items in the stack are different, clear them, create new ones. + if (previousModel == nil) || MoleculeStackView.nameForReuse(previousModel, delegateObject) != MoleculeStackView.nameForReuse(model, delegateObject) { + stackItems = [] + createStackItemsFromModel(with: delegateObject) + } else if let models = stackModel?.molecules { + for (index, element) in models.enumerated() { + (stackItems[index] as? ModelMoleculeViewProtocol)?.setWithModel(element as? MoleculeProtocol, delegateObject, additionalData) + } + } + + restack() + } + + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + if model == nil { + let data = try! JSONSerialization.data(withJSONObject: json!) + let decoder = JSONDecoder() + let model = try! decoder.decode(MoleculeStackModel.self, from: data) + setWithModel(model, delegateObject, additionalData) + } else { + setWithModel(model, delegateObject, additionalData) + } + } + + public override class func nameForReuse(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + // This will aggregate names of molecules to make an id. + guard let model = model as? MoleculeStackModel else { + return "stack<>" + } + var name = "stack<" + for case let item in model.molecules { + if let moleculeName = item.molecule.moleculeName { + if let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? ModelMoleculeViewProtocol.Type, + let nameForReuse = moleculeClass.nameForReuse(item.molecule, delegateObject) { + name.append(nameForReuse + ",") + } else { + name.append(moleculeName + ",") + } + } + } + name.append(">") + return name + } + + // Need to update to take into account first spacing flag + public override class func estimatedHeight(forRow molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + guard let model = molecule as? MoleculeStackModel else { return 0 } + let horizontal = model.axis == .horizontal + var estimatedHeight: CGFloat = 0 + for case let item in model.molecules { + if item.gone { continue } + let height = (MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(item) as? ModelMoleculeViewProtocol.Type)?.estimatedHeight(forRow: item, delegateObject: delegateObject) ?? 0 + if !horizontal { + // Vertical stack aggregates the items + let spacing = item.spacing ?? model.spacing + estimatedHeight += (height + spacing) + } else { + // Horizontal stack takes the tallest item. + estimatedHeight = max(estimatedHeight, height) + } + } + return estimatedHeight + } + + public override class func requiredModules(_ molecule: MoleculeProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + guard let model = molecule as? MoleculeStackModel else { return nil } + var modules: [String] = [] + for case let item in model.molecules { + if let modulesForMolecule = (MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(item) as? ModelMoleculeViewProtocol.Type)?.requiredModules(item, delegateObject: delegateObject, error: error) { + modules += modulesForMolecule + } + } + return modules.count > 0 ? modules : nil + } + + // MARK: - Adding to stack + /// Can be subclassed to create views when we get stack item models and have no views yet + func createStackItemsFromModel(with delegate: MVMCoreUIDelegateObject?) { + } + + /// Convenience function, adds a view to a StackItem to the Stack + func addViewToItemToStack(_ view: UIView, lastItem: Bool) { + let stackItemModel = StackItemModel() + let stackItem = StackItem(andContain: view) + addView(stackItem, stackItemModel, lastItem: lastItem) + } + + /// Adds the stack item view + func addView(_ view: UIView,_ model: StackItemModelProtocol, lastItem: Bool) { + let stackModel = self.stackModel! + guard !model.gone else { + // Gone views do not show + stackItems.append(view) + return + } + contentView.addSubview(view) + view.translatesAutoresizingMaskIntoConstraints = false + + let spacing = model.spacing ?? stackModel.spacing + if let container = view as? ContainerProtocol { + let verticalAlignment = (model as? ContainerModelProtocol)?.verticalAlignment ?? (view as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() ?? (model.percent == nil && stackModel.axis == .vertical ? .fill : (stackModel.axis == .vertical ? .leading : .center)) + let horizontalAlignment = (model as? ContainerModelProtocol)?.horizontalAlignment ?? (view as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() ?? (stackModel.axis == .vertical || model.percent == nil ? .fill : .leading) + container.alignHorizontal(horizontalAlignment) + container.alignVertical(verticalAlignment) + } + + let first = contentView.subviews.count == 1 + if stackModel.axis == .vertical { + if first { + pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: stackModel.useStackSpacingBeforeFirstItem ? spacing : model.spacing ?? 0) + } else if let previousView = stackItems.last(where: { item in + return !model.gone + }) { + view.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: spacing).isActive = true + } + pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: 0) + pinView(contentView, toView: view, attribute: .trailing, relation: .equal, priority: .required, constant: 0) + if let percent = model.percent { + view.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: CGFloat(percent)/100.0).isActive = true + } + if lastItem { + pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0) + } + } else { + if first { + // First horizontal item has no spacing by default unless told otherwise. + pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: stackModel.useStackSpacingBeforeFirstItem ? spacing : model.spacing ?? 0) + } else if let previousView = stackItems.last(where: { item in + return !model.gone + }) { + view.leftAnchor.constraint(equalTo: previousView.rightAnchor, constant: spacing).isActive = true + } + pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0) + pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0) + if let percent = model.percent { + view.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: CGFloat(percent)/100.0).isActive = true + } + if lastItem { + pinView(contentView, toView: view, attribute: .right, relation: .equal, priority: .required, constant: 0) + } + } + stackItems.append(view) + } +} diff --git a/MVMCoreUI/Organisms/StackModel.swift b/MVMCoreUI/Organisms/StackModel.swift new file mode 100644 index 00000000..b647b791 --- /dev/null +++ b/MVMCoreUI/Organisms/StackModel.swift @@ -0,0 +1,22 @@ +// +// StackModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class StackModel: StackModelProtocol, MoleculeProtocol { + public static var identifier: String = "simpleStack" + public var backgroundColor: Color? + public var molecules: [StackItemModel] + @Axis public var axis: NSLayoutConstraint.Axis = .vertical + public var spacing: CGFloat = 16.0 + public var useStackSpacingBeforeFirstItem = false + + public init(molecules: [StackItemModel]) { + self.molecules = molecules + } +} diff --git a/MVMCoreUI/Organisms/StackModelProtocol.swift b/MVMCoreUI/Organisms/StackModelProtocol.swift new file mode 100644 index 00000000..b7385466 --- /dev/null +++ b/MVMCoreUI/Organisms/StackModelProtocol.swift @@ -0,0 +1,18 @@ +// +// StackModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol StackModelProtocol { + associatedtype AnyStackItemModel: StackItemModelProtocol + + var molecules: [AnyStackItemModel] { get set } + var axis: NSLayoutConstraint.Axis { get set } + var spacing: CGFloat { get set } + var useStackSpacingBeforeFirstItem: Bool { get set } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 6c3e139e..ab054232 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -31,7 +31,6 @@ @"button": PrimaryButton.class, @"link": MFTextButton.class, @"header": StandardHeaderView.class, - @"stack": MoleculeStackView.class, @"twoButtonView": TwoButtonView.class, @"footer": StandardFooterView.class, @"caretView": CaretView.class, diff --git a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift index 86e7ee7e..ddc2d13f 100644 --- a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift +++ b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift @@ -10,11 +10,13 @@ import Foundation @objcMembers public class MoleculeObjectMapping: NSObject { public static func registerObjects() { + MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(MoleculeStackView.self, forKey: "stack" as NSString) + ModelRegistry.register(LabelModel.self) ModelRegistry.register(HeaderModel.self) ModelRegistry.register(HeadlineBodyModel.self) ModelRegistry.register(MoleculeStackModel.self) - ModelRegistry.register(StackItemModel.self) + ModelRegistry.register(MoleculeStackItemModel.self) ModelRegistry.register(TextFieldModel.self) ModelRegistry.register(ProgressBarModel.self) ModelRegistry.register(MultiProgressBarModel.self) diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Templates/MoleculeStackTemplate.swift index a4f69518..03d21c6b 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackTemplate.swift @@ -49,8 +49,8 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol { } let stack = MoleculeStackView(frame: .zero) - stack.useStackSpacingBeforeFirstItem = true - stack.moleculesShouldSetHorizontalMargins = true + moleculeStackModel.useStackSpacingBeforeFirstItem = true + moleculeStackModel.useHorizontalMargins = true stack.setWithModel(moleculeStackModel, delegateObject() as? MVMCoreUIDelegateObject, nil) return stack } diff --git a/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift b/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift index 05d13bcd..f989e17b 100644 --- a/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift +++ b/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift @@ -30,6 +30,11 @@ import Foundation try container.encode(axis.rawValueString, forKey: .axis) } */ + +enum AxisError: Error { + case notAnAxis +} + extension NSLayoutConstraint.Axis: RawRepresentable { init?(rawValue: String) { @@ -54,3 +59,29 @@ extension NSLayoutConstraint.Axis: RawRepresentable { } } } + +@propertyWrapper +public struct Axis { + public var wrappedValue: NSLayoutConstraint.Axis + + public init(wrappedValue value: NSLayoutConstraint.Axis) { + self.wrappedValue = value + } +} + +extension Axis: Codable { + public init(from decoder: Decoder) throws { + let typeContainer = try decoder.singleValueContainer() + let string = try typeContainer.decode(String.self) + guard let axis = NSLayoutConstraint.Axis(rawValue: string) else { + throw AxisError.notAnAxis + } + wrappedValue = axis + } + + public func encode(to encoder: Encoder) throws { + let string = wrappedValue.rawValueString + var container = encoder.singleValueContainer() + try container.encode(string) + } +} From 7d5bee5654525ed5dc7af19c73c66e65078abf5b Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 16 Jan 2020 15:51:15 -0500 Subject: [PATCH 46/54] Label fix --- MVMCoreUI/Atoms/Views/Label.swift | 17 ++++++++++++++--- .../LabelModel/LabelAttributeActionModel.swift | 11 ++++++++++- .../Molecules/Doughnut/DoughnutChart.swift | 9 ++++----- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index e5491baf..b4173add 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -244,10 +244,15 @@ public typealias ActionBlock = () -> () } if let fontStyle = labelModel.fontStyle { MFStyler.styleLabel(self, withStyle: fontStyle) + MFStyler.styleLabel(self, withStyle: fontStyle, genericScaling: false) + standardFontSize = font.pointSize } else { let fontSize = labelModel.fontSize + if let fontSize = fontSize { + standardFontSize = fontSize + } if let fontName = labelModel.fontName { - font = MFFonts.mfFont(withName: fontName, size: fontSize ?? font.pointSize) + font = MFFonts.mfFont(withName: fontName, size: fontSize ?? standardFontSize) } else if let fontSize = fontSize { font = font.withSize(fontSize) } @@ -262,9 +267,9 @@ public typealias ActionBlock = () -> () for attribute in attributes { let range = NSRange(location: attribute.location, length: attribute.length) switch attribute { - case let underLineAtt as LabelAttributeUnderlineModel: + case let _ as LabelAttributeUnderlineModel: attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range) - case let strikeAtt as LabelAttributeStrikeThroughModel: + case let _ as LabelAttributeStrikeThroughModel: attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) attributedString.addAttribute(.baselineOffset, value: 0, range: range) case let colorAtt as LabelAttributeColorModel: @@ -309,11 +314,17 @@ public typealias ActionBlock = () -> () } } case let actionAtt as LabelAttributeActionModel: + addTappableLinkAttribute(range: NSRange(location: range.location, length: range.length)) { + if let data = try? actionAtt.encode(using: JSONEncoder()), let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any] { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } addActionAttributes(range: range, string: attributedString) default: continue } } + attributedText = attributedString originalAttributedString = attributedText hero = labelModel.hero } diff --git a/MVMCoreUI/Atoms/Views/LabelModel/LabelAttributeActionModel.swift b/MVMCoreUI/Atoms/Views/LabelModel/LabelAttributeActionModel.swift index eb5a3cd7..8dd5c562 100644 --- a/MVMCoreUI/Atoms/Views/LabelModel/LabelAttributeActionModel.swift +++ b/MVMCoreUI/Atoms/Views/LabelModel/LabelAttributeActionModel.swift @@ -12,12 +12,21 @@ class LabelAttributeActionModel: LabelAttributeModel { override public class var identifier: String { return "action" } - + var action: ActionProtocol + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + action = try typeContainer.decodeModel(codingKey: .action, typeCodingKey: ActionCodingKey.actionType) try super.init(from: decoder) } public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeModel(action, forKey: .action) + } + + private enum CodingKeys: String, CodingKey { + case action } } diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift index a3961833..acb2e9ea 100644 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift @@ -179,10 +179,9 @@ open class DoughnutChart: View { let labelheight = labelContainer.frame.height/2 let padding = sizeObject.getValueBasedOnApplicationWidth()/2 - sqrt(pow(radius, 2) - pow(labelheight, 2)) - labelContainer.leftPin?.constant = padding - labelContainer.rightPin?.constant = padding - labelContainer.topPin?.constant = max(radius - labelheight, labelheight) - labelContainer.bottomPin?.constant = max(radius - labelheight, labelheight) + labelContainerLeftConstraint?.constant = padding + labelContainerRightConstraint?.constant = padding + labelContainerTopConstraint?.constant = max(radius - labelheight, labelheight) + labelContainerBottomConstraint?.constant = max(radius - labelheight, labelheight) } - } From cb89795b4cb78ba86fa43b40333557cebeb27413 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 16 Jan 2020 16:17:36 -0500 Subject: [PATCH 47/54] remove ! --- MVMCoreUI/Organisms/Stack.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Organisms/Stack.swift b/MVMCoreUI/Organisms/Stack.swift index 29f9cd9f..0fc1ca8f 100644 --- a/MVMCoreUI/Organisms/Stack.swift +++ b/MVMCoreUI/Organisms/Stack.swift @@ -107,12 +107,11 @@ open class Stack: Container where T: StackModelProtocol { } open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - if model == nil { - let data = try! JSONSerialization.data(withJSONObject: json!) - let decoder = JSONDecoder() - let model = try! decoder.decode(MoleculeStackModel.self, from: data) + if let model = model { setWithModel(model, delegateObject, additionalData) - } else { + } else if let json = json, + let data = try? JSONSerialization.data(withJSONObject: json), + let model = try? JSONDecoder().decode(MoleculeStackModel.self, from: data) { setWithModel(model, delegateObject, additionalData) } } @@ -182,7 +181,7 @@ open class Stack: Container where T: StackModelProtocol { /// Adds the stack item view func addView(_ view: UIView,_ model: StackItemModelProtocol, lastItem: Bool) { - let stackModel = self.stackModel! + guard let stackModel = self.stackModel else { return } guard !model.gone else { // Gone views do not show stackItems.append(view) From 36cbccd39c0e5efb0f11dcaea6d609408d9c964c Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Thu, 16 Jan 2020 16:30:05 -0500 Subject: [PATCH 48/54] update name --- MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift | 4 ++-- MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift index acb2e9ea..233ca7d7 100644 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift @@ -135,7 +135,7 @@ open class DoughnutChart: View { let clockwise = true let value: CGFloat = chartModel.percent - let gap: CGFloat = spaceReuired() ? lineGap() : 0.0 + let gap: CGFloat = spaceRequired() ? lineGap() : 0.0 let startAngle = ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi) let endAngle = ((value / 100.0) * 2 * .pi) + startAngle - gap let circlePath = UIBezierPath(arcCenter: arcCenter, @@ -168,7 +168,7 @@ open class DoughnutChart: View { return doughnutChartModel?.sections.count == 1 ? 0.0 : 0.05 } - func spaceReuired() -> Bool { + func spaceRequired() -> Bool { return doughnutChartModel?.spaceRequired ?? true } diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift index 4893edbc..31091796 100644 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift @@ -50,7 +50,7 @@ import Foundation centerY.isActive = true colorLablesStack.leadingAnchor.constraint(equalTo: doughnutChart.trailingAnchor, constant: PaddingThree).isActive = true - colorLablesStack.backgroundColor = UIColor.clear + colorLablesStack.backgroundColor = .clear } open override func updateView(_ size: CGFloat) { From b1abe0810cf78c9db39df2868d5bad64483152eb Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Thu, 16 Jan 2020 16:43:16 -0500 Subject: [PATCH 49/54] put clockwise into method --- MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift index 233ca7d7..130536e4 100644 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift +++ b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift @@ -132,8 +132,7 @@ open class DoughnutChart: View { let arcCenter = shapeLayer.position let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth()/2.0 - let clockwise = true - + let value: CGFloat = chartModel.percent let gap: CGFloat = spaceRequired() ? lineGap() : 0.0 let startAngle = ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi) @@ -142,7 +141,7 @@ open class DoughnutChart: View { radius: radius, startAngle: startAngle, endAngle: endAngle, - clockwise: clockwise) + clockwise: true) shapeLayer.path = circlePath.cgPath doughnutLayer.addSublayer(shapeLayer) From 3cfe5aa4237a1f6ce6bb0d05aa9c53f49a21f0da Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 16 Jan 2020 16:47:53 -0500 Subject: [PATCH 50/54] StackModel --- MVMCoreUI/Organisms/StackModel.swift | 29 +++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Organisms/StackModel.swift b/MVMCoreUI/Organisms/StackModel.swift index b647b791..deea2457 100644 --- a/MVMCoreUI/Organisms/StackModel.swift +++ b/MVMCoreUI/Organisms/StackModel.swift @@ -12,11 +12,38 @@ import Foundation public static var identifier: String = "simpleStack" public var backgroundColor: Color? public var molecules: [StackItemModel] - @Axis public var axis: NSLayoutConstraint.Axis = .vertical + public var axis: NSLayoutConstraint.Axis = .vertical public var spacing: CGFloat = 16.0 public var useStackSpacingBeforeFirstItem = false public init(molecules: [StackItemModel]) { self.molecules = molecules } + + enum StackCodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case molecules + case axis + case spacing + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: StackCodingKeys.self) + + molecules = try typeContainer.decode([StackItemModel].self, forKey: .molecules) + if let axisString = try typeContainer.decodeIfPresent(String.self, forKey: .axis), let optionalAxis = NSLayoutConstraint.Axis(rawValue: axisString) { + axis = optionalAxis + } + if let spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing) { + self.spacing = spacing + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StackCodingKeys.self) + try container.encodeIfPresent(molecules, forKey: .molecules) + try container.encodeIfPresent(axis.rawValueString, forKey: .axis) + try container.encodeIfPresent(spacing, forKey: .spacing) + } } From 4dde423d2f8668e47a806eff6ed06bc9dd53f4c7 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 16 Jan 2020 17:32:00 -0500 Subject: [PATCH 51/54] button fixes --- MVMCoreUI/Atoms/Buttons/ButtonModel.swift | 7 +++++++ .../PrimaryButton+MoleculeProtocolExtension.swift | 10 ++++++++++ MVMCoreUI/Atoms/Buttons/PrimaryButton.h | 3 +++ MVMCoreUI/Atoms/Buttons/PrimaryButton.m | 2 -- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atoms/Buttons/ButtonModel.swift b/MVMCoreUI/Atoms/Buttons/ButtonModel.swift index 4329ccff..64d9a3c3 100644 --- a/MVMCoreUI/Atoms/Buttons/ButtonModel.swift +++ b/MVMCoreUI/Atoms/Buttons/ButtonModel.swift @@ -25,6 +25,8 @@ public class ButtonModel: MoleculeProtocol { public var action: ActionProtocol public var style: ButtonStyle? = .primary public var size: ButtonSize? = .standard + public var required: Bool? + public var requiredGroups: [String]? init(with title: String, action: ActionProtocol) { self.title = title @@ -37,6 +39,8 @@ public class ButtonModel: MoleculeProtocol { case action case style case size + case required + case requiredGroups } required public init(from decoder: Decoder) throws { @@ -44,6 +48,8 @@ public class ButtonModel: MoleculeProtocol { backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) title = try typeContainer.decode(String.self, forKey: .title) action = try typeContainer.decodeModel(codingKey: .action, typeCodingKey: ActionCodingKey.actionType) + required = try typeContainer.decodeIfPresent(Bool.self, forKey: .required) + requiredGroups = try typeContainer.decodeIfPresent([String].self, forKey: .requiredGroups) if let style = try typeContainer.decodeIfPresent(ButtonStyle.self, forKey: .style) { self.style = style } @@ -59,5 +65,6 @@ public class ButtonModel: MoleculeProtocol { try container.encodeModel(action, forKey: .action) try container.encodeIfPresent(style, forKey: .style) try container.encodeIfPresent(size, forKey: .size) + try container.encodeIfPresent(required, forKey: .required) } } diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift b/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift index 2d7ecf40..09c94bdf 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift @@ -14,6 +14,16 @@ extension PrimaryButton: ModelMoleculeViewProtocol { guard let model = model as? ButtonModel else { return } setTitle(model.title, for: .normal) backgroundColor = model.backgroundColor?.uiColor + + self.validationRequired = model.required ?? false + self.requiredGroupsList = model.requiredGroups + + if self.validationRequired, + let selfForm = self as? FormValidationEnableDisableProtocol { + FormValidator.setupValidation(molecule: selfForm, delegate: delegateObject?.formValidationProtocol) + } + + if let style = model.style { switch style { case .primary: diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.h b/MVMCoreUI/Atoms/Buttons/PrimaryButton.h index 99929430..e83484a0 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.h +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.h @@ -46,6 +46,9 @@ static CGFloat const PrimaryButtonSmallHeight = 30.0; // set YES to skip highlight method, for customer color button @property (nonatomic) BOOL skipHighlighted; +@property (nonatomic) BOOL validationRequired; +@property (nullable, nonatomic, strong) NSArray *requiredGroupsList; + // The main creation functions, changed to black button for 2.0 for default + (nullable instancetype)primaryButton:(BOOL)enabled; + (nullable instancetype)primarySmallButton:(BOOL)enabled; diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m index 2bc9e026..439d5479 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m @@ -20,8 +20,6 @@ @interface PrimaryButton() -@property (nonatomic) BOOL validationRequired; -@property (nonatomic, strong) NSArray *requiredGroupsList; @property (nonatomic) BOOL smallButton; @property (assign, nonatomic) BOOL tinyButton; @property (nonatomic) CGFloat sizeForSizing; From 8209101b73683a59ab27cde568fee99fe0f5d230 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 16 Jan 2020 18:54:01 -0500 Subject: [PATCH 52/54] hot fix --- MVMCoreUI/Molecules/Items/MoleculeStackItem.swift | 2 +- MVMCoreUI/Organisms/MoleculeStackView.swift | 2 +- MVMCoreUI/OtherHandlers/CoreUIObject.swift | 1 + .../OtherHandlers/MVMCoreUIMoleculeMappingObject.m | 10 +--------- MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift | 3 ++- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift b/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift index d883f93e..279e7948 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift @@ -1,5 +1,5 @@ // -// StackItem.swift +// MoleculeStackItem.swift // MVMCoreUI // // Created by Scott Pfeil on 12/13/19. diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 2674de0a..a7dd1640 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -26,7 +26,7 @@ open class MoleculeStackView: Stack { override func createStackItemsFromModel(with delegate: MVMCoreUIDelegateObject?) { guard let stackItemModels = stackModel?.molecules else { return } for model in stackItemModels { - if let stackItem = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model, delegate) as? StackItem { + if let stackItem = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model, delegate) as? MoleculeStackItem { stackItems.append(stackItem) } } diff --git a/MVMCoreUI/OtherHandlers/CoreUIObject.swift b/MVMCoreUI/OtherHandlers/CoreUIObject.swift index af0f5023..427fcfba 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIObject.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIObject.swift @@ -19,5 +19,6 @@ import UIKit viewControllerMapping = MVMCoreUIViewControllerMappingObject() loggingDelegate = MVMCoreUILoggingHandler() moleculeMap = MVMCoreUIMoleculeMappingObject() + MoleculeObjectMapping.registerObjects() } } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 09c98c1a..1eda2454 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -70,10 +70,9 @@ @"tabsListItem": TabsTableViewCell.class, @"dropDownListItem": DropDownFilterTableViewCell.class, @"headlineBodyButton": HeadlineBodyButton.class, - @"stackItem": StackItem.class, + @"stackItem": MoleculeStackItem.class, @"eyebrowHeadlineBodyLink": EyebrowHeadlineBodyLink.class, @"headlineBodyCaretLinkImage" : HeadLineBodyCaretLinkImage.class, - @"stackItem": StackItem.class, @"doughnutChart": DoughnutChartView.class, @"headLineBodyCaretLinkImage" : HeadLineBodyCaretLinkImage.class } mutableCopy]; @@ -85,13 +84,6 @@ return [MVMCoreActionUtility initializerClassCheck:[CoreUIObject sharedInstance].moleculeMap classToVerify:self]; } -- (instancetype)init { - if (self = [super init]) { - [MoleculeObjectMapping registerObjects]; - } - return self; -} - - (nullable Class)getMoleculeClassWithJSON:(nonnull NSDictionary *)json { NSString *moleculeName = [json string:KeyMoleculeName]; if (moleculeName) { diff --git a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift index fe4c7e11..6bcdd4cd 100644 --- a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift +++ b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift @@ -10,7 +10,8 @@ import Foundation @objcMembers public class MoleculeObjectMapping: NSObject { public static func registerObjects() { - MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(MoleculeStackView.self, forKey: "stack" as NSString) + let mapping = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping + mapping?.setObject(MoleculeStackView.self, forKey: "stack" as NSString) ModelRegistry.register(LabelModel.self) ModelRegistry.register(HeaderModel.self) From 54cc1f54261e76fb2d126bb8fb4a3f4b86815129 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 16 Jan 2020 19:48:14 -0500 Subject: [PATCH 53/54] registerModels() --- MVMCoreUI/OtherHandlers/CoreUIObject.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/OtherHandlers/CoreUIObject.swift b/MVMCoreUI/OtherHandlers/CoreUIObject.swift index 427fcfba..9d2957c2 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIObject.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIObject.swift @@ -10,7 +10,11 @@ import UIKit @objcMembers open class CoreUIObject: MVMCoreObject { public var moleculeMap: MVMCoreUIMoleculeMappingObject? - + + open func registerModels() { + MoleculeObjectMapping.registerObjects() + } + open override func defaultInitialSetup() { cache = MVMCoreCache() sessionHandler = MVMCoreSessionTimeHandler() @@ -19,6 +23,6 @@ import UIKit viewControllerMapping = MVMCoreUIViewControllerMappingObject() loggingDelegate = MVMCoreUILoggingHandler() moleculeMap = MVMCoreUIMoleculeMappingObject() - MoleculeObjectMapping.registerObjects() + registerModels() } } From d8a7efde8a727fb0f6f9849041ebacfdfd69c669 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 16 Jan 2020 20:57:17 -0500 Subject: [PATCH 54/54] undo register change --- MVMCoreUI/OtherHandlers/CoreUIObject.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/MVMCoreUI/OtherHandlers/CoreUIObject.swift b/MVMCoreUI/OtherHandlers/CoreUIObject.swift index 9d2957c2..d60cdd6a 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIObject.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIObject.swift @@ -11,10 +11,6 @@ import UIKit @objcMembers open class CoreUIObject: MVMCoreObject { public var moleculeMap: MVMCoreUIMoleculeMappingObject? - open func registerModels() { - MoleculeObjectMapping.registerObjects() - } - open override func defaultInitialSetup() { cache = MVMCoreCache() sessionHandler = MVMCoreSessionTimeHandler() @@ -23,6 +19,6 @@ import UIKit viewControllerMapping = MVMCoreUIViewControllerMappingObject() loggingDelegate = MVMCoreUILoggingHandler() moleculeMap = MVMCoreUIMoleculeMappingObject() - registerModels() + MoleculeObjectMapping.registerObjects() } }