From 65c3f0427a64d4ad45ab55a7ef143579102655fa Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 18 Apr 2019 11:07:00 -0400 Subject: [PATCH 01/71] radiobuttons --- MVMCoreUI.xcodeproj/project.pbxproj | 6 + MVMCoreUI/Atoms/Views/MFLabel.m | 4 +- MVMCoreUI/Molecules/RadioButton.swift | 135 ++++++++++++++++++ .../MVMCoreUIMoleculeMappingObject.m | 3 +- 4 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 MVMCoreUI/Molecules/RadioButton.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 1f3e1d53..e9fd6b52 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618A224BBE7700E1557D /* FormValidator.swift */; }; 0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618B224BBE7700E1557D /* FormValidator+TextFields.swift */; }; 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618C224BBE7700E1557D /* FormValidator+FormParams.swift */; }; + 01157B94225D376D00F15D92 /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01157B93225D376D00F15D92 /* RadioButton.swift */; }; 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 */; }; @@ -168,6 +169,7 @@ 0105618A224BBE7700E1557D /* FormValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormValidator.swift; sourceTree = ""; }; 0105618B224BBE7700E1557D /* FormValidator+TextFields.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FormValidator+TextFields.swift"; sourceTree = ""; }; 0105618C224BBE7700E1557D /* FormValidator+FormParams.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FormValidator+FormParams.swift"; sourceTree = ""; }; + 01157B93225D376D00F15D92 /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; 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 = ""; }; @@ -442,6 +444,7 @@ D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */, D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */, D274CA322236A78900B01B62 /* StandardFooterView.swift */, + 01157B93225D376D00F15D92 /* RadioButton.swift */, ); path = Molecules; sourceTree = ""; @@ -560,6 +563,8 @@ D29DF17D21E69E26003B2FB9 /* Views */ = { isa = PBXGroup; children = ( + 0198F7A02256A80A0066C936 /* MFRadioButton.h */, + 0198F7A22256A80A0066C936 /* MFRadioButton.m */, DBC4391622442196001AB423 /* CaretView.swift */, DBC4391722442197001AB423 /* DashLine.swift */, D29DF17E21E69E2E003B2FB9 /* MFView.h */, @@ -898,6 +903,7 @@ D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */, + 01157B94225D376D00F15D92 /* RadioButton.swift in Sources */, D29DF18121E69E50003B2FB9 /* MFView.m in Sources */, D29DF18321E69E54003B2FB9 /* SeparatorView.m in Sources */, D29DF17A21E69E1F003B2FB9 /* MFCustomButton.m in Sources */, diff --git a/MVMCoreUI/Atoms/Views/MFLabel.m b/MVMCoreUI/Atoms/Views/MFLabel.m index 5fb19037..60d42178 100644 --- a/MVMCoreUI/Atoms/Views/MFLabel.m +++ b/MVMCoreUI/Atoms/Views/MFLabel.m @@ -154,7 +154,9 @@ } + (nonnull MFLabel *)label { - return [[MFLabel alloc] initWithFrame:CGRectZero]; + MFLabel *labelToReturn = [[MFLabel alloc] initWithFrame:CGRectZero]; + labelToReturn.translatesAutoresizingMaskIntoConstraints = NO; + return labelToReturn; } #pragma mark - Setters diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift new file mode 100644 index 00000000..7703b7dc --- /dev/null +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -0,0 +1,135 @@ +// +// RadioButton.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 4/9/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +@objcMembers open class RadioButton: ViewConstrainingView, FormValidationProtocol{ + + var moleculeJson: [AnyHashable: Any]? + var selectedRadioButton: MFRadioButton? + var selectedValue: String? + + // MARK: - Inits + public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + self.moleculeJson = json + + // Configure class properties with JSON values + guard let jsonDictionary = moleculeJson, + let optionsList = jsonDictionary.optionalArrayForKey("optionsList") else { + return + } + + if let delegateObject = delegateObject as? MVMCoreUIDelegateObject { + FormValidator.setupValidation(molecule: self, delegate: delegateObject.formValidationProtocol) + } + + var items:[UIView] = [] + for option in optionsList { + if let itemUI = createOptionItem(option as? [AnyHashable: Any]) { + items.append(itemUI) + } + } + + let verticalSpace = MFStyler.defaultVerticalPadding(forSize: MVMCoreUIUtility.getWidth()) + StackableViewController.populateView(self, + withUIArray: items) { (item) -> UIEdgeInsets in + return UIEdgeInsets(top: verticalSpace, + left: 0, + bottom: 0, + right: 0) + } + } + + func createOptionItem(_ optionJson: [AnyHashable: Any]?) -> UIView? { + + guard let json = optionJson else { + return nil + } + + let containerView = ViewConstrainingView.empty() + let radioButton = MFRadioButton() + radioButton.translatesAutoresizingMaskIntoConstraints = false + containerView.addSubview(radioButton) + + let label = MFLabel.commonLabel() + label.setWithJSON(json.dictionaryForKey(KeyLabel), delegateObject: nil, additionalData: nil) + containerView.addSubview(label) + + radioButton.leftAnchor.constraint(equalTo: containerView.leftAnchor, constant: 0).isActive = true + radioButton.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor, constant: PaddingOne).isActive = true + containerView.bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true + radioButton.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true + + label.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingTwo).isActive = true + label.rightAnchor.constraint(equalTo: containerView.rightAnchor, constant: 0).isActive = true + label.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor, constant: PaddingOne).isActive = true + label.bottomAnchor.constraint(greaterThanOrEqualTo: containerView.bottomAnchor, constant: PaddingOne).isActive = true + label.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true + + addActionHandler(containerView, radioButton, json) + return containerView + } + + func addActionHandler(_ containerView: UIView, _ radioButton: MFRadioButton, _ optionJson: [AnyHashable: Any]) { + let dummyButton = MFCustomButton(frame: .zero) + dummyButton.translatesAutoresizingMaskIntoConstraints = false + containerView.addSubview(dummyButton) + NSLayoutConstraint.constraintPinSubview(toSuperview: dummyButton) + containerView.bringSubviewToFront(dummyButton) + + dummyButton.add({ (button) in + if let selectedbutton = self.selectedRadioButton { + selectedbutton.isSelected = false + } + self.selectedValue = optionJson.optionalStringForKey(KeyValue) + self.selectedRadioButton = radioButton + self.selectedRadioButton?.isSelected = true + }, for: .touchUpInside) + } + + func getColor( _ json: [AnyHashable: Any], _ key: String) -> UIColor? { + if let colorHex = json.optionalStringForKey(key) { + return UIColor.mfGet(forHex: colorHex) + } else { + return nil + } + } + + open override func setupView() { + super.setupView() + self.translatesAutoresizingMaskIntoConstraints = false + } + + // Used to check the validity of the field, to enable/disable the primary button. + @objc public func isValidField() -> Bool { + return selectedValue != nil + } + + // The Field name key value pair for sending to server + @objc public func formFieldName() -> String? { + return moleculeJson?.stringForkey("fieldKey") + } + + // The Feild value key value paid for sending to server + @objc public func formFieldValue() -> String? { + return selectedValue + } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index c1d7a36d..8c7a88ec 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -32,7 +32,8 @@ @"standardFooter": StandardFooterView.class, @"caretView": CaretView.class, @"caretButton": CaretButton.class, - @"textField" : MFTextField.class + @"textField" : MFTextField.class, + @"radioButton": RadioButton.class } mutableCopy]; }); return mapping; From 382a4c5d3b3db66d6b0822882e02a33968ba579b Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Fri, 26 Apr 2019 16:45:13 -0400 Subject: [PATCH 02/71] improvments --- MVMCoreUI.xcodeproj/project.pbxproj | 18 ++- .../Atoms/Views/MFView+FormValidation.swift | 21 +++ MVMCoreUI/Atoms/Views/MFView.h | 3 + MVMCoreUI/FormUIHelpers/FormValidator.swift | 9 +- MVMCoreUI/Molecules/RadioButton.swift | 134 ++++++++---------- MVMCoreUI/Molecules/RadioButtonList.swift | 92 ++++++++++++ .../MVMCoreUIMoleculeMappingObject.m | 1 + 7 files changed, 190 insertions(+), 88 deletions(-) create mode 100644 MVMCoreUI/Atoms/Views/MFView+FormValidation.swift create mode 100644 MVMCoreUI/Molecules/RadioButtonList.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index b294f7d1..63ff3d19 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -7,10 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + 01004F3022721C3800991ECC /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01004F2F22721C3800991ECC /* RadioButton.swift */; }; + 01004F322273972F00991ECC /* MFView+FormValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01004F312273972F00991ECC /* MFView+FormValidation.swift */; }; 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618A224BBE7700E1557D /* FormValidator.swift */; }; 0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618B224BBE7700E1557D /* FormValidator+TextFields.swift */; }; 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618C224BBE7700E1557D /* FormValidator+FormParams.swift */; }; - 01157B94225D376D00F15D92 /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01157B93225D376D00F15D92 /* RadioButton.swift */; }; + 01157B94225D376D00F15D92 /* RadioButtonList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01157B93225D376D00F15D92 /* RadioButtonList.swift */; }; 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 */; }; @@ -165,10 +167,12 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 01004F2F22721C3800991ECC /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; + 01004F312273972F00991ECC /* MFView+FormValidation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MFView+FormValidation.swift"; sourceTree = ""; }; 0105618A224BBE7700E1557D /* FormValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormValidator.swift; sourceTree = ""; }; 0105618B224BBE7700E1557D /* FormValidator+TextFields.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FormValidator+TextFields.swift"; sourceTree = ""; }; 0105618C224BBE7700E1557D /* FormValidator+FormParams.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FormValidator+FormParams.swift"; sourceTree = ""; }; - 01157B93225D376D00F15D92 /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; + 01157B93225D376D00F15D92 /* RadioButtonList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonList.swift; sourceTree = ""; }; 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 = ""; }; @@ -442,7 +446,8 @@ D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */, D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */, D274CA322236A78900B01B62 /* StandardFooterView.swift */, - 01157B93225D376D00F15D92 /* RadioButton.swift */, + 01157B93225D376D00F15D92 /* RadioButtonList.swift */, + 01004F2F22721C3800991ECC /* RadioButton.swift */, ); path = Molecules; sourceTree = ""; @@ -561,11 +566,10 @@ D29DF17D21E69E26003B2FB9 /* Views */ = { isa = PBXGroup; children = ( - 0198F7A02256A80A0066C936 /* MFRadioButton.h */, - 0198F7A22256A80A0066C936 /* MFRadioButton.m */, DBC4391622442196001AB423 /* CaretView.swift */, DBC4391722442197001AB423 /* DashLine.swift */, D29DF17E21E69E2E003B2FB9 /* MFView.h */, + 01004F312273972F00991ECC /* MFView+FormValidation.swift */, D29DF17F21E69E2E003B2FB9 /* MFView.m */, D29DF31E21ED0CBA003B2FB9 /* LabelView.h */, D29DF31F21ED0CBA003B2FB9 /* LabelView.m */, @@ -871,6 +875,7 @@ D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */, + 01004F3022721C3800991ECC /* RadioButton.swift in Sources */, D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */, D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, D29DF25321E6A177003B2FB9 /* MFDigitTextField.m in Sources */, @@ -885,6 +890,7 @@ D22D1F572204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m in Sources */, 01DF567021FA5AB300CC099B /* TextFieldListFormViewController.swift in Sources */, D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */, + 01004F322273972F00991ECC /* MFView+FormValidation.swift in Sources */, D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */, D29DF24D21E6A177003B2FB9 /* MFTextField.m in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, @@ -900,7 +906,7 @@ D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */, - 01157B94225D376D00F15D92 /* RadioButton.swift in Sources */, + 01157B94225D376D00F15D92 /* RadioButtonList.swift in Sources */, D29DF18121E69E50003B2FB9 /* MFView.m in Sources */, D29DF18321E69E54003B2FB9 /* SeparatorView.m in Sources */, D29DF17A21E69E1F003B2FB9 /* MFCustomButton.m in Sources */, diff --git a/MVMCoreUI/Atoms/Views/MFView+FormValidation.swift b/MVMCoreUI/Atoms/Views/MFView+FormValidation.swift new file mode 100644 index 00000000..9e4fe2a9 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/MFView+FormValidation.swift @@ -0,0 +1,21 @@ +// +// MFView+FormValidation.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 4/26/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + + +extension FormValidationProtocol where Self: MFView { + + func setupFormValidation(delegateObject: DelegateObject?) { + if let delegateObject = delegateObject as? MVMCoreUIDelegateObject, + let formValidationProtocol = delegateObject.formValidationProtocol { + FormValidator.setupValidation(molecule: self, delegate:formValidationProtocol) + formValidator = FormValidator.getFormValidatorFor(delegate:formValidationProtocol) + } + } +} diff --git a/MVMCoreUI/Atoms/Views/MFView.h b/MVMCoreUI/Atoms/Views/MFView.h index 5e32ff0a..079e3c3a 100644 --- a/MVMCoreUI/Atoms/Views/MFView.h +++ b/MVMCoreUI/Atoms/Views/MFView.h @@ -10,8 +10,11 @@ #import @import MVMCore.MVMCoreViewProtocol; +@class FormValidator; + @interface MFView : UIView +@property (nullable, strong, nonatomic) FormValidator *formValidator; @property (nullable, nonatomic, strong) NSDictionary *json; // Called in the initialization functions. Can setup ui here. diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index 1b546d69..5eb28904 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -40,10 +40,7 @@ import UIKit public func enableByValidation() { var valid = true for molecule in molecules { - if let isValidField = molecule.isValidField, - isValidField() == false { - valid = false - } + valid = valid && (molecule.isValidField?() ?? true) } let enableField = valid && (extraValidationBlock?() ?? true) shouldEnable(enableField) @@ -51,9 +48,7 @@ import UIKit public func shouldEnable(_ enable: Bool) { for molecule in molecules { - if let enableField = molecule.enableField { - enableField(enable) - } + molecule.enableField?(enable) } } } diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index 7703b7dc..4fb469fd 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -2,17 +2,19 @@ // RadioButton.swift // MVMCoreUI // -// Created by Suresh, Kamlesh on 4/9/19. +// Created by Suresh, Kamlesh on 4/25/19. // Copyright © 2019 Verizon Wireless. All rights reserved. // import UIKit -@objcMembers open class RadioButton: ViewConstrainingView, FormValidationProtocol{ - var moleculeJson: [AnyHashable: Any]? - var selectedRadioButton: MFRadioButton? - var selectedValue: String? +@objcMembers open class RadioButton: ViewConstrainingView, FormValidationProtocol{ + + var selectedRadioButton: MFRadioButton? + let radioButton = MFRadioButton() + let label = Label() + var target: RadioButtonListProtocol? // MARK: - Inits public init() { @@ -27,84 +29,67 @@ import UIKit super.init(coder: aDecoder) } + public init(target: RadioButtonListProtocol) { + super.init(frame: .zero) + self.target = target + } + + open override func setupView() { + super.setupView() + self.translatesAutoresizingMaskIntoConstraints = false + + radioButton.translatesAutoresizingMaskIntoConstraints = false + addSubview(radioButton) + + addSubview(label) + + radioButton.leftAnchor.constraint(equalTo: leftAnchor, constant: 0).isActive = true + radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true + radioButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + label.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingTwo).isActive = true + label.rightAnchor.constraint(equalTo: rightAnchor, constant: 0).isActive = true + label.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true + label.bottomAnchor.constraint(greaterThanOrEqualTo: bottomAnchor, constant: PaddingOne).isActive = true + label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + } + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - self.moleculeJson = json // Configure class properties with JSON values - guard let jsonDictionary = moleculeJson, - let optionsList = jsonDictionary.optionalArrayForKey("optionsList") else { - return + guard let jsonDictionary = json else { + return } - - if let delegateObject = delegateObject as? MVMCoreUIDelegateObject { - FormValidator.setupValidation(molecule: self, delegate: delegateObject.formValidationProtocol) - } - - var items:[UIView] = [] - for option in optionsList { - if let itemUI = createOptionItem(option as? [AnyHashable: Any]) { - items.append(itemUI) - } - } - - let verticalSpace = MFStyler.defaultVerticalPadding(forSize: MVMCoreUIUtility.getWidth()) - StackableViewController.populateView(self, - withUIArray: items) { (item) -> UIEdgeInsets in - return UIEdgeInsets(top: verticalSpace, - left: 0, - bottom: 0, - right: 0) - } - } - - func createOptionItem(_ optionJson: [AnyHashable: Any]?) -> UIView? { - - guard let json = optionJson else { - return nil - } - - let containerView = ViewConstrainingView.empty() - let radioButton = MFRadioButton() - radioButton.translatesAutoresizingMaskIntoConstraints = false - containerView.addSubview(radioButton) - - let label = MFLabel.commonLabel() - label.setWithJSON(json.dictionaryForKey(KeyLabel), delegateObject: nil, additionalData: nil) - containerView.addSubview(label) - - radioButton.leftAnchor.constraint(equalTo: containerView.leftAnchor, constant: 0).isActive = true - radioButton.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor, constant: PaddingOne).isActive = true - containerView.bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true - radioButton.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true - label.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingTwo).isActive = true - label.rightAnchor.constraint(equalTo: containerView.rightAnchor, constant: 0).isActive = true - label.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor, constant: PaddingOne).isActive = true - label.bottomAnchor.constraint(greaterThanOrEqualTo: containerView.bottomAnchor, constant: PaddingOne).isActive = true - label.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true - - addActionHandler(containerView, radioButton, json) - return containerView + setupFormValidation(delegateObject: delegateObject) + label.setWithJSON(jsonDictionary.dictionaryForKey(KeyLabel), delegateObject: nil, additionalData: nil) + addActionHandler() } - func addActionHandler(_ containerView: UIView, _ radioButton: MFRadioButton, _ optionJson: [AnyHashable: Any]) { + func addActionHandler() { + let dummyButton = MFCustomButton(frame: .zero) dummyButton.translatesAutoresizingMaskIntoConstraints = false - containerView.addSubview(dummyButton) + addSubview(dummyButton) NSLayoutConstraint.constraintPinSubview(toSuperview: dummyButton) - containerView.bringSubviewToFront(dummyButton) + bringSubviewToFront(dummyButton) dummyButton.add({ (button) in - if let selectedbutton = self.selectedRadioButton { - selectedbutton.isSelected = false - } - self.selectedValue = optionJson.optionalStringForKey(KeyValue) - self.selectedRadioButton = radioButton - self.selectedRadioButton?.isSelected = true + self.tapAction() }, for: .touchUpInside) } + func tapAction() { + if let target = target { + target.selected?(self) + } else { + radioButton.isSelected = !radioButton.isSelected + } + formValidator?.enableByValidation() + } + func getColor( _ json: [AnyHashable: Any], _ key: String) -> UIColor? { if let colorHex = json.optionalStringForKey(key) { return UIColor.mfGet(forHex: colorHex) @@ -112,24 +97,23 @@ import UIKit return nil } } - - open override func setupView() { - super.setupView() - self.translatesAutoresizingMaskIntoConstraints = false - } // Used to check the validity of the field, to enable/disable the primary button. @objc public func isValidField() -> Bool { - return selectedValue != nil + if !radioButton.isSelected { + return true + } + return formFieldValue() != nil } // The Field name key value pair for sending to server @objc public func formFieldName() -> String? { - return moleculeJson?.stringForkey("fieldKey") + return radioButton.isSelected ? json?.stringForkey("fieldKey") : nil } // The Feild value key value paid for sending to server @objc public func formFieldValue() -> String? { - return selectedValue + return radioButton.isSelected ? json?.stringForkey(KeyValue) : nil } } + diff --git a/MVMCoreUI/Molecules/RadioButtonList.swift b/MVMCoreUI/Molecules/RadioButtonList.swift new file mode 100644 index 00000000..e0393927 --- /dev/null +++ b/MVMCoreUI/Molecules/RadioButtonList.swift @@ -0,0 +1,92 @@ +// +// RadioButton.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 4/9/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +@objc public protocol RadioButtonListProtocol: NSObjectProtocol { + + @objc optional func selected(_ radioButton: RadioButton) +} + + +@objcMembers open class RadioButtonList: ViewConstrainingView, FormValidationProtocol, RadioButtonListProtocol { + + var selectedRadioButton: RadioButton? + var selectedValue: String? + + // MARK: - Inits + public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + + // Configure class properties with JSON values + guard let jsonDictionary = json, + let optionsList = jsonDictionary.optionalArrayForKey("optionsList") as? [[AnyHashable: Any]], + let delegateObject = delegateObject as? MVMCoreUIDelegateObject else { + return + } + + setupFormValidation(delegateObject: delegateObject) + var items:[UIView] = [] + for option in optionsList { + let radioButton = RadioButton(target: self) + radioButton.setWithJSON(option, delegateObject: delegateObject, additionalData: nil) + items.append(radioButton) + } + + let verticalSpace = MFStyler.defaultVerticalPadding(forSize: MVMCoreUIUtility.getWidth()) + StackableViewController.populateView(self, + withUIArray: items) { (item) -> UIEdgeInsets in + return UIEdgeInsets(top: verticalSpace, + left: 0, + bottom: 0, + right: 0) + } + } + + public func selected(_ radioButton: RadioButton) { + selectedRadioButton?.radioButton.isSelected = false + selectedRadioButton = radioButton + selectedRadioButton?.radioButton.isSelected = true + } + + open override func setupView() { + super.setupView() + self.translatesAutoresizingMaskIntoConstraints = false + } + + // Used to check the validity of the field, to enable/disable the primary button. + @objc public func isValidField() -> Bool { + if !(json?.boolForKey("required") ?? true) { + return true + } + + return selectedRadioButton?.isValidField() ?? false + } + + // The Field name key value pair for sending to server + @objc public func formFieldName() -> String? { + return selectedRadioButton?.formFieldName() + } + + // The Feild value key value paid for sending to server + @objc public func formFieldValue() -> String? { + return selectedRadioButton?.formFieldValue() + } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 6f1b577b..759f65c4 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -33,6 +33,7 @@ @"caretView": CaretView.class, @"caretButton": CaretButton.class, @"textField" : MFTextField.class, + @"radioButtonList": RadioButtonList.class, @"radioButton": RadioButton.class } mutableCopy]; }); From 24ab8f5ece8daac56851e0bc90aca84820812b7a Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 1 May 2019 10:41:04 -0400 Subject: [PATCH 03/71] cleanup --- MVMCoreUI/Molecules/RadioButton.swift | 67 +++++++++++--------- MVMCoreUI/Molecules/RadioButtonList.swift | 76 ++++++++++++++--------- 2 files changed, 83 insertions(+), 60 deletions(-) diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index 4fb469fd..c66bdedd 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -9,7 +9,7 @@ import UIKit -@objcMembers open class RadioButton: ViewConstrainingView, FormValidationProtocol{ +@objcMembers open class RadioButton: ViewConstrainingView { var selectedRadioButton: MFRadioButton? let radioButton = MFRadioButton() @@ -36,11 +36,13 @@ import UIKit open override func setupView() { super.setupView() - self.translatesAutoresizingMaskIntoConstraints = false + guard subviews.count == 0 else { + return + } + translatesAutoresizingMaskIntoConstraints = false radioButton.translatesAutoresizingMaskIntoConstraints = false addSubview(radioButton) - addSubview(label) radioButton.leftAnchor.constraint(equalTo: leftAnchor, constant: 0).isActive = true @@ -55,19 +57,6 @@ import UIKit label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true } - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - // Configure class properties with JSON values - guard let jsonDictionary = json else { - return - } - - setupFormValidation(delegateObject: delegateObject) - label.setWithJSON(jsonDictionary.dictionaryForKey(KeyLabel), delegateObject: nil, additionalData: nil) - addActionHandler() - } - func addActionHandler() { let dummyButton = MFCustomButton(frame: .zero) @@ -76,8 +65,8 @@ import UIKit NSLayoutConstraint.constraintPinSubview(toSuperview: dummyButton) bringSubviewToFront(dummyButton) - dummyButton.add({ (button) in - self.tapAction() + dummyButton.add({ [weak self] (button) in + self?.tapAction() }, for: .touchUpInside) } @@ -97,23 +86,43 @@ import UIKit return nil } } +} + +// MARK: - MVMCoreUIMoleculeViewProtocol +extension RadioButton { - // Used to check the validity of the field, to enable/disable the primary button. - @objc public func isValidField() -> Bool { - if !radioButton.isSelected { - return true + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + + // Configure class properties with JSON values + guard let jsonDictionary = json else { + return } - return formFieldValue() != nil + + label.setWithJSON(jsonDictionary.optionalDictionaryForKey(KeyLabel), + delegateObject: delegateObject, + additionalData: additionalData) + addActionHandler() } - // The Field name key value pair for sending to server - @objc public func formFieldName() -> String? { - return radioButton.isSelected ? json?.stringForkey("fieldKey") : nil + open override func needsToBeConstrained() -> Bool { + return true } - // The Feild value key value paid for sending to server - @objc public func formFieldValue() -> String? { - return radioButton.isSelected ? json?.stringForkey(KeyValue) : nil + open override func moleculeAlignment() -> UIStackView.Alignment { + return UIStackView.Alignment.leading; } } +// MARK: - FormValidationProtocol +extension RadioButton: FormValidationProtocol { + // The Field name key value pair for sending to server + @objc public func formFieldName() -> String? { + return json?.optionalStringForKey("fieldKey") + } + + // The Field value key value paid for sending to server + @objc public func formFieldValue() -> Any? { + return json?.optionalStringForKey(KeyValue) + } +} diff --git a/MVMCoreUI/Molecules/RadioButtonList.swift b/MVMCoreUI/Molecules/RadioButtonList.swift index e0393927..fdfbd00a 100644 --- a/MVMCoreUI/Molecules/RadioButtonList.swift +++ b/MVMCoreUI/Molecules/RadioButtonList.swift @@ -13,8 +13,7 @@ import UIKit @objc optional func selected(_ radioButton: RadioButton) } - -@objcMembers open class RadioButtonList: ViewConstrainingView, FormValidationProtocol, RadioButtonListProtocol { +@objcMembers open class RadioButtonList: ViewConstrainingView, RadioButtonListProtocol { var selectedRadioButton: RadioButton? var selectedValue: String? @@ -32,6 +31,43 @@ import UIKit super.init(coder: aDecoder) } + public func selected(_ radioButton: RadioButton) { + selectedRadioButton?.radioButton.isSelected = false + selectedRadioButton = radioButton + selectedRadioButton?.radioButton.isSelected = true + formValidator?.enableByValidation() + } + + open override func setupView() { + super.setupView() + self.translatesAutoresizingMaskIntoConstraints = false + } +} + +// MARK: - FormValidationProtocol +extension RadioButtonList: FormValidationProtocol { + // Used to check the validity of the field, to enable/disable the primary button. + @objc public func isValidField() -> Bool { + if !(json?.boolForKey("required") ?? true) { + return true + } + return selectedRadioButton != nil + } + + // The Field name key value pair for sending to server + @objc public func formFieldName() -> String? { + return selectedRadioButton?.formFieldName() + } + + // The Feild value key value paid for sending to server + @objc public func formFieldValue() -> Any? { + return selectedRadioButton?.formFieldValue() + } +} + +// MARK: - MVMCoreUIMoleculeViewProtocol +extension RadioButtonList { + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) @@ -39,9 +75,9 @@ import UIKit guard let jsonDictionary = json, let optionsList = jsonDictionary.optionalArrayForKey("optionsList") as? [[AnyHashable: Any]], let delegateObject = delegateObject as? MVMCoreUIDelegateObject else { - return + return } - + setupFormValidation(delegateObject: delegateObject) var items:[UIView] = [] for option in optionsList { @@ -49,7 +85,7 @@ import UIKit radioButton.setWithJSON(option, delegateObject: delegateObject, additionalData: nil) items.append(radioButton) } - + let verticalSpace = MFStyler.defaultVerticalPadding(forSize: MVMCoreUIUtility.getWidth()) StackableViewController.populateView(self, withUIArray: items) { (item) -> UIEdgeInsets in @@ -60,33 +96,11 @@ import UIKit } } - public func selected(_ radioButton: RadioButton) { - selectedRadioButton?.radioButton.isSelected = false - selectedRadioButton = radioButton - selectedRadioButton?.radioButton.isSelected = true - } - - open override func setupView() { - super.setupView() - self.translatesAutoresizingMaskIntoConstraints = false + open override func needsToBeConstrained() -> Bool { + return true } - // Used to check the validity of the field, to enable/disable the primary button. - @objc public func isValidField() -> Bool { - if !(json?.boolForKey("required") ?? true) { - return true - } - - return selectedRadioButton?.isValidField() ?? false - } - - // The Field name key value pair for sending to server - @objc public func formFieldName() -> String? { - return selectedRadioButton?.formFieldName() - } - - // The Feild value key value paid for sending to server - @objc public func formFieldValue() -> String? { - return selectedRadioButton?.formFieldValue() + open override func moleculeAlignment() -> UIStackView.Alignment { + return UIStackView.Alignment.leading; } } From 312bc0b868df2c264f06943bca8b26d61e017200 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Fri, 3 May 2019 17:45:10 -0400 Subject: [PATCH 04/71] code review --- MVMCoreUI/Molecules/RadioButton.swift | 7 ++++++- MVMCoreUI/Molecules/RadioButtonList.swift | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index c66bdedd..d5cc4d87 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -11,10 +11,10 @@ import UIKit @objcMembers open class RadioButton: ViewConstrainingView { - var selectedRadioButton: MFRadioButton? let radioButton = MFRadioButton() let label = Label() var target: RadioButtonListProtocol? + var dummyButton: MFCustomButton? // MARK: - Inits public init() { @@ -59,7 +59,12 @@ import UIKit func addActionHandler() { + if dummyButton != nil { + return + } + let dummyButton = MFCustomButton(frame: .zero) + self.dummyButton = dummyButton dummyButton.translatesAutoresizingMaskIntoConstraints = false addSubview(dummyButton) NSLayoutConstraint.constraintPinSubview(toSuperview: dummyButton) diff --git a/MVMCoreUI/Molecules/RadioButtonList.swift b/MVMCoreUI/Molecules/RadioButtonList.swift index fdfbd00a..6a1083fd 100644 --- a/MVMCoreUI/Molecules/RadioButtonList.swift +++ b/MVMCoreUI/Molecules/RadioButtonList.swift @@ -79,6 +79,9 @@ extension RadioButtonList { } setupFormValidation(delegateObject: delegateObject) + + StackableViewController.remove(self.subviews) + var items:[UIView] = [] for option in optionsList { let radioButton = RadioButton(target: self) @@ -86,7 +89,7 @@ extension RadioButtonList { items.append(radioButton) } - let verticalSpace = MFStyler.defaultVerticalPadding(forSize: MVMCoreUIUtility.getWidth()) + let verticalSpace = MFStyler.defaultVerticalPaddingForApplicationWidth() StackableViewController.populateView(self, withUIArray: items) { (item) -> UIEdgeInsets in return UIEdgeInsets(top: verticalSpace, From 8d2f5e1de05f7ce376b677e26888b703cc2e9974 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 7 May 2019 12:16:45 -0400 Subject: [PATCH 05/71] MFTextField with forDropDownWithBothDelegates --- MVMCoreUI/Atoms/Views/MFView+FormValidation.swift | 13 +++++++++++++ MVMCoreUI/Atoms/Views/MFView.h | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/MFView+FormValidation.swift b/MVMCoreUI/Atoms/Views/MFView+FormValidation.swift index 9e4fe2a9..66d3d27a 100644 --- a/MVMCoreUI/Atoms/Views/MFView+FormValidation.swift +++ b/MVMCoreUI/Atoms/Views/MFView+FormValidation.swift @@ -9,8 +9,21 @@ import Foundation +fileprivate struct FormValidatorHolder { + static var formValidator: FormValidator? +} + extension FormValidationProtocol where Self: MFView { + var formValidator: FormValidator? { + get { + return FormValidatorHolder.formValidator + } + set(newValue) { + FormValidatorHolder.formValidator = newValue + } + } + func setupFormValidation(delegateObject: DelegateObject?) { if let delegateObject = delegateObject as? MVMCoreUIDelegateObject, let formValidationProtocol = delegateObject.formValidationProtocol { diff --git a/MVMCoreUI/Atoms/Views/MFView.h b/MVMCoreUI/Atoms/Views/MFView.h index 079e3c3a..243408ac 100644 --- a/MVMCoreUI/Atoms/Views/MFView.h +++ b/MVMCoreUI/Atoms/Views/MFView.h @@ -14,7 +14,7 @@ @interface MFView : UIView -@property (nullable, strong, nonatomic) FormValidator *formValidator; +//@property (nullable, strong, nonatomic) FormValidator *formValidator; @property (nullable, nonatomic, strong) NSDictionary *json; // Called in the initialization functions. Can setup ui here. From e90b4c299e84ce13a69fc77d4e69ad027b55fa09 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 13 May 2019 13:42:18 -0400 Subject: [PATCH 06/71] enhancements --- MVMCoreUI.xcodeproj/project.pbxproj | 6 +--- .../Atoms/Views/MFView+FormValidation.swift | 34 ------------------- MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m | 9 ++--- .../FormValidator+FormParams.swift | 9 ++--- MVMCoreUI/FormUIHelpers/FormValidator.swift | 18 +++++----- MVMCoreUI/Molecules/RadioButton.swift | 1 - MVMCoreUI/Molecules/RadioButtonList.swift | 10 ++++-- .../MVMCoreUIMoleculeMappingObject.m | 3 +- 8 files changed, 24 insertions(+), 66 deletions(-) delete mode 100644 MVMCoreUI/Atoms/Views/MFView+FormValidation.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 5409c5a9..ddf5c036 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 01004F3022721C3800991ECC /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01004F2F22721C3800991ECC /* RadioButton.swift */; }; - 01004F322273972F00991ECC /* MFView+FormValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01004F312273972F00991ECC /* MFView+FormValidation.swift */; }; 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618A224BBE7700E1557D /* FormValidator.swift */; }; 0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618B224BBE7700E1557D /* FormValidator+TextFields.swift */; }; 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618C224BBE7700E1557D /* FormValidator+FormParams.swift */; }; @@ -171,7 +170,6 @@ /* Begin PBXFileReference section */ 01004F2F22721C3800991ECC /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; - 01004F312273972F00991ECC /* MFView+FormValidation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MFView+FormValidation.swift"; sourceTree = ""; }; 0105618A224BBE7700E1557D /* FormValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormValidator.swift; sourceTree = ""; }; 0105618B224BBE7700E1557D /* FormValidator+TextFields.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FormValidator+TextFields.swift"; sourceTree = ""; }; 0105618C224BBE7700E1557D /* FormValidator+FormParams.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FormValidator+FormParams.swift"; sourceTree = ""; }; @@ -325,10 +323,10 @@ D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = ""; }; D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = ""; }; D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIDelegateObject.swift; sourceTree = ""; }; - DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTableViewCell.swift; sourceTree = ""; }; D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTableViewController.swift; sourceTree = ""; }; D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.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 = ""; }; DBC4391722442197001AB423 /* DashLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashLine.swift; sourceTree = ""; }; DBC4391A224421A0001AB423 /* CaretButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretButton.swift; sourceTree = ""; }; @@ -578,7 +576,6 @@ DBC4391622442196001AB423 /* CaretView.swift */, DBC4391722442197001AB423 /* DashLine.swift */, D29DF17E21E69E2E003B2FB9 /* MFView.h */, - 01004F312273972F00991ECC /* MFView+FormValidation.swift */, D29DF17F21E69E2E003B2FB9 /* MFView.m */, D29DF31E21ED0CBA003B2FB9 /* LabelView.h */, D29DF31F21ED0CBA003B2FB9 /* LabelView.m */, @@ -899,7 +896,6 @@ D22D1F572204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m in Sources */, 01DF567021FA5AB300CC099B /* TextFieldListFormViewController.swift in Sources */, D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */, - 01004F322273972F00991ECC /* MFView+FormValidation.swift in Sources */, D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */, D29DF24D21E6A177003B2FB9 /* MFTextField.m in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, diff --git a/MVMCoreUI/Atoms/Views/MFView+FormValidation.swift b/MVMCoreUI/Atoms/Views/MFView+FormValidation.swift deleted file mode 100644 index 66d3d27a..00000000 --- a/MVMCoreUI/Atoms/Views/MFView+FormValidation.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// MFView+FormValidation.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 4/26/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation - - -fileprivate struct FormValidatorHolder { - static var formValidator: FormValidator? -} - -extension FormValidationProtocol where Self: MFView { - - var formValidator: FormValidator? { - get { - return FormValidatorHolder.formValidator - } - set(newValue) { - FormValidatorHolder.formValidator = newValue - } - } - - func setupFormValidation(delegateObject: DelegateObject?) { - if let delegateObject = delegateObject as? MVMCoreUIDelegateObject, - let formValidationProtocol = delegateObject.formValidationProtocol { - FormValidator.setupValidation(molecule: self, delegate:formValidationProtocol) - formValidator = FormValidator.getFormValidatorFor(delegate:formValidationProtocol) - } - } -} diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m index 0158beb1..757d1b50 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m @@ -42,7 +42,7 @@ static const CGFloat CheckBoxHeightWidth = 18.0; @property (nonatomic) BOOL isRequired; @property (nullable, strong, nonatomic) NSString *fieldKey; -@property (nullable, strong, nonatomic) DelegateObject *delegate; +@property (nullable, strong, nonatomic) MVMCoreUIDelegateObject *delegateObject; @end @@ -61,7 +61,7 @@ static const CGFloat CheckBoxHeightWidth = 18.0; - (void)setWithJSON:(NSDictionary *)json delegateObject:(DelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { [FormValidator setupValidationWithMolecule:self delegate:((MVMCoreUIDelegateObject *)delegateObject).formValidationProtocol]; - self.delegate = delegateObject; + self.delegateObject = (MVMCoreUIDelegateObject *) delegateObject; self.fieldKey = [json stringForKey:KeyFieldKey]; self.isRequired = [json boolForKey:KeyRequired]; @@ -342,10 +342,7 @@ static const CGFloat CheckBoxHeightWidth = 18.0; [self.checkMark updateCheckSelected:NO animated:animated]; } - if (self.delegate && [self.delegate respondsToSelector:@selector(formValidationProtocol)] && [[self.delegate performSelector:@selector(formValidationProtocol)] respondsToSelector:@selector(formValidatorModel)]) { - FormValidator *formValidator = [[self.delegate performSelector:@selector(formValidationProtocol)] performSelector:@selector(formValidatorModel)]; - [formValidator enableByValidation]; - } + [FormValidator enableByValidationWithDelegate:((MVMCoreUIDelegateObject *)self.delegateObject).formValidationProtocol]; } - (void)setColor:(nullable UIColor *)color forState:(UIControlState)state { diff --git a/MVMCoreUI/FormUIHelpers/FormValidator+FormParams.swift b/MVMCoreUI/FormUIHelpers/FormValidator+FormParams.swift index 17da0e72..bb9885ab 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator+FormParams.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator+FormParams.swift @@ -17,12 +17,9 @@ import Foundation var extraParam: [String: Any] = [:] MVMCoreDispatchUtility.performSyncBlock(onMainThread: { for molecule in self.molecules { - if let formFieldName = molecule.formFieldName, - let formFieldValue = molecule.formFieldValue, - let fieldName = formFieldName(), - let fieldValue = formFieldValue() { - - extraParam[fieldName] = fieldValue + if let formFieldName = molecule.formFieldName?(), + let formFieldValue = molecule.formFieldValue?() { + extraParam[formFieldName] = formFieldValue } } }) diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index 5eb28904..07f4a583 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -19,19 +19,19 @@ import UIKit molecules.append(molecule) } - public static func getFormValidatorFor(delegate: FormValidationProtocol) -> FormValidator? { - if let delegateFormValidatorModel = delegate.formValidatorModel, - let validator = delegateFormValidatorModel() { - return validator - } else { - return nil + public static func enableByValidationWith(delegate: FormValidationProtocol?) { + if let delegate = delegate { + let formValidator = FormValidator.getFormValidatorFor(delegate: delegate) + formValidator?.enableByValidation() } } + public static func getFormValidatorFor(delegate: FormValidationProtocol) -> FormValidator? { + return delegate.formValidatorModel?() + } + public static func setupValidation(molecule: UIView & FormValidationProtocol, delegate: FormValidationProtocol?) { - if let delegateFormValidatorModel = delegate?.formValidatorModel, - let validator = delegateFormValidatorModel() { - + if let validator = delegate?.formValidatorModel?() { validator.delegate = delegate validator.insertMolecule(molecule) } diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index d5cc4d87..f55ef302 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -81,7 +81,6 @@ import UIKit } else { radioButton.isSelected = !radioButton.isSelected } - formValidator?.enableByValidation() } func getColor( _ json: [AnyHashable: Any], _ key: String) -> UIColor? { diff --git a/MVMCoreUI/Molecules/RadioButtonList.swift b/MVMCoreUI/Molecules/RadioButtonList.swift index 6a1083fd..1b597214 100644 --- a/MVMCoreUI/Molecules/RadioButtonList.swift +++ b/MVMCoreUI/Molecules/RadioButtonList.swift @@ -17,6 +17,8 @@ import UIKit var selectedRadioButton: RadioButton? var selectedValue: String? + var delegateObject:MVMCoreUIDelegateObject? + // MARK: - Inits public init() { @@ -35,7 +37,8 @@ import UIKit selectedRadioButton?.radioButton.isSelected = false selectedRadioButton = radioButton selectedRadioButton?.radioButton.isSelected = true - formValidator?.enableByValidation() + + FormValidator.enableByValidationWith(delegate: self.delegateObject?.formValidationProtocol) } open override func setupView() { @@ -56,7 +59,7 @@ extension RadioButtonList: FormValidationProtocol { // The Field name key value pair for sending to server @objc public func formFieldName() -> String? { - return selectedRadioButton?.formFieldName() + return json?.optionalStringForKey("fieldKey") } // The Feild value key value paid for sending to server @@ -78,7 +81,8 @@ extension RadioButtonList { return } - setupFormValidation(delegateObject: delegateObject) + self.delegateObject = delegateObject + FormValidator.setupValidation(molecule: self, delegate: delegateObject.formValidationProtocol) StackableViewController.remove(self.subviews) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index f8d97233..0a37d62e 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -33,8 +33,7 @@ @"caretView": CaretView.class, @"caretButton": CaretButton.class, @"textField" : MFTextField.class, - @"radioButtonList": RadioButtonList.class, - @"radioButton": RadioButton.class, + @"radioButton": RadioButtonList.class, @"checkbox" : MVMCoreUICheckBox.class } mutableCopy]; }); From 45dde90fdcef4340a1ceadc0f6d492cbbc8478c6 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 14 May 2019 09:52:52 -0400 Subject: [PATCH 07/71] review --- MVMCoreUI/Atoms/Views/MFView.h | 3 --- MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m | 6 +++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/MFView.h b/MVMCoreUI/Atoms/Views/MFView.h index 243408ac..5e32ff0a 100644 --- a/MVMCoreUI/Atoms/Views/MFView.h +++ b/MVMCoreUI/Atoms/Views/MFView.h @@ -10,11 +10,8 @@ #import @import MVMCore.MVMCoreViewProtocol; -@class FormValidator; - @interface MFView : UIView -//@property (nullable, strong, nonatomic) FormValidator *formValidator; @property (nullable, nonatomic, strong) NSDictionary *json; // Called in the initialization functions. Can setup ui here. diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m index ff46aa6a..76f2d0f6 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m @@ -60,7 +60,11 @@ static const CGFloat CheckBoxHeightWidth = 18.0; - (void)setWithJSON:(NSDictionary *)json delegateObject:(DelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - [FormValidator setupValidationWithMolecule:self delegate:((MVMCoreUIDelegateObject *)delegateObject).formValidationProtocol]; + if ([delegateObject isKindOfClass:[MVMCoreUIDelegateObject class]]) { + [FormValidator setupValidationWithMolecule:self delegate:((MVMCoreUIDelegateObject *)delegateObject).formValidationProtocol]; + } + + self.delegate = delegateObject; self.fieldKey = [json stringForKey:KeyFieldKey]; self.isRequired = [json boolForKey:KeyRequired]; From ee09cfa2784e10b19a14f10f372b1f6aa0740eac Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 14 May 2019 11:04:33 -0400 Subject: [PATCH 08/71] conflicts --- MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m | 6 ------ 1 file changed, 6 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m index 48a46ef6..cb6f7eaf 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m @@ -60,17 +60,11 @@ static const CGFloat CheckBoxHeightWidth = 18.0; - (void)setWithJSON:(NSDictionary *)json delegateObject:(DelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { -<<<<<<< HEAD if ([delegateObject isKindOfClass:[MVMCoreUIDelegateObject class]]) { [FormValidator setupValidationWithMolecule:self delegate:((MVMCoreUIDelegateObject *)delegateObject).formValidationProtocol]; } - - self.delegate = delegateObject; -======= - [FormValidator setupValidationWithMolecule:self delegate:((MVMCoreUIDelegateObject *)delegateObject).formValidationProtocol]; self.delegateObject = (MVMCoreUIDelegateObject *) delegateObject; ->>>>>>> e90b4c299e84ce13a69fc77d4e69ad027b55fa09 self.fieldKey = [json stringForKey:KeyFieldKey]; self.isRequired = [json boolForKey:KeyRequired]; From 132b810a664e899be27b60a74edeec699142815f Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 14 May 2019 21:52:32 -0400 Subject: [PATCH 09/71] refactoring --- MVMCoreUI.xcodeproj/project.pbxproj | 8 +- MVMCoreUI/FormUIHelpers/FormValidator.swift | 8 +- MVMCoreUI/Molecules/RadioButton.swift | 89 +++++++++++++------ MVMCoreUI/Molecules/RadioButtonModel.swift | 57 ++++++++++++ .../MVMCoreUIMoleculeMappingObject.m | 2 +- 5 files changed, 128 insertions(+), 36 deletions(-) create mode 100644 MVMCoreUI/Molecules/RadioButtonModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index ddf5c036..e2b1e234 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -11,7 +11,7 @@ 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618A224BBE7700E1557D /* FormValidator.swift */; }; 0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618B224BBE7700E1557D /* FormValidator+TextFields.swift */; }; 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105618C224BBE7700E1557D /* FormValidator+FormParams.swift */; }; - 01157B94225D376D00F15D92 /* RadioButtonList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01157B93225D376D00F15D92 /* RadioButtonList.swift */; }; + 0116A4E5228B19640094F3ED /* RadioButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0116A4E4228B19640094F3ED /* RadioButtonModel.swift */; }; 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 */; }; @@ -173,7 +173,7 @@ 0105618A224BBE7700E1557D /* FormValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormValidator.swift; sourceTree = ""; }; 0105618B224BBE7700E1557D /* FormValidator+TextFields.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FormValidator+TextFields.swift"; sourceTree = ""; }; 0105618C224BBE7700E1557D /* FormValidator+FormParams.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FormValidator+FormParams.swift"; sourceTree = ""; }; - 01157B93225D376D00F15D92 /* RadioButtonList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonList.swift; sourceTree = ""; }; + 0116A4E4228B19640094F3ED /* RadioButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonModel.swift; sourceTree = ""; }; 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 = ""; }; @@ -451,9 +451,9 @@ D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */, D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */, D274CA322236A78900B01B62 /* StandardFooterView.swift */, - 01157B93225D376D00F15D92 /* RadioButtonList.swift */, 01004F2F22721C3800991ECC /* RadioButton.swift */, D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */, + 0116A4E4228B19640094F3ED /* RadioButtonModel.swift */, ); path = Molecules; sourceTree = ""; @@ -879,6 +879,7 @@ D29770F221F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.m in Sources */, DBC4391922442197001AB423 /* DashLine.swift in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, + 0116A4E5228B19640094F3ED /* RadioButtonModel.swift in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */, 01004F3022721C3800991ECC /* RadioButton.swift in Sources */, @@ -912,7 +913,6 @@ 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */, D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */, - 01157B94225D376D00F15D92 /* RadioButtonList.swift in Sources */, D29DF18121E69E50003B2FB9 /* MFView.m in Sources */, D29DF18321E69E54003B2FB9 /* SeparatorView.m in Sources */, D29DF17A21E69E1F003B2FB9 /* MFCustomButton.m in Sources */, diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index 07f4a583..af40b2de 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -12,10 +12,11 @@ import UIKit @objcMembers public class FormValidator: NSObject { var delegate: FormValidationProtocol? - var molecules: [UIView & FormValidationProtocol] = [] + var molecules: [FormValidationProtocol] = [] var extraValidationBlock: (() -> Bool)? + var radioButtonsModelByGroup: [String: RadioButtonModel] = [:] - public func insertMolecule(_ molecule: UIView & FormValidationProtocol) { + public func insertMolecule(_ molecule: FormValidationProtocol) { molecules.append(molecule) } @@ -30,7 +31,7 @@ import UIKit return delegate.formValidatorModel?() } - public static func setupValidation(molecule: UIView & FormValidationProtocol, delegate: FormValidationProtocol?) { + public static func setupValidation(molecule: FormValidationProtocol, delegate: FormValidationProtocol?) { if let validator = delegate?.formValidatorModel?() { validator.delegate = delegate validator.insertMolecule(molecule) @@ -52,3 +53,4 @@ import UIKit } } } + diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index f55ef302..c814de13 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -12,10 +12,16 @@ import UIKit @objcMembers open class RadioButton: ViewConstrainingView { let radioButton = MFRadioButton() - let label = Label() - var target: RadioButtonListProtocol? + var delegateObject:MVMCoreUIDelegateObject? var dummyButton: MFCustomButton? - + let label = Label() + + var groupName: String? + var fieldKey: String? + var formValue: Bool? + var isRequired: Bool = false + var radioButtonModel: RadioButtonModel? + // MARK: - Inits public init() { super.init(frame: .zero) @@ -29,11 +35,6 @@ import UIKit super.init(coder: aDecoder) } - public init(target: RadioButtonListProtocol) { - super.init(frame: .zero) - self.target = target - } - open override func setupView() { super.setupView() guard subviews.count == 0 else { @@ -43,18 +44,26 @@ import UIKit translatesAutoresizingMaskIntoConstraints = false radioButton.translatesAutoresizingMaskIntoConstraints = false addSubview(radioButton) - addSubview(label) radioButton.leftAnchor.constraint(equalTo: leftAnchor, constant: 0).isActive = true radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true radioButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - - label.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingTwo).isActive = true - label.rightAnchor.constraint(equalTo: rightAnchor, constant: 0).isActive = true - label.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true - label.bottomAnchor.constraint(greaterThanOrEqualTo: bottomAnchor, constant: PaddingOne).isActive = true - label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + if let rightView = createRightView() { + addSubview(rightView) + rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingTwo).isActive = true + rightView.rightAnchor.constraint(equalTo: rightAnchor, constant: 0).isActive = true + rightView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true + rightView.bottomAnchor.constraint(greaterThanOrEqualTo: bottomAnchor, constant: PaddingOne).isActive = true + rightView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + } + addActionHandler() + } + + func createRightView() -> ViewConstrainingView? { + let rightView = ViewConstrainingView(constrainingView: label) + return rightView } func addActionHandler() { @@ -76,19 +85,12 @@ import UIKit } func tapAction() { - if let target = target { - target.selected?(self) + if let radioButtonModel = radioButtonModel { + radioButtonModel.selected(self) } else { radioButton.isSelected = !radioButton.isSelected } - } - - func getColor( _ json: [AnyHashable: Any], _ key: String) -> UIColor? { - if let colorHex = json.optionalStringForKey(key) { - return UIColor.mfGet(forHex: colorHex) - } else { - return nil - } + FormValidator.enableByValidationWith(delegate: self.delegateObject?.formValidationProtocol) } } @@ -102,11 +104,27 @@ extension RadioButton { guard let jsonDictionary = json else { return } + groupName = jsonDictionary.optionalStringForKey("groupName") + fieldKey = jsonDictionary.optionalStringForKey("fieldKey") + isRequired = jsonDictionary.boolForKey("required") + self.delegateObject = delegateObject as? MVMCoreUIDelegateObject + + radioButtonModel = RadioButtonModel.setupForRadioButtonGroup(radioButton: self, + formValidator: self.delegateObject?.formValidationProtocol?.formValidatorModel?()) + + /* If the radio button has a group, it will have RadioButtonModel. + In this case the RadioButtonModel should be the validator + */ + if let radioButtonModel = radioButtonModel { + FormValidator.setupValidation(molecule: radioButtonModel, delegate: self.delegateObject?.formValidationProtocol) + } else { + FormValidator.setupValidation(molecule: self, delegate: self.delegateObject?.formValidationProtocol) + } + label.setWithJSON(jsonDictionary.optionalDictionaryForKey(KeyLabel), delegateObject: delegateObject, additionalData: additionalData) - addActionHandler() } open override func needsToBeConstrained() -> Bool { @@ -118,15 +136,30 @@ extension RadioButton { } } + // MARK: - FormValidationProtocol extension RadioButton: FormValidationProtocol { + // Used to check the validity of the field, to enable/disable the primary button. + @objc public func isValidField() -> Bool { + if isRequired == false { + return true + } + return radioButtonModel?.isValidField() ?? false + } + // The Field name key value pair for sending to server @objc public func formFieldName() -> String? { + if let radioButtonModel = radioButtonModel { + return radioButtonModel.formFieldName() + } return json?.optionalStringForKey("fieldKey") } - // The Field value key value paid for sending to server + // The Feild value key value paid for sending to server @objc public func formFieldValue() -> Any? { - return json?.optionalStringForKey(KeyValue) + if let radioButtonModel = radioButtonModel { + return radioButtonModel.formFieldValue() + } + return radioButton.isSelected } } diff --git a/MVMCoreUI/Molecules/RadioButtonModel.swift b/MVMCoreUI/Molecules/RadioButtonModel.swift new file mode 100644 index 00000000..fbbb1a60 --- /dev/null +++ b/MVMCoreUI/Molecules/RadioButtonModel.swift @@ -0,0 +1,57 @@ +// +// RadioButtonModel.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 5/14/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation +import UIKit + +@objcMembers public class RadioButtonModel: NSObject { + + private var selectedRadioButton: RadioButton? + private var radioButtons: [RadioButton] = [] + + public static func setupForRadioButtonGroup(radioButton: RadioButton, formValidator: FormValidator?) -> RadioButtonModel? { + + guard let groupName = radioButton.groupName, + let formValidator = formValidator else { + return nil + } + + let radioButtonModel = formValidator.radioButtonsModelByGroup[groupName] ?? RadioButtonModel() + formValidator.radioButtonsModelByGroup[groupName] = radioButtonModel + radioButtonModel.radioButtons.append(radioButton) + + return radioButtonModel + } + + public func selected(_ radioButton: RadioButton) { + selectedRadioButton?.radioButton.isSelected = false + selectedRadioButton = radioButton + selectedRadioButton?.radioButton.isSelected = true + } +} + +// MARK: - FormValidationProtocol +extension RadioButtonModel: FormValidationProtocol { + // Used to check the validity of the field, to enable/disable the primary button. + @objc public func isValidField() -> Bool { + if selectedRadioButton != nil { + return true + } + return false + } + + // The Field name key value pair for sending to server + @objc public func formFieldName() -> String? { + return selectedRadioButton?.fieldKey + } + + // The Feild value key value paid for sending to server + @objc public func formFieldValue() -> Any? { + return selectedRadioButton != nil ? true : false + } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 0a37d62e..d6e7d9f2 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -33,7 +33,7 @@ @"caretView": CaretView.class, @"caretButton": CaretButton.class, @"textField" : MFTextField.class, - @"radioButton": RadioButtonList.class, + @"radioButton": RadioButton.class, @"checkbox" : MVMCoreUICheckBox.class } mutableCopy]; }); From 6828f833e723b928835fd3d0c12a36f4dec4f8fb Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Fri, 31 May 2019 17:29:32 -0400 Subject: [PATCH 10/71] code review --- MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m | 8 +++--- MVMCoreUI/Molecules/RadioButton.swift | 29 +++++++++------------- MVMCoreUI/Molecules/RadioButtonModel.swift | 13 ++-------- MVMCoreUI/Styles/MFStyler.h | 3 +++ MVMCoreUI/Styles/MFStyler.m | 1 + 5 files changed, 21 insertions(+), 33 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m index cf608538..9bd8eaee 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m @@ -60,11 +60,9 @@ static const CGFloat CheckBoxHeightWidth = 18.0; - (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - if ([delegateObject isKindOfClass:[MVMCoreUIDelegateObject class]]) { - [FormValidator setupValidationWithMolecule:self delegate:((MVMCoreUIDelegateObject *)delegateObject).formValidationProtocol]; - } + [FormValidator setupValidationWithMolecule:self delegate:delegateObject.formValidationProtocol]; - self.delegateObject = (MVMCoreUIDelegateObject *) delegateObject; + self.delegateObject = delegateObject; self.fieldKey = [json stringForKey:KeyFieldKey]; self.isRequired = [json boolForKey:KeyRequired]; @@ -345,7 +343,7 @@ static const CGFloat CheckBoxHeightWidth = 18.0; [self.checkMark updateCheckSelected:NO animated:animated]; } - [FormValidator enableByValidationWithDelegate:((MVMCoreUIDelegateObject *)self.delegateObject).formValidationProtocol]; + [FormValidator enableByValidationWithDelegate:self.delegateObject.formValidationProtocol]; } - (void)setColor:(nullable UIColor *)color forState:(UIControlState)state { diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index c814de13..ae2f7d52 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -12,7 +12,7 @@ import UIKit @objcMembers open class RadioButton: ViewConstrainingView { let radioButton = MFRadioButton() - var delegateObject:MVMCoreUIDelegateObject? + var delegateObject: MVMCoreUIDelegateObject? var dummyButton: MFCustomButton? let label = Label() @@ -52,13 +52,14 @@ import UIKit if let rightView = createRightView() { addSubview(rightView) - rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingTwo).isActive = true + rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingHorizontalBetweenRelatedItems).isActive = true rightView.rightAnchor.constraint(equalTo: rightAnchor, constant: 0).isActive = true rightView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true rightView.bottomAnchor.constraint(greaterThanOrEqualTo: bottomAnchor, constant: PaddingOne).isActive = true rightView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true } addActionHandler() + addActionHandler() } func createRightView() -> ViewConstrainingView? { @@ -68,7 +69,7 @@ import UIKit func addActionHandler() { - if dummyButton != nil { + guard dummyButton == nil else { return } @@ -97,7 +98,8 @@ import UIKit // MARK: - MVMCoreUIMoleculeViewProtocol extension RadioButton { - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { + @objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) // Configure class properties with JSON values @@ -106,9 +108,8 @@ extension RadioButton { } groupName = jsonDictionary.optionalStringForKey("groupName") fieldKey = jsonDictionary.optionalStringForKey("fieldKey") - isRequired = jsonDictionary.boolForKey("required") - - self.delegateObject = delegateObject as? MVMCoreUIDelegateObject + isRequired = jsonDictionary.boolForKey("required") + self.delegateObject = delegateObject radioButtonModel = RadioButtonModel.setupForRadioButtonGroup(radioButton: self, formValidator: self.delegateObject?.formValidationProtocol?.formValidatorModel?()) @@ -130,7 +131,7 @@ extension RadioButton { open override func needsToBeConstrained() -> Bool { return true } - + open override func moleculeAlignment() -> UIStackView.Alignment { return UIStackView.Alignment.leading; } @@ -141,7 +142,7 @@ extension RadioButton { extension RadioButton: FormValidationProtocol { // Used to check the validity of the field, to enable/disable the primary button. @objc public func isValidField() -> Bool { - if isRequired == false { + guard isRequired else { return true } return radioButtonModel?.isValidField() ?? false @@ -149,17 +150,11 @@ extension RadioButton: FormValidationProtocol { // The Field name key value pair for sending to server @objc public func formFieldName() -> String? { - if let radioButtonModel = radioButtonModel { - return radioButtonModel.formFieldName() - } - return json?.optionalStringForKey("fieldKey") + return radioButtonModel?.formFieldName() ?? json?.optionalStringForKey("fieldKey") } // The Feild value key value paid for sending to server @objc public func formFieldValue() -> Any? { - if let radioButtonModel = radioButtonModel { - return radioButtonModel.formFieldValue() - } - return radioButton.isSelected + return radioButtonModel?.formFieldValue() ?? radioButton.isSelected } } diff --git a/MVMCoreUI/Molecules/RadioButtonModel.swift b/MVMCoreUI/Molecules/RadioButtonModel.swift index fbbb1a60..096cb80a 100644 --- a/MVMCoreUI/Molecules/RadioButtonModel.swift +++ b/MVMCoreUI/Molecules/RadioButtonModel.swift @@ -12,10 +12,8 @@ import UIKit @objcMembers public class RadioButtonModel: NSObject { private var selectedRadioButton: RadioButton? - private var radioButtons: [RadioButton] = [] public static func setupForRadioButtonGroup(radioButton: RadioButton, formValidator: FormValidator?) -> RadioButtonModel? { - guard let groupName = radioButton.groupName, let formValidator = formValidator else { return nil @@ -23,8 +21,6 @@ import UIKit let radioButtonModel = formValidator.radioButtonsModelByGroup[groupName] ?? RadioButtonModel() formValidator.radioButtonsModelByGroup[groupName] = radioButtonModel - radioButtonModel.radioButtons.append(radioButton) - return radioButtonModel } @@ -39,17 +35,12 @@ import UIKit extension RadioButtonModel: FormValidationProtocol { // Used to check the validity of the field, to enable/disable the primary button. @objc public func isValidField() -> Bool { - if selectedRadioButton != nil { - return true - } - return false + return selectedRadioButton != nil ? true : false } - - // The Field name key value pair for sending to server + // Name of the field to send to server @objc public func formFieldName() -> String? { return selectedRadioButton?.fieldKey } - // The Feild value key value paid for sending to server @objc public func formFieldValue() -> Any? { return selectedRadioButton != nil ? true : false diff --git a/MVMCoreUI/Styles/MFStyler.h b/MVMCoreUI/Styles/MFStyler.h index 0a52c468..83fd0271 100644 --- a/MVMCoreUI/Styles/MFStyler.h +++ b/MVMCoreUI/Styles/MFStyler.h @@ -36,6 +36,9 @@ extern CGFloat const PaddingVerticalWhiteGrayView; extern CGFloat const PaddingVerticalHeadlineAlternate; extern CGFloat const PaddingPrimaryButtonTop; +// +extern CGFloat const PaddingHorizontalBetweenRelatedItems; + // These are based on the multiple of 6 rule extern CGFloat const PaddingOne; extern CGFloat const PaddingTwo; diff --git a/MVMCoreUI/Styles/MFStyler.m b/MVMCoreUI/Styles/MFStyler.m index 261181b1..d0d436a2 100644 --- a/MVMCoreUI/Styles/MFStyler.m +++ b/MVMCoreUI/Styles/MFStyler.m @@ -25,6 +25,7 @@ CGFloat const PaddingVerticalWhiteGrayView = 72; CGFloat const PaddingVerticalHeadlineAlternate = 48; CGFloat const PaddingPrimaryButtonTop = 36; +CGFloat const PaddingHorizontalBetweenRelatedItems = 16; CGFloat const PaddingOne = 6; CGFloat const PaddingTwo = 12; CGFloat const PaddingThree = 18; From b72f9a2b71186e206df6dfa64d74b9984569b136 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 4 Jun 2019 09:43:16 -0400 Subject: [PATCH 11/71] code review --- MVMCoreUI/Molecules/RadioButton.swift | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index ae2f7d52..90c4fd5e 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -44,8 +44,9 @@ import UIKit translatesAutoresizingMaskIntoConstraints = false radioButton.translatesAutoresizingMaskIntoConstraints = false addSubview(radioButton) - - radioButton.leftAnchor.constraint(equalTo: leftAnchor, constant: 0).isActive = true + + + radioButton.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 0).isActive = true radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true radioButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true @@ -53,7 +54,7 @@ import UIKit if let rightView = createRightView() { addSubview(rightView) rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingHorizontalBetweenRelatedItems).isActive = true - rightView.rightAnchor.constraint(equalTo: rightAnchor, constant: 0).isActive = true + rightView.rightAnchor.constraint(equalTo: layoutMarginsGuide.rightAnchor, constant: 0).isActive = true rightView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true rightView.bottomAnchor.constraint(greaterThanOrEqualTo: bottomAnchor, constant: PaddingOne).isActive = true rightView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true @@ -127,14 +128,6 @@ extension RadioButton { delegateObject: delegateObject, additionalData: additionalData) } - - open override func needsToBeConstrained() -> Bool { - return true - } - - open override func moleculeAlignment() -> UIStackView.Alignment { - return UIStackView.Alignment.leading; - } } From 8eae881bf1539d972196cc15b55eeae86543cf22 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 24 Jun 2019 10:47:43 -0400 Subject: [PATCH 12/71] code review --- MVMCoreUI/FormUIHelpers/FormValidator.swift | 2 +- MVMCoreUI/Molecules/RadioButton.swift | 48 ++++++-- MVMCoreUI/Molecules/RadioButtonList.swift | 113 ------------------ MVMCoreUI/Styles/MFStyler.h | 2 - .../Strings/en.lproj/Localizable.strings | 7 ++ .../Templates/MoleculeStackTemplate.swift | 16 +-- 6 files changed, 53 insertions(+), 135 deletions(-) delete mode 100644 MVMCoreUI/Molecules/RadioButtonList.swift diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index af40b2de..91928f3e 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -16,7 +16,7 @@ import UIKit var extraValidationBlock: (() -> Bool)? var radioButtonsModelByGroup: [String: RadioButtonModel] = [:] - public func insertMolecule(_ molecule: FormValidationProtocol) { + public func insertMolecule(_ molecule: FormValidationProtocol) { molecules.append(molecule) } diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index 90c4fd5e..ad9e72e7 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -45,22 +45,35 @@ import UIKit radioButton.translatesAutoresizingMaskIntoConstraints = false addSubview(radioButton) + isAccessibilityElement = true + accessibilityTraits = .none + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") radioButton.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 0).isActive = true - radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true - bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true + var constraint = radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne) + constraint.priority = .defaultLow + constraint.isActive = true + + constraint = bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne) + constraint.priority = .defaultLow + constraint.isActive = true + radioButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true if let rightView = createRightView() { addSubview(rightView) rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingHorizontalBetweenRelatedItems).isActive = true rightView.rightAnchor.constraint(equalTo: layoutMarginsGuide.rightAnchor, constant: 0).isActive = true - rightView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true - rightView.bottomAnchor.constraint(greaterThanOrEqualTo: bottomAnchor, constant: PaddingOne).isActive = true - rightView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + constraint = rightView.topAnchor.constraint(equalTo: topAnchor, constant: PaddingOne) + constraint.priority = .defaultHigh + constraint.isActive = true + + constraint = bottomAnchor.constraint(greaterThanOrEqualTo: rightView.bottomAnchor, constant: PaddingOne) + constraint.priority = .defaultHigh + constraint.isActive = true } addActionHandler() - addActionHandler() } func createRightView() -> ViewConstrainingView? { @@ -92,7 +105,8 @@ import UIKit } else { radioButton.isSelected = !radioButton.isSelected } - FormValidator.enableByValidationWith(delegate: self.delegateObject?.formValidationProtocol) + FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) + changeAccessibilityLabel() } } @@ -113,20 +127,21 @@ extension RadioButton { self.delegateObject = delegateObject radioButtonModel = RadioButtonModel.setupForRadioButtonGroup(radioButton: self, - formValidator: self.delegateObject?.formValidationProtocol?.formValidatorModel?()) + formValidator: delegateObject?.formValidationProtocol?.formValidatorModel?()) /* If the radio button has a group, it will have RadioButtonModel. In this case the RadioButtonModel should be the validator */ if let radioButtonModel = radioButtonModel { - FormValidator.setupValidation(molecule: radioButtonModel, delegate: self.delegateObject?.formValidationProtocol) + FormValidator.setupValidation(molecule: radioButtonModel, delegate: delegateObject?.formValidationProtocol) } else { - FormValidator.setupValidation(molecule: self, delegate: self.delegateObject?.formValidationProtocol) + FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) } label.setWithJSON(jsonDictionary.optionalDictionaryForKey(KeyLabel), delegateObject: delegateObject, additionalData: additionalData) + changeAccessibilityLabel() } } @@ -146,8 +161,19 @@ extension RadioButton: FormValidationProtocol { return radioButtonModel?.formFieldName() ?? json?.optionalStringForKey("fieldKey") } - // The Feild value key value paid for sending to server + // The Field value key value pair for sending to server @objc public func formFieldValue() -> Any? { return radioButtonModel?.formFieldValue() ?? radioButton.isSelected } } + +// MARK: Accessibility +extension RadioButton { + func changeAccessibilityLabel() { + let stateString = radioButton.isSelected ? "radio_selected_state" : "radio_not_selected_state" + let localizedStringState = MVMCoreUIUtility.hardcodedString(withKey: stateString) ?? "" + let accebilityString = (label.accessibilityLabel ?? (json?.optionalStringForKey("accessibilityText") ?? "")) + + (MVMCoreUIUtility.hardcodedString(withKey: "radio_desc_state") ?? "") + localizedStringState + accessibilityLabel = accebilityString + } +} diff --git a/MVMCoreUI/Molecules/RadioButtonList.swift b/MVMCoreUI/Molecules/RadioButtonList.swift deleted file mode 100644 index 1b597214..00000000 --- a/MVMCoreUI/Molecules/RadioButtonList.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// RadioButton.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 4/9/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import UIKit - -@objc public protocol RadioButtonListProtocol: NSObjectProtocol { - - @objc optional func selected(_ radioButton: RadioButton) -} - -@objcMembers open class RadioButtonList: ViewConstrainingView, RadioButtonListProtocol { - - var selectedRadioButton: RadioButton? - var selectedValue: String? - var delegateObject:MVMCoreUIDelegateObject? - - - // MARK: - Inits - public init() { - super.init(frame: .zero) - } - - public override init(frame: CGRect) { - super.init(frame: frame) - } - - required public init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - public func selected(_ radioButton: RadioButton) { - selectedRadioButton?.radioButton.isSelected = false - selectedRadioButton = radioButton - selectedRadioButton?.radioButton.isSelected = true - - FormValidator.enableByValidationWith(delegate: self.delegateObject?.formValidationProtocol) - } - - open override func setupView() { - super.setupView() - self.translatesAutoresizingMaskIntoConstraints = false - } -} - -// MARK: - FormValidationProtocol -extension RadioButtonList: FormValidationProtocol { - // Used to check the validity of the field, to enable/disable the primary button. - @objc public func isValidField() -> Bool { - if !(json?.boolForKey("required") ?? true) { - return true - } - return selectedRadioButton != nil - } - - // The Field name key value pair for sending to server - @objc public func formFieldName() -> String? { - return json?.optionalStringForKey("fieldKey") - } - - // The Feild value key value paid for sending to server - @objc public func formFieldValue() -> Any? { - return selectedRadioButton?.formFieldValue() - } -} - -// MARK: - MVMCoreUIMoleculeViewProtocol -extension RadioButtonList { - - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - // Configure class properties with JSON values - guard let jsonDictionary = json, - let optionsList = jsonDictionary.optionalArrayForKey("optionsList") as? [[AnyHashable: Any]], - let delegateObject = delegateObject as? MVMCoreUIDelegateObject else { - return - } - - self.delegateObject = delegateObject - FormValidator.setupValidation(molecule: self, delegate: delegateObject.formValidationProtocol) - - StackableViewController.remove(self.subviews) - - var items:[UIView] = [] - for option in optionsList { - let radioButton = RadioButton(target: self) - radioButton.setWithJSON(option, delegateObject: delegateObject, additionalData: nil) - items.append(radioButton) - } - - let verticalSpace = MFStyler.defaultVerticalPaddingForApplicationWidth() - StackableViewController.populateView(self, - withUIArray: items) { (item) -> UIEdgeInsets in - return UIEdgeInsets(top: verticalSpace, - left: 0, - bottom: 0, - right: 0) - } - } - - open override func needsToBeConstrained() -> Bool { - return true - } - - open override func moleculeAlignment() -> UIStackView.Alignment { - return UIStackView.Alignment.leading; - } -} diff --git a/MVMCoreUI/Styles/MFStyler.h b/MVMCoreUI/Styles/MFStyler.h index 095426ad..f5632dca 100644 --- a/MVMCoreUI/Styles/MFStyler.h +++ b/MVMCoreUI/Styles/MFStyler.h @@ -35,8 +35,6 @@ extern CGFloat const PaddingHorizontalLarge; extern CGFloat const PaddingVerticalWhiteGrayView; extern CGFloat const PaddingVerticalHeadlineAlternate; extern CGFloat const PaddingPrimaryButtonTop; - -// extern CGFloat const PaddingHorizontalBetweenRelatedItems; // These are based on the multiple of 6 rule diff --git a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings index 96088cab..8bfb4511 100644 --- a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings @@ -35,6 +35,13 @@ "checkbox_checked_state" = "Checked"; "checkbox_unchecked_state" = "Unchecked"; "checkbox_desc_state" = "%@ CheckBox %@"; + +// Radio Button +"radio_action_hint" = "Double tap to select"; +"radio_selected_state" = "Selected"; +"radio_not_selected_state" = "Not Selected"; +"radio_desc_state" = "Option"; + // Switch "mfswitch_buttonlabel" = "Switch Button"; "AccOn" = "on"; diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Templates/MoleculeStackTemplate.swift index 3f5e04da..1fb6e0c8 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackTemplate.swift @@ -8,9 +8,9 @@ import UIKit -public class MoleculeStackTemplate: ThreeLayerViewController { +open class MoleculeStackTemplate: ThreeLayerViewController { - public override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { + open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { var shouldFinish = super.shouldFinishProcessingLoad(loadObject, error: error) if shouldFinish, let firstError = modulesNeeded().errors?.first { // Don't continue if there was an error loading needed modules. @@ -20,25 +20,25 @@ public class MoleculeStackTemplate: ThreeLayerViewController { return shouldFinish } - public override func spaceBetweenTopAndMiddle() -> CGFloat? { + open override func spaceBetweenTopAndMiddle() -> CGFloat? { return 0 } - public override func viewForTop() -> UIView? { + open override func viewForTop() -> UIView? { guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, constrainIfNeeded: true) else { return nil } return molecule } - public override func viewForMiddle() -> UIView? { + open override func viewForMiddle() -> UIView? { guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("moleculeStack") else { return nil } return MoleculeStackView(withJSON: moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil) } - override public func viewForBottom() -> UIView? { + override open func viewForBottom() -> UIView? { guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, constrainIfNeeded: true) else { return nil } @@ -46,14 +46,14 @@ public class MoleculeStackTemplate: ThreeLayerViewController { } // MARK: - cache handling - public override func pageTypesToListenFor() -> [Any]? { + open override func pageTypesToListenFor() -> [Any]? { guard let pageType = self.pageType else { return super.pageTypesToListenFor() } return [pageType] } - public override func modulesToListenFor() -> [Any]? { + open override func modulesToListenFor() -> [Any]? { // Get all of the molecules that need modules. return modulesNeeded().modules } From 73e6f7f513d15a648211e88ab9db09f50aa7b3d8 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 25 Jun 2019 09:20:27 -0400 Subject: [PATCH 13/71] spelling --- MVMCoreUI/Molecules/RadioButtonModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Molecules/RadioButtonModel.swift b/MVMCoreUI/Molecules/RadioButtonModel.swift index 096cb80a..2063fd83 100644 --- a/MVMCoreUI/Molecules/RadioButtonModel.swift +++ b/MVMCoreUI/Molecules/RadioButtonModel.swift @@ -41,7 +41,7 @@ extension RadioButtonModel: FormValidationProtocol { @objc public func formFieldName() -> String? { return selectedRadioButton?.fieldKey } - // The Feild value key value paid for sending to server + // The field value key value pair for sending to server @objc public func formFieldValue() -> Any? { return selectedRadioButton != nil ? true : false } From dcd782903eb1d305bbea9bdd3e45731ad79244b8 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 25 Jun 2019 09:49:05 -0400 Subject: [PATCH 14/71] constraints --- MVMCoreUI/Molecules/RadioButton.swift | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index ad9e72e7..0fc89c1b 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -50,14 +50,8 @@ import UIKit accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") radioButton.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 0).isActive = true - var constraint = radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne) - constraint.priority = .defaultLow - constraint.isActive = true - - constraint = bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne) - constraint.priority = .defaultLow - constraint.isActive = true - + radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true radioButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true if let rightView = createRightView() { @@ -65,11 +59,11 @@ import UIKit rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingHorizontalBetweenRelatedItems).isActive = true rightView.rightAnchor.constraint(equalTo: layoutMarginsGuide.rightAnchor, constant: 0).isActive = true - constraint = rightView.topAnchor.constraint(equalTo: topAnchor, constant: PaddingOne) + var constraint = rightView.topAnchor.constraint(equalTo: topAnchor, constant: PaddingOne) constraint.priority = .defaultHigh constraint.isActive = true - constraint = bottomAnchor.constraint(greaterThanOrEqualTo: rightView.bottomAnchor, constant: PaddingOne) + constraint = bottomAnchor.constraint(equalTo: rightView.bottomAnchor, constant: PaddingOne) constraint.priority = .defaultHigh constraint.isActive = true } From 6b71115eb03ce61cf1123cfa0e5bf4473884f77a Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 27 Aug 2019 20:25:07 -0400 Subject: [PATCH 15/71] fixes --- MVMCoreUI/Molecules/RadioButton.swift | 43 +++++----------------- MVMCoreUI/Molecules/RadioButtonModel.swift | 4 +- 2 files changed, 11 insertions(+), 36 deletions(-) diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index 0fc89c1b..acd5f016 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -16,12 +16,16 @@ import UIKit var dummyButton: MFCustomButton? let label = Label() - var groupName: String? var fieldKey: String? var formValue: Bool? var isRequired: Bool = false var radioButtonModel: RadioButtonModel? + lazy var groupName: String? = { + [unowned self] in + return json?.optionalStringForKey("groupName") ?? json?.optionalStringForKey("fieldKey") + }() + // MARK: - Inits public init() { super.init(frame: .zero) @@ -115,22 +119,15 @@ extension RadioButton { guard let jsonDictionary = json else { return } - groupName = jsonDictionary.optionalStringForKey("groupName") + fieldKey = jsonDictionary.optionalStringForKey("fieldKey") isRequired = jsonDictionary.boolForKey("required") self.delegateObject = delegateObject - radioButtonModel = RadioButtonModel.setupForRadioButtonGroup(radioButton: self, + let radioButtonModel = RadioButtonModel.setupForRadioButtonGroup(radioButton: self, formValidator: delegateObject?.formValidationProtocol?.formValidatorModel?()) - - /* If the radio button has a group, it will have RadioButtonModel. - In this case the RadioButtonModel should be the validator - */ - if let radioButtonModel = radioButtonModel { - FormValidator.setupValidation(molecule: radioButtonModel, delegate: delegateObject?.formValidationProtocol) - } else { - FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) - } + FormValidator.setupValidation(molecule: radioButtonModel, delegate: delegateObject?.formValidationProtocol) + self.radioButtonModel = radioButtonModel label.setWithJSON(jsonDictionary.optionalDictionaryForKey(KeyLabel), delegateObject: delegateObject, @@ -139,28 +136,6 @@ extension RadioButton { } } - -// MARK: - FormValidationProtocol -extension RadioButton: FormValidationProtocol { - // Used to check the validity of the field, to enable/disable the primary button. - @objc public func isValidField() -> Bool { - guard isRequired else { - return true - } - return radioButtonModel?.isValidField() ?? false - } - - // The Field name key value pair for sending to server - @objc public func formFieldName() -> String? { - return radioButtonModel?.formFieldName() ?? json?.optionalStringForKey("fieldKey") - } - - // The Field value key value pair for sending to server - @objc public func formFieldValue() -> Any? { - return radioButtonModel?.formFieldValue() ?? radioButton.isSelected - } -} - // MARK: Accessibility extension RadioButton { func changeAccessibilityLabel() { diff --git a/MVMCoreUI/Molecules/RadioButtonModel.swift b/MVMCoreUI/Molecules/RadioButtonModel.swift index 2063fd83..40238307 100644 --- a/MVMCoreUI/Molecules/RadioButtonModel.swift +++ b/MVMCoreUI/Molecules/RadioButtonModel.swift @@ -13,10 +13,10 @@ import UIKit private var selectedRadioButton: RadioButton? - public static func setupForRadioButtonGroup(radioButton: RadioButton, formValidator: FormValidator?) -> RadioButtonModel? { + public static func setupForRadioButtonGroup(radioButton: RadioButton, formValidator: FormValidator?) -> RadioButtonModel { guard let groupName = radioButton.groupName, let formValidator = formValidator else { - return nil + return RadioButtonModel() } let radioButtonModel = formValidator.radioButtonsModelByGroup[groupName] ?? RadioButtonModel() From 062edc5d4114ddde718107674c6c9155642f03fa Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Fri, 30 Aug 2019 15:33:05 -0400 Subject: [PATCH 16/71] refactoring for validation based on group --- MVMCoreUI/Atoms/Buttons/PrimaryButton.m | 10 ++++-- MVMCoreUI/Atoms/TextFields/MFTextField.h | 7 ++-- MVMCoreUI/Atoms/TextFields/MFTextField.m | 32 ++++++++--------- MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m | 2 +- .../FormValidationProtocol.swift | 24 +++++++++---- .../FormValidator+FormParams.swift | 6 ++-- MVMCoreUI/FormUIHelpers/FormValidator.swift | 35 +++++++++++++------ MVMCoreUI/Molecules/RadioButton.swift | 23 ++++++++++-- MVMCoreUI/Molecules/RadioButtonModel.swift | 10 ++++-- MVMCoreUI/Molecules/Switch.swift | 6 +++- MVMCoreUI/Molecules/SwitchLineItem.swift | 21 ++++++++++- 11 files changed, 124 insertions(+), 52 deletions(-) diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m index 57470bf9..fa719012 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m @@ -17,9 +17,10 @@ @import MVMCore.MVMCoreGetterUtility; @import MVMCore.NSDictionary_MFConvenience; -@interface PrimaryButton() +@interface PrimaryButton() @property (nonatomic) BOOL validationRequired; +@property (nonatomic, strong) NSArray *requiredFieldsList; @property (nonatomic) BOOL smallButton; @property (assign, nonatomic) BOOL tinyButton; @property (nonatomic) CGFloat sizeForSizing; @@ -697,6 +698,7 @@ self.disabledBorderColor = [UIColor mfGetColorForHex:color]; } self.validationRequired = [json boolForKey:@"validationRequired"]; + self.requiredFieldsList = [json array:@"requiredFields"]; NSString *size = [json string:@"size"]; if ([size isEqualToString:@"small"]) { @@ -773,7 +775,11 @@ } } -#pragma mark - FormValidationProtocol +#pragma mark - FormValidationEnableDisableProtocol + +- (NSArray *)requiredFields { + return self.requiredFieldsList; +} - (void)enableField:(BOOL)enable { if (!self.validationRequired) { diff --git a/MVMCoreUI/Atoms/TextFields/MFTextField.h b/MVMCoreUI/Atoms/TextFields/MFTextField.h index 72adf682..3e229092 100644 --- a/MVMCoreUI/Atoms/TextFields/MFTextField.h +++ b/MVMCoreUI/Atoms/TextFields/MFTextField.h @@ -43,9 +43,10 @@ @property (nonatomic,getter=isEnabled) BOOL enabled; // To set the placeholder and text -@property (nullable, weak, nonatomic) NSString *text; -@property (nullable, weak, nonatomic) NSString *formText; -@property (nullable, weak, nonatomic) NSString *fieldKey; +@property (nullable, strong, nonatomic) NSString *text; +@property (nullable, strong, nonatomic) NSString *formText; +@property (nullable, strong, nonatomic) NSString *fieldKey; +@property (nullable, strong, nonatomic) NSString *groupName; @property (nullable, weak, nonatomic) NSString *placeholder; // will move out in Feb release diff --git a/MVMCoreUI/Atoms/TextFields/MFTextField.m b/MVMCoreUI/Atoms/TextFields/MFTextField.m index ba68c0ed..0e9be054 100644 --- a/MVMCoreUI/Atoms/TextFields/MFTextField.m +++ b/MVMCoreUI/Atoms/TextFields/MFTextField.m @@ -18,7 +18,7 @@ @import MVMCore.NSDictionary_MFConvenience; @import MVMCore.MVMCoreJSONConstants; -@interface MFTextField() +@interface MFTextField() @property (strong, nonatomic) UIColor *customPlaceHolderColor; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *separatorHeightConstraint; @@ -303,28 +303,20 @@ return; } - NSString *string = [map string:KeyLabel]; - if (string.length > 0) { - self.formText = string; - } - string = [map string:KeyValue]; - if (string.length > 0) { + self.formText = [map string:KeyLabel];; + self.groupName = [map string:@"groupName"]; + self.errMessage = [map string:KeyErrorMessage]; + self.fieldKey = [map string:KeyFieldKey]; + + NSString *string = [map stringForKey:KeyValue]; + if (string.length) { self.text = string; } + string = [map stringForKey:KeyDisable]; if ([string isEqual:StringY] || [map boolForKey:KeyDisable]) { [self enable:NO]; } - string = [map string:KeyErrorMessage]; - if (string.length > 0) { - self.errMessage = string; - } - - // key used to send text value to server - string = [map string:KeyFieldKey]; - if (string.length > 0) { - self.fieldKey = string; - } string = [map string:KeyType]; if ([string isEqualToString:@"dropDown"]) { @@ -588,5 +580,9 @@ - (nullable id)formFieldValue { return self.text; } - + +- (NSString * _Nullable)formFieldGroupName { + return self.groupName; +} + @end diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m index 2b5a7f37..f31aace3 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m @@ -21,7 +21,7 @@ static const CGFloat FaultTolerance = 20.f; static const CGFloat CheckBoxHeightWidth = 18.0; -@interface MVMCoreUICheckBox () +@interface MVMCoreUICheckBox () @property (nonatomic, readwrite) BOOL isSelected; @property (weak, nonatomic) UIView *checkedSquare; diff --git a/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift b/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift index b593a503..0af41b42 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift @@ -12,16 +12,26 @@ import Foundation // Getter method to get the FormValidator form the delegate (Mostly from the parent View Controller) @objc optional func formValidatorModel() -> FormValidator? - +} + +@objc public protocol FormValidationFormFieldProtocol: FormValidationProtocol { // Used to check the validity of the field, to enable/disable the primary button. - @objc optional func isValidField() -> Bool + @objc func isValidField() -> Bool + // The Field name key value pair for sending to server + @objc func formFieldName() -> String? + + // Returns the group name for validation + @objc func formFieldGroupName() -> String? + + // The Field value key value pair for sending to server + @objc func formFieldValue() -> Any? +} + +@objc public protocol FormValidationEnableDisableProtocol: FormValidationProtocol { // Based on the isValidField(), the fields which needs to be enabled can call this method @objc optional func enableField(_ enable: Bool) - // The Field name key value pair for sending to server - @objc optional func formFieldName() -> String? - - // The Field value key value pair for sending to server - @objc optional func formFieldValue() -> Any? + // Returns the list of field keys required to enable/disable + @objc optional func requiredFields() -> [String]? } diff --git a/MVMCoreUI/FormUIHelpers/FormValidator+FormParams.swift b/MVMCoreUI/FormUIHelpers/FormValidator+FormParams.swift index bb9885ab..4c4945a4 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator+FormParams.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator+FormParams.swift @@ -16,9 +16,9 @@ import Foundation @objc func getFormParams() -> [String: Any] { var extraParam: [String: Any] = [:] MVMCoreDispatchUtility.performSyncBlock(onMainThread: { - for molecule in self.molecules { - if let formFieldName = molecule.formFieldName?(), - let formFieldValue = molecule.formFieldValue?() { + for molecule in self.fieldMolecules { + if let formFieldName = molecule.formFieldName(), + let formFieldValue = molecule.formFieldValue() { extraParam[formFieldName] = formFieldValue } } diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index 91928f3e..821db784 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -8,16 +8,22 @@ import Foundation import UIKit +import MVMCore @objcMembers public class FormValidator: NSObject { var delegate: FormValidationProtocol? - var molecules: [FormValidationProtocol] = [] - var extraValidationBlock: (() -> Bool)? + var fieldMolecules: [FormValidationFormFieldProtocol] = [] + var enableDisableMolecules: [FormValidationEnableDisableProtocol] = [] var radioButtonsModelByGroup: [String: RadioButtonModel] = [:] public func insertMolecule(_ molecule: FormValidationProtocol) { - molecules.append(molecule) + if let molecule = molecule as? FormValidationFormFieldProtocol { + fieldMolecules.append(molecule) + } + if let molecule = molecule as? FormValidationEnableDisableProtocol { + enableDisableMolecules.append(molecule) + } } public static func enableByValidationWith(delegate: FormValidationProtocol?) { @@ -39,18 +45,25 @@ import UIKit } public func enableByValidation() { - var valid = true - for molecule in molecules { - valid = valid && (molecule.isValidField?() ?? true) + var groupValue: [String: Bool] = [:] + for molecule in fieldMolecules { + let valid = molecule.isValidField() + if let grouName = molecule.formFieldGroupName() { + groupValue[grouName] = valid && (groupValue[grouName] ?? true) + } } - let enableField = valid && (extraValidationBlock?() ?? true) - shouldEnable(enableField) + shouldEnable(groupValue) } - public func shouldEnable(_ enable: Bool) { - for molecule in molecules { - molecule.enableField?(enable) + public func shouldEnable(_ groupValue: [String: Bool]) { + for molecule in enableDisableMolecules { + var valid = false + for groupName in molecule.requiredFields?() ?? [] { + valid = groupValue[groupName] ?? false + } + molecule.enableField?(valid) } } + } diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index acd5f016..b95b2ab7 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -9,9 +9,9 @@ import UIKit -@objcMembers open class RadioButton: ViewConstrainingView { +@objcMembers open class RadioButton: ViewConstrainingView, FormValidationFormFieldProtocol { - let radioButton = MFRadioButton() + public let radioButton = MFRadioButton() var delegateObject: MVMCoreUIDelegateObject? var dummyButton: MFCustomButton? let label = Label() @@ -20,10 +20,11 @@ import UIKit var formValue: Bool? var isRequired: Bool = false var radioButtonModel: RadioButtonModel? + lazy var groupName: String? = { [unowned self] in - return json?.optionalStringForKey("groupName") ?? json?.optionalStringForKey("fieldKey") + return json?.optionalStringForKey("radioGroupName") ?? json?.optionalStringForKey("fieldKey") }() // MARK: - Inits @@ -106,6 +107,22 @@ import UIKit FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) changeAccessibilityLabel() } + + public func isValidField() -> Bool { + return radioButton.isSelected + } + + public func formFieldName() -> String? { + return json?.optionalStringForKey("fieldKey") + } + + public func formFieldGroupName() -> String? { + return json?.optionalStringForKey("radioGroupName") + } + + public func formFieldValue() -> Any? { + return radioButton.isSelected + } } // MARK: - MVMCoreUIMoleculeViewProtocol diff --git a/MVMCoreUI/Molecules/RadioButtonModel.swift b/MVMCoreUI/Molecules/RadioButtonModel.swift index 40238307..812ea36a 100644 --- a/MVMCoreUI/Molecules/RadioButtonModel.swift +++ b/MVMCoreUI/Molecules/RadioButtonModel.swift @@ -12,14 +12,16 @@ import UIKit @objcMembers public class RadioButtonModel: NSObject { private var selectedRadioButton: RadioButton? + private var fieldGroupName: String? public static func setupForRadioButtonGroup(radioButton: RadioButton, formValidator: FormValidator?) -> RadioButtonModel { guard let groupName = radioButton.groupName, let formValidator = formValidator else { return RadioButtonModel() } - + let radioButtonModel = formValidator.radioButtonsModelByGroup[groupName] ?? RadioButtonModel() + radioButtonModel.fieldGroupName = radioButton.formFieldGroupName() formValidator.radioButtonsModelByGroup[groupName] = radioButtonModel return radioButtonModel } @@ -32,7 +34,11 @@ import UIKit } // MARK: - FormValidationProtocol -extension RadioButtonModel: FormValidationProtocol { +extension RadioButtonModel: FormValidationFormFieldProtocol { + public func formFieldGroupName() -> String? { + return selectedRadioButton?.formFieldGroupName() ?? self.fieldGroupName + } + // Used to check the validity of the field, to enable/disable the primary button. @objc public func isValidField() -> Bool { return selectedRadioButton != nil ? true : false diff --git a/MVMCoreUI/Molecules/Switch.swift b/MVMCoreUI/Molecules/Switch.swift index 5b23b1d5..8cb19909 100644 --- a/MVMCoreUI/Molecules/Switch.swift +++ b/MVMCoreUI/Molecules/Switch.swift @@ -8,7 +8,7 @@ import UIKit -@objcMembers public class Switch: ViewConstrainingView, FormValidationProtocol{ +@objcMembers public class Switch: ViewConstrainingView, FormValidationFormFieldProtocol { public var mvmSwitch = MVMCoreUISwitch() var isRequired = false var delegateObject: DelegateObject? @@ -76,6 +76,10 @@ import UIKit return mvmSwitch.isOn } + public func formFieldGroupName() -> String? { + return json?.optionalStringForKey("groupName") + } + public override func needsToBeConstrained() -> Bool { return true } diff --git a/MVMCoreUI/Molecules/SwitchLineItem.swift b/MVMCoreUI/Molecules/SwitchLineItem.swift index 8c15e8ec..54860158 100644 --- a/MVMCoreUI/Molecules/SwitchLineItem.swift +++ b/MVMCoreUI/Molecules/SwitchLineItem.swift @@ -8,7 +8,9 @@ import UIKit -@objcMembers public class SwitchLineItem: ViewConstrainingView, FormValidationProtocol{ +@objcMembers public class SwitchLineItem: ViewConstrainingView, FormValidationFormFieldProtocol { + + public var mvmSwitch = Switch() public var label = Label() public var leftContainerView = UIView() @@ -116,6 +118,23 @@ import UIKit public override func alignment() -> UIStackView.Alignment { return UIStackView.Alignment.leading } + + + public func isValidField() -> Bool { + return mvmSwitch.isValidField() + } + + public func formFieldName() -> String? { + return mvmSwitch.formFieldName() + } + + public func formFieldGroupName() -> String? { + return mvmSwitch.formFieldGroupName() + } + + public func formFieldValue() -> Any? { + return mvmSwitch.formFieldValue() + } } From c8611ca83ad96c8f4286759e8d56f2129cbe3ff3 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 4 Sep 2019 14:39:19 -0400 Subject: [PATCH 17/71] fixes --- MVMCoreUI/Molecules/RadioButton.swift | 4 ++-- MVMCoreUI/Molecules/RadioButtonModel.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index b95b2ab7..53fbaefc 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -22,7 +22,7 @@ import UIKit var radioButtonModel: RadioButtonModel? - lazy var groupName: String? = { + lazy var radioGroupName: String? = { [unowned self] in return json?.optionalStringForKey("radioGroupName") ?? json?.optionalStringForKey("fieldKey") }() @@ -117,7 +117,7 @@ import UIKit } public func formFieldGroupName() -> String? { - return json?.optionalStringForKey("radioGroupName") + return json?.optionalStringForKey("groupName") } public func formFieldValue() -> Any? { diff --git a/MVMCoreUI/Molecules/RadioButtonModel.swift b/MVMCoreUI/Molecules/RadioButtonModel.swift index 812ea36a..68e95790 100644 --- a/MVMCoreUI/Molecules/RadioButtonModel.swift +++ b/MVMCoreUI/Molecules/RadioButtonModel.swift @@ -15,7 +15,7 @@ import UIKit private var fieldGroupName: String? public static func setupForRadioButtonGroup(radioButton: RadioButton, formValidator: FormValidator?) -> RadioButtonModel { - guard let groupName = radioButton.groupName, + guard let groupName = radioButton.radioGroupName, let formValidator = formValidator else { return RadioButtonModel() } From eac6bf3aa3fb1850bf6955ec57f6926dd0714ee1 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 11 Sep 2019 15:56:24 -0400 Subject: [PATCH 18/71] impreovements --- MVMCoreUI/Atoms/Buttons/PrimaryButton.h | 1 + MVMCoreUI/Atoms/Buttons/PrimaryButton.m | 10 ++++-- .../FormValidationProtocol.swift | 3 ++ MVMCoreUI/FormUIHelpers/FormValidator.swift | 33 +++++++++++++++---- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.h b/MVMCoreUI/Atoms/Buttons/PrimaryButton.h index 99929430..be780b0f 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.h +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.h @@ -26,6 +26,7 @@ typedef enum : NSUInteger { static CGFloat const PrimaryButtonHeight = 42.0; static CGFloat const PrimaryButtonSmallHeight = 30.0; + @interface PrimaryButton : MFCustomButton @property (nonatomic, readonly, assign) PrimaryButtonType primaryButtonType; //use reset function to set diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m index fa719012..daeb2751 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m @@ -13,6 +13,7 @@ #import "MFStyler.h" #import "UIColor+MFConvenience.h" #import + @import MVMCore.MVMCoreDispatchUtility; @import MVMCore.MVMCoreGetterUtility; @import MVMCore.NSDictionary_MFConvenience; @@ -667,6 +668,9 @@ } - (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { + + self.validationRequired = [json boolForKey:@"validationRequired"]; + self.requiredFieldsList = [json array:@"requiredFields"]; [FormValidator setupValidationWithMolecule:self delegate:delegateObject.formValidationProtocol]; self.primaryButtonType = PrimaryButtonTypeCustom; @@ -697,8 +701,6 @@ if ((color = [json string:@"disabledBorderColor"])) { self.disabledBorderColor = [UIColor mfGetColorForHex:color]; } - self.validationRequired = [json boolForKey:@"validationRequired"]; - self.requiredFieldsList = [json array:@"requiredFields"]; NSString *size = [json string:@"size"]; if ([size isEqualToString:@"small"]) { @@ -777,6 +779,10 @@ #pragma mark - FormValidationEnableDisableProtocol +- (BOOL) isValidationRequired { + return self.validationRequired; +} + - (NSArray *)requiredFields { return self.requiredFieldsList; } diff --git a/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift b/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift index 0af41b42..2e109811 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift @@ -29,6 +29,9 @@ import Foundation } @objc public protocol FormValidationEnableDisableProtocol: FormValidationProtocol { + + @objc func isValidationRequired() -> Bool + // Based on the isValidField(), the fields which needs to be enabled can call this method @objc optional func enableField(_ enable: Bool) diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index 821db784..e1c2b13c 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -13,6 +13,7 @@ import MVMCore @objcMembers public class FormValidator: NSObject { var delegate: FormValidationProtocol? + var extraValidationBlock: (() -> Bool)? var fieldMolecules: [FormValidationFormFieldProtocol] = [] var enableDisableMolecules: [FormValidationEnableDisableProtocol] = [] var radioButtonsModelByGroup: [String: RadioButtonModel] = [:] @@ -21,8 +22,9 @@ import MVMCore if let molecule = molecule as? FormValidationFormFieldProtocol { fieldMolecules.append(molecule) } - if let molecule = molecule as? FormValidationEnableDisableProtocol { - enableDisableMolecules.append(molecule) + if let moleculeT = molecule as? FormValidationEnableDisableProtocol, + moleculeT.isValidationRequired() { + enableDisableMolecules.append(moleculeT) } } @@ -45,6 +47,27 @@ import MVMCore } public func enableByValidation() { + if enableDisableMolecules.count > 1 { + enableByGroup() + } else { + enableByFieldValidation() + } + } + + public func enableByFieldValidation() { + guard let enableMolecule = enableDisableMolecules.first else { + return; + } + + var valid = true + for molecule in fieldMolecules { + valid = valid && molecule.isValidField() + } + let enableField = valid && (extraValidationBlock?() ?? true) + enableMolecule.enableField?(enableField) + } + + func enableByGroup() { var groupValue: [String: Bool] = [:] for molecule in fieldMolecules { let valid = molecule.isValidField() @@ -52,10 +75,7 @@ import MVMCore groupValue[grouName] = valid && (groupValue[grouName] ?? true) } } - shouldEnable(groupValue) - } - - public func shouldEnable(_ groupValue: [String: Bool]) { + for molecule in enableDisableMolecules { var valid = false for groupName in molecule.requiredFields?() ?? [] { @@ -64,6 +84,7 @@ import MVMCore molecule.enableField?(valid) } } + } From daed451c7afa9022f1ff273be08c8a29cf65a800 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Fri, 13 Sep 2019 15:25:13 -0400 Subject: [PATCH 19/71] improvements --- MVMCoreUI/Atoms/TextFields/MFTextField.m | 2 +- .../FormValidationProtocol.swift | 11 ++-- MVMCoreUI/FormUIHelpers/FormValidator.swift | 54 +++++++++---------- 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/MVMCoreUI/Atoms/TextFields/MFTextField.m b/MVMCoreUI/Atoms/TextFields/MFTextField.m index 0e9be054..c52c4972 100644 --- a/MVMCoreUI/Atoms/TextFields/MFTextField.m +++ b/MVMCoreUI/Atoms/TextFields/MFTextField.m @@ -303,7 +303,7 @@ return; } - self.formText = [map string:KeyLabel];; + self.formText = [map string:KeyLabel]; self.groupName = [map string:@"groupName"]; self.errMessage = [map string:KeyErrorMessage]; self.fieldKey = [map string:KeyFieldKey]; diff --git a/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift b/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift index 2e109811..27f3f754 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift @@ -10,26 +10,27 @@ import Foundation @objc public protocol FormValidationProtocol: NSObjectProtocol { - // Getter method to get the FormValidator form the delegate (Mostly from the parent View Controller) + // Getter method to get the FormValidator from the delegate (Mostly from the parent View Controller) @objc optional func formValidatorModel() -> FormValidator? } -@objc public protocol FormValidationFormFieldProtocol: FormValidationProtocol { - // Used to check the validity of the field, to enable/disable the primary button. +@objc public protocol FormValidationFormFieldProtocol: FormValidationProtocol { + // Used to check the validity of the field. For example, to enable/disable the primary button. @objc func isValidField() -> Bool // The Field name key value pair for sending to server @objc func formFieldName() -> String? - // Returns the group name for validation + // Returns the group name for validation. The class should always return a value. @objc func formFieldGroupName() -> String? // The Field value key value pair for sending to server @objc func formFieldValue() -> Any? } -@objc public protocol FormValidationEnableDisableProtocol: FormValidationProtocol { +@objc public protocol FormValidationEnableDisableProtocol: FormValidationProtocol { + // Returns true if the button needs to be enabled/disabled based on the validation @objc func isValidationRequired() -> Bool // Based on the isValidField(), the fields which needs to be enabled can call this method diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index e1c2b13c..c3cc77fa 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -12,8 +12,9 @@ import MVMCore @objcMembers public class FormValidator: NSObject { - var delegate: FormValidationProtocol? var extraValidationBlock: (() -> Bool)? + var dummyGroupName = "dummyGroupName" + weak var delegate: FormValidationProtocol? var fieldMolecules: [FormValidationFormFieldProtocol] = [] var enableDisableMolecules: [FormValidationEnableDisableProtocol] = [] var radioButtonsModelByGroup: [String: RadioButtonModel] = [:] @@ -47,44 +48,37 @@ import MVMCore } public func enableByValidation() { - if enableDisableMolecules.count > 1 { - enableByGroup() - } else { - enableByFieldValidation() + for molecule in enableDisableMolecules { + if let requiredFeilds = molecule.requiredFields?(), requiredFeilds.count > 0 { + enableWithGroupName(requiredFeilds, molecule) + } else { + enableIgnoreGroupName(molecule) + } } } - public func enableByFieldValidation() { - guard let enableMolecule = enableDisableMolecules.first else { - return; + public func enableWithGroupName(_ requiredGroupList: [String], _ enableDisableMolecules: FormValidationEnableDisableProtocol) { + + var groupValidityMap: [String: Bool] = [:] + for molecule in fieldMolecules { + let valid = molecule.isValidField() + let groupName = molecule.formFieldGroupName() ?? dummyGroupName + groupValidityMap[groupName] = valid && (groupValidityMap[groupName] ?? true) } + var valid = false + for groupName in requiredGroupList { + valid = groupValidityMap[groupName] ?? false + } + enableDisableMolecules.enableField?(valid) + } + + public func enableIgnoreGroupName(_ enableDisableMolecules: FormValidationEnableDisableProtocol) { var valid = true for molecule in fieldMolecules { valid = valid && molecule.isValidField() } let enableField = valid && (extraValidationBlock?() ?? true) - enableMolecule.enableField?(enableField) + enableDisableMolecules.enableField?(enableField) } - - func enableByGroup() { - var groupValue: [String: Bool] = [:] - for molecule in fieldMolecules { - let valid = molecule.isValidField() - if let grouName = molecule.formFieldGroupName() { - groupValue[grouName] = valid && (groupValue[grouName] ?? true) - } - } - - for molecule in enableDisableMolecules { - var valid = false - for groupName in molecule.requiredFields?() ?? [] { - valid = groupValue[groupName] ?? false - } - molecule.enableField?(valid) - } - } - - } - From b51f1b82f5439450a18b8fa3f67bd1efa8e7d1ac Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Sat, 14 Sep 2019 04:55:40 +0530 Subject: [PATCH 20/71] adding action block, which should only call after user interaction, but not at the time of initial loading. Fixing apple crash, where even after deletion, apple not updating number of rows, which is leading to index out of bounds crash. Adding remove list item method which has specific molecule inside. --- MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h | 1 + MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m | 12 ++++++++++++ MVMCoreUI/Templates/MoleculeListTemplate.swift | 17 +++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h index 32e98f2a..256aabfa 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h @@ -24,6 +24,7 @@ typedef void(^ValueChangeBlock)(void); @property (nonatomic) BOOL shouldTouchToSwitch; @property (nullable, copy, nonatomic) ValueChangeBlock valueChangedBlock; +@property (nullable, copy, nonatomic) ValueChangeBlock actionBlock; + (nonnull instancetype)mvmSwitchDefault; + (nonnull instancetype)mvmSwitchDefaultWithValueChangeBlock:(nullable ValueChangeBlock)block; diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m index f994b320..a3bbea81 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m @@ -169,6 +169,18 @@ const CGFloat SwitchShakeIntensity = 2; } [self setState:[json boolForKey:@"state"] animated:false]; + + self.delegate = delegateObject; + NSDictionary *actionMap = [json dict:@"actionMap"]; + if (actionMap) { + [self addTarget:self action:@selector(addCustomAction) forControlEvents:UIControlEventTouchUpInside]; + } +} + +- (void)addCustomAction { + if (self.actionBlock) { + self.actionBlock(); + } } + (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject { diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 0270cb63..4fb10ed0 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -149,6 +149,23 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { } } self.tableView?.deleteRows(at: indexPaths, with: animation) + // crash fix + self.tableView?.reloadData() + self.updateViewConstraints() + self.view.layoutIfNeeded() + } + + public func removeListItemWhichHas(_ molecule: [AnyHashable : Any], animation: UITableView.RowAnimation) { + var indexPaths: [IndexPath] = [] + if let removeIndex = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in + return NSDictionary(dictionary: molecule).isEqual(to: moleculeInfo.molecule["molecule"] as? [AnyHashable : Any] ?? [:]) + }) { + moleculesInfo?.remove(at: removeIndex) + indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0)) + } + self.tableView?.deleteRows(at: indexPaths, with: animation) + // crash fix + self.tableView?.reloadData() self.updateViewConstraints() self.view.layoutIfNeeded() } From decf0bf9e6ce3646711be0c6831ba225e847d1fd Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Wed, 18 Sep 2019 21:58:30 +0530 Subject: [PATCH 21/71] adding settingsList template --- MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m | 1 + 1 file changed, 1 insertion(+) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m index e770ede5..4b89ae5a 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m @@ -23,6 +23,7 @@ @"moleculeStack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeStackTemplate class]], @"centerMoleculeStack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeStackCenteredTemplate class]], @"moleculeList" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeListTemplate class]], + @"settingsList" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeListTemplate class]], @"threeLayer" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[ThreeLayerTemplate class]] } mutableCopy]; From 0d6b904a7f6375cc74ed854a5484aeab16b06cd8 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 19 Sep 2019 10:54:28 -0400 Subject: [PATCH 22/71] current state. --- MVMCoreUI/Atoms/Views/Label.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index ed99fcac..c9fa1b06 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -34,6 +34,10 @@ public typealias ActionBlock = () -> () return !text.isEmpty || !attributedText.string.isEmpty } + public var getRange: NSRange { + return NSRange(location: 0, length: text?.count ?? 0) + } + //------------------------------------------------------ // MARK: - Multi-Action Text //------------------------------------------------------ @@ -611,6 +615,19 @@ extension Label { appendActionableClause(range: range, actionBlock: actionBlock) } + @objc public func makeAllTextLinkable(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { + + setActionAttributes(range: getRange) + let actionBlock = createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) + appendActionableClause(range: getRange, actionBlock: actionBlock) + } + + @objc public func makeAllTextLinkable(actionBlock: @escaping ActionBlock) { + + setActionAttributes(range: getRange) + appendActionableClause(range: getRange, actionBlock: actionBlock) + } + @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { for clause in clauses { From efbb68b4cd5c7cdb5aab4ee2a301389cbcdc19a2 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 19 Sep 2019 14:09:51 -0400 Subject: [PATCH 23/71] latest. not perfect. --- MVMCoreUI/Atoms/Views/Label.swift | 63 ++++++++++++++----- .../Atoms/Views/LabelWithInternalButton.swift | 4 +- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index c9fa1b06..aa677cd1 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -42,6 +42,7 @@ public typealias ActionBlock = () -> () // MARK: - Multi-Action Text //------------------------------------------------------ + /// Data store of the tappable ranges of the text. public var clauses: [ActionableClause] = [] { didSet { isUserInteractionEnabled = !clauses.isEmpty @@ -114,6 +115,29 @@ public typealias ActionBlock = () -> () standardFontSize = size } + /// Convenience to init Label as a TextButton. All text will behave as a link. + @objc convenience public init(text: String, actionBlock: @escaping ActionBlock) { + self.init() + self.text = text + setTextLinkState(range: getRange, actionBlock: actionBlock) + } + + /// Convenience to init Label where the provided text will be marked as tappable by the given range. + @objc convenience public init(text: String, range: NSRange, actionBlock: @escaping ActionBlock) { + self.init() + self.text = text + setTextLinkState(range: range, actionBlock: actionBlock) + } + + /// Convenience to init Label with the link comprised of range, actionMap and delegateObject + @objc convenience public init(text: String, range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { + self.init() + self.text = text + if let actionBlock = Label.createActionBlockFor(label: self, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) { + setTextLinkState(range: range, actionBlock: actionBlock) + } + } + //------------------------------------------------------ // MARK: - Factory Functions //------------------------------------------------------ @@ -309,8 +333,9 @@ public typealias ActionBlock = () -> () guard let actionLabel = label as? Label else { continue } actionLabel.addActionAttributes(range: range, string: attributedString) - let actionBlock = actionLabel.createActionBlockFrom(actionMap: json, additionalData: additionalData, delegateObject: delegate) - actionLabel.appendActionableClause(range: range, actionBlock: actionBlock) + if let actionBlock = Label.createActionBlockFor(label: actionLabel, actionMap: json, additionalData: additionalData, delegateObject: delegate) { + actionLabel.appendActionableClause(range: range, actionBlock: actionBlock) + } default: continue @@ -563,9 +588,10 @@ extension Label { clauses = [] } - public func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock { - return { [weak self] in - if let wSelf = self, (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { + public static func createActionBlockFor(label: Label?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock? { + guard let label = label else { return nil } + return { + if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(label, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } @@ -595,8 +621,7 @@ extension Label { */ @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { - setActionAttributes(range: range) - appendActionableClause(range: range, actionBlock: actionBlock) + setTextLinkState(range: range, actionBlock: actionBlock) } /** @@ -610,22 +635,28 @@ extension Label { */ @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - setActionAttributes(range: range) - let actionBlock = createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) - appendActionableClause(range: range, actionBlock: actionBlock) + if let actionBlock = Label.createActionBlockFor(label: self, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) { + setTextLinkState(range: range, actionBlock: actionBlock) + } } @objc public func makeAllTextLinkable(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - - setActionAttributes(range: getRange) - let actionBlock = createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) - appendActionableClause(range: getRange, actionBlock: actionBlock) + + if let actionBlock = Label.createActionBlockFor(label: self, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) { + setTextLinkState(range: getRange, actionBlock: actionBlock) + } } @objc public func makeAllTextLinkable(actionBlock: @escaping ActionBlock) { - setActionAttributes(range: getRange) - appendActionableClause(range: getRange, actionBlock: actionBlock) + setTextLinkState(range: getRange, actionBlock: actionBlock) + } + + /// Underlines the tappable region and stores the tap logic for interation. + private func setTextLinkState(range: NSRange, actionBlock: @escaping ActionBlock) { + + setActionAttributes(range: range) + appendActionableClause(range: range, actionBlock: actionBlock) } @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index ff2e751d..44f254f4 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -207,7 +207,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @objc public func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) + actionBlock = Label.createActionBlockFor(label: label, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) } //------------------------------------------------------ @@ -377,7 +377,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt actionText = actionMap?.optionalStringForKey(KeyTitle) backText = actionMap?.optionalStringForKey(KeyTitlePostfix) text = getTextFromStringComponents() - actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) + actionBlock = Label.createActionBlockFor(label: label, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) setLabelAttributes() } From b3cc938a9e4504ad3a5dd9af81f7f39e2592f602 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 20 Sep 2019 16:11:46 -0400 Subject: [PATCH 24/71] Improved accuracy of link label detection. --- MVMCoreUI/Atoms/Views/Label.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index aa677cd1..e1c4cc5e 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -689,7 +689,7 @@ extension UITapGestureRecognizer { paragraph.alignment = label.textAlignment let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText) - stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count)) + stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count)) let textStorage = NSTextStorage(attributedString: stagedAttributedString) let layoutManager = NSLayoutManager() @@ -704,8 +704,8 @@ extension UITapGestureRecognizer { textContainer.size = label.bounds.size let indexOfGlyph = layoutManager.glyphIndex(for: location(in: label), in: textContainer) - - return NSLocationInRange(indexOfGlyph, targetRange) + + return layoutManager.boundingRect(forGlyphRange: targetRange, in: textContainer).contains(location(in: label)) && NSLocationInRange(indexOfGlyph, targetRange) } } From 4770aa6e6bd4c78fd557e42754d766fd488f97b4 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 23 Sep 2019 12:35:51 -0400 Subject: [PATCH 25/71] Label Improvements. Conveniences added. Greater accuracy to tap detection provided. --- MVMCoreUI/Atoms/Views/Label.swift | 67 +++++++++++-------- .../Atoms/Views/LabelWithInternalButton.swift | 4 +- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index e1c4cc5e..c62bdf52 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -115,25 +115,11 @@ public typealias ActionBlock = () -> () standardFontSize = size } - /// Convenience to init Label as a TextButton. All text will behave as a link. - @objc convenience public init(text: String, actionBlock: @escaping ActionBlock) { - self.init() - self.text = text - setTextLinkState(range: getRange, actionBlock: actionBlock) - } - - /// Convenience to init Label where the provided text will be marked as tappable by the given range. - @objc convenience public init(text: String, range: NSRange, actionBlock: @escaping ActionBlock) { - self.init() - self.text = text - setTextLinkState(range: range, actionBlock: actionBlock) - } - /// Convenience to init Label with the link comprised of range, actionMap and delegateObject @objc convenience public init(text: String, range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { self.init() self.text = text - if let actionBlock = Label.createActionBlockFor(label: self, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) { + if let actionBlock = createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) { setTextLinkState(range: range, actionBlock: actionBlock) } } @@ -333,7 +319,7 @@ public typealias ActionBlock = () -> () guard let actionLabel = label as? Label else { continue } actionLabel.addActionAttributes(range: range, string: attributedString) - if let actionBlock = Label.createActionBlockFor(label: actionLabel, actionMap: json, additionalData: additionalData, delegateObject: delegate) { + if let actionBlock = actionLabel.createActionBlockFor(actionMap: json, additionalData: additionalData, delegateObject: delegate) { actionLabel.appendActionableClause(range: range, actionBlock: actionBlock) } @@ -510,7 +496,7 @@ public typealias ActionBlock = () -> () } /// Call to detect in the attributedText contains an NSTextAttachment. - func textContainsTextAttachment() -> Bool { + func containsTextAttachment() -> Bool { guard let attributedText = attributedText else { return false } @@ -588,10 +574,9 @@ extension Label { clauses = [] } - public static func createActionBlockFor(label: Label?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock? { - guard let label = label else { return nil } - return { - if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(label, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { + public func createActionBlockFor(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock? { + return { [weak self] in + if let wSelf = self, (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } @@ -635,19 +620,19 @@ extension Label { */ @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - if let actionBlock = Label.createActionBlockFor(label: self, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) { + if let actionBlock = createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) { setTextLinkState(range: range, actionBlock: actionBlock) } } - @objc public func makeAllTextLinkable(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { + @objc public func makeTextButton(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - if let actionBlock = Label.createActionBlockFor(label: self, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) { + if let actionBlock = createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) { setTextLinkState(range: getRange, actionBlock: actionBlock) } } - @objc public func makeAllTextLinkable(actionBlock: @escaping ActionBlock) { + @objc public func makeTextButton(actionBlock: @escaping ActionBlock) { setTextLinkState(range: getRange, actionBlock: actionBlock) } @@ -689,7 +674,7 @@ extension UITapGestureRecognizer { paragraph.alignment = label.textAlignment let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText) - stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count)) + stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count)) let textStorage = NSTextStorage(attributedString: stagedAttributedString) let layoutManager = NSLayoutManager() @@ -703,9 +688,33 @@ extension UITapGestureRecognizer { textContainer.maximumNumberOfLines = label.numberOfLines textContainer.size = label.bounds.size - let indexOfGlyph = layoutManager.glyphIndex(for: location(in: label), in: textContainer) - - return layoutManager.boundingRect(forGlyphRange: targetRange, in: textContainer).contains(location(in: label)) && NSLocationInRange(indexOfGlyph, targetRange) + let tapLocation = location(in: label) + let indexOfGlyph = layoutManager.glyphIndex(for: tapLocation, in: textContainer) + let intrinsicWidth = label.intrinsicContentSize.width + + // Assert that tapped occured within acceptable bounds based on alignment. + switch label.textAlignment { + case .right: + if tapLocation.x < label.bounds.width - intrinsicWidth { + return false + } + case .center: + let halfBounds = label.bounds.width / 2 + let halfintrinsicWidth = intrinsicWidth / 2 + + if tapLocation.x > halfBounds + halfintrinsicWidth{ + return false + } else if tapLocation.x < halfBounds - halfintrinsicWidth { + return false + } + default: // Left align + if tapLocation.x > intrinsicWidth { + return false + } + } + + // Affirms that the tap occured in the desired rect of provided by the target range. + return layoutManager.boundingRect(forGlyphRange: targetRange, in: textContainer).contains(tapLocation) && NSLocationInRange(indexOfGlyph, targetRange) } } diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index 44f254f4..070cc0f7 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -207,7 +207,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @objc public func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - actionBlock = Label.createActionBlockFor(label: label, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) + actionBlock = label?.createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) } //------------------------------------------------------ @@ -377,7 +377,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt actionText = actionMap?.optionalStringForKey(KeyTitle) backText = actionMap?.optionalStringForKey(KeyTitlePostfix) text = getTextFromStringComponents() - actionBlock = Label.createActionBlockFor(label: label, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) + actionBlock = label?.createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) setLabelAttributes() } From d0797a4825b99e30780da40ed86b3ed17000e141 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 23 Sep 2019 12:43:52 -0400 Subject: [PATCH 26/71] mild updates. --- MVMCoreUI/Atoms/Views/Label.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index c62bdf52..3c2d73c1 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -115,7 +115,7 @@ public typealias ActionBlock = () -> () standardFontSize = size } - /// Convenience to init Label with the link comprised of range, actionMap and delegateObject + /// Convenience to init Label with a link comprised of range, actionMap and delegateObject @objc convenience public init(text: String, range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { self.init() self.text = text @@ -555,7 +555,7 @@ extension Label { } } -// MARK: - Multi-Action Functionality +// MARK: - Multi-Link Functionality extension Label { /// Applied to existing text. Removes underlines of tappable links and assoated actionable clauses. @@ -582,7 +582,7 @@ extension Label { } } - func addActionAttributes(range: NSRange, string: NSMutableAttributedString?) { + private func addActionAttributes(range: NSRange, string: NSMutableAttributedString?) { guard let string = string else { return } string.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue], range: range) @@ -625,6 +625,7 @@ extension Label { } } + /// Converts the entire text into a link. All characters will be underlined and the intrinsic bounds will respond to tap. @objc public func makeTextButton(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { if let actionBlock = createActionBlockFor(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) { @@ -632,6 +633,7 @@ extension Label { } } + /// Converts the entire text into a link. All characters will be underlined and the intrinsic bounds will respond to tap. @objc public func makeTextButton(actionBlock: @escaping ActionBlock) { setTextLinkState(range: getRange, actionBlock: actionBlock) @@ -647,7 +649,6 @@ extension Label { @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { for clause in clauses { - // This determines if we tapped on the desired range of text. if let range = clause.range, gesture.didTapAttributedTextInLabel(self, inRange: range) { clause.performAction() From 7b6f3d6b88c52c99badb25f48bfd06ef84472e3e Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 23 Sep 2019 14:23:37 -0400 Subject: [PATCH 27/71] spelling. --- MVMCoreUI/Atoms/Views/Label.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 3c2d73c1..74394a90 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -701,11 +701,11 @@ extension UITapGestureRecognizer { } case .center: let halfBounds = label.bounds.width / 2 - let halfintrinsicWidth = intrinsicWidth / 2 + let halfIntrinsicWidth = intrinsicWidth / 2 - if tapLocation.x > halfBounds + halfintrinsicWidth{ + if tapLocation.x > halfBounds + halfIntrinsicWidth { return false - } else if tapLocation.x < halfBounds - halfintrinsicWidth { + } else if tapLocation.x < halfBounds - halfIntrinsicWidth { return false } default: // Left align From fe7fb2db03d2fbc2a500b5ec600b755c1cc302cb Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Mon, 30 Sep 2019 22:01:11 +0530 Subject: [PATCH 28/71] changing separator to line mapping as per server and Android confirmation. --- MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 945eb7bf..4760de89 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -27,7 +27,7 @@ dispatch_once(&onceToken, ^{ mapping = [@{ @"label": Label.class, - @"separator": SeparatorView.class, + @"line": SeparatorView.class, @"button": ButtonView.class, @"textButton": MFTextButton.class, @"header": StandardHeaderView.class, From 3606ce4c8255dd1e0fe748a70ef38f495c431670 Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Wed, 9 Oct 2019 13:53:10 +0530 Subject: [PATCH 29/71] VQA bug fix for bottom spacing below tableview. --- .../BaseControllers/ThreeLayerTableViewController.swift | 7 ++++++- MVMCoreUI/Templates/MoleculeListTemplate.swift | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift index 0293e860..d4a2c452 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -61,6 +61,11 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { return nil } + /// Space between the bottom view and the table sections, nil to fill. nil default + open func spaceBelowBottomView() -> CGFloat? { + return nil + } + /// can override to return a minimum fill space. open func minimumFillSpace() -> CGFloat { return 0 @@ -154,7 +159,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { bottomViewTopConstraint?.isActive = true bottomView.leftAnchor.constraint(equalTo: footerView.leftAnchor).isActive = true footerView.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true - footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true + footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor, constant: spaceBelowBottomView() ?? 0).isActive = true self.footerView = footerView showFooter(nil) } diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 4fb10ed0..b2c89d4b 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -42,6 +42,11 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { return molecule } + // for bottom gutter/free space + open override func spaceBelowBottomView() -> CGFloat? { + return PaddingDefaultVerticalSpacing + } + open override func newDataBuildScreen() { super.newDataBuildScreen() setup() From 034610af5ee8a2999f947d03444e10a3ef15bd2a Mon Sep 17 00:00:00 2001 From: panxi Date: Thu, 17 Oct 2019 15:50:25 -0400 Subject: [PATCH 30/71] fixing merging issue --- MVMCoreUI.xcodeproj/project.pbxproj | 5 -- MVMCoreUI/Atoms/Buttons/PrimaryButton.h | 1 - MVMCoreUI/Atoms/Buttons/PrimaryButton.m | 2 +- MVMCoreUI/Atoms/Views/ProgressBar.swift | 81 ------------------------- 4 files changed, 1 insertion(+), 88 deletions(-) delete mode 100644 MVMCoreUI/Atoms/Views/ProgressBar.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 08b9caf0..78207194 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -28,8 +28,6 @@ 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; 9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */; }; 948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; }; - B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E142280C4CF007245F4 /* ProgressBar.swift */; }; - B8200E192281DC1A007245F4 /* CornerLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E182281DC1A007245F4 /* CornerLabels.swift */; }; D206997721FB8A0B00CAE0DE /* MVMCoreUINavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = D206997521FB8A0B00CAE0DE /* MVMCoreUINavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; }; D206997821FB8A0B00CAE0DE /* MVMCoreUINavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = D206997621FB8A0B00CAE0DE /* MVMCoreUINavigationController.m */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; @@ -220,8 +218,6 @@ 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = ""; }; 948DB67D2326DCD90011F916 /* MultiProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgress.swift; sourceTree = ""; }; - B8200E142280C4CF007245F4 /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = ""; }; - B8200E182281DC1A007245F4 /* CornerLabels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerLabels.swift; sourceTree = ""; }; D206997521FB8A0B00CAE0DE /* MVMCoreUINavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUINavigationController.h; sourceTree = ""; }; D206997621FB8A0B00CAE0DE /* MVMCoreUINavigationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUINavigationController.m; sourceTree = ""; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; @@ -706,7 +702,6 @@ D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */, D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */, D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */, - B8200E142280C4CF007245F4 /* ProgressBar.swift */, 948DB67D2326DCD90011F916 /* MultiProgress.swift */, DBC4391622442196001AB423 /* CaretView.swift */, DBC4391722442197001AB423 /* DashLine.swift */, diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.h b/MVMCoreUI/Atoms/Buttons/PrimaryButton.h index be780b0f..99929430 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.h +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.h @@ -26,7 +26,6 @@ typedef enum : NSUInteger { static CGFloat const PrimaryButtonHeight = 42.0; static CGFloat const PrimaryButtonSmallHeight = 30.0; - @interface PrimaryButton : MFCustomButton @property (nonatomic, readonly, assign) PrimaryButtonType primaryButtonType; //use reset function to set diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m index daeb2751..33cadc8c 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m @@ -779,7 +779,7 @@ #pragma mark - FormValidationEnableDisableProtocol -- (BOOL) isValidationRequired { +- (BOOL)isValidationRequired { return self.validationRequired; } diff --git a/MVMCoreUI/Atoms/Views/ProgressBar.swift b/MVMCoreUI/Atoms/Views/ProgressBar.swift deleted file mode 100644 index a9333966..00000000 --- a/MVMCoreUI/Atoms/Views/ProgressBar.swift +++ /dev/null @@ -1,81 +0,0 @@ -// -// ProgressBar.swift -// MVMCoreUI -// -// Created by Panth Patel on 5/3/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation - -@objcMembers open class ProgressBar: UIProgressView, MVMCoreUIMoleculeViewProtocol, MVMCoreViewProtocol { - var isRounded = false - var thickness: CGFloat = 8.0 { - willSet(newValue) { - heightAnchor.constraint(equalToConstant: newValue).isActive = true - if isRounded { - layer.cornerRadius = newValue/2.0 - } else { - progressViewStyle = .bar - } - } - } - - public override init(frame: CGRect) { - super.init(frame: frame) - setupView() - } - - public required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - setupView() - } - - init() { - super.init(frame: .zero) - setupView() - } - - // MARK: - MVMCoreViewProtocol - public func setupView() { - clipsToBounds = true - translatesAutoresizingMaskIntoConstraints = false - reset() - } - - public func updateView(_ size: CGFloat) { - } - - // MARK: - MVMCoreUIMoleculeViewProtocol - public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - if let isRounded = json?.optionalBoolForKey("roundedRect") { - self.isRounded = isRounded - } - if let thickness = json?.optionalCGFloatForKey("thickness") { - self.thickness = thickness - } - if let percentage = json?["percent"] as? Float { - progress = percentage/100.0 - } - if let progressColor = json?.optionalStringForKey("progressColor") { - progressTintColor = UIColor.mfGet(forHex: progressColor) - } - if let backgroundColor = json?.optionalStringForKey("backgroundColor") { - trackTintColor = UIColor.mfGet(forHex: backgroundColor) - } - } - - public func reset() { - isRounded = false - thickness = 8 - progress = 0 - progressTintColor = UIColor.mfCerulean() - trackTintColor = UIColor.mfLightSilver() - } - - public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return 8 - } -} - - From ed7329cdcfe98b7f04e09d593c003847fbaf7e10 Mon Sep 17 00:00:00 2001 From: panxi Date: Thu, 17 Oct 2019 15:52:38 -0400 Subject: [PATCH 31/71] add progress bar --- MVMCoreUI/Atoms/Views/ProgressBar.swift | 81 +++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 MVMCoreUI/Atoms/Views/ProgressBar.swift diff --git a/MVMCoreUI/Atoms/Views/ProgressBar.swift b/MVMCoreUI/Atoms/Views/ProgressBar.swift new file mode 100644 index 00000000..a9333966 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/ProgressBar.swift @@ -0,0 +1,81 @@ +// +// ProgressBar.swift +// MVMCoreUI +// +// Created by Panth Patel on 5/3/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ProgressBar: UIProgressView, MVMCoreUIMoleculeViewProtocol, MVMCoreViewProtocol { + var isRounded = false + var thickness: CGFloat = 8.0 { + willSet(newValue) { + heightAnchor.constraint(equalToConstant: newValue).isActive = true + if isRounded { + layer.cornerRadius = newValue/2.0 + } else { + progressViewStyle = .bar + } + } + } + + public override init(frame: CGRect) { + super.init(frame: frame) + setupView() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setupView() + } + + init() { + super.init(frame: .zero) + setupView() + } + + // MARK: - MVMCoreViewProtocol + public func setupView() { + clipsToBounds = true + translatesAutoresizingMaskIntoConstraints = false + reset() + } + + public func updateView(_ size: CGFloat) { + } + + // MARK: - MVMCoreUIMoleculeViewProtocol + public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + if let isRounded = json?.optionalBoolForKey("roundedRect") { + self.isRounded = isRounded + } + if let thickness = json?.optionalCGFloatForKey("thickness") { + self.thickness = thickness + } + if let percentage = json?["percent"] as? Float { + progress = percentage/100.0 + } + if let progressColor = json?.optionalStringForKey("progressColor") { + progressTintColor = UIColor.mfGet(forHex: progressColor) + } + if let backgroundColor = json?.optionalStringForKey("backgroundColor") { + trackTintColor = UIColor.mfGet(forHex: backgroundColor) + } + } + + public func reset() { + isRounded = false + thickness = 8 + progress = 0 + progressTintColor = UIColor.mfCerulean() + trackTintColor = UIColor.mfLightSilver() + } + + public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 8 + } +} + + From d94bbc1daee3a3b6cbdd4091cc2867adb362153d Mon Sep 17 00:00:00 2001 From: panxi Date: Thu, 17 Oct 2019 16:33:05 -0400 Subject: [PATCH 32/71] check property back to weak, remove unaided switch files which supposed replaced by mvmcoreuiswitch --- MVMCoreUI/Atoms/TextFields/MFTextField.h | 8 +- MVMCoreUI/Molecules/Switch.swift | 95 --------------- MVMCoreUI/Molecules/SwitchLineItem.swift | 140 ----------------------- 3 files changed, 4 insertions(+), 239 deletions(-) delete mode 100644 MVMCoreUI/Molecules/Switch.swift delete mode 100644 MVMCoreUI/Molecules/SwitchLineItem.swift diff --git a/MVMCoreUI/Atoms/TextFields/MFTextField.h b/MVMCoreUI/Atoms/TextFields/MFTextField.h index f7d8803c..abf54942 100644 --- a/MVMCoreUI/Atoms/TextFields/MFTextField.h +++ b/MVMCoreUI/Atoms/TextFields/MFTextField.h @@ -44,10 +44,10 @@ @property (nonatomic,getter=isEnabled) BOOL enabled; // To set the placeholder and text -@property (nullable, strong, nonatomic) NSString *text; -@property (nullable, strong, nonatomic) NSString *formText; -@property (nullable, strong, nonatomic) NSString *fieldKey; -@property (nullable, strong, nonatomic) NSString *groupName; +@property (nullable, weak, nonatomic) NSString *text; +@property (nullable, weak, nonatomic) NSString *formText; +@property (nullable, weak, nonatomic) NSString *fieldKey; +@property (nullable, weak, nonatomic) NSString *groupName; @property (nullable, weak, nonatomic) NSString *placeholder; // will move out in Feb release diff --git a/MVMCoreUI/Molecules/Switch.swift b/MVMCoreUI/Molecules/Switch.swift deleted file mode 100644 index 8cb19909..00000000 --- a/MVMCoreUI/Molecules/Switch.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// Switch.swift -// MVMCoreUI -// -// Created by Priya on 5/23/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import UIKit - -@objcMembers public class Switch: ViewConstrainingView, FormValidationFormFieldProtocol { - public var mvmSwitch = MVMCoreUISwitch() - var isRequired = false - var delegateObject: DelegateObject? - - @objc func switchChanged() { - let delegate = delegateObject as? MVMCoreUIDelegateObject - if let delegate = delegate { - let formValidator = delegate.formValidationProtocol?.formValidatorModel?() - formValidator?.enableByValidation() - } - } - - open override func setupView() { - super.setupView() - mvmSwitch.addTarget(self, action: #selector(Switch.switchChanged), for: .valueChanged) - self.clipsToBounds = true - addSubview(mvmSwitch) - mvmSwitch.translatesAutoresizingMaskIntoConstraints = false - setupContainerConstraints() - } - - public override func updateView(_ size: CGFloat) { - super.updateView(size) - mvmSwitch.updateView(size) - } - - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - isRequired = json?[KeyRequired] as? Bool ?? false - self.delegateObject = delegateObject - if let delegateObject = delegateObject { - FormValidator.setupValidation(molecule: self, delegate: delegateObject.formValidationProtocol) - } - if let onColorString = json?.optionalStringForKey("onTintColor") { - mvmSwitch.onTintColor = .mfGet(forHex: onColorString) - } - if let offColorString = json?.optionalStringForKey("offTintColor") { - mvmSwitch.offTintColor = .mfGet(forHex: offColorString) - } - if let onKnobColorString = json?.optionalStringForKey("onKnobTintColor") { - mvmSwitch.onKnobTintColor = .mfGet(forHex: onKnobColorString) - } - if let offKnobColorString = json?.optionalStringForKey("offKnobTintColor") { - mvmSwitch.offKnobTintColor = .mfGet(forHex: offKnobColorString) - } - mvmSwitch.setState(json?.optionalBoolForKey("state") ?? false, animated: true) - } - - func setupContainerConstraints() { - mvmSwitch.topAnchor.constraint(equalTo: topAnchor).isActive = true - mvmSwitch.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - mvmSwitch.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - mvmSwitch.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true - } - - public func isValidField() -> Bool { - return (isRequired == false) ? true : mvmSwitch.isOn - } - - public func formFieldName() -> String? { - return json?.optionalStringForKey(KeyFieldKey) - } - - public func formFieldValue() -> Any? { - return mvmSwitch.isOn - } - - public func formFieldGroupName() -> String? { - return json?.optionalStringForKey("groupName") - } - - public override func needsToBeConstrained() -> Bool { - return true - } - - public override func alignment() -> UIStackView.Alignment { - return UIStackView.Alignment.leading - } - - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return MVMCoreUISwitch.getHeight() - } -} - diff --git a/MVMCoreUI/Molecules/SwitchLineItem.swift b/MVMCoreUI/Molecules/SwitchLineItem.swift deleted file mode 100644 index 54860158..00000000 --- a/MVMCoreUI/Molecules/SwitchLineItem.swift +++ /dev/null @@ -1,140 +0,0 @@ -// -// SwitchLineItem.swift -// MVMCoreUI -// -// Created by Priya on 5/6/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import UIKit - -@objcMembers public class SwitchLineItem: ViewConstrainingView, FormValidationFormFieldProtocol { - - - public var mvmSwitch = Switch() - public var label = Label() - public var leftContainerView = UIView() - public var mfTextButton = MFTextButton(nil, constrainHeight: true, forWidth: 0) - var isRequired = false - var delegateObject: DelegateObject? - - @objc func switchChanged() { - let delegate = delegateObject as? MVMCoreUIDelegateObject - if let delegate = delegate { - let formValidator = delegate.formValidationProtocol?.formValidatorModel?() - formValidator?.enableByValidation() - } - } - - open override func setupView() { - super.setupView() - leftContainerView.addSubview(label) - leftContainerView.addSubview(mfTextButton) - addSubview(leftContainerView) - addSubview(mvmSwitch) - - leftContainerView.translatesAutoresizingMaskIntoConstraints = false - mvmSwitch.translatesAutoresizingMaskIntoConstraints = false - mfTextButton.translatesAutoresizingMaskIntoConstraints = false - label.translatesAutoresizingMaskIntoConstraints = false - setupContainerConstraints() - } - - public override func updateView(_ size: CGFloat) { - super.updateView(size) - label.updateView(size) - mvmSwitch.updateView(size) - mfTextButton.updateView(size) - } - - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - mvmSwitch.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - label.setWithJSON(json?.optionalDictionaryForKey("label"), delegateObject: delegateObject, additionalData: additionalData) - mfTextButton.setWithJSON(json?.optionalDictionaryForKey("textButton"), delegateObject: delegateObject, additionalData: additionalData) - if (label.text?.count ?? 0) <= 0 && (mfTextButton.titleLabel?.text?.count ?? 0) <= 0 { - mvmSwitch.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true - } - } - - func setupContainerConstraints() { - leftContainerView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true - - var constraint = leftContainerView.topAnchor.constraint(equalTo: topAnchor) - constraint.priority = UILayoutPriority(249) - constraint.isActive = true - - mvmSwitch.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true - - constraint = mvmSwitch.topAnchor.constraint(equalTo: topAnchor) - constraint.priority = UILayoutPriority(249) - constraint.isActive = true - - trailingAnchor.constraint(equalTo: mvmSwitch.trailingAnchor).isActive = true - - constraint = bottomAnchor.constraint(equalTo: mvmSwitch.bottomAnchor) - constraint.priority = UILayoutPriority(249) - constraint.isActive = true - - bottomAnchor.constraint(greaterThanOrEqualTo: mvmSwitch.bottomAnchor).isActive = true - - constraint = bottomAnchor.constraint(equalTo: leftContainerView.bottomAnchor) - constraint.isActive = true - - bottomAnchor.constraint(greaterThanOrEqualTo: leftContainerView.bottomAnchor).isActive = true - leftContainerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - - NSLayoutConstraint.constraintPinSubview(leftContainerView, pinCenterX: false, pinCenterY: true) - constraint = mvmSwitch.leadingAnchor.constraint(greaterThanOrEqualTo: leftContainerView.trailingAnchor) - constraint.priority = UILayoutPriority(999) - constraint.isActive = true - NSLayoutConstraint.constraintPinSubview(mvmSwitch, pinCenterX: false, pinCenterY: true) - - leftContainerView.topAnchor.constraint(equalTo: label.topAnchor).isActive = true - leftContainerView.trailingAnchor.constraint(greaterThanOrEqualTo: label.trailingAnchor).isActive = true - - constraint = leftContainerView.trailingAnchor.constraint(equalTo: label.trailingAnchor) - constraint.priority = UILayoutPriority(249) - constraint.isActive = true - - leftContainerView.trailingAnchor.constraint(greaterThanOrEqualTo: mfTextButton.trailingAnchor).isActive = true - - constraint = leftContainerView.trailingAnchor.constraint(equalTo: mfTextButton.trailingAnchor) - constraint.priority = UILayoutPriority(249) - constraint.isActive = true - - leftContainerView.bottomAnchor.constraint(equalTo: mfTextButton.bottomAnchor).isActive = true - mfTextButton.leadingAnchor.constraint(equalTo: leftContainerView.leadingAnchor).isActive = true - label.leadingAnchor.constraint(equalTo: leftContainerView.leadingAnchor).isActive = true - mfTextButton.topAnchor.constraint(equalTo: label.bottomAnchor).isActive = true - leftContainerView.setContentHuggingPriority(.defaultHigh, for: .horizontal) - mvmSwitch.setContentHuggingPriority(.defaultLow, for: .horizontal) - } - - public override func needsToBeConstrained() -> Bool { - return true - } - - public override func alignment() -> UIStackView.Alignment { - return UIStackView.Alignment.leading - } - - - public func isValidField() -> Bool { - return mvmSwitch.isValidField() - } - - public func formFieldName() -> String? { - return mvmSwitch.formFieldName() - } - - public func formFieldGroupName() -> String? { - return mvmSwitch.formFieldGroupName() - } - - public func formFieldValue() -> Any? { - return mvmSwitch.formFieldValue() - } -} - - From 4e1a6ca479afac2939ce0dd1d46813cac2052f1f Mon Sep 17 00:00:00 2001 From: panxi Date: Thu, 17 Oct 2019 16:52:05 -0400 Subject: [PATCH 33/71] update based on scott's comments --- MVMCoreUI/Atoms/TextFields/MFTextField.h | 4 +- .../Strings/es-MX.lproj/Localizable.strings | 5 + .../Strings/es.lproj/Localizable.strings | 7 + .../Templates/MoleculeStackTemplate.swift | 144 +++++++++--------- 4 files changed, 86 insertions(+), 74 deletions(-) diff --git a/MVMCoreUI/Atoms/TextFields/MFTextField.h b/MVMCoreUI/Atoms/TextFields/MFTextField.h index abf54942..8ccbe22e 100644 --- a/MVMCoreUI/Atoms/TextFields/MFTextField.h +++ b/MVMCoreUI/Atoms/TextFields/MFTextField.h @@ -46,8 +46,8 @@ // To set the placeholder and text @property (nullable, weak, nonatomic) NSString *text; @property (nullable, weak, nonatomic) NSString *formText; -@property (nullable, weak, nonatomic) NSString *fieldKey; -@property (nullable, weak, nonatomic) NSString *groupName; +@property (nullable, strong, nonatomic) NSString *fieldKey; +@property (nullable, strong, nonatomic) NSString *groupName; @property (nullable, weak, nonatomic) NSString *placeholder; // will move out in Feb release diff --git a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings index d6505503..93c5dda5 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings @@ -37,6 +37,11 @@ "checkbox_checked_state" = "Verificado"; "checkbox_unchecked_state" = "Sin marcar"; "checkbox_desc_state" = "%@ Casilla %@"; +// Radio Button +"radio_action_hint" = "Toca dos veces para seleccionar."; +"radio_selected_state" = "Seleccionado"; +"radio_not_selected_state" = "No Seleccionado"; +"radio_desc_state" = "Opción"; // Switch "mfswitch_buttonlabel" = "Botón Cambiar"; "AccOn" = "encendido"; diff --git a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings index d6505503..053c4b02 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings @@ -37,6 +37,13 @@ "checkbox_checked_state" = "Verificado"; "checkbox_unchecked_state" = "Sin marcar"; "checkbox_desc_state" = "%@ Casilla %@"; + +// Radio Button +"radio_action_hint" = "Toca dos veces para seleccionar."; +"radio_selected_state" = "Seleccionado"; +"radio_not_selected_state" = "No Seleccionado"; +"radio_desc_state" = "Opción"; + // Switch "mfswitch_buttonlabel" = "Botón Cambiar"; "AccOn" = "encendido"; diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Templates/MoleculeStackTemplate.swift index 8515f9a4..d6f78b05 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackTemplate.swift @@ -9,76 +9,76 @@ import UIKit open class MoleculeStackTemplate: ThreeLayerViewController { -var observer: NSKeyValueObservation? - -open override var loadObject: MVMCoreLoadObject? { -didSet { -if loadObject != oldValue { -updateRequiredModules() -observer?.invalidate() -if let newObject = loadObject { -observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] (object, change) in -self?.updateRequiredModules() -} -} -} -} -} - -open override func spaceBetweenTopAndMiddle() -> CGFloat? { -return 0 -} - -open override func viewForTop() -> UIView? { -guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, constrainIfNeeded: true) else { -return nil -} -return molecule -} - -open override func viewForMiddle() -> UIView? { -guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("moleculeStack") else { -return nil -} -let stack = MoleculeStackView(frame: .zero) -stack.useStackSpacingBeforeFirstItem = true -stack.setWithJSON(moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil) -return stack -} - -override open func viewForBottom() -> UIView? { -guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, constrainIfNeeded: true) else { -return nil -} -return molecule -} - -// MARK: - cache handling -open override func pageTypesToListenFor() -> [Any]? { -guard let pageType = self.pageType else { -return super.pageTypesToListenFor() -} -return [pageType] -} - -open override func modulesToListenFor() -> [Any]? { -return loadObject?.requestParameters?.modules -} - -/// Adds modules from requiredModules() to the MVMCoreViewControllerMapping.requiredModules map. -open func updateRequiredModules() { -if let requiredModules = requiredModules(), let pageType = pageType { -MVMCoreViewControllerMappingObject.shared()?.addRequiredModules(toMapping: requiredModules, forPageType: pageType) -} -} - -/// Gets modules required by the loadObject.pageJSON. -open func requiredModules() -> [Any]? { -let modules: NSMutableArray = [] -let delegate = delegateObject() as? MVMCoreUIDelegateObject -MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate, moduleList: modules, errorList: nil) -MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate, moduleList: modules, errorList: nil) -MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("moleculeStack"), delegateObject: delegate, moduleList: modules, errorList: nil) -return modules as? [Any] -} + var observer: NSKeyValueObservation? + + open override var loadObject: MVMCoreLoadObject? { + didSet { + if loadObject != oldValue { + updateRequiredModules() + observer?.invalidate() + if let newObject = loadObject { + observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] (object, change) in + self?.updateRequiredModules() + } + } + } + } + } + + open override func spaceBetweenTopAndMiddle() -> CGFloat? { + return 0 + } + + open override func viewForTop() -> UIView? { + guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, constrainIfNeeded: true) else { + return nil + } + return molecule + } + + open override func viewForMiddle() -> UIView? { + guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("moleculeStack") else { + return nil + } + let stack = MoleculeStackView(frame: .zero) + stack.useStackSpacingBeforeFirstItem = true + stack.setWithJSON(moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil) + return stack + } + + override open func viewForBottom() -> UIView? { + guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, constrainIfNeeded: true) else { + return nil + } + return molecule + } + + // MARK: - cache handling + open override func pageTypesToListenFor() -> [Any]? { + guard let pageType = self.pageType else { + return super.pageTypesToListenFor() + } + return [pageType] + } + + open override func modulesToListenFor() -> [Any]? { + return loadObject?.requestParameters?.modules + } + + /// Adds modules from requiredModules() to the MVMCoreViewControllerMapping.requiredModules map. + open func updateRequiredModules() { + if let requiredModules = requiredModules(), let pageType = pageType { + MVMCoreViewControllerMappingObject.shared()?.addRequiredModules(toMapping: requiredModules, forPageType: pageType) + } + } + + /// Gets modules required by the loadObject.pageJSON. + open func requiredModules() -> [Any]? { + let modules: NSMutableArray = [] + let delegate = delegateObject() as? MVMCoreUIDelegateObject + MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate, moduleList: modules, errorList: nil) + MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate, moduleList: modules, errorList: nil) + MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("moleculeStack"), delegateObject: delegate, moduleList: modules, errorList: nil) + return modules as? [Any] + } } From f1c319244a1e7c04e9c79e60362714b209f4cfba Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 21 Oct 2019 17:26:33 -0400 Subject: [PATCH 34/71] radioButton and radioButtonWithLabel and code review --- MVMCoreUI.xcodeproj/project.pbxproj | 4 + MVMCoreUI/Atoms/Buttons/PrimaryButton.m | 2 +- MVMCoreUI/Atoms/TextFields/MFTextField.m | 26 ++-- .../FormValidationProtocol.swift | 2 +- MVMCoreUI/FormUIHelpers/FormValidator.swift | 11 +- MVMCoreUI/Molecules/RadioButton.swift | 73 ++--------- .../Molecules/RadioButtonWithLabel.swift | 117 ++++++++++++++++++ .../MVMCoreUIMoleculeMappingObject.m | 1 + 8 files changed, 156 insertions(+), 80 deletions(-) create mode 100644 MVMCoreUI/Molecules/RadioButtonWithLabel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 78207194..2b965e63 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 01DF55E021F8FAA800CC099B /* MFTextFieldListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DF55DF21F8FAA800CC099B /* MFTextFieldListView.swift */; }; 01DF567021FA5AB300CC099B /* TextFieldListFormViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */; }; 01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; + 01FC09E3235E246D003AC9B3 /* RadioButtonWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FC09E2235E246D003AC9B3 /* RadioButtonWithLabel.swift */; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; }; 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; @@ -212,6 +213,7 @@ 0198F7A22256A80A0066C936 /* MFRadioButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFRadioButton.m; sourceTree = ""; }; 01DF55DF21F8FAA800CC099B /* MFTextFieldListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFTextFieldListView.swift; sourceTree = ""; }; 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldListFormViewController.swift; sourceTree = ""; }; + 01FC09E2235E246D003AC9B3 /* RadioButtonWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonWithLabel.swift; sourceTree = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = ""; }; 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; @@ -573,6 +575,7 @@ D2A514662213885800345BFB /* StandardHeaderView.swift */, D274CA322236A78900B01B62 /* StandardFooterView.swift */, 01004F2F22721C3800991ECC /* RadioButton.swift */, + 01FC09E2235E246D003AC9B3 /* RadioButtonWithLabel.swift */, 0116A4E4228B19640094F3ED /* RadioButtonModel.swift */, D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */, D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */, @@ -1059,6 +1062,7 @@ D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */, D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, + 01FC09E3235E246D003AC9B3 /* RadioButtonWithLabel.swift in Sources */, D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m index 33cadc8c..e4f889eb 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m @@ -783,7 +783,7 @@ return self.validationRequired; } -- (NSArray *)requiredFields { +- (NSArray *)requiredGroups { return self.requiredFieldsList; } diff --git a/MVMCoreUI/Atoms/TextFields/MFTextField.m b/MVMCoreUI/Atoms/TextFields/MFTextField.m index 28cd5343..cfef9090 100644 --- a/MVMCoreUI/Atoms/TextFields/MFTextField.m +++ b/MVMCoreUI/Atoms/TextFields/MFTextField.m @@ -301,20 +301,30 @@ return; } - self.formText = [map string:KeyLabel]; - self.groupName = [map string:@"groupName"]; - self.errMessage = [map string:KeyErrorMessage]; - self.fieldKey = [map string:KeyFieldKey]; - - NSString *string = [map stringForKey:KeyValue]; - if (string.length) { + NSString *string = [map string:KeyLabel]; + if (string.length > 0) { + self.formText = string; + } + string = [map string:KeyValue]; + if (string.length > 0) { self.text = string; } - string = [map stringForKey:KeyDisable]; if ([string isEqual:StringY] || [map boolForKey:KeyDisable]) { [self enable:NO]; } + string = [map string:KeyErrorMessage]; + if (string.length > 0) { + self.errMessage = string; + } + + // key used to send text value to server + string = [map string:KeyFieldKey]; + if (string.length > 0) { + self.fieldKey = string; + } + + self.groupName = [map string:@"groupName"]; string = [map string:KeyType]; if ([string isEqualToString:@"dropDown"]) { diff --git a/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift b/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift index 27f3f754..49cfacc9 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidationProtocol.swift @@ -37,5 +37,5 @@ import Foundation @objc optional func enableField(_ enable: Bool) // Returns the list of field keys required to enable/disable - @objc optional func requiredFields() -> [String]? + @objc optional func requiredGroups() -> [String]? } diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index c3cc77fa..fe23da84 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -49,15 +49,15 @@ import MVMCore public func enableByValidation() { for molecule in enableDisableMolecules { - if let requiredFeilds = molecule.requiredFields?(), requiredFeilds.count > 0 { - enableWithGroupName(requiredFeilds, molecule) + if let requiredFields = molecule.requiredGroups?(), requiredFields.count > 0 { + enableWithGroups(requiredFields, molecule) } else { enableIgnoreGroupName(molecule) } } } - public func enableWithGroupName(_ requiredGroupList: [String], _ enableDisableMolecules: FormValidationEnableDisableProtocol) { + public func enableWithGroups(_ requiredGroupList: [String], _ enableDisableMolecule: FormValidationEnableDisableProtocol) { var groupValidityMap: [String: Bool] = [:] for molecule in fieldMolecules { @@ -70,13 +70,16 @@ import MVMCore for groupName in requiredGroupList { valid = groupValidityMap[groupName] ?? false } - enableDisableMolecules.enableField?(valid) + enableDisableMolecule.enableField?(valid) } public func enableIgnoreGroupName(_ enableDisableMolecules: FormValidationEnableDisableProtocol) { var valid = true for molecule in fieldMolecules { valid = valid && molecule.isValidField() + if (!valid) { + break + } } let enableField = valid && (extraValidationBlock?() ?? true) enableDisableMolecules.enableField?(enableField) diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index 53fbaefc..a161272a 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -8,19 +8,15 @@ import UIKit - @objcMembers open class RadioButton: ViewConstrainingView, FormValidationFormFieldProtocol { public let radioButton = MFRadioButton() var delegateObject: MVMCoreUIDelegateObject? - var dummyButton: MFCustomButton? - let label = Label() - + var fieldKey: String? var formValue: Bool? var isRequired: Bool = false var radioButtonModel: RadioButtonModel? - lazy var radioGroupName: String? = { [unowned self] in @@ -53,49 +49,11 @@ import UIKit isAccessibilityElement = true accessibilityTraits = .none accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") - - radioButton.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 0).isActive = true - radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true - bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true - radioButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + NSLayoutConstraint.constraintPinSubview(toSuperview: radioButton) - if let rightView = createRightView() { - addSubview(rightView) - rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingHorizontalBetweenRelatedItems).isActive = true - rightView.rightAnchor.constraint(equalTo: layoutMarginsGuide.rightAnchor, constant: 0).isActive = true - - var constraint = rightView.topAnchor.constraint(equalTo: topAnchor, constant: PaddingOne) - constraint.priority = .defaultHigh - constraint.isActive = true - - constraint = bottomAnchor.constraint(equalTo: rightView.bottomAnchor, constant: PaddingOne) - constraint.priority = .defaultHigh - constraint.isActive = true - } - addActionHandler() - } - - func createRightView() -> ViewConstrainingView? { - let rightView = ViewConstrainingView(constrainingView: label) - return rightView - } - - func addActionHandler() { - - guard dummyButton == nil else { - return - } - - let dummyButton = MFCustomButton(frame: .zero) - self.dummyButton = dummyButton - dummyButton.translatesAutoresizingMaskIntoConstraints = false - addSubview(dummyButton) - NSLayoutConstraint.constraintPinSubview(toSuperview: dummyButton) - bringSubviewToFront(dummyButton) - - dummyButton.add({ [weak self] (button) in + radioButton.performActionForCheck = { [weak self] in self?.tapAction() - }, for: .touchUpInside) + } } func tapAction() { @@ -105,7 +63,6 @@ import UIKit radioButton.isSelected = !radioButton.isSelected } FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) - changeAccessibilityLabel() } public func isValidField() -> Bool { @@ -138,28 +95,12 @@ extension RadioButton { } fieldKey = jsonDictionary.optionalStringForKey("fieldKey") - isRequired = jsonDictionary.boolForKey("required") + isRequired = jsonDictionary.boolForKey("required") self.delegateObject = delegateObject let radioButtonModel = RadioButtonModel.setupForRadioButtonGroup(radioButton: self, - formValidator: delegateObject?.formValidationProtocol?.formValidatorModel?()) + formValidator: delegateObject?.formValidationProtocol?.formValidatorModel?()) FormValidator.setupValidation(molecule: radioButtonModel, delegate: delegateObject?.formValidationProtocol) - self.radioButtonModel = radioButtonModel - - label.setWithJSON(jsonDictionary.optionalDictionaryForKey(KeyLabel), - delegateObject: delegateObject, - additionalData: additionalData) - changeAccessibilityLabel() - } -} - -// MARK: Accessibility -extension RadioButton { - func changeAccessibilityLabel() { - let stateString = radioButton.isSelected ? "radio_selected_state" : "radio_not_selected_state" - let localizedStringState = MVMCoreUIUtility.hardcodedString(withKey: stateString) ?? "" - let accebilityString = (label.accessibilityLabel ?? (json?.optionalStringForKey("accessibilityText") ?? "")) - + (MVMCoreUIUtility.hardcodedString(withKey: "radio_desc_state") ?? "") + localizedStringState - accessibilityLabel = accebilityString + self.radioButtonModel = radioButtonModel } } diff --git a/MVMCoreUI/Molecules/RadioButtonWithLabel.swift b/MVMCoreUI/Molecules/RadioButtonWithLabel.swift new file mode 100644 index 00000000..3dc5f6e5 --- /dev/null +++ b/MVMCoreUI/Molecules/RadioButtonWithLabel.swift @@ -0,0 +1,117 @@ +// +// RadioButtonWithLabel.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 10/21/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +@objcMembers open class RadioButtonWithLabel: ViewConstrainingView { + + public let radioButton = RadioButton() + var delegateObject: MVMCoreUIDelegateObject? + var dummyButton: MFCustomButton? + let label = Label() + + // MARK: - Inits + public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + open override func setupView() { + super.setupView() + guard subviews.count == 0 else { + return + } + + translatesAutoresizingMaskIntoConstraints = false + radioButton.translatesAutoresizingMaskIntoConstraints = false + addSubview(radioButton) + + isAccessibilityElement = true + accessibilityTraits = .none + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") + + radioButton.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 0).isActive = true + radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true + radioButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + if let rightView = createRightView() { + addSubview(rightView) + rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingHorizontalBetweenRelatedItems).isActive = true + rightView.rightAnchor.constraint(equalTo: layoutMarginsGuide.rightAnchor, constant: 0).isActive = true + + var constraint = rightView.topAnchor.constraint(equalTo: topAnchor, constant: PaddingOne) + constraint.priority = .defaultHigh + constraint.isActive = true + + constraint = bottomAnchor.constraint(equalTo: rightView.bottomAnchor, constant: PaddingOne) + constraint.priority = .defaultHigh + constraint.isActive = true + } + addActionHandler() + } + + func createRightView() -> ViewConstrainingView? { + let rightView = ViewConstrainingView(constrainingView: label) + return rightView + } + + func addActionHandler() { + + guard dummyButton == nil else { + return + } + + let dummyButton = MFCustomButton(frame: .zero) + self.dummyButton = dummyButton + dummyButton.translatesAutoresizingMaskIntoConstraints = false + addSubview(dummyButton) + NSLayoutConstraint.constraintPinSubview(toSuperview: dummyButton) + bringSubviewToFront(dummyButton) + + dummyButton.add({ [weak self] (button) in + self?.tapAction() + }, for: .touchUpInside) + } + + func tapAction() { + radioButton.tapAction() + changeAccessibilityLabel() + } +} + +// MARK: - MVMCoreUIMoleculeViewProtocol +extension RadioButtonWithLabel { + @objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + self.delegateObject = delegateObject + radioButton.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + label.setWithJSON(json?.optionalDictionaryForKey(KeyLabel), + delegateObject: delegateObject, + additionalData: additionalData) + changeAccessibilityLabel() + } +} + +// MARK: Accessibility +extension RadioButtonWithLabel { + func changeAccessibilityLabel() { + let stateString = radioButton.radioButton.isSelected ? "radio_selected_state" : "radio_not_selected_state" + let localizedStringState = MVMCoreUIUtility.hardcodedString(withKey: stateString) ?? "" + let accebilityString = (label.accessibilityLabel ?? (json?.optionalStringForKey("accessibilityText") ?? "")) + + (MVMCoreUIUtility.hardcodedString(withKey: "radio_desc_state") ?? "") + localizedStringState + accessibilityLabel = accebilityString + } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 27302b20..6c2100f3 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -45,6 +45,7 @@ @"multiProgressBar": MultiProgress.class, @"checkbox": MVMCoreUICheckBox.class, @"radioButton": RadioButton.class, + @"radioButtonLabel": RadioButtonWithLabel.class, @"listItem": MoleculeTableViewCell.class, @"accordionListItem": AccordionMoleculeTableViewCell.class, @"switch": MVMCoreUISwitch.class, From fcb377df33e4645d8c3c8546feb67be1c48af279 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 22 Oct 2019 09:13:07 -0400 Subject: [PATCH 35/71] space --- MVMCoreUI/Molecules/RadioButton.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index a161272a..57115d68 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -70,7 +70,7 @@ import UIKit } public func formFieldName() -> String? { - return json?.optionalStringForKey("fieldKey") + return json?.optionalStringForKey("fieldKey") } public func formFieldGroupName() -> String? { From 4ef662d1cad00adef17d1d111d47dff11d915291 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 22 Oct 2019 09:59:37 -0400 Subject: [PATCH 36/71] reivew --- MVMCoreUI/Molecules/RadioButton.swift | 14 +++++++++++--- MVMCoreUI/Molecules/RadioButtonModel.swift | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Molecules/RadioButton.swift index 57115d68..059bc5b6 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Molecules/RadioButton.swift @@ -16,12 +16,21 @@ import UIKit var fieldKey: String? var formValue: Bool? var isRequired: Bool = false - var radioButtonModel: RadioButtonModel? - + lazy var radioGroupName: String? = { [unowned self] in return json?.optionalStringForKey("radioGroupName") ?? json?.optionalStringForKey("fieldKey") }() + + lazy var radioButtonModel: RadioButtonModel? = { + [unowned self] in + if let radioGroupName = radioGroupName, + let radioButtonModel = delegateObject?.formValidationProtocol?.formValidatorModel?()?.radioButtonsModelByGroup[radioGroupName] { + return radioButtonModel + } else { + return nil + } + }() // MARK: - Inits public init() { @@ -101,6 +110,5 @@ extension RadioButton { let radioButtonModel = RadioButtonModel.setupForRadioButtonGroup(radioButton: self, formValidator: delegateObject?.formValidationProtocol?.formValidatorModel?()) FormValidator.setupValidation(molecule: radioButtonModel, delegate: delegateObject?.formValidationProtocol) - self.radioButtonModel = radioButtonModel } } diff --git a/MVMCoreUI/Molecules/RadioButtonModel.swift b/MVMCoreUI/Molecules/RadioButtonModel.swift index 68e95790..a72bd889 100644 --- a/MVMCoreUI/Molecules/RadioButtonModel.swift +++ b/MVMCoreUI/Molecules/RadioButtonModel.swift @@ -49,6 +49,6 @@ extension RadioButtonModel: FormValidationFormFieldProtocol { } // The field value key value pair for sending to server @objc public func formFieldValue() -> Any? { - return selectedRadioButton != nil ? true : false + return selectedRadioButton != nil ? true : false } } From 252beb311a35837f657111f8803f163cca7c7403 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 22 Oct 2019 11:40:50 -0400 Subject: [PATCH 37/71] dynamic type --- MVMCoreUI/Atoms/Views/Label.swift | 2 +- MVMCoreUI/Utility/MFFonts.m | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 6fc496bd..d3195f45 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -80,7 +80,7 @@ public typealias ActionBlock = () -> () //------------------------------------------------------ @objc public func setupView() { - + adjustsFontForContentSizeCategory = true backgroundColor = .clear numberOfLines = 0 lineBreakMode = .byWordWrapping diff --git a/MVMCoreUI/Utility/MFFonts.m b/MVMCoreUI/Utility/MFFonts.m index 65326e6c..fad79299 100644 --- a/MVMCoreUI/Utility/MFFonts.m +++ b/MVMCoreUI/Utility/MFFonts.m @@ -41,12 +41,16 @@ + (nullable UIFont *)mfFont75Bd:(CGFloat)size { [self loadMVMFonts]; - return [UIFont fontWithName:@"NHaasGroteskDSStd-75Bd" size:size]; + UIFont *font = [UIFont fontWithName:@"NHaasGroteskDSStd-75Bd" size:size]; + UIFontMetrics *metrics = [UIFontMetrics metricsForTextStyle:UIFontTextStyleHeadline]; + return [metrics scaledFontForFont:font]; } + (nullable UIFont *)mfFont55Rg:(CGFloat)size { [self loadMVMFonts]; - return [UIFont fontWithName:@"NHaasGroteskDSStd-55Rg" size:size]; + UIFont *font = [UIFont fontWithName:@"NHaasGroteskDSStd-55Rg" size:size];; + UIFontMetrics *metrics = [UIFontMetrics metricsForTextStyle:UIFontTextStyleBody]; + return [metrics scaledFontForFont:font]; } + (nullable UIFont *)mfFontOcratxt:(CGFloat)size { From 5ee34fb027c26b6d69e099a7b9a8ed001ab8e0d7 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 22 Oct 2019 15:11:16 -0400 Subject: [PATCH 38/71] dummy group --- MVMCoreUI/FormUIHelpers/FormValidator.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index fe23da84..d11e4847 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -49,16 +49,16 @@ import MVMCore public func enableByValidation() { for molecule in enableDisableMolecules { - if let requiredFields = molecule.requiredGroups?(), requiredFields.count > 0 { - enableWithGroups(requiredFields, molecule) - } else { - enableIgnoreGroupName(molecule) + var requiredFields = molecule.requiredGroups?() ?? [dummyGroupName] + if requiredFields.count == 0 { + requiredFields = [dummyGroupName] } + enableWithGroups(requiredFields, molecule) } } public func enableWithGroups(_ requiredGroupList: [String], _ enableDisableMolecule: FormValidationEnableDisableProtocol) { - + var groupValidityMap: [String: Bool] = [:] for molecule in fieldMolecules { let valid = molecule.isValidField() @@ -66,9 +66,9 @@ import MVMCore groupValidityMap[groupName] = valid && (groupValidityMap[groupName] ?? true) } - var valid = false + var valid = true for groupName in requiredGroupList { - valid = groupValidityMap[groupName] ?? false + valid = valid && groupValidityMap[groupName] ?? false } enableDisableMolecule.enableField?(valid) } From 75632696804d4ebb44e84e07a2d6e1ca659a47b3 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 23 Oct 2019 10:36:32 -0400 Subject: [PATCH 39/71] changes to test --- MVMCoreUI.xcodeproj/project.pbxproj | 18 ++++- .../Views}/RadioButton.swift | 72 +++++++++---------- MVMCoreUI/BaseClasses/Control.swift | 52 ++++++++++++++ MVMCoreUI/BaseClasses/View.swift | 52 ++++++++++++++ MVMCoreUI/Molecules/RadioButtonModel.swift | 4 +- .../Molecules/RadioButtonWithLabel.swift | 4 +- 6 files changed, 160 insertions(+), 42 deletions(-) rename MVMCoreUI/{Molecules => Atoms/Views}/RadioButton.swift (78%) create mode 100644 MVMCoreUI/BaseClasses/Control.swift create mode 100644 MVMCoreUI/BaseClasses/View.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 2b965e63..4389ff65 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -182,6 +182,8 @@ D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A638FC22CA98280052ED1F /* HeadlineBody.swift */; }; D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A6390022CBB1820052ED1F /* Carousel.swift */; }; D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */; }; + D2B18B7F2360913400A9AEDC /* Control.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B7E2360913400A9AEDC /* Control.swift */; }; + D2B18B812360945C00A9AEDC /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B802360945C00A9AEDC /* View.swift */; }; D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B1E3E422F37D6A0065F95C /* ImageHeadlineBody.swift */; }; D2C5001821F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; }; @@ -377,6 +379,8 @@ D2A638FC22CA98280052ED1F /* HeadlineBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBody.swift; sourceTree = ""; }; D2A6390022CBB1820052ED1F /* Carousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Carousel.swift; sourceTree = ""; }; D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeCollectionViewCell.swift; sourceTree = ""; }; + D2B18B7E2360913400A9AEDC /* Control.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Control.swift; sourceTree = ""; }; + D2B18B802360945C00A9AEDC /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; D2B1E3E422F37D6A0065F95C /* ImageHeadlineBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageHeadlineBody.swift; sourceTree = ""; }; D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = ""; }; D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = ""; }; @@ -508,6 +512,7 @@ D29DF0CE21E404D4003B2FB9 /* MVMCoreUI */ = { isa = PBXGroup; children = ( + D2B18B7D236090D500A9AEDC /* BaseClasses */, 01C74D87224298E2009C25A3 /* FormUIHelpers */, D29DF31421ECECA7003B2FB9 /* SupportingFiles */, D29DF27021E79B2C003B2FB9 /* OtherHandlers */, @@ -574,7 +579,6 @@ D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */, D2A514662213885800345BFB /* StandardHeaderView.swift */, D274CA322236A78900B01B62 /* StandardFooterView.swift */, - 01004F2F22721C3800991ECC /* RadioButton.swift */, 01FC09E2235E246D003AC9B3 /* RadioButtonWithLabel.swift */, 0116A4E4228B19640094F3ED /* RadioButtonModel.swift */, D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */, @@ -740,6 +744,7 @@ 0198F7A22256A80A0066C936 /* MFRadioButton.m */, 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */, 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */, + 01004F2F22721C3800991ECC /* RadioButton.swift */, ); path = Views; sourceTree = ""; @@ -858,6 +863,15 @@ path = Strings; sourceTree = ""; }; + D2B18B7D236090D500A9AEDC /* BaseClasses */ = { + isa = PBXGroup; + children = ( + D2B18B7E2360913400A9AEDC /* Control.swift */, + D2B18B802360945C00A9AEDC /* View.swift */, + ); + path = BaseClasses; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1033,6 +1047,7 @@ D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */, D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, D29DF25321E6A177003B2FB9 /* MFDigitTextField.m in Sources */, + D2B18B7F2360913400A9AEDC /* Control.swift in Sources */, D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */, DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */, D224798C231450C8003FCCF9 /* HeadlineBodySwitch.swift in Sources */, @@ -1059,6 +1074,7 @@ D28B4F8B21FF967C00712C7A /* MVMCoreUIObject.m in Sources */, 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */, D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */, + D2B18B812360945C00A9AEDC /* View.swift in Sources */, D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */, D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, diff --git a/MVMCoreUI/Molecules/RadioButton.swift b/MVMCoreUI/Atoms/Views/RadioButton.swift similarity index 78% rename from MVMCoreUI/Molecules/RadioButton.swift rename to MVMCoreUI/Atoms/Views/RadioButton.swift index 059bc5b6..5b0d0c00 100644 --- a/MVMCoreUI/Molecules/RadioButton.swift +++ b/MVMCoreUI/Atoms/Views/RadioButton.swift @@ -8,9 +8,12 @@ import UIKit -@objcMembers open class RadioButton: ViewConstrainingView, FormValidationFormFieldProtocol { +@objcMembers public class RadioButton: Control, FormValidationFormFieldProtocol { + + var diameter = 30 + var enabledColor = UIColor.black + var disabledColor = UIColor.mfSilver() - public let radioButton = MFRadioButton() var delegateObject: MVMCoreUIDelegateObject? var fieldKey: String? @@ -32,50 +35,25 @@ import UIKit } }() + open override func draw(_ rect: CGRect) { + guard let context = UIGraphicsGetCurrentContext() else { return } + context.addEllipse(in: CGRect(x: bounds.width*0.2, y: bounds.height*0.2, width: bounds.width*0.6, height: bounds.height*0.6)) + context.setFillColor(isUserInteractionEnabled ? enabledColor.cgColor : disabledColor.cgColor) + context.fillPath() + } + // MARK: - Inits - public init() { - super.init(frame: .zero) - } - - public override init(frame: CGRect) { - super.init(frame: frame) - } - - required public init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - open override func setupView() { - super.setupView() - guard subviews.count == 0 else { - return - } - - translatesAutoresizingMaskIntoConstraints = false - radioButton.translatesAutoresizingMaskIntoConstraints = false - addSubview(radioButton) - - isAccessibilityElement = true - accessibilityTraits = .none - accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") - NSLayoutConstraint.constraintPinSubview(toSuperview: radioButton) - - radioButton.performActionForCheck = { [weak self] in - self?.tapAction() - } - } - func tapAction() { if let radioButtonModel = radioButtonModel { radioButtonModel.selected(self) } else { - radioButton.isSelected = !radioButton.isSelected + isSelected = !isSelected } FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) } public func isValidField() -> Bool { - return radioButton.isSelected + return isSelected } public func formFieldName() -> String? { @@ -87,7 +65,27 @@ import UIKit } public func formFieldValue() -> Any? { - return radioButton.isSelected + return isSelected + } + +// MARK: - MVMViewProtocol + open override func setupView() { + super.setupView() + guard subviews.count == 0 else { + return + } + + /*translatesAutoresizingMaskIntoConstraints = false + addSubview(radioButton) + + isAccessibilityElement = true + accessibilityTraits = .none + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") + NSLayoutConstraint.constraintPinSubview(toSuperview: radioButton) + + radioButton.performActionForCheck = { [weak self] in + self?.tapAction() + }*/ } } diff --git a/MVMCoreUI/BaseClasses/Control.swift b/MVMCoreUI/BaseClasses/Control.swift new file mode 100644 index 00000000..bad1a6c8 --- /dev/null +++ b/MVMCoreUI/BaseClasses/Control.swift @@ -0,0 +1,52 @@ +// +// Control.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/23/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +public class Control: UIControl { + var json: [AnyHashable: Any]? + + public override init(frame: CGRect) { + super.init(frame: .zero) + setupView() + } + + init() { + super.init(frame: .zero) + setupView() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + setupView() + } +} + +extension Control: MVMCoreViewProtocol { + public func updateView(_ size: CGFloat) { + } + + public func setupView() { + translatesAutoresizingMaskIntoConstraints = false + insetsLayoutMarginsFromSafeArea = false + } +} + +extension Control: MVMCoreUIMoleculeViewProtocol { + public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + self.json = json + + if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { + backgroundColor = UIColor.mfGet(forHex: backgroundColorString) + } + } + + public func reset() { + backgroundColor = .clear + } +} diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift new file mode 100644 index 00000000..25b750b0 --- /dev/null +++ b/MVMCoreUI/BaseClasses/View.swift @@ -0,0 +1,52 @@ +// +// View.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/23/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +public class View: UIView { + var json: [AnyHashable: Any]? + + public override init(frame: CGRect) { + super.init(frame: .zero) + setupView() + } + + init() { + super.init(frame: .zero) + setupView() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + setupView() + } +} + +extension View: MVMCoreViewProtocol { + public func updateView(_ size: CGFloat) { + } + + public func setupView() { + translatesAutoresizingMaskIntoConstraints = false + insetsLayoutMarginsFromSafeArea = false + } +} + +extension View: MVMCoreUIMoleculeViewProtocol { + public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + self.json = json + + if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { + backgroundColor = UIColor.mfGet(forHex: backgroundColorString) + } + } + + public func reset() { + backgroundColor = .clear + } +} diff --git a/MVMCoreUI/Molecules/RadioButtonModel.swift b/MVMCoreUI/Molecules/RadioButtonModel.swift index a72bd889..e2e6b27b 100644 --- a/MVMCoreUI/Molecules/RadioButtonModel.swift +++ b/MVMCoreUI/Molecules/RadioButtonModel.swift @@ -27,9 +27,9 @@ import UIKit } public func selected(_ radioButton: RadioButton) { - selectedRadioButton?.radioButton.isSelected = false + selectedRadioButton?.isSelected = false selectedRadioButton = radioButton - selectedRadioButton?.radioButton.isSelected = true + selectedRadioButton?.isSelected = true } } diff --git a/MVMCoreUI/Molecules/RadioButtonWithLabel.swift b/MVMCoreUI/Molecules/RadioButtonWithLabel.swift index 3dc5f6e5..4f42067a 100644 --- a/MVMCoreUI/Molecules/RadioButtonWithLabel.swift +++ b/MVMCoreUI/Molecules/RadioButtonWithLabel.swift @@ -8,7 +8,7 @@ import UIKit -@objcMembers open class RadioButtonWithLabel: ViewConstrainingView { +@objcMembers public class RadioButtonWithLabel: ViewConstrainingView { public let radioButton = RadioButton() var delegateObject: MVMCoreUIDelegateObject? @@ -108,7 +108,7 @@ extension RadioButtonWithLabel { // MARK: Accessibility extension RadioButtonWithLabel { func changeAccessibilityLabel() { - let stateString = radioButton.radioButton.isSelected ? "radio_selected_state" : "radio_not_selected_state" + let stateString = radioButton.isSelected ? "radio_selected_state" : "radio_not_selected_state" let localizedStringState = MVMCoreUIUtility.hardcodedString(withKey: stateString) ?? "" let accebilityString = (label.accessibilityLabel ?? (json?.optionalStringForKey("accessibilityText") ?? "")) + (MVMCoreUIUtility.hardcodedString(withKey: "radio_desc_state") ?? "") + localizedStringState From 0b364fa004d0e885dd1c81dc3ba602e999cfcfb0 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 23 Oct 2019 11:32:34 -0400 Subject: [PATCH 40/71] Radio button changes --- MVMCoreUI/Atoms/Views/RadioButton.swift | 55 +++++++++++++------- MVMCoreUI/Atoms/Views/ViewConstrainingView.m | 12 ++--- MVMCoreUI/BaseClasses/Control.swift | 16 ++++-- MVMCoreUI/BaseClasses/View.swift | 38 +++++++++----- 4 files changed, 77 insertions(+), 44 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/RadioButton.swift b/MVMCoreUI/Atoms/Views/RadioButton.swift index 5b0d0c00..6347a862 100644 --- a/MVMCoreUI/Atoms/Views/RadioButton.swift +++ b/MVMCoreUI/Atoms/Views/RadioButton.swift @@ -10,7 +10,11 @@ import UIKit @objcMembers public class RadioButton: Control, FormValidationFormFieldProtocol { - var diameter = 30 + var diameter: CGFloat = 30 { + didSet { + widthConstraint?.constant = diameter + } + } var enabledColor = UIColor.black var disabledColor = UIColor.mfSilver() @@ -19,6 +23,9 @@ import UIKit var fieldKey: String? var formValue: Bool? var isRequired: Bool = false + + var widthConstraint: NSLayoutConstraint? + var heightConstraint: NSLayoutConstraint? lazy var radioGroupName: String? = { [unowned self] in @@ -37,12 +44,19 @@ import UIKit open override func draw(_ rect: CGRect) { guard let context = UIGraphicsGetCurrentContext() else { return } - context.addEllipse(in: CGRect(x: bounds.width*0.2, y: bounds.height*0.2, width: bounds.width*0.6, height: bounds.height*0.6)) - context.setFillColor(isUserInteractionEnabled ? enabledColor.cgColor : disabledColor.cgColor) - context.fillPath() + let color = isEnabled ? enabledColor.cgColor : disabledColor.cgColor + layer.cornerRadius = bounds.width * 0.5 + layer.borderColor = color + layer.borderWidth = bounds.width * 0.0333 + if isSelected { + // Space around inner circle is 1/5 the size + context.addEllipse(in: CGRect(x: bounds.width*0.2, y: bounds.height*0.2, width: bounds.width*0.6, height: bounds.height*0.6)) + context.setFillColor(color) + context.fillPath() + } } - // MARK: - Inits + /// The action performed when tapped. func tapAction() { if let radioButtonModel = radioButtonModel { radioButtonModel.selected(self) @@ -71,21 +85,24 @@ import UIKit // MARK: - MVMViewProtocol open override func setupView() { super.setupView() - guard subviews.count == 0 else { - return - } - - /*translatesAutoresizingMaskIntoConstraints = false - addSubview(radioButton) - - isAccessibilityElement = true - accessibilityTraits = .none - accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") - NSLayoutConstraint.constraintPinSubview(toSuperview: radioButton) - radioButton.performActionForCheck = { [weak self] in - self?.tapAction() - }*/ + clipsToBounds = true + widthConstraint = widthAnchor.constraint(equalToConstant: 30) + widthConstraint?.isActive = true + heightConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1) + heightConstraint?.isActive = true + + addTarget(self, action: #selector(tapAction), for: .touchUpInside) + isAccessibilityElement = true + accessibilityTraits = .button + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") + } +} + +// MARK: - MVMCoreUIViewConstrainingProtocol +extension RadioButton: MVMCoreUIViewConstrainingProtocol { + public func needsToBeConstrained() -> Bool { + return true } } diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m index 66a3a83d..c6b1023e 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m @@ -314,14 +314,10 @@ [self.molecule updateView:size]; [MFStyler setMarginsForView:self size:size defaultHorizontal:self.updateViewHorizontalDefaults top:(self.updateViewVerticalDefaults ? self.topMarginPadding : 0) bottom:(self.updateViewVerticalDefaults ? self.bottomMarginPadding : 0)]; UIEdgeInsets margins = [MVMCoreUIUtility getMarginsForView:self]; - if (self.updateViewHorizontalDefaults) { - [self setLeftPinConstant:margins.left]; - [self setRightPinConstant:margins.right]; - } - if (self.updateViewVerticalDefaults) { - [self setTopPinConstant:margins.top]; - [self setBottomPinConstant:margins.bottom]; - } + [self setLeftPinConstant:margins.left]; + [self setRightPinConstant:margins.right]; + [self setTopPinConstant:margins.top]; + [self setBottomPinConstant:margins.bottom]; } #pragma mark - MVMCoreUIMoleculeViewProtocol diff --git a/MVMCoreUI/BaseClasses/Control.swift b/MVMCoreUI/BaseClasses/Control.swift index bad1a6c8..fdf8204f 100644 --- a/MVMCoreUI/BaseClasses/Control.swift +++ b/MVMCoreUI/BaseClasses/Control.swift @@ -10,20 +10,29 @@ import UIKit public class Control: UIControl { var json: [AnyHashable: Any]? + + private var initialSetupPerformed = false public override init(frame: CGRect) { super.init(frame: .zero) - setupView() + initialSetup() } init() { super.init(frame: .zero) - setupView() + initialSetup() } public required init?(coder: NSCoder) { super.init(coder: coder) - setupView() + initialSetup() + } + + public func initialSetup() { + if !initialSetupPerformed { + initialSetupPerformed = true + setupView() + } } } @@ -31,6 +40,7 @@ extension Control: MVMCoreViewProtocol { public func updateView(_ size: CGFloat) { } + /// Will be called only once. public func setupView() { translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index 25b750b0..735243df 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -11,26 +11,36 @@ import UIKit public class View: UIView { var json: [AnyHashable: Any]? - public override init(frame: CGRect) { - super.init(frame: .zero) - setupView() - } + private var initialSetupPerformed = false - init() { - super.init(frame: .zero) - setupView() + public override init(frame: CGRect) { + super.init(frame: .zero) + initialSetup() + } + + init() { + super.init(frame: .zero) + initialSetup() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + public func initialSetup() { + if !initialSetupPerformed { + initialSetupPerformed = true + setupView() + } + } } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - setupView() - } -} extension View: MVMCoreViewProtocol { public func updateView(_ size: CGFloat) { } - + + /// Will be called only once. public func setupView() { translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false From 0cc81c3255bc542673fc43096f56de80bbac2c3a Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 23 Oct 2019 11:38:15 -0400 Subject: [PATCH 41/71] default background color --- MVMCoreUI/Atoms/Views/RadioButton.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/RadioButton.swift b/MVMCoreUI/Atoms/Views/RadioButton.swift index 6347a862..23588a4c 100644 --- a/MVMCoreUI/Atoms/Views/RadioButton.swift +++ b/MVMCoreUI/Atoms/Views/RadioButton.swift @@ -64,6 +64,7 @@ import UIKit isSelected = !isSelected } FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) + setNeedsDisplay() } public func isValidField() -> Bool { @@ -85,7 +86,7 @@ import UIKit // MARK: - MVMViewProtocol open override func setupView() { super.setupView() - + backgroundColor = .white clipsToBounds = true widthConstraint = widthAnchor.constraint(equalToConstant: 30) widthConstraint?.isActive = true @@ -126,4 +127,9 @@ extension RadioButton { formValidator: delegateObject?.formValidationProtocol?.formValidatorModel?()) FormValidator.setupValidation(molecule: radioButtonModel, delegate: delegateObject?.formValidationProtocol) } + + public override func reset() { + super.reset() + backgroundColor = .white + } } From 52fca407e3a09212d8731b675df7dbcc490dff34 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 23 Oct 2019 12:37:26 -0400 Subject: [PATCH 42/71] remove tap and accessibility --- .../Molecules/RadioButtonWithLabel.swift | 43 +------------------ 1 file changed, 1 insertion(+), 42 deletions(-) diff --git a/MVMCoreUI/Molecules/RadioButtonWithLabel.swift b/MVMCoreUI/Molecules/RadioButtonWithLabel.swift index 4f42067a..e7a29186 100644 --- a/MVMCoreUI/Molecules/RadioButtonWithLabel.swift +++ b/MVMCoreUI/Molecules/RadioButtonWithLabel.swift @@ -12,7 +12,6 @@ import UIKit public let radioButton = RadioButton() var delegateObject: MVMCoreUIDelegateObject? - var dummyButton: MFCustomButton? let label = Label() // MARK: - Inits @@ -38,10 +37,6 @@ import UIKit radioButton.translatesAutoresizingMaskIntoConstraints = false addSubview(radioButton) - isAccessibilityElement = true - accessibilityTraits = .none - accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") - radioButton.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 0).isActive = true radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true @@ -60,36 +55,12 @@ import UIKit constraint.priority = .defaultHigh constraint.isActive = true } - addActionHandler() } func createRightView() -> ViewConstrainingView? { let rightView = ViewConstrainingView(constrainingView: label) return rightView } - - func addActionHandler() { - - guard dummyButton == nil else { - return - } - - let dummyButton = MFCustomButton(frame: .zero) - self.dummyButton = dummyButton - dummyButton.translatesAutoresizingMaskIntoConstraints = false - addSubview(dummyButton) - NSLayoutConstraint.constraintPinSubview(toSuperview: dummyButton) - bringSubviewToFront(dummyButton) - - dummyButton.add({ [weak self] (button) in - self?.tapAction() - }, for: .touchUpInside) - } - - func tapAction() { - radioButton.tapAction() - changeAccessibilityLabel() - } } // MARK: - MVMCoreUIMoleculeViewProtocol @@ -100,18 +71,6 @@ extension RadioButtonWithLabel { radioButton.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) label.setWithJSON(json?.optionalDictionaryForKey(KeyLabel), delegateObject: delegateObject, - additionalData: additionalData) - changeAccessibilityLabel() - } -} - -// MARK: Accessibility -extension RadioButtonWithLabel { - func changeAccessibilityLabel() { - let stateString = radioButton.isSelected ? "radio_selected_state" : "radio_not_selected_state" - let localizedStringState = MVMCoreUIUtility.hardcodedString(withKey: stateString) ?? "" - let accebilityString = (label.accessibilityLabel ?? (json?.optionalStringForKey("accessibilityText") ?? "")) - + (MVMCoreUIUtility.hardcodedString(withKey: "radio_desc_state") ?? "") + localizedStringState - accessibilityLabel = accebilityString + additionalData: additionalData) } } From d398a4505418bb56c14b2d3327d99704c4849ed2 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 23 Oct 2019 13:53:58 -0400 Subject: [PATCH 43/71] code review --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++++---- ...onWithLabel.swift => RadioButtonLabel.swift} | 17 +++++++---------- .../MVMCoreUIMoleculeMappingObject.m | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) rename MVMCoreUI/Molecules/{RadioButtonWithLabel.swift => RadioButtonLabel.swift} (75%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 4389ff65..8ff82cfc 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -16,13 +16,13 @@ 01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D902327ECE600EF99AA /* CornerLabels.swift */; }; 01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D922327ECFB00EF99AA /* ProgressBar.swift */; }; 01509D952327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D942327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift */; }; + 017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */; }; 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 */; }; 01DF55E021F8FAA800CC099B /* MFTextFieldListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DF55DF21F8FAA800CC099B /* MFTextFieldListView.swift */; }; 01DF567021FA5AB300CC099B /* TextFieldListFormViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */; }; 01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; - 01FC09E3235E246D003AC9B3 /* RadioButtonWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FC09E2235E246D003AC9B3 /* RadioButtonWithLabel.swift */; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; }; 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; @@ -210,12 +210,12 @@ 01509D902327ECE600EF99AA /* CornerLabels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CornerLabels.swift; sourceTree = ""; }; 01509D922327ECFB00EF99AA /* ProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = ""; }; 01509D942327ED1900EF99AA /* HeadlineBodyTextButtonSwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeadlineBodyTextButtonSwitch.swift; sourceTree = ""; }; + 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonLabel.swift; sourceTree = ""; }; 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 = ""; }; 01DF55DF21F8FAA800CC099B /* MFTextFieldListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFTextFieldListView.swift; sourceTree = ""; }; 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldListFormViewController.swift; sourceTree = ""; }; - 01FC09E2235E246D003AC9B3 /* RadioButtonWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonWithLabel.swift; sourceTree = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = ""; }; 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; @@ -568,6 +568,7 @@ D29DF10E21E67A77003B2FB9 /* Molecules */ = { isa = PBXGroup; children = ( + 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */, D22479912316A9EF003FCCF9 /* Items */, D224798F2316A99F003FCCF9 /* LeftRightViews */, D224798E2316A995003FCCF9 /* HorizontalCombinationViews */, @@ -579,7 +580,6 @@ D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */, D2A514662213885800345BFB /* StandardHeaderView.swift */, D274CA322236A78900B01B62 /* StandardFooterView.swift */, - 01FC09E2235E246D003AC9B3 /* RadioButtonWithLabel.swift */, 0116A4E4228B19640094F3ED /* RadioButtonModel.swift */, D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */, D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */, @@ -1078,13 +1078,13 @@ D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */, D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, - 01FC09E3235E246D003AC9B3 /* RadioButtonWithLabel.swift in Sources */, D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */, D22479962316AF6E003FCCF9 /* HeadlineBodyTextButton.swift in Sources */, D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */, + 017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */, D29DF18121E69E50003B2FB9 /* MFView.m in Sources */, D29DF18321E69E54003B2FB9 /* SeparatorView.m in Sources */, D29DF17A21E69E1F003B2FB9 /* MFCustomButton.m in Sources */, diff --git a/MVMCoreUI/Molecules/RadioButtonWithLabel.swift b/MVMCoreUI/Molecules/RadioButtonLabel.swift similarity index 75% rename from MVMCoreUI/Molecules/RadioButtonWithLabel.swift rename to MVMCoreUI/Molecules/RadioButtonLabel.swift index e7a29186..244a4829 100644 --- a/MVMCoreUI/Molecules/RadioButtonWithLabel.swift +++ b/MVMCoreUI/Molecules/RadioButtonLabel.swift @@ -8,7 +8,7 @@ import UIKit -@objcMembers public class RadioButtonWithLabel: ViewConstrainingView { +@objcMembers public class RadioButtonLabel: ViewConstrainingView { public let radioButton = RadioButton() var delegateObject: MVMCoreUIDelegateObject? @@ -33,13 +33,10 @@ import UIKit return } - translatesAutoresizingMaskIntoConstraints = false - radioButton.translatesAutoresizingMaskIntoConstraints = false addSubview(radioButton) - radioButton.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 0).isActive = true - radioButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: PaddingOne).isActive = true - bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true + radioButton.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor, constant: PaddingOne).isActive = true + layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true radioButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true if let rightView = createRightView() { @@ -47,11 +44,11 @@ import UIKit rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingHorizontalBetweenRelatedItems).isActive = true rightView.rightAnchor.constraint(equalTo: layoutMarginsGuide.rightAnchor, constant: 0).isActive = true - var constraint = rightView.topAnchor.constraint(equalTo: topAnchor, constant: PaddingOne) + var constraint = rightView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: PaddingOne) constraint.priority = .defaultHigh constraint.isActive = true - constraint = bottomAnchor.constraint(equalTo: rightView.bottomAnchor, constant: PaddingOne) + constraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: rightView.bottomAnchor, constant: PaddingOne) constraint.priority = .defaultHigh constraint.isActive = true } @@ -64,11 +61,11 @@ import UIKit } // MARK: - MVMCoreUIMoleculeViewProtocol -extension RadioButtonWithLabel { +extension RadioButtonLabel { @objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) self.delegateObject = delegateObject - radioButton.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + radioButton.setWithJSON(json?.optionalDictionaryForKey("radioButton"), delegateObject: delegateObject, additionalData: additionalData) label.setWithJSON(json?.optionalDictionaryForKey(KeyLabel), delegateObject: delegateObject, additionalData: additionalData) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 6c2100f3..323e9d84 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -45,7 +45,7 @@ @"multiProgressBar": MultiProgress.class, @"checkbox": MVMCoreUICheckBox.class, @"radioButton": RadioButton.class, - @"radioButtonLabel": RadioButtonWithLabel.class, + @"radioButtonLabel": RadioButtonLabel.class, @"listItem": MoleculeTableViewCell.class, @"accordionListItem": AccordionMoleculeTableViewCell.class, @"switch": MVMCoreUISwitch.class, From 73342e40d1389fd42183f0302800d87f6f71ae81 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 23 Oct 2019 14:51:35 -0400 Subject: [PATCH 44/71] code optimization --- MVMCoreUI/Atoms/Buttons/PrimaryButton.m | 6 +++--- MVMCoreUI/FormUIHelpers/FormValidator.swift | 24 +++++++++------------ 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m index e4f889eb..35395557 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m @@ -21,7 +21,7 @@ @interface PrimaryButton() @property (nonatomic) BOOL validationRequired; -@property (nonatomic, strong) NSArray *requiredFieldsList; +@property (nonatomic, strong) NSArray *requiredGroupsList; @property (nonatomic) BOOL smallButton; @property (assign, nonatomic) BOOL tinyButton; @property (nonatomic) CGFloat sizeForSizing; @@ -670,7 +670,7 @@ - (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { self.validationRequired = [json boolForKey:@"validationRequired"]; - self.requiredFieldsList = [json array:@"requiredFields"]; + self.requiredGroupsList = [json array:@"requiredGroups"]; [FormValidator setupValidationWithMolecule:self delegate:delegateObject.formValidationProtocol]; self.primaryButtonType = PrimaryButtonTypeCustom; @@ -784,7 +784,7 @@ } - (NSArray *)requiredGroups { - return self.requiredFieldsList; + return self.requiredGroupsList; } - (void)enableField:(BOOL)enable { diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index d11e4847..7f689dd8 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -49,26 +49,22 @@ import MVMCore public func enableByValidation() { for molecule in enableDisableMolecules { - var requiredFields = molecule.requiredGroups?() ?? [dummyGroupName] - if requiredFields.count == 0 { - requiredFields = [dummyGroupName] + var requiredGroups = molecule.requiredGroups?() ?? [dummyGroupName] + if requiredGroups.count == 0 { + requiredGroups = [dummyGroupName] } - enableWithGroups(requiredFields, molecule) + enableWithGroups(requiredGroups, molecule) } } public func enableWithGroups(_ requiredGroupList: [String], _ enableDisableMolecule: FormValidationEnableDisableProtocol) { - - var groupValidityMap: [String: Bool] = [:] - for molecule in fieldMolecules { - let valid = molecule.isValidField() - let groupName = molecule.formFieldGroupName() ?? dummyGroupName - groupValidityMap[groupName] = valid && (groupValidityMap[groupName] ?? true) - } - + let requiredGroupSet = Set(requiredGroupList) var valid = true - for groupName in requiredGroupList { - valid = valid && groupValidityMap[groupName] ?? false + for molecule in fieldMolecules { + let groupName = molecule.formFieldGroupName() ?? dummyGroupName + if requiredGroupSet.contains(groupName) { + valid = valid && molecule.isValidField() + } } enableDisableMolecule.enableField?(valid) } From 8aaa8cb77f82eb3f0b40025f5b9f315fc730af88 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 23 Oct 2019 15:17:01 -0400 Subject: [PATCH 45/71] centered. --- MVMCoreUI/Atoms/Views/Label.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index fba8545f..b0871957 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -732,7 +732,7 @@ extension UITapGestureRecognizer { return false } case .center: - let halfBounds = label.bounds.width / 2 + let halfBounds = label.center.x let halfIntrinsicWidth = intrinsicWidth / 2 if tapLocation.x > halfBounds + halfIntrinsicWidth { From a4aedaad90c1bc08d9b15aae13dd8b36804f142e Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 23 Oct 2019 15:43:57 -0400 Subject: [PATCH 46/71] revert. --- MVMCoreUI/Atoms/Views/Label.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index b0871957..fba8545f 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -732,7 +732,7 @@ extension UITapGestureRecognizer { return false } case .center: - let halfBounds = label.center.x + let halfBounds = label.bounds.width / 2 let halfIntrinsicWidth = intrinsicWidth / 2 if tapLocation.x > halfBounds + halfIntrinsicWidth { From d2ca3adacf891a0c5f3ecb02fff62b16e5ad25c7 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 23 Oct 2019 15:59:03 -0400 Subject: [PATCH 47/71] code review --- MVMCoreUI/Molecules/RadioButtonLabel.swift | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Molecules/RadioButtonLabel.swift b/MVMCoreUI/Molecules/RadioButtonLabel.swift index 244a4829..5d70b7e4 100644 --- a/MVMCoreUI/Molecules/RadioButtonLabel.swift +++ b/MVMCoreUI/Molecules/RadioButtonLabel.swift @@ -37,20 +37,21 @@ import UIKit radioButton.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 0).isActive = true radioButton.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor, constant: PaddingOne).isActive = true layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: radioButton.bottomAnchor, constant: PaddingOne).isActive = true - radioButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + radioButton.centerYAnchor.constraint(equalTo: layoutMarginsGuide.centerYAnchor).isActive = true if let rightView = createRightView() { addSubview(rightView) rightView.leftAnchor.constraint(equalTo: radioButton.rightAnchor, constant: PaddingHorizontalBetweenRelatedItems).isActive = true rightView.rightAnchor.constraint(equalTo: layoutMarginsGuide.rightAnchor, constant: 0).isActive = true - var constraint = rightView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: PaddingOne) + var constraint = rightView.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor, constant: PaddingOne) constraint.priority = .defaultHigh constraint.isActive = true - constraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: rightView.bottomAnchor, constant: PaddingOne) + constraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: rightView.bottomAnchor, constant: PaddingOne) constraint.priority = .defaultHigh constraint.isActive = true + layoutMarginsGuide.centerYAnchor.constraint(equalTo: rightView.centerYAnchor).isActive = true } } @@ -70,4 +71,13 @@ extension RadioButtonLabel { delegateObject: delegateObject, additionalData: additionalData) } + + public override func reset() { + radioButton.reset() + label.reset() + } + + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 45 + } } From b8e1389798a360cca3e79c13d17f19433074ffd3 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 23 Oct 2019 17:21:44 -0400 Subject: [PATCH 48/71] review --- MVMCoreUI/FormUIHelpers/FormValidator.swift | 3 +++ MVMCoreUI/Molecules/RadioButtonLabel.swift | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index 7f689dd8..77c52249 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -64,6 +64,9 @@ import MVMCore let groupName = molecule.formFieldGroupName() ?? dummyGroupName if requiredGroupSet.contains(groupName) { valid = valid && molecule.isValidField() + if valid == false { + break + } } } enableDisableMolecule.enableField?(valid) diff --git a/MVMCoreUI/Molecules/RadioButtonLabel.swift b/MVMCoreUI/Molecules/RadioButtonLabel.swift index 5d70b7e4..e889b4f2 100644 --- a/MVMCoreUI/Molecules/RadioButtonLabel.swift +++ b/MVMCoreUI/Molecules/RadioButtonLabel.swift @@ -27,6 +27,12 @@ import UIKit super.init(coder: aDecoder) } + public override func updateView(_ size: CGFloat) { + super.updateView(size) + radioButton.updateView(size) + label.updateView(size) + } + open override func setupView() { super.setupView() guard subviews.count == 0 else { @@ -73,6 +79,7 @@ extension RadioButtonLabel { } public override func reset() { + super.reset() radioButton.reset() label.reset() } From f0c7f6bce6ddf379677c965dd67c625c602c0a07 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 24 Oct 2019 09:19:16 -0400 Subject: [PATCH 49/71] unwrap. --- MVMCoreUI/Atoms/Views/Label.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 9da9a50d..109f2f97 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -322,8 +322,9 @@ public typealias ActionBlock = () -> () guard let actionLabel = label as? Label else { continue } actionLabel.addActionAttributes(range: range, string: attributedString) - let actionBlock = actionLabel.createActionBlockFor(actionMap: attribute, additionalData: additionalData, delegateObject: delegate) - actionLabel.appendActionableClause(range: range, actionBlock: actionBlock!) + if let actionBlock = actionLabel.createActionBlockFor(actionMap: attribute, additionalData: additionalData, delegateObject: delegate) { + actionLabel.appendActionableClause(range: range, actionBlock: actionBlock) + } default: continue From 0ff661f3c4941c0b277c99eab0e5294046a257bc Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 24 Oct 2019 09:31:01 -0400 Subject: [PATCH 50/71] revert for legacy --- MVMCoreUI/Atoms/Views/ViewConstrainingView.m | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m index c6b1023e..66a3a83d 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m @@ -314,10 +314,14 @@ [self.molecule updateView:size]; [MFStyler setMarginsForView:self size:size defaultHorizontal:self.updateViewHorizontalDefaults top:(self.updateViewVerticalDefaults ? self.topMarginPadding : 0) bottom:(self.updateViewVerticalDefaults ? self.bottomMarginPadding : 0)]; UIEdgeInsets margins = [MVMCoreUIUtility getMarginsForView:self]; - [self setLeftPinConstant:margins.left]; - [self setRightPinConstant:margins.right]; - [self setTopPinConstant:margins.top]; - [self setBottomPinConstant:margins.bottom]; + if (self.updateViewHorizontalDefaults) { + [self setLeftPinConstant:margins.left]; + [self setRightPinConstant:margins.right]; + } + if (self.updateViewVerticalDefaults) { + [self setTopPinConstant:margins.top]; + [self setBottomPinConstant:margins.bottom]; + } } #pragma mark - MVMCoreUIMoleculeViewProtocol From 603652537dd900baa040169dac39b176d6005a7c Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 24 Oct 2019 09:34:51 -0400 Subject: [PATCH 51/71] remove large text --- MVMCoreUI/Atoms/Views/Label.swift | 2 +- MVMCoreUI/Utility/MFFonts.m | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 5c825bb5..52ee43d1 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -80,7 +80,7 @@ public typealias ActionBlock = () -> () //------------------------------------------------------ @objc public func setupView() { - adjustsFontForContentSizeCategory = true + backgroundColor = .clear numberOfLines = 0 lineBreakMode = .byWordWrapping diff --git a/MVMCoreUI/Utility/MFFonts.m b/MVMCoreUI/Utility/MFFonts.m index fad79299..65326e6c 100644 --- a/MVMCoreUI/Utility/MFFonts.m +++ b/MVMCoreUI/Utility/MFFonts.m @@ -41,16 +41,12 @@ + (nullable UIFont *)mfFont75Bd:(CGFloat)size { [self loadMVMFonts]; - UIFont *font = [UIFont fontWithName:@"NHaasGroteskDSStd-75Bd" size:size]; - UIFontMetrics *metrics = [UIFontMetrics metricsForTextStyle:UIFontTextStyleHeadline]; - return [metrics scaledFontForFont:font]; + return [UIFont fontWithName:@"NHaasGroteskDSStd-75Bd" size:size]; } + (nullable UIFont *)mfFont55Rg:(CGFloat)size { [self loadMVMFonts]; - UIFont *font = [UIFont fontWithName:@"NHaasGroteskDSStd-55Rg" size:size];; - UIFontMetrics *metrics = [UIFontMetrics metricsForTextStyle:UIFontTextStyleBody]; - return [metrics scaledFontForFont:font]; + return [UIFont fontWithName:@"NHaasGroteskDSStd-55Rg" size:size]; } + (nullable UIFont *)mfFontOcratxt:(CGFloat)size { From c1adbe2f2ee5abe7662eab1920cea1cde56aae97 Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Fri, 25 Oct 2019 01:17:54 +0530 Subject: [PATCH 52/71] Crash fixes in Label update molecule name from progressBar to progressbar as per oneconfluence mapping name and Android mapping name. --- MVMCoreUI/Atoms/Views/Label.swift | 50 +++++++++++++++++-- .../MVMCoreUIMoleculeMappingObject.m | 2 +- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 6fc496bd..620b3caa 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -252,7 +252,7 @@ public typealias ActionBlock = () -> () let length = attribute["length"] as? Int else { continue } - let range = NSRange(location: location, length: length) + var range = NSRange(location: location, length: length) switch attributeType { case "underline": @@ -264,7 +264,17 @@ public typealias ActionBlock = () -> () case "color": if let colorHex = attribute.optionalStringForKey(KeyTextColor), !colorHex.isEmpty { - attributedString.removeAttribute(.foregroundColor, range: range) + // crash fix: removing attribute, even though it does not exists + let foregroundColorAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in + if attribute.key == .foregroundColor { + return true + } else { + return false + } + } + if foregroundColorAttributesArray.isEmpty == false { + attributedString.removeAttribute(.foregroundColor, range: range) + } attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range) } case "image": @@ -286,8 +296,28 @@ public typealias ActionBlock = () -> () case "font": if let fontStyle = attribute.optionalStringForKey("style") { let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle) - attributedString.removeAttribute(.font, range: range) - attributedString.removeAttribute(.foregroundColor, range: range) + // crash fix: removing font attribute, even though it does not exists + let fontAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in + if attribute.key == .font { + return true + } else { + return false + } + } + if fontAttributesArray.isEmpty == false { + attributedString.removeAttribute(.font, range: range) + } + // crash fix: removing attribute, even though it does not exists + let foregroundColorAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in + if attribute.key == .foregroundColor { + return true + } else { + return false + } + } + if foregroundColorAttributesArray.isEmpty == false { + attributedString.removeAttribute(.foregroundColor, range: range) + } attributedString.addAttributes(styles.attributes(at: 0, effectiveRange: nil), range: range) } else { let fontSize = attribute["size"] as? CGFloat @@ -300,7 +330,17 @@ public typealias ActionBlock = () -> () } if let font = font { - attributedString.removeAttribute(.font, range: range) + // crash fix: removing font attribute, even though it does not exists + let fontAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in + if attribute.key == .font { + return true + } else { + return false + } + } + if fontAttributesArray.isEmpty == false { + attributedString.removeAttribute(.font, range: range) + } attributedString.addAttribute(.font, value: font, range: range) } } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index bad7a224..c8679a56 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -41,7 +41,7 @@ @"checkbox" : Checkbox.class, @"checkboxWithLabelView" : CheckboxWithLabelView.class, @"cornerLabels" : CornerLabels.class, - @"progressBar": ProgressBar.class, + @"progressbar": ProgressBar.class, @"multiProgressBar": MultiProgress.class, @"listItem": MoleculeTableViewCell.class, @"accordionListItem": AccordionMoleculeTableViewCell.class, From ab5bb26c1aa05fea8917539dd69ccf4f17cad3b8 Mon Sep 17 00:00:00 2001 From: "Hedden, Kyle Matthew" Date: Thu, 24 Oct 2019 18:00:29 -0400 Subject: [PATCH 53/71] fix checkbox crash --- MVMCoreUI/Atoms/Views/Checkbox.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index d139f130..5ee397a3 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -308,8 +308,9 @@ import MVMCore /// Adjust accessibility label based on state of Checkbox. func updateAccessibilityLabel() { // Attention: This needs to be addressed with the accessibility team. + // NOTE: Currently emptying description part of MVMCoreUICheckBox accessibility label to avoid crashing! if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") { - accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state) + accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@%@", "", state) } } From da8dded2e18c2eae5814d1da9fd432cd2047c0cd Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Fri, 25 Oct 2019 04:10:24 +0530 Subject: [PATCH 54/71] opening HeadlineBodySwitch to reuse in MF --- .../SwitchMolecules/HeadlineBodySwitch.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift index d373ce87..f585c2da 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift @@ -8,9 +8,9 @@ import UIKit -@objcMembers public class HeadlineBodySwitch: ViewConstrainingView { - let headlineBody = HeadlineBody(frame: .zero) - let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault() +@objcMembers open class HeadlineBodySwitch: ViewConstrainingView { + public let headlineBody = HeadlineBody(frame: .zero) + public let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault() // MARK: - MVMCoreViewProtocol open override func updateView(_ size: CGFloat) { @@ -35,7 +35,7 @@ import UIKit } // MARK: - MVMCoreUIMoleculeViewProtocol - public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) headlineBody.setWithJSON(json?.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData) mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData) From 45b0b01646d65bc99115bbcc25399059f2deb3ec Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Fri, 25 Oct 2019 21:04:53 +0530 Subject: [PATCH 55/71] implemented feedback --- MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m | 1 - 1 file changed, 1 deletion(-) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m index 4b89ae5a..e770ede5 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m @@ -23,7 +23,6 @@ @"moleculeStack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeStackTemplate class]], @"centerMoleculeStack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeStackCenteredTemplate class]], @"moleculeList" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeListTemplate class]], - @"settingsList" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeListTemplate class]], @"threeLayer" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[ThreeLayerTemplate class]] } mutableCopy]; From 8ea52a21f725002e8462dc1d444ecba9ae2decb0 Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Fri, 25 Oct 2019 23:07:42 +0530 Subject: [PATCH 56/71] Rename method name to removeListItem --- MVMCoreUI/Templates/MoleculeListTemplate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index d8e3be20..9df9fbc2 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -164,7 +164,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { self.view.layoutIfNeeded() } - public func removeListItemWhichHas(_ molecule: [AnyHashable : Any], animation: UITableView.RowAnimation) { + public func removeListItem(_ molecule: [AnyHashable : Any], animation: UITableView.RowAnimation) { var indexPaths: [IndexPath] = [] if let removeIndex = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in return NSDictionary(dictionary: molecule).isEqual(to: moleculeInfo.molecule["molecule"] as? [AnyHashable : Any] ?? [:]) From a188de84cb5c5e8066113fdc07b0474cc4902c9d Mon Sep 17 00:00:00 2001 From: "Hedden, Kyle Matthew" Date: Mon, 28 Oct 2019 08:55:44 -0400 Subject: [PATCH 57/71] Bugfix/gray safe area for fixed buttons --- .../TopLabelsAndBottomButtonsTableViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsTableViewController.m b/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsTableViewController.m index 1c90d9e0..5d6a6778 100644 --- a/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsTableViewController.m +++ b/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsTableViewController.m @@ -330,7 +330,7 @@ bottomViewBot.active = YES; UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view]; - safeAreaView.backgroundColor = footerView.backgroundColor; + safeAreaView.backgroundColor = self.bottomView.backgroundColor; self.safeAreaView = safeAreaView; [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[footerView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(footerView)]]; From a454d0e3bde57f81c8971e24093606eb337b404c Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 28 Oct 2019 09:31:20 -0400 Subject: [PATCH 58/71] Revert "Merge branch 'feature/mva_3_0' into 'develop'" This reverts merge request !162 --- MVMCoreUI/Atoms/Views/Label.swift | 50 ++----------------- MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h | 1 - MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m | 12 ----- .../ThreeLayerTableViewController.swift | 7 +-- .../SwitchMolecules/HeadlineBodySwitch.swift | 8 +-- .../MVMCoreUIMoleculeMappingObject.m | 4 +- .../Templates/MoleculeListTemplate.swift | 22 -------- 7 files changed, 12 insertions(+), 92 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 83a44659..109f2f97 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -266,7 +266,7 @@ public typealias ActionBlock = () -> () let length = attribute["length"] as? Int else { continue } - var range = NSRange(location: location, length: length) + let range = NSRange(location: location, length: length) switch attributeType { case "underline": @@ -278,17 +278,7 @@ public typealias ActionBlock = () -> () case "color": if let colorHex = attribute.optionalStringForKey(KeyTextColor), !colorHex.isEmpty { - // crash fix: removing attribute, even though it does not exists - let foregroundColorAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in - if attribute.key == .foregroundColor { - return true - } else { - return false - } - } - if foregroundColorAttributesArray.isEmpty == false { - attributedString.removeAttribute(.foregroundColor, range: range) - } + attributedString.removeAttribute(.foregroundColor, range: range) attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range) } case "image": @@ -310,28 +300,8 @@ public typealias ActionBlock = () -> () case "font": if let fontStyle = attribute.optionalStringForKey("style") { let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle) - // crash fix: removing font attribute, even though it does not exists - let fontAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in - if attribute.key == .font { - return true - } else { - return false - } - } - if fontAttributesArray.isEmpty == false { - attributedString.removeAttribute(.font, range: range) - } - // crash fix: removing attribute, even though it does not exists - let foregroundColorAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in - if attribute.key == .foregroundColor { - return true - } else { - return false - } - } - if foregroundColorAttributesArray.isEmpty == false { - attributedString.removeAttribute(.foregroundColor, range: range) - } + attributedString.removeAttribute(.font, range: range) + attributedString.removeAttribute(.foregroundColor, range: range) attributedString.addAttributes(styles.attributes(at: 0, effectiveRange: nil), range: range) } else { let fontSize = attribute["size"] as? CGFloat @@ -344,17 +314,7 @@ public typealias ActionBlock = () -> () } if let font = font { - // crash fix: removing font attribute, even though it does not exists - let fontAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in - if attribute.key == .font { - return true - } else { - return false - } - } - if fontAttributesArray.isEmpty == false { - attributedString.removeAttribute(.font, range: range) - } + attributedString.removeAttribute(.font, range: range) attributedString.addAttribute(.font, value: font, range: range) } } diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h index 256aabfa..32e98f2a 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h @@ -24,7 +24,6 @@ typedef void(^ValueChangeBlock)(void); @property (nonatomic) BOOL shouldTouchToSwitch; @property (nullable, copy, nonatomic) ValueChangeBlock valueChangedBlock; -@property (nullable, copy, nonatomic) ValueChangeBlock actionBlock; + (nonnull instancetype)mvmSwitchDefault; + (nonnull instancetype)mvmSwitchDefaultWithValueChangeBlock:(nullable ValueChangeBlock)block; diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m index a3bbea81..f994b320 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m @@ -169,18 +169,6 @@ const CGFloat SwitchShakeIntensity = 2; } [self setState:[json boolForKey:@"state"] animated:false]; - - self.delegate = delegateObject; - NSDictionary *actionMap = [json dict:@"actionMap"]; - if (actionMap) { - [self addTarget:self action:@selector(addCustomAction) forControlEvents:UIControlEventTouchUpInside]; - } -} - -- (void)addCustomAction { - if (self.actionBlock) { - self.actionBlock(); - } } + (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject { diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift index d698b728..d1bfa0db 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -61,11 +61,6 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { return nil } - /// Space between the bottom view and the table sections, nil to fill. nil default - open func spaceBelowBottomView() -> CGFloat? { - return nil - } - /// can override to return a minimum fill space. open func minimumFillSpace() -> CGFloat { return 0 @@ -159,7 +154,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { bottomViewTopConstraint?.isActive = true bottomView.leftAnchor.constraint(equalTo: footerView.leftAnchor).isActive = true footerView.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true - footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor, constant: spaceBelowBottomView() ?? 0).isActive = true + footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true self.footerView = footerView showFooter(nil) } diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift index f585c2da..d373ce87 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift @@ -8,9 +8,9 @@ import UIKit -@objcMembers open class HeadlineBodySwitch: ViewConstrainingView { - public let headlineBody = HeadlineBody(frame: .zero) - public let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault() +@objcMembers public class HeadlineBodySwitch: ViewConstrainingView { + let headlineBody = HeadlineBody(frame: .zero) + let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault() // MARK: - MVMCoreViewProtocol open override func updateView(_ size: CGFloat) { @@ -35,7 +35,7 @@ import UIKit } // MARK: - MVMCoreUIMoleculeViewProtocol - open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) headlineBody.setWithJSON(json?.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData) mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index e64f643f..4559e23b 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -27,7 +27,7 @@ dispatch_once(&onceToken, ^{ mapping = [@{ @"label": Label.class, - @"line": SeparatorView.class, + @"separator": SeparatorView.class, @"button": ButtonView.class, @"textButton": MFTextButton.class, @"header": StandardHeaderView.class, @@ -41,7 +41,7 @@ @"checkbox" : Checkbox.class, @"checkboxWithLabelView" : CheckboxWithLabelView.class, @"cornerLabels" : CornerLabels.class, - @"progressbar": ProgressBar.class, + @"progressBar": ProgressBar.class, @"multiProgressBar": MultiProgress.class, @"checkbox": MVMCoreUICheckBox.class, @"radioButton": RadioButton.class, diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 9df9fbc2..b400f0a9 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -42,11 +42,6 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { return molecule } - // for bottom gutter/free space - open override func spaceBelowBottomView() -> CGFloat? { - return PaddingDefaultVerticalSpacing - } - open override func newDataBuildScreen() { super.newDataBuildScreen() setup() @@ -158,23 +153,6 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { } } self.tableView?.deleteRows(at: indexPaths, with: animation) - // crash fix - self.tableView?.reloadData() - self.updateViewConstraints() - self.view.layoutIfNeeded() - } - - public func removeListItem(_ molecule: [AnyHashable : Any], animation: UITableView.RowAnimation) { - var indexPaths: [IndexPath] = [] - if let removeIndex = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in - return NSDictionary(dictionary: molecule).isEqual(to: moleculeInfo.molecule["molecule"] as? [AnyHashable : Any] ?? [:]) - }) { - moleculesInfo?.remove(at: removeIndex) - indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0)) - } - self.tableView?.deleteRows(at: indexPaths, with: animation) - // crash fix - self.tableView?.reloadData() self.updateViewConstraints() self.view.layoutIfNeeded() } From 3a32d780045c2d0427da03a1661c97f29aa10671 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 28 Oct 2019 16:25:25 -0400 Subject: [PATCH 59/71] form validation fixes --- MVMCoreUI/Atoms/Views/Checkbox.swift | 21 +++++++++++---------- MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m | 2 +- MVMCoreUI/Molecules/RadioButtonModel.swift | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 5ee397a3..e5a82b1d 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -19,7 +19,8 @@ import MVMCore // Form Validation var isRequired = false var fieldKey: String? - var delegateObject: DelegateObject? + var groupName: String? + var delegateObject: MVMCoreUIDelegateObject? public static let defaultHeightWidth: CGFloat = 18.0 @@ -104,12 +105,8 @@ import MVMCore layoutIfNeeded() shapeLayer?.removeAllAnimations() - updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) - - if let delegate = delegateObject as? FormValidationProtocol { - delegate.formValidatorModel?()?.enableByValidation() - } - + updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) + FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) updateAccessibilityLabel() } } @@ -211,7 +208,6 @@ import MVMCore /// This will toggle the state of the Checkbox and execute the actionBlock if provided. public func toggleAndAction() { - isSelected.toggle() actionBlock?() } @@ -409,7 +405,8 @@ import MVMCore FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) guard let dictionary = json else { return } - + + groupName = dictionary.optionalStringForKey("groupName") if let fieldKey = dictionary[KeyFieldKey] as? String { self.fieldKey = fieldKey } @@ -463,7 +460,11 @@ import MVMCore } // MARK:- FormValidationProtocol -extension Checkbox: FormValidationProtocol { +extension Checkbox: FormValidationFormFieldProtocol { + + public func formFieldGroupName() -> String? { + return groupName + } public func isValidField() -> Bool { return isRequired ? isSelected : true diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m index a3bbea81..2e32d73b 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m @@ -22,7 +22,7 @@ const CGFloat SwitchKnobWidth = 20; const CGFloat SwitchKnobHeight = 20; const CGFloat SwitchShakeIntensity = 2; -@interface MVMCoreUISwitch () +@interface MVMCoreUISwitch () @property (weak, nonatomic) UIView *baseView; @property (weak, nonatomic) UIView *knobView; diff --git a/MVMCoreUI/Molecules/RadioButtonModel.swift b/MVMCoreUI/Molecules/RadioButtonModel.swift index e2e6b27b..bb70dec7 100644 --- a/MVMCoreUI/Molecules/RadioButtonModel.swift +++ b/MVMCoreUI/Molecules/RadioButtonModel.swift @@ -33,7 +33,7 @@ import UIKit } } -// MARK: - FormValidationProtocol +// MARK: - FormValidationFormFieldProtocol extension RadioButtonModel: FormValidationFormFieldProtocol { public func formFieldGroupName() -> String? { return selectedRadioButton?.formFieldGroupName() ?? self.fieldGroupName From d196aa3cef3fa3a018ad1178f0f512ad3776196a Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 09:03:19 -0400 Subject: [PATCH 60/71] Design defects fix --- .../BaseControllers/ThreeLayerTableViewController.swift | 6 ++++-- MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift index d1bfa0db..5045d5a4 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -214,14 +214,16 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { /// Subclass for a top view. open func viewForTop() -> UIView { let view = MVMCoreUICommonViewsUtility.commonView() - view.heightAnchor.constraint(equalToConstant: 0).isActive = true + // Small height is needed to stop apple from adding padding for grouped tables when no header. + view.heightAnchor.constraint(equalToConstant: 1).isActive = true return view } /// Subclass for a bottom view. open func viewForBottom() -> UIView { + // Default spacing is standard when no buttons. let view = MVMCoreUICommonViewsUtility.commonView() - view.heightAnchor.constraint(equalToConstant: 0).isActive = true + view.heightAnchor.constraint(equalToConstant: PaddingDefaultVerticalSpacing).isActive = true return view } diff --git a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m index fd5c9b21..4a21b1a5 100644 --- a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m @@ -33,6 +33,7 @@ static const CGFloat VertialShadowOffset = 6; UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; view.translatesAutoresizingMaskIntoConstraints = NO; view.backgroundColor = [UIColor clearColor]; + view.directionalLayoutMargins = NSDirectionalEdgeInsetsZero; return view; } From 48a940f1606b1a4283af6d1d64feb92089a55d08 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 10:12:59 -0400 Subject: [PATCH 61/71] fix to typos --- MVMCoreUI/Atoms/Buttons/ButtonView.swift | 2 +- MVMCoreUI/Atoms/Buttons/CaretButton.swift | 2 +- MVMCoreUI/Atoms/Views/MFLoadImageView.swift | 2 +- MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h | 1 + MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m | 14 +++++++++++++- MVMCoreUI/Atoms/Views/ProgressBar.swift | 2 +- MVMCoreUI/Molecules/ActionDetailWithImage.swift | 2 +- .../ImageHeadlineBody.swift | 2 +- .../Molecules/Items/MoleculeTableViewCell.swift | 2 +- .../Molecules/LeftRightViews/CornerLabels.swift | 2 +- .../SwitchMolecules/HeadlineBodySwitch.swift | 2 +- .../HeadlineBodyTextButtonSwitch.swift | 2 +- .../SwitchMolecules/LabelSwitch.swift | 2 +- MVMCoreUI/Molecules/ModuleMolecule.swift | 2 +- MVMCoreUI/Molecules/StandardFooterView.swift | 2 +- MVMCoreUI/Molecules/StandardHeaderView.swift | 2 +- .../EyebrowHeadlineBodyLink.swift | 2 +- .../VerticalCombinationViews/HeadlineBody.swift | 2 +- .../HeadlineBodyTextButton.swift | 2 +- MVMCoreUI/Organisms/MoleculeStackView.swift | 2 +- .../OtherHandlers/MVMCoreUIMoleculeMappingObject.m | 2 +- 21 files changed, 33 insertions(+), 20 deletions(-) diff --git a/MVMCoreUI/Atoms/Buttons/ButtonView.swift b/MVMCoreUI/Atoms/Buttons/ButtonView.swift index 0fc0f3c3..088c4a00 100644 --- a/MVMCoreUI/Atoms/Buttons/ButtonView.swift +++ b/MVMCoreUI/Atoms/Buttons/ButtonView.swift @@ -64,7 +64,7 @@ import UIKit primaryButton?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 42 } diff --git a/MVMCoreUI/Atoms/Buttons/CaretButton.swift b/MVMCoreUI/Atoms/Buttons/CaretButton.swift index 4ca750c6..b01d2758 100644 --- a/MVMCoreUI/Atoms/Buttons/CaretButton.swift +++ b/MVMCoreUI/Atoms/Buttons/CaretButton.swift @@ -140,7 +140,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI return UIStackView.Alignment.leading; } - public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 10 } } diff --git a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift index 21802b38..361c0aa2 100644 --- a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift +++ b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift @@ -215,7 +215,7 @@ import UIKit pinEdges(.all) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return json?.optionalCGFloatForKey("height") ?? 0 } diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h index 32e98f2a..256aabfa 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.h @@ -24,6 +24,7 @@ typedef void(^ValueChangeBlock)(void); @property (nonatomic) BOOL shouldTouchToSwitch; @property (nullable, copy, nonatomic) ValueChangeBlock valueChangedBlock; +@property (nullable, copy, nonatomic) ValueChangeBlock actionBlock; + (nonnull instancetype)mvmSwitchDefault; + (nonnull instancetype)mvmSwitchDefaultWithValueChangeBlock:(nullable ValueChangeBlock)block; diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m index f994b320..d68ee31a 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m @@ -145,7 +145,8 @@ const CGFloat SwitchShakeIntensity = 2; - (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { self.json = json; - + self.delegate = delegateObject; + [FormValidator setupValidationWithMolecule:self delegate:delegateObject.formValidationProtocol]; NSString *color = [json string:@"onTintColor"]; @@ -169,6 +170,17 @@ const CGFloat SwitchShakeIntensity = 2; } [self setState:[json boolForKey:@"state"] animated:false]; + + NSDictionary *actionMap = [json dict:@"actionMap"]; + if (actionMap) { + [self addTarget:self action:@selector(addCustomAction) forControlEvents:UIControlEventTouchUpInside]; + } +} + +- (void)addCustomAction { + if (self.actionBlock) { + self.actionBlock(); + } } + (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject { diff --git a/MVMCoreUI/Atoms/Views/ProgressBar.swift b/MVMCoreUI/Atoms/Views/ProgressBar.swift index a9333966..1aecd537 100644 --- a/MVMCoreUI/Atoms/Views/ProgressBar.swift +++ b/MVMCoreUI/Atoms/Views/ProgressBar.swift @@ -73,7 +73,7 @@ import Foundation trackTintColor = UIColor.mfLightSilver() } - public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 8 } } diff --git a/MVMCoreUI/Molecules/ActionDetailWithImage.swift b/MVMCoreUI/Molecules/ActionDetailWithImage.swift index 86cee41b..4811f20d 100644 --- a/MVMCoreUI/Molecules/ActionDetailWithImage.swift +++ b/MVMCoreUI/Molecules/ActionDetailWithImage.swift @@ -91,7 +91,7 @@ import UIKit imageLoader.updateView(size) } - public override static func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 197 } diff --git a/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift b/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift index aca450aa..6e10a1a0 100644 --- a/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift +++ b/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift @@ -82,7 +82,7 @@ import UIKit imageView.reset() } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 95 } } diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 2ae52775..404e63ec 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -221,7 +221,7 @@ import UIKit backgroundColor = .white } - public static func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) else { return 80 } diff --git a/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift b/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift index 06917608..9b7dc062 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift @@ -174,7 +174,7 @@ import UIKit bottomRightLabel.styleB3(true) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 34 } } diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift index d373ce87..cbffccb2 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift @@ -41,7 +41,7 @@ import UIKit mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 30 } diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodyTextButtonSwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodyTextButtonSwitch.swift index 0f184544..b0cf6956 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodyTextButtonSwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodyTextButtonSwitch.swift @@ -41,7 +41,7 @@ import UIKit mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return HeadlineBodyTextButton.estimatedHeight(forRow: json, delegateObject: delegateObject) } diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift index d47cf44c..f60202b3 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift @@ -41,7 +41,7 @@ import UIKit mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return MVMCoreUISwitch.estimatedHeight(forRow: json, delegateObject: delegateObject) } diff --git a/MVMCoreUI/Molecules/ModuleMolecule.swift b/MVMCoreUI/Molecules/ModuleMolecule.swift index c993b0c2..1dc7297e 100644 --- a/MVMCoreUI/Molecules/ModuleMolecule.swift +++ b/MVMCoreUI/Molecules/ModuleMolecule.swift @@ -54,7 +54,7 @@ open class ModuleMolecule: ViewConstrainingView { moduleMolecule?.reset?() } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let moduleName = json?.optionalStringForKey("moduleName"), let module = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else { // Critical error return 0 diff --git a/MVMCoreUI/Molecules/StandardFooterView.swift b/MVMCoreUI/Molecules/StandardFooterView.swift index 7734883e..7c9c9282 100644 --- a/MVMCoreUI/Molecules/StandardFooterView.swift +++ b/MVMCoreUI/Molecules/StandardFooterView.swift @@ -26,7 +26,7 @@ open class StandardFooterView: ViewConstrainingView { (molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(false) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { if let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) { return height + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing } diff --git a/MVMCoreUI/Molecules/StandardHeaderView.swift b/MVMCoreUI/Molecules/StandardHeaderView.swift index 12819313..d54bde87 100644 --- a/MVMCoreUI/Molecules/StandardHeaderView.swift +++ b/MVMCoreUI/Molecules/StandardHeaderView.swift @@ -62,7 +62,7 @@ public class StandardHeaderView: ViewConstrainingView { separatorView?.show() } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { if let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) { return height + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift index 306cf690..a7d96a34 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift @@ -64,7 +64,7 @@ import UIKit body.styleB2(true) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 65 } } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift index e63eb2ba..d236fc37 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift @@ -136,7 +136,7 @@ open class HeadlineBody: ViewConstrainingView { stylePageHeader() } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 58 } } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyTextButton.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyTextButton.swift index 009189cb..7c24cff4 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyTextButton.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyTextButton.swift @@ -76,7 +76,7 @@ import UIKit textButton.reset() } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 60 } } diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 95855f52..bda3f25b 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -199,7 +199,7 @@ public class MoleculeStackView: ViewConstrainingView { return name } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let items = json?.optionalArrayForKey(KeyMolecules) else { return 0 } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 4559e23b..7bdaf8af 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -27,7 +27,7 @@ dispatch_once(&onceToken, ^{ mapping = [@{ @"label": Label.class, - @"separator": SeparatorView.class, + @"line": SeparatorView.class, @"button": ButtonView.class, @"textButton": MFTextButton.class, @"header": StandardHeaderView.class, From d4cacec3102511c10de3bd6db1e4462fcda0b77b Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 12:05:37 -0400 Subject: [PATCH 62/71] list item separation --- MVMCoreUI.xcodeproj/project.pbxproj | 6 +- .../Items/MoleculeCollectionViewCell.swift | 2 +- .../Items/MoleculeTableViewCell.swift | 295 +---------------- MVMCoreUI/Molecules/Items/TableViewCell.swift | 312 ++++++++++++++++++ .../Molecules/Items/TabsTableViewCell.swift | 2 +- MVMCoreUI/Molecules/ModuleMolecule.swift | 4 +- MVMCoreUI/Organisms/MoleculeStackView.swift | 4 +- 7 files changed, 331 insertions(+), 294 deletions(-) create mode 100644 MVMCoreUI/Molecules/Items/TableViewCell.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 9c75e016..955d6cf1 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -185,6 +185,7 @@ D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */; }; D2B18B7F2360913400A9AEDC /* Control.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B7E2360913400A9AEDC /* Control.swift */; }; D2B18B812360945C00A9AEDC /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B802360945C00A9AEDC /* View.swift */; }; + D2B18B9A236883BA00A9AEDC /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B99236883BA00A9AEDC /* TableViewCell.swift */; }; D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B1E3E422F37D6A0065F95C /* ImageHeadlineBody.swift */; }; D2C5001821F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; }; @@ -383,6 +384,7 @@ D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeCollectionViewCell.swift; sourceTree = ""; }; D2B18B7E2360913400A9AEDC /* Control.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Control.swift; sourceTree = ""; }; D2B18B802360945C00A9AEDC /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; +\ D2B18B99236883BA00A9AEDC /* TableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = ""; }; D2B1E3E422F37D6A0065F95C /* ImageHeadlineBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageHeadlineBody.swift; sourceTree = ""; }; D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = ""; }; D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = ""; }; @@ -473,6 +475,7 @@ D22479912316A9EF003FCCF9 /* Items */ = { isa = PBXGroup; children = ( + D2B18B99236883BA00A9AEDC /* TableViewCell.swift */, 01509D8E2327EC6F00EF99AA /* MoleculeTableViewCell.swift */, D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */, D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */, @@ -570,7 +573,6 @@ D29DF10E21E67A77003B2FB9 /* Molecules */ = { isa = PBXGroup; children = ( - 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */, D22479912316A9EF003FCCF9 /* Items */, D224798F2316A99F003FCCF9 /* LeftRightViews */, D224798E2316A995003FCCF9 /* HorizontalCombinationViews */, @@ -586,6 +588,7 @@ D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */, D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */, 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */, + 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */, ); path = Molecules; sourceTree = ""; @@ -1104,6 +1107,7 @@ D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */, D29DF2C721E7BF57003B2FB9 /* MFTabBarInteractor.m in Sources */, D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */, + D2B18B9A236883BA00A9AEDC /* TableViewCell.swift in Sources */, D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */, D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, diff --git a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift index 685d2e7a..75249f7d 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift @@ -115,7 +115,7 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi backgroundColor = .white } - public static func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { + public class func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { guard let molecule = molecule?.optionalDictionaryForKey(KeyMolecule) else { return nil } diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 404e63ec..4769b7e5 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -8,227 +8,24 @@ import UIKit -@objcMembers open class MoleculeTableViewCell: UITableViewCell, MVMCoreUIMoleculeViewProtocol, MoleculeListCellProtocol { - - open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? - open var json: [AnyHashable: Any]? - - // In updateView, will set padding to default. - open var updateViewHorizontalDefaults = true - - // For the accessory view convenience. - public var caretView: CaretView? - private var caretViewWidthSizeObject: MFSizeObject? - private var caretViewHeightSizeObject: MFSizeObject? - - // For separation between cells. - public var topSeparatorView: SeparatorView? - public var bottomSeparatorView: SeparatorView? - public enum SeparatorFrequency: String { - case All = "all" - case AllExceptTop = "allExceptTop" - case AllExceptBottom = "allExceptBottom" - case Between = "between" - } - - /// For subclasses that want to use a custom accessory view. - open var customAccessoryView = false - - public var topMarginPadding: CGFloat = 24 - public var bottomMarginPadding: CGFloat = 24 - - // MARK: - Styling - func style(with styleString: String?) { - guard let styleString = styleString else { - return - } - switch styleString { - case "standard": - styleStandard() - case "header": - styleHeader() - case "none": - styleNone() - default: break - } - } - - open override func layoutSubviews() { - super.layoutSubviews() - - // Ensures accessory view aligns to the center y derived from the - if let center = heroAccessoryCenter { - accessoryView?.center.y = center.y - } - } - - var heroAccessoryCenter: CGPoint? - - func styleStandard() { - topMarginPadding = 24 - bottomMarginPadding = 24 - bottomSeparatorView?.show() - bottomSeparatorView?.setAsLight() - } - - func styleHeader() { - topMarginPadding = 48 - bottomMarginPadding = 16 - bottomSeparatorView?.show() - bottomSeparatorView?.setAsRegular() - } - - func styleNone() { - topMarginPadding = 0 - bottomMarginPadding = 0 - bottomSeparatorView?.hide() - } - - public func willDisplay() { - - alignAccessoryToHero() - } - - // MARK: - Inits - public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - setupView() - } - - public required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - setupView() - } - - // MARK: - MFViewProtocol - public func updateView(_ size: CGFloat) { - MFStyler.setMarginsFor(self, size: size, defaultHorizontal: updateViewHorizontalDefaults, top: topMarginPadding, bottom: bottomMarginPadding) - if accessoryView != nil { - // Smaller left margin if accessory view. - var margin = directionalLayoutMargins - margin.trailing = 16 - contentView.directionalLayoutMargins = margin - } else { - contentView.directionalLayoutMargins = directionalLayoutMargins - } - topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) - bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) - - molecule?.updateView(size) - if let _ = accessoryView, let caretView = caretView, let widthObject = caretViewWidthSizeObject, let heightObject = caretViewHeightSizeObject { - caretView.frame = CGRect(x: 0, y: 0, width: widthObject.getValueBased(onSize: size), height: heightObject.getValueBased(onSize: size)) - } - topSeparatorView?.updateView(size) - bottomSeparatorView?.updateView(size) - } - - public func setupView() { - selectionStyle = .none - insetsLayoutMarginsFromSafeArea = false - contentView.insetsLayoutMarginsFromSafeArea = false - contentView.preservesSuperviewLayoutMargins = false - } - - /// NOTE: Should only be called when displayed or about to be displayed. - public func alignAccessoryToHero() { - - // Layout call required to force draw in memory to get dimensions of subviews. - layoutIfNeeded() - guard let heroLabel = findHeroLabel(views: contentView.subviews), let hero = heroLabel.hero else { return } - let rect = Label.boundingRect(forCharacterRange: NSRange(location: hero, length: 1), in: heroLabel) - accessoryView?.center.y = contentView.convert(UIView(frame: rect).center, from: heroLabel).y - heroAccessoryCenter = accessoryView?.center - } - - /// Traverses the view hierarchy for a 🦸‍♂️heroic Label. - private func findHeroLabel(views: [UIView]) -> Label? { - - if views.isEmpty { - return nil - } - - var queue = [UIView]() - - for view in views { - // Only one Label will have a hero in a table cell. - if let label = view as? Label, label.hero != nil { - return label - } - queue.append(contentsOf: view.subviews) - } - - return findHeroLabel(views: queue) - } +@objcMembers open class MoleculeTableViewCell: TableViewCell { // MARK: - MVMCoreUIMoleculeViewProtocol - public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - self.json = json + public override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - style(with: json?.optionalStringForKey("style")) - - if let useHorizontalMargins = json?.optionalBoolForKey("useHorizontalMargins") { - updateViewHorizontalDefaults = useHorizontalMargins - } - - if (json?.optionalBoolForKey("useVerticalMargins") ?? true) == false { - topMarginPadding = 0 - bottomMarginPadding = 0 - } - - if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { - backgroundColor = UIColor.mfGet(forHex: backgroundColorString) - } - - // Add the caret if there is an action and it's not declared hidden. - if !customAccessoryView { - if let _ = json?.optionalDictionaryForKey("actionMap"), !json!.boolForKey("hideArrow") { - addCaretViewAccessory() - } else { - accessoryView = nil - } - } - - // override the separator - if let separator = json?.optionalDictionaryForKey("separator") { - addSeparatorsIfNeeded() - bottomSeparatorView?.setWithJSON(separator, delegateObject: delegateObject, additionalData: additionalData) - } - - guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { return } - - if molecule == nil { - if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { - contentView.addSubview(moleculeView) - let standardConstraints = (moleculeView as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true - NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: standardConstraints).values)) - molecule = moleculeView - } - } else { - molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) - } - - // This molecule will by default handle margins. - if let castView = molecule as? MVMCoreUIViewConstrainingProtocol { - castView.shouldSetHorizontalMargins?(false) - castView.shouldSetVerticalMargins?(false) - } + guard molecule == nil, let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule), let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) else { return } + addMolecule(moleculeView) } - public func reset() { - molecule?.reset?() - updateViewHorizontalDefaults = true - styleStandard() - backgroundColor = .white - } - - public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) else { return 80 } return max(2 * PaddingDefaultVerticalSpacing3, height) } - public static func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { + public override class func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { guard let molecule = molecule?.optionalDictionaryForKey(KeyMolecule) else { return "\(self)<>" } @@ -236,87 +33,11 @@ import UIKit return "\(self)<\(moleculeName)>" } - public static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + 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 { return nil } return theClass.requiredModules?(moleculeJSON, delegateObject: delegateObject, error: error) } - - // MARK: - Arrow - /// Adds the standard mvm style caret to the accessory view - @objc public func addCaretViewAccessory() { - guard accessoryView == nil else { return } - let width: CGFloat = 6 - let height: CGFloat = 10 - caretView = CaretView(lineThickness: CaretView.thin) - caretView?.frame = CGRect(x: 0, y: 0, width: width, height: height) - caretViewWidthSizeObject = MFSizeObject(standardSize: width, standardiPadPortraitSize: 9) - caretViewHeightSizeObject = MFSizeObject(standardSize: height, standardiPadPortraitSize: 16) - accessoryView = caretView - } - - // MARK: - MoleculeListCellProtocol - /// For when the separator between cells shows using json and frequency. Default is type: standard, frequency: allExceptTop. - public func setSeparatorWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, indexPath: IndexPath) { - addSeparatorsIfNeeded() - if let json = json { - topSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - bottomSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - if let separatorFrequencyString = json.optionalStringForKey("frequency"), let separatorFrequency = SeparatorFrequency(rawValue: separatorFrequencyString) { - setSeparatorFrequency(separatorFrequency, indexPath: indexPath) - } - } else { - topSeparatorView?.hide() - bottomSeparatorView?.setAsLight() - setSeparatorFrequency(MoleculeTableViewCell.SeparatorFrequency.AllExceptTop, indexPath: indexPath) - } - } - - public func didSelectCell(atIndex indexPath: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - if let actionMap = json?.optionalDictionaryForKey("actionMap") { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) - } - } - - // MARK: - Separator - func addSeparatorsIfNeeded() { - if topSeparatorView == nil { - topSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionTop) - topSeparatorView?.hide() - } - if bottomSeparatorView == nil { - bottomSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionBot) - bottomSeparatorView?.hide() - } - } - - /// For when the separator between cells shows. - public func setSeparatorFrequency(_ separatorFrequency: SeparatorFrequency, indexPath: IndexPath) { - switch separatorFrequency { - case .All: - if indexPath.row == 0 { - topSeparatorView?.show() - } else { - topSeparatorView?.hide() - } - bottomSeparatorView?.show() - case .AllExceptBottom: - topSeparatorView?.show() - bottomSeparatorView?.hide() - case .Between: - if indexPath.row == 0 { - topSeparatorView?.hide() - } else { - topSeparatorView?.show() - } - bottomSeparatorView?.hide() - case .AllExceptTop: - fallthrough - default: - topSeparatorView?.hide() - bottomSeparatorView?.show() - } - } } diff --git a/MVMCoreUI/Molecules/Items/TableViewCell.swift b/MVMCoreUI/Molecules/Items/TableViewCell.swift new file mode 100644 index 00000000..4386dc85 --- /dev/null +++ b/MVMCoreUI/Molecules/Items/TableViewCell.swift @@ -0,0 +1,312 @@ +// +// TableViewCell.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/29/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +@objcMembers open class TableViewCell: UITableViewCell { + open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? + open var json: [AnyHashable: Any]? + + // In updateView, will set padding to default. + open var updateViewHorizontalDefaults = true + + // For the accessory view convenience. + private var caretView: CaretView? + private var caretViewWidthSizeObject: MFSizeObject? + private var caretViewHeightSizeObject: MFSizeObject? + + // For separation between cells. + public var topSeparatorView: SeparatorView? + public var bottomSeparatorView: SeparatorView? + public enum SeparatorFrequency: String { + case All = "all" + case AllExceptTop = "allExceptTop" + case AllExceptBottom = "allExceptBottom" + case Between = "between" + } + + /// For subclasses that want to use a custom accessory view. + open var customAccessoryView = false + + open var topMarginPadding: CGFloat = 24 + open var bottomMarginPadding: CGFloat = 24 + + private var heroAccessoryCenter: CGPoint? + + // MARK: - Styling + open func style(with styleString: String?) { + guard let styleString = styleString else { + return + } + switch styleString { + case "standard": + styleStandard() + case "header": + styleHeader() + case "none": + styleNone() + default: break + } + } + + open func styleStandard() { + topMarginPadding = 24 + bottomMarginPadding = 24 + bottomSeparatorView?.show() + bottomSeparatorView?.setAsLight() + } + + open func styleHeader() { + topMarginPadding = 48 + bottomMarginPadding = 16 + bottomSeparatorView?.show() + bottomSeparatorView?.setAsRegular() + } + + open func styleNone() { + topMarginPadding = 0 + bottomMarginPadding = 0 + bottomSeparatorView?.hide() + } + + /// Adds the molecule to the view. + open func addMolecule(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) { + contentView.addSubview(molecule) + let standardConstraints = (molecule as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true + NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: molecule, useMargins: standardConstraints).values)) + + // This molecule will by default handle margins. + if let castView = molecule as? MVMCoreUIViewConstrainingProtocol { + castView.shouldSetHorizontalMargins?(false) + castView.shouldSetVerticalMargins?(false) + } + self.molecule = molecule + } + + open override func layoutSubviews() { + super.layoutSubviews() + + // Ensures accessory view aligns to the center y derived from the + if let center = heroAccessoryCenter { + accessoryView?.center.y = center.y + } + } + + // MARK: - Inits + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setupView() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setupView() + } + + // MARK: - MFViewProtocol + public func updateView(_ size: CGFloat) { + MFStyler.setMarginsFor(self, size: size, defaultHorizontal: updateViewHorizontalDefaults, top: topMarginPadding, bottom: bottomMarginPadding) + if accessoryView != nil { + // Smaller left margin if accessory view. + var margin = directionalLayoutMargins + margin.trailing = 16 + contentView.directionalLayoutMargins = margin + } else { + contentView.directionalLayoutMargins = directionalLayoutMargins + } + topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) + bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) + + molecule?.updateView(size) + if let _ = accessoryView, let caretView = caretView, let widthObject = caretViewWidthSizeObject, let heightObject = caretViewHeightSizeObject { + caretView.frame = CGRect(x: 0, y: 0, width: widthObject.getValueBased(onSize: size), height: heightObject.getValueBased(onSize: size)) + } + topSeparatorView?.updateView(size) + bottomSeparatorView?.updateView(size) + } + + public func setupView() { + selectionStyle = .none + insetsLayoutMarginsFromSafeArea = false + contentView.insetsLayoutMarginsFromSafeArea = false + contentView.preservesSuperviewLayoutMargins = false + } + + // MARK: - MVMCoreUIMoleculeViewProtocol + public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + self.json = json + + style(with: json?.optionalStringForKey("style")) + + if let useHorizontalMargins = json?.optionalBoolForKey("useHorizontalMargins") { + updateViewHorizontalDefaults = useHorizontalMargins + } + + if (json?.optionalBoolForKey("useVerticalMargins") ?? true) == false { + topMarginPadding = 0 + bottomMarginPadding = 0 + } + + if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { + backgroundColor = UIColor.mfGet(forHex: backgroundColorString) + } + + // Add the caret if there is an action and it's not declared hidden. + if !customAccessoryView { + if let _ = json?.optionalDictionaryForKey("actionMap"), !json!.boolForKey("hideArrow") { + addCaretViewAccessory() + } else { + accessoryView = nil + } + } + + // override the separator + if let separator = json?.optionalDictionaryForKey("separator") { + addSeparatorsIfNeeded() + bottomSeparatorView?.setWithJSON(separator, delegateObject: delegateObject, additionalData: additionalData) + } + + guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { return } + molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) + + // This molecule will by default handle margins. + if let castView = molecule as? MVMCoreUIViewConstrainingProtocol { + castView.shouldSetHorizontalMargins?(false) + castView.shouldSetVerticalMargins?(false) + } + } + + public func reset() { + molecule?.reset?() + updateViewHorizontalDefaults = true + styleStandard() + backgroundColor = .white + } + + public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) else { + return 80 + } + return max(2 * PaddingDefaultVerticalSpacing3, height) + } + + public class func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { + return molecule?.optionalStringForKey(KeyMoleculeName) ?? "" + } + + // MARK: - Arrow + /// Adds the standard mvm style caret to the accessory view + @objc public func addCaretViewAccessory() { + guard accessoryView == nil else { return } + let width: CGFloat = 6 + let height: CGFloat = 10 + caretView = CaretView(lineThickness: CaretView.thin) + caretView?.frame = CGRect(x: 0, y: 0, width: width, height: height) + caretViewWidthSizeObject = MFSizeObject(standardSize: width, standardiPadPortraitSize: 9) + caretViewHeightSizeObject = MFSizeObject(standardSize: height, standardiPadPortraitSize: 16) + accessoryView = caretView + } + + /// NOTE: Should only be called when displayed or about to be displayed. + public func alignAccessoryToHero() { + + // Layout call required to force draw in memory to get dimensions of subviews. + layoutIfNeeded() + guard let heroLabel = findHeroLabel(views: contentView.subviews), let hero = heroLabel.hero else { return } + let rect = Label.boundingRect(forCharacterRange: NSRange(location: hero, length: 1), in: heroLabel) + accessoryView?.center.y = contentView.convert(UIView(frame: rect).center, from: heroLabel).y + heroAccessoryCenter = accessoryView?.center + } + + /// Traverses the view hierarchy for a 🦸‍♂️heroic Label. + private func findHeroLabel(views: [UIView]) -> Label? { + + if views.isEmpty { + return nil + } + + var queue = [UIView]() + + for view in views { + // Only one Label will have a hero in a table cell. + if let label = view as? Label, label.hero != nil { + return label + } + queue.append(contentsOf: view.subviews) + } + + return findHeroLabel(views: queue) + } + + // MARK: - MoleculeListCellProtocol + /// For when the separator between cells shows using json and frequency. Default is type: standard, frequency: allExceptTop. + public func setSeparatorWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, indexPath: IndexPath) { + addSeparatorsIfNeeded() + if let json = json { + topSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + bottomSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + if let separatorFrequencyString = json.optionalStringForKey("frequency"), let separatorFrequency = SeparatorFrequency(rawValue: separatorFrequencyString) { + setSeparatorFrequency(separatorFrequency, indexPath: indexPath) + } + } else { + topSeparatorView?.hide() + bottomSeparatorView?.setAsLight() + setSeparatorFrequency(TableViewCell.SeparatorFrequency.AllExceptTop, indexPath: indexPath) + } + } + + public func didSelectCell(atIndex indexPath: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + if let actionMap = json?.optionalDictionaryForKey("actionMap") { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } + + public func willDisplay() { + alignAccessoryToHero() + } + + // MARK: - Separator + open func addSeparatorsIfNeeded() { + if topSeparatorView == nil { + topSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionTop) + topSeparatorView?.hide() + } + if bottomSeparatorView == nil { + bottomSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionBot) + bottomSeparatorView?.hide() + } + } + + /// For when the separator between cells shows. + public func setSeparatorFrequency(_ separatorFrequency: SeparatorFrequency, indexPath: IndexPath) { + switch separatorFrequency { + case .All: + if indexPath.row == 0 { + topSeparatorView?.show() + } else { + topSeparatorView?.hide() + } + bottomSeparatorView?.show() + case .AllExceptBottom: + topSeparatorView?.show() + bottomSeparatorView?.hide() + case .Between: + if indexPath.row == 0 { + topSeparatorView?.hide() + } else { + topSeparatorView?.show() + } + bottomSeparatorView?.hide() + case .AllExceptTop: + fallthrough + default: + topSeparatorView?.hide() + bottomSeparatorView?.show() + } + } +} diff --git a/MVMCoreUI/Molecules/Items/TabsTableViewCell.swift b/MVMCoreUI/Molecules/Items/TabsTableViewCell.swift index 1c509a8f..1a1796cd 100644 --- a/MVMCoreUI/Molecules/Items/TabsTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/TabsTableViewCell.swift @@ -8,7 +8,7 @@ import UIKit -@objcMembers public class TabsTableViewCell: MoleculeTableViewCell { +@objcMembers public class TabsTableViewCell: TableViewCell { let tabs = TopTabbar(frame: .zero) var delegateObject: MVMCoreUIDelegateObject? var previousTabIndex = 0 diff --git a/MVMCoreUI/Molecules/ModuleMolecule.swift b/MVMCoreUI/Molecules/ModuleMolecule.swift index 1dc7297e..77acc007 100644 --- a/MVMCoreUI/Molecules/ModuleMolecule.swift +++ b/MVMCoreUI/Molecules/ModuleMolecule.swift @@ -62,7 +62,7 @@ open class ModuleMolecule: ViewConstrainingView { return MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: module)?.estimatedHeight?(forRow: module, delegateObject: delegateObject) ?? 0 } - public override static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { + public override class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { guard let moduleName = molecule?.optionalStringForKey("moduleName"), let module = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else { // Critical error return "moduleMolecule<>" @@ -70,7 +70,7 @@ open class ModuleMolecule: ViewConstrainingView { return "moduleMolecule<" + (MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: module)?.name?(forReuse: module, delegateObject: delegateObject) ?? module.stringForkey(KeyMoleculeName)) + ">" } - public override static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { let moduleName = json?.optionalStringForKey("moduleName") if moduleName == nil || delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) == nil { if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), code: CoreUIErrorCode.ErrorCodeModuleMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) { diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index bda3f25b..cafe21b0 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -184,7 +184,7 @@ public class MoleculeStackView: ViewConstrainingView { } } - public override static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { + public override 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<>" @@ -221,7 +221,7 @@ public class MoleculeStackView: ViewConstrainingView { return estimatedHeight } - public override static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { guard let items = json?.optionalArrayForKey(KeyMolecules) else { return nil } From 77a11a69d5eb3a08b141d49e8a1ff63a577704a2 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 12:12:58 -0400 Subject: [PATCH 63/71] Fix for project file --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 955d6cf1..a0ef1271 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 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, ); }; }; D274CA332236A78900B01B62 /* StandardFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D274CA322236A78900B01B62 /* StandardFooterView.swift */; }; + D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2755D7A23689C7500485468 /* TableViewCell.swift */; }; D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */; }; D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */; }; D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */; }; @@ -185,7 +186,6 @@ D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */; }; D2B18B7F2360913400A9AEDC /* Control.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B7E2360913400A9AEDC /* Control.swift */; }; D2B18B812360945C00A9AEDC /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B802360945C00A9AEDC /* View.swift */; }; - D2B18B9A236883BA00A9AEDC /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B99236883BA00A9AEDC /* TableViewCell.swift */; }; D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B1E3E422F37D6A0065F95C /* ImageHeadlineBody.swift */; }; D2C5001821F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; }; @@ -245,6 +245,7 @@ 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 = ""; }; D274CA322236A78900B01B62 /* StandardFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardFooterView.swift; sourceTree = ""; }; + D2755D7A23689C7500485468 /* TableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = ""; }; D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsTableViewCell.swift; sourceTree = ""; }; D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EyebrowHeadlineBodyLink.swift; sourceTree = ""; }; D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFLoadImageView.swift; sourceTree = ""; }; @@ -384,7 +385,6 @@ D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeCollectionViewCell.swift; sourceTree = ""; }; D2B18B7E2360913400A9AEDC /* Control.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Control.swift; sourceTree = ""; }; D2B18B802360945C00A9AEDC /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; -\ D2B18B99236883BA00A9AEDC /* TableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = ""; }; D2B1E3E422F37D6A0065F95C /* ImageHeadlineBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageHeadlineBody.swift; sourceTree = ""; }; D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = ""; }; D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = ""; }; @@ -475,7 +475,7 @@ D22479912316A9EF003FCCF9 /* Items */ = { isa = PBXGroup; children = ( - D2B18B99236883BA00A9AEDC /* TableViewCell.swift */, + D2755D7A23689C7500485468 /* TableViewCell.swift */, 01509D8E2327EC6F00EF99AA /* MoleculeTableViewCell.swift */, D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */, D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */, @@ -1071,6 +1071,7 @@ D29DF24D21E6A177003B2FB9 /* MFTextField.m in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, + D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */, D29DF25421E6A177003B2FB9 /* MFMdnTextField.m in Sources */, D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */, D2A514672213885800345BFB /* StandardHeaderView.swift in Sources */, @@ -1107,7 +1108,6 @@ D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */, D29DF2C721E7BF57003B2FB9 /* MFTabBarInteractor.m in Sources */, D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */, - D2B18B9A236883BA00A9AEDC /* TableViewCell.swift in Sources */, D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */, D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, From 75ea4d60f0b5c359a24237596add0903a8094a88 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 12:18:20 -0400 Subject: [PATCH 64/71] commit missed protocol --- MVMCoreUI/Molecules/Items/TableViewCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Molecules/Items/TableViewCell.swift b/MVMCoreUI/Molecules/Items/TableViewCell.swift index 4386dc85..77e2c57f 100644 --- a/MVMCoreUI/Molecules/Items/TableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/TableViewCell.swift @@ -8,7 +8,7 @@ import UIKit -@objcMembers open class TableViewCell: UITableViewCell { +@objcMembers open class TableViewCell: UITableViewCell, MVMCoreUIMoleculeViewProtocol, MoleculeListCellProtocol { open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? open var json: [AnyHashable: Any]? From e5c0191b18b3b7418c89c3ee1f84dadf909245c4 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 12:30:45 -0400 Subject: [PATCH 65/71] fix to check --- MVMCoreUI/Templates/MoleculeListTemplate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index b400f0a9..ea2913c2 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -126,7 +126,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { open override func addMolecules(_ molecules: [[AnyHashable : Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) { // This dispatch is needed to fix a race condition that can occur if this function is called during the table setup. DispatchQueue.main.async { - guard let cell = sender as? MoleculeTableViewCell, let indexPath = self.tableView?.indexPath(for: cell) else { return } + guard let indexPath = self.tableView?.indexPath(for: sender) else { return } var indexPaths: [IndexPath] = [] for molecule in molecules { if let info = self.getMoleculeInfo(with: molecule) { From 26dac186dc4a069a2247e10228f1428a39ab4ed0 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 13:13:35 -0400 Subject: [PATCH 66/71] undo undo --- .../SwitchMolecules/HeadlineBodySwitch.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift index cbffccb2..79110460 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift @@ -8,9 +8,9 @@ import UIKit -@objcMembers public class HeadlineBodySwitch: ViewConstrainingView { - let headlineBody = HeadlineBody(frame: .zero) - let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault() +@objcMembers open class HeadlineBodySwitch: ViewConstrainingView { + public let headlineBody = HeadlineBody(frame: .zero) + public let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault() // MARK: - MVMCoreViewProtocol open override func updateView(_ size: CGFloat) { @@ -19,7 +19,7 @@ import UIKit mvmSwitch.updateView(size) } - public override func setupView() { + open override func setupView() { super.setupView() guard mvmSwitch.superview == nil else { return @@ -35,24 +35,24 @@ import UIKit } // MARK: - MVMCoreUIMoleculeViewProtocol - public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) headlineBody.setWithJSON(json?.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData) mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData) } - public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 30 } - public override func setAsMolecule() { + open override func setAsMolecule() { super.setAsMolecule() headlineBody.setAsMolecule() (mvmSwitch as MVMCoreUIMoleculeViewProtocol).setAsMolecule?() headlineBody.styleListItem() } - public override func reset() { + open override func reset() { super.reset() headlineBody.reset() (mvmSwitch as MVMCoreUIMoleculeViewProtocol).reset?() From 7e810ec334d231b67f5a08ed50bebb24eca875ae Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 13:17:40 -0400 Subject: [PATCH 67/71] undo undo --- MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 7bdaf8af..e64f643f 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -41,7 +41,7 @@ @"checkbox" : Checkbox.class, @"checkboxWithLabelView" : CheckboxWithLabelView.class, @"cornerLabels" : CornerLabels.class, - @"progressBar": ProgressBar.class, + @"progressbar": ProgressBar.class, @"multiProgressBar": MultiProgress.class, @"checkbox": MVMCoreUICheckBox.class, @"radioButton": RadioButton.class, From 59be22c0e0146bfaff2a4e8a759a7aa3c21ced2c Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 13:47:49 -0400 Subject: [PATCH 68/71] enum clean --- MVMCoreUI/Molecules/Items/TableViewCell.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/MVMCoreUI/Molecules/Items/TableViewCell.swift b/MVMCoreUI/Molecules/Items/TableViewCell.swift index 77e2c57f..e8cfc179 100644 --- a/MVMCoreUI/Molecules/Items/TableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/TableViewCell.swift @@ -24,10 +24,10 @@ import UIKit public var topSeparatorView: SeparatorView? public var bottomSeparatorView: SeparatorView? public enum SeparatorFrequency: String { - case All = "all" - case AllExceptTop = "allExceptTop" - case AllExceptBottom = "allExceptBottom" - case Between = "between" + case all + case allExceptTop + case allExceptBottom + case between } /// For subclasses that want to use a custom accessory view. @@ -256,7 +256,7 @@ import UIKit } else { topSeparatorView?.hide() bottomSeparatorView?.setAsLight() - setSeparatorFrequency(TableViewCell.SeparatorFrequency.AllExceptTop, indexPath: indexPath) + setSeparatorFrequency(TableViewCell.SeparatorFrequency.allExceptTop, indexPath: indexPath) } } @@ -285,24 +285,24 @@ import UIKit /// For when the separator between cells shows. public func setSeparatorFrequency(_ separatorFrequency: SeparatorFrequency, indexPath: IndexPath) { switch separatorFrequency { - case .All: + case .all: if indexPath.row == 0 { topSeparatorView?.show() } else { topSeparatorView?.hide() } bottomSeparatorView?.show() - case .AllExceptBottom: + case .allExceptBottom: topSeparatorView?.show() bottomSeparatorView?.hide() - case .Between: + case .between: if indexPath.row == 0 { topSeparatorView?.hide() } else { topSeparatorView?.show() } bottomSeparatorView?.hide() - case .AllExceptTop: + case .allExceptTop: fallthrough default: topSeparatorView?.hide() From d901ded4c12a1e91ad56864b035fd0f136ef0734 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 13:56:19 -0400 Subject: [PATCH 69/71] open view up! --- MVMCoreUI/BaseClasses/View.swift | 49 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index 735243df..d470c539 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -8,47 +8,46 @@ import UIKit -public class View: UIView { - var json: [AnyHashable: Any]? +open class View: UIView { + open var json: [AnyHashable: Any]? private var initialSetupPerformed = false public override init(frame: CGRect) { - super.init(frame: .zero) - initialSetup() - } + super.init(frame: .zero) + initialSetup() + } - init() { - super.init(frame: .zero) - initialSetup() - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - initialSetup() - } - - public func initialSetup() { - if !initialSetupPerformed { - initialSetupPerformed = true - setupView() - } + init() { + super.init(frame: .zero) + initialSetup() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + public func initialSetup() { + if !initialSetupPerformed { + initialSetupPerformed = true + setupView() } } +} extension View: MVMCoreViewProtocol { - public func updateView(_ size: CGFloat) { - } + open func updateView(_ size: CGFloat) {} /// Will be called only once. - public func setupView() { + open func setupView() { translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false } } extension View: MVMCoreUIMoleculeViewProtocol { - public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { self.json = json if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { @@ -56,7 +55,7 @@ extension View: MVMCoreUIMoleculeViewProtocol { } } - public func reset() { + open func reset() { backgroundColor = .clear } } From e98a0ce76bde5d00cd6a1c1d00da1dac0d2b15b8 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 15:23:44 -0400 Subject: [PATCH 70/71] update pr comments --- MVMCoreUI/BaseClasses/View.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index d470c539..bc6e7634 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -8,7 +8,7 @@ import UIKit -open class View: UIView { +@objcMembers open class View: UIView { open var json: [AnyHashable: Any]? private var initialSetupPerformed = false @@ -18,7 +18,7 @@ open class View: UIView { initialSetup() } - init() { + public init() { super.init(frame: .zero) initialSetup() } From 2fab14d946404e49d310b6da8b7df091536050f8 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 29 Oct 2019 15:26:43 -0400 Subject: [PATCH 71/71] kevin comment --- MVMCoreUI/Molecules/Items/TableViewCell.swift | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Molecules/Items/TableViewCell.swift b/MVMCoreUI/Molecules/Items/TableViewCell.swift index e8cfc179..cc531d75 100644 --- a/MVMCoreUI/Molecules/Items/TableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/TableViewCell.swift @@ -141,24 +141,26 @@ import UIKit public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { self.json = json - style(with: json?.optionalStringForKey("style")) + guard let json = json else { return } - if let useHorizontalMargins = json?.optionalBoolForKey("useHorizontalMargins") { + style(with: json.optionalStringForKey("style")) + + if let useHorizontalMargins = json.optionalBoolForKey("useHorizontalMargins") { updateViewHorizontalDefaults = useHorizontalMargins } - if (json?.optionalBoolForKey("useVerticalMargins") ?? true) == false { + if (json.optionalBoolForKey("useVerticalMargins") ?? true) == false { topMarginPadding = 0 bottomMarginPadding = 0 } - if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { + if let backgroundColorString = json.optionalStringForKey(KeyBackgroundColor) { backgroundColor = UIColor.mfGet(forHex: backgroundColorString) } // Add the caret if there is an action and it's not declared hidden. if !customAccessoryView { - if let _ = json?.optionalDictionaryForKey("actionMap"), !json!.boolForKey("hideArrow") { + if let _ = json.optionalDictionaryForKey("actionMap"), !json.boolForKey("hideArrow") { addCaretViewAccessory() } else { accessoryView = nil @@ -166,12 +168,12 @@ import UIKit } // override the separator - if let separator = json?.optionalDictionaryForKey("separator") { + if let separator = json.optionalDictionaryForKey("separator") { addSeparatorsIfNeeded() bottomSeparatorView?.setWithJSON(separator, delegateObject: delegateObject, additionalData: additionalData) } - guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { return } + guard let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { return } molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) // This molecule will by default handle margins.