From 65c3f0427a64d4ad45ab55a7ef143579102655fa Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 18 Apr 2019 11:07:00 -0400 Subject: [PATCH 001/131] 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 002/131] 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 003/131] 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 004/131] 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 005/131] 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 006/131] 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 007/131] 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 008/131] 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 009/131] 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 010/131] 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 011/131] 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 012/131] 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 013/131] 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 014/131] 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 015/131] 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 096c95f6e048b9231d4b10c66acfda0be59a33cd Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 28 Aug 2019 12:16:07 -0400 Subject: [PATCH 016/131] Setting up new molecule. --- MVMCoreUI.xcodeproj/project.pbxproj | 4 ++ .../Atoms/Buttons/HeadlineBodyButton.swift | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 MVMCoreUI/Atoms/Buttons/HeadlineBodyButton.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 22b92460..952f8826 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 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, ); }; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; + 0A804A3D2316CB79009A8656 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A804A3C2316CB79009A8656 /* HeadlineBodyButton.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, ); }; }; @@ -197,6 +198,7 @@ 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 = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; + 0A804A3C2316CB79009A8656 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.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 = ""; }; @@ -608,6 +610,7 @@ DBC4391A224421A0001AB423 /* CaretButton.swift */, D29DF25821E6A22D003B2FB9 /* MFButtonProtocol.h */, D29DF16B21E69E1F003B2FB9 /* ButtonDelegateProtocol.h */, + 0A804A3C2316CB79009A8656 /* HeadlineBodyButton.swift */, D29DF16A21E69E1F003B2FB9 /* MFCustomButton.h */, D29DF17021E69E1F003B2FB9 /* MFCustomButton.m */, D29DF16C21E69E1F003B2FB9 /* PrimaryButton.h */, @@ -953,6 +956,7 @@ 01DF567021FA5AB300CC099B /* TextFieldListFormViewController.swift in Sources */, D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */, D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */, + 0A804A3D2316CB79009A8656 /* HeadlineBodyButton.swift in Sources */, D29DF24D21E6A177003B2FB9 /* MFTextField.m in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, diff --git a/MVMCoreUI/Atoms/Buttons/HeadlineBodyButton.swift b/MVMCoreUI/Atoms/Buttons/HeadlineBodyButton.swift new file mode 100644 index 00000000..78b11f67 --- /dev/null +++ b/MVMCoreUI/Atoms/Buttons/HeadlineBodyButton.swift @@ -0,0 +1,57 @@ +// +// HeadlineBodyButton.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 8/28/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + + +@objcMembers open class HeadlineBodyButton: ViewConstrainingView { + //------------------------------------------------------ + // MARK: - Outlets + //------------------------------------------------------ + + + + //------------------------------------------------------ + // MARK: - Properties + //------------------------------------------------------ + + + + //------------------------------------------------------ + // MARK: - Lifecycle + //------------------------------------------------------ + + open override func setupView() { + super.setupView() + guard subviews.count == 0 else { + return + } + + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + } + + //------------------------------------------------------ + // MARK: - JSON + //------------------------------------------------------ + + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + + } + + open override func reset() { + + } + + open override func setAsMolecule() { + super.setAsMolecule() + } +} From 9ca6ba50da9a486988e5e17ad3dc530f014508b6 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 29 Aug 2019 10:50:38 -0400 Subject: [PATCH 017/131] New class. --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++++---- ...utton.swift => HeadlineBodyPrimaryButton.swift} | 14 +++++--------- 2 files changed, 9 insertions(+), 13 deletions(-) rename MVMCoreUI/Atoms/Buttons/{HeadlineBodyButton.swift => HeadlineBodyPrimaryButton.swift} (72%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 952f8826..16458819 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -19,7 +19,7 @@ 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, ); }; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; - 0A804A3D2316CB79009A8656 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A804A3C2316CB79009A8656 /* HeadlineBodyButton.swift */; }; + 0A804A3D2316CB79009A8656 /* HeadlineBodyPrimaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A804A3C2316CB79009A8656 /* HeadlineBodyPrimaryButton.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, ); }; }; @@ -198,7 +198,7 @@ 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 = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; - 0A804A3C2316CB79009A8656 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = ""; }; + 0A804A3C2316CB79009A8656 /* HeadlineBodyPrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyPrimaryButton.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 = ""; }; @@ -610,7 +610,7 @@ DBC4391A224421A0001AB423 /* CaretButton.swift */, D29DF25821E6A22D003B2FB9 /* MFButtonProtocol.h */, D29DF16B21E69E1F003B2FB9 /* ButtonDelegateProtocol.h */, - 0A804A3C2316CB79009A8656 /* HeadlineBodyButton.swift */, + 0A804A3C2316CB79009A8656 /* HeadlineBodyPrimaryButton.swift */, D29DF16A21E69E1F003B2FB9 /* MFCustomButton.h */, D29DF17021E69E1F003B2FB9 /* MFCustomButton.m */, D29DF16C21E69E1F003B2FB9 /* PrimaryButton.h */, @@ -956,7 +956,7 @@ 01DF567021FA5AB300CC099B /* TextFieldListFormViewController.swift in Sources */, D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */, D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */, - 0A804A3D2316CB79009A8656 /* HeadlineBodyButton.swift in Sources */, + 0A804A3D2316CB79009A8656 /* HeadlineBodyPrimaryButton.swift in Sources */, D29DF24D21E6A177003B2FB9 /* MFTextField.m in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, diff --git a/MVMCoreUI/Atoms/Buttons/HeadlineBodyButton.swift b/MVMCoreUI/Atoms/Buttons/HeadlineBodyPrimaryButton.swift similarity index 72% rename from MVMCoreUI/Atoms/Buttons/HeadlineBodyButton.swift rename to MVMCoreUI/Atoms/Buttons/HeadlineBodyPrimaryButton.swift index 78b11f67..029a42c0 100644 --- a/MVMCoreUI/Atoms/Buttons/HeadlineBodyButton.swift +++ b/MVMCoreUI/Atoms/Buttons/HeadlineBodyPrimaryButton.swift @@ -1,5 +1,5 @@ // -// HeadlineBodyButton.swift +// HeadlineBodyPrimaryButton.swift // MVMCoreUI // // Created by Kevin Christiano on 8/28/19. @@ -9,7 +9,7 @@ import Foundation -@objcMembers open class HeadlineBodyButton: ViewConstrainingView { +@objcMembers open class HeadlineBodyPrimaryButton: ViewConstrainingView { //------------------------------------------------------ // MARK: - Outlets //------------------------------------------------------ @@ -28,9 +28,8 @@ import Foundation open override func setupView() { super.setupView() - guard subviews.count == 0 else { - return - } + + guard subviews.count == 0 else { return } } @@ -42,10 +41,7 @@ import Foundation // MARK: - JSON //------------------------------------------------------ - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - } + open override func reset() { From 062edc5d4114ddde718107674c6c9155642f03fa Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Fri, 30 Aug 2019 15:33:05 -0400 Subject: [PATCH 018/131] 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 019/131] 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 020/131] 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 bedd05daa6d947224a160bcce2ecdb1e471cb7c2 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 12 Sep 2019 14:42:10 -0400 Subject: [PATCH 021/131] Creation and inclusion of HeadlineBodyButton molecule. --- MVMCoreUI.xcodeproj/project.pbxproj | 5 +- .../Molecules/ActionDetailWithImage.swift | 60 ++------ MVMCoreUI/Molecules/HeadlineBodyButton.swift | 129 ++++++++++++++++++ .../MVMCoreUIMoleculeMappingObject.m | 3 +- 4 files changed, 148 insertions(+), 49 deletions(-) create mode 100644 MVMCoreUI/Molecules/HeadlineBodyButton.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index f606284e..68d9e8f5 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 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, ); }; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; + 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; }; 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 */; }; @@ -202,6 +203,7 @@ 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 = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; + 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = ""; }; 0A804A3C2316CB79009A8656 /* HeadlineBodyPrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyPrimaryButton.swift; 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 = ""; }; @@ -562,6 +564,7 @@ D274CA322236A78900B01B62 /* StandardFooterView.swift */, D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */, D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */, + 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */, ); path = Molecules; sourceTree = ""; @@ -1003,6 +1006,7 @@ D29770F221F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.m in Sources */, D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */, DBC4391922442197001AB423 /* DashLine.swift in Sources */, + 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */, @@ -1024,7 +1028,6 @@ 01DF567021FA5AB300CC099B /* TextFieldListFormViewController.swift in Sources */, D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */, D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */, - 0A804A3D2316CB79009A8656 /* HeadlineBodyPrimaryButton.swift in Sources */, D29DF24D21E6A177003B2FB9 /* MFTextField.m in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, diff --git a/MVMCoreUI/Molecules/ActionDetailWithImage.swift b/MVMCoreUI/Molecules/ActionDetailWithImage.swift index ec91c479..76dde8e6 100644 --- a/MVMCoreUI/Molecules/ActionDetailWithImage.swift +++ b/MVMCoreUI/Molecules/ActionDetailWithImage.swift @@ -14,10 +14,8 @@ import UIKit // MARK: - Outlets //------------------------------------------------------ - let header = HeadlineBody(frame: .zero) - let button = PrimaryButton.primaryTinyButton(false)! + let headlineBodyButton = HeadlineBodyButton(frame: .zero) let imageLoader = MFLoadImageView(pinnedEdges: .all) - let leftContainer = ViewConstrainingView.empty() //------------------------------------------------------ // MARK: - Properties @@ -30,7 +28,6 @@ import UIKit //------------------------------------------------------ var imageLeadingConstraint: NSLayoutConstraint? - var buttonTopConstraint: NSLayoutConstraint? //------------------------------------------------------ // MARK: - Initialization @@ -64,33 +61,20 @@ import UIKit setDefaultState() - addSubview(leftContainer) addSubview(imageLoader) - leftContainer.addSubview(header) - leftContainer.addSubview(button) + addSubview(headlineBodyButton) - leftContainer.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true - leftContainer.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true + headlineBodyButton.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true + headlineBodyButton.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true - layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: leftContainer.bottomAnchor).isActive = true - let leftContainerBottom = leftContainer.bottomAnchor.constraint(equalTo: bottomAnchor) + layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: headlineBodyButton.bottomAnchor).isActive = true + let leftContainerBottom = headlineBodyButton.bottomAnchor.constraint(equalTo: bottomAnchor) leftContainerBottom.priority = UILayoutPriority(249) leftContainerBottom.isActive = true - header.topAnchor.constraint(equalTo: leftContainer.topAnchor).isActive = true - header.leadingAnchor.constraint(equalTo: leftContainer.leadingAnchor).isActive = true - leftContainer.trailingAnchor.constraint(equalTo: header.trailingAnchor).isActive = true - - buttonTopConstraint = button.topAnchor.constraint(equalTo: header.bottomAnchor, constant: buttonHeaderPadding) - buttonTopConstraint?.isActive = true - - button.leadingAnchor.constraint(equalTo: leftContainer.leadingAnchor).isActive = true - leftContainer.bottomAnchor.constraint(equalTo: button.bottomAnchor).isActive = true - leftContainer.trailingAnchor.constraint(greaterThanOrEqualTo: button.trailingAnchor).isActive = true - imageLoader.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true layoutMarginsGuide.trailingAnchor.constraint(equalTo: imageLoader.trailingAnchor).isActive = true - imageLeadingConstraint = imageLoader.leadingAnchor.constraint(greaterThanOrEqualTo: leftContainer.trailingAnchor, constant: 16) + imageLeadingConstraint = imageLoader.leadingAnchor.constraint(greaterThanOrEqualTo: headlineBodyButton.trailingAnchor, constant: 16) imageLeadingConstraint?.isActive = true imageLoader.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true @@ -103,12 +87,8 @@ import UIKit override open func updateView(_ size: CGFloat) { super.updateView(size) - header.updateView(size) - button.updateView(size) + headlineBodyButton.updateView(size) imageLoader.updateView(size) - leftContainer.updateView(size) - - buttonTopConstraint?.constant = header.hasText() ? buttonHeaderPadding : 0 } public override static func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { @@ -121,10 +101,8 @@ import UIKit private func setDefaultState() { - header.headlineLabel.font = MFStyler.fontH3() - header.messageLabel.font = MFStyler.fontB3() - button.setAsSecondaryCustom() - button.isHidden = false + headlineBodyButton.headline.headlineLabel.font = MFStyler.fontH3() + headlineBodyButton.headline.messageLabel.font = MFStyler.fontB3() imageLoader.imageView.contentMode = .scaleAspectFit imageLoader.addSizeConstraintsForAspectRatio = true buttonHeaderPadding = PaddingTwo @@ -133,8 +111,7 @@ import UIKit override open func reset() { super.reset() - header.reset() - button.reset() + headlineBodyButton.reset() imageLeadingConstraint?.constant = 16 imageLoader.reset() setDefaultState() @@ -143,8 +120,7 @@ import UIKit open override func setAsMolecule() { super.setAsMolecule() - header.setAsMolecule() - button.setAsMolecule() + headlineBodyButton.setAsMolecule() imageLoader.setAsMolecule() setDefaultState() } @@ -153,18 +129,8 @@ import UIKit super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) guard let dictionary = json else { return } - - if let padding = dictionary.optionalCGFloatForKey("buttonHeaderPadding") { - buttonHeaderPadding = padding - } - header.setWithJSON(dictionary.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData) + headlineBodyButton.setWithJSON(dictionary.optionalDictionaryForKey("headlineBodyButton"), delegateObject: delegateObject, additionalData: additionalData) imageLoader.setWithJSON(dictionary.optionalDictionaryForKey("image"), delegateObject: delegateObject, additionalData: additionalData) - - if let buttonDictionary = dictionary.optionalDictionaryForKey("button") { - button.setWithJSON(buttonDictionary, delegateObject: delegateObject, additionalData: additionalData) - } else { - button.isHidden = true - } } } diff --git a/MVMCoreUI/Molecules/HeadlineBodyButton.swift b/MVMCoreUI/Molecules/HeadlineBodyButton.swift new file mode 100644 index 00000000..df8ae918 --- /dev/null +++ b/MVMCoreUI/Molecules/HeadlineBodyButton.swift @@ -0,0 +1,129 @@ +// +// HeadlineBodyButton.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 9/12/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + + +@objcMembers open class HeadlineBodyButton: ViewConstrainingView { + //------------------------------------------------------ + // MARK: - Outlets + //------------------------------------------------------ + + let headline = HeadlineBody(frame: .zero) + let button = PrimaryButton.primaryTinyButton(false)! + + //------------------------------------------------------ + // MARK: - Properties + //------------------------------------------------------ + + var buttonHeadlinePadding: CGFloat = 16 + + //------------------------------------------------------ + // MARK: - Constraints + //------------------------------------------------------ + + var buttonTopConstraint: NSLayoutConstraint? + + //------------------------------------------------------ + // MARK: - Initialization + //------------------------------------------------------ + + 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 convenience init(json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + self.init() + setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + } + + //------------------------------------------------------ + // MARK: - View Lifecycle + //------------------------------------------------------ + + override open func setupView() { + super.setupView() + + guard subviews.isEmpty else { return } + + setDefaultState() + + addSubview(headline) + addSubview(button) + + headline.topAnchor.constraint(equalTo: topAnchor).isActive = true + headline.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + trailingAnchor.constraint(equalTo: headline.trailingAnchor).isActive = true + + buttonTopConstraint = button.topAnchor.constraint(equalTo: headline.bottomAnchor, constant: buttonHeadlinePadding) + buttonTopConstraint?.isActive = true + + button.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + bottomAnchor.constraint(equalTo: button.bottomAnchor).isActive = true + trailingAnchor.constraint(greaterThanOrEqualTo: button.trailingAnchor).isActive = true + } + + override open func updateView(_ size: CGFloat) { + super.updateView(size) + + headline.updateView(size) + button.updateView(size) + + buttonTopConstraint?.constant = headline.hasText() ? buttonHeadlinePadding : 0 + } + private func setDefaultState() { + + headline.headlineLabel.font = MFStyler.fontH3() + headline.messageLabel.font = MFStyler.fontB3() + button.setAsSecondaryCustom() + button.isHidden = false + buttonHeadlinePadding = PaddingTwo + } + + override open func reset() { + super.reset() + + headline.reset() + button.reset() + setDefaultState() + } + + open override func setAsMolecule() { + super.setAsMolecule() + + headline.setAsMolecule() + button.setAsMolecule() + setDefaultState() + } + + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + + guard let dictionary = json else { return } + + if let padding = dictionary.optionalCGFloatForKey("buttonHeadlinePadding") { + buttonHeadlinePadding = padding + } + + headline.setWithJSON(dictionary.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData) + + if let buttonDictionary = dictionary.optionalDictionaryForKey("button") { + button.setWithJSON(buttonDictionary, delegateObject: delegateObject, additionalData: additionalData) + } else { + button.isHidden = true + } + } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 86267332..c80c25d6 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -60,7 +60,8 @@ @"headlineBodySwitch": HeadlineBodySwitch.class, @"headlineBodyTextButton": HeadlineBodyTextButton.class, @"headlineBodyTextButtonSwitch": HeadlineBodyTextButtonSwitch.class, - @"tabsListItem": TabsTableViewCell.class + @"tabsListItem": TabsTableViewCell.class, + @"HeadlineBodyButton": HeadlineBodyButton.class } mutableCopy]; }); return mapping; From ad1def8ddc648b6866f54208302a695a2c4e597f Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 12 Sep 2019 16:38:18 -0400 Subject: [PATCH 022/131] changed default state name. --- MVMCoreUI/Molecules/HeadlineBodyButton.swift | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Molecules/HeadlineBodyButton.swift b/MVMCoreUI/Molecules/HeadlineBodyButton.swift index df8ae918..1d1105c0 100644 --- a/MVMCoreUI/Molecules/HeadlineBodyButton.swift +++ b/MVMCoreUI/Molecules/HeadlineBodyButton.swift @@ -59,7 +59,7 @@ import UIKit guard subviews.isEmpty else { return } - setDefaultState() + defaultState() addSubview(headline) addSubview(button) @@ -84,7 +84,8 @@ import UIKit buttonTopConstraint?.constant = headline.hasText() ? buttonHeadlinePadding : 0 } - private func setDefaultState() { + + private func defaultState() { headline.headlineLabel.font = MFStyler.fontH3() headline.messageLabel.font = MFStyler.fontB3() @@ -93,12 +94,16 @@ import UIKit buttonHeadlinePadding = PaddingTwo } + //------------------------------------------------------ + // MARK: - Molecule + //------------------------------------------------------ + override open func reset() { super.reset() headline.reset() button.reset() - setDefaultState() + defaultState() } open override func setAsMolecule() { @@ -106,7 +111,7 @@ import UIKit headline.setAsMolecule() button.setAsMolecule() - setDefaultState() + defaultState() } open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { From cdc7ae2f1e7e0599e3a9ef09c932120da2cd6784 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 13 Sep 2019 09:08:33 -0400 Subject: [PATCH 023/131] added layoutMarginsGuide. --- MVMCoreUI/Molecules/HeadlineBodyButton.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Molecules/HeadlineBodyButton.swift b/MVMCoreUI/Molecules/HeadlineBodyButton.swift index 1d1105c0..feb0f751 100644 --- a/MVMCoreUI/Molecules/HeadlineBodyButton.swift +++ b/MVMCoreUI/Molecules/HeadlineBodyButton.swift @@ -64,16 +64,16 @@ import UIKit addSubview(headline) addSubview(button) - headline.topAnchor.constraint(equalTo: topAnchor).isActive = true - headline.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - trailingAnchor.constraint(equalTo: headline.trailingAnchor).isActive = true + headline.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true + headline.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true + layoutMarginsGuide.trailingAnchor.constraint(equalTo: headline.trailingAnchor).isActive = true buttonTopConstraint = button.topAnchor.constraint(equalTo: headline.bottomAnchor, constant: buttonHeadlinePadding) buttonTopConstraint?.isActive = true - button.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - bottomAnchor.constraint(equalTo: button.bottomAnchor).isActive = true - trailingAnchor.constraint(greaterThanOrEqualTo: button.trailingAnchor).isActive = true + button.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true + layoutMarginsGuide.bottomAnchor.constraint(equalTo: button.bottomAnchor).isActive = true + layoutMarginsGuide.trailingAnchor.constraint(greaterThanOrEqualTo: button.trailingAnchor).isActive = true } override open func updateView(_ size: CGFloat) { From 8238a42177fb749d63b1e9f946bb604a83c88f9c Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 13 Sep 2019 12:28:34 -0400 Subject: [PATCH 024/131] Added the new classes. --- MVMCoreUI.xcodeproj/project.pbxproj | 8 + MVMCoreUI/Atoms/Views/CheckBox.swift | 165 ++++++++++++++++++ MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift | 46 +++++ 3 files changed, 219 insertions(+) create mode 100644 MVMCoreUI/Atoms/Views/CheckBox.swift create mode 100644 MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index c1f4a798..c8cebe2b 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -18,6 +18,8 @@ 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, ); }; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; + 0A7BAFA1232BE61800FB8E22 /* CheckBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* CheckBox.swift */; }; + 0A7BAFA3232BE63400FB8E22 /* CheckBoxWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckBoxWithLabel.swift */; }; 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 */; }; @@ -202,6 +204,8 @@ 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 = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; + 0A7BAFA0232BE61800FB8E22 /* CheckBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBox.swift; sourceTree = ""; }; + 0A7BAFA2232BE63400FB8E22 /* CheckBoxWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBoxWithLabel.swift; 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 = ""; }; @@ -719,6 +723,8 @@ DB891E822253FA8500022516 /* Label.swift */, 0198F7A02256A80A0066C936 /* MFRadioButton.h */, 0198F7A22256A80A0066C936 /* MFRadioButton.m */, + 0A7BAFA0232BE61800FB8E22 /* CheckBox.swift */, + 0A7BAFA2232BE63400FB8E22 /* CheckBoxWithLabel.swift */, ); path = Views; sourceTree = ""; @@ -1040,6 +1046,7 @@ 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 */, D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */, D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */, @@ -1067,6 +1074,7 @@ D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */, D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */, D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */, + 0A7BAFA3232BE63400FB8E22 /* CheckBoxWithLabel.swift in Sources */, D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, 0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */, D29DF29821E7ADB8003B2FB9 /* MFScrollingViewController.m in Sources */, diff --git a/MVMCoreUI/Atoms/Views/CheckBox.swift b/MVMCoreUI/Atoms/Views/CheckBox.swift new file mode 100644 index 00000000..a990aa90 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/CheckBox.swift @@ -0,0 +1,165 @@ +// +// CheckBox.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 9/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import MVMCore + +class CheckBox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + var lineWidth: CGFloat = 0.0 + var lineColor: UIColor? + + + let startXOffset: Float = 42.0 / 124.0 + let startYOffset: Float = 66.0 / 124.0 + let pivotXOffset: Float = 58.0 / 124.0 + let pivotYOffset: Float = 80.0 / 124.0 + let endXOffset: Float = 83.0 / 124.0 + let endYOffset: Float = 46.0 / 124.0 + let pivotPercentage: Float = 0.34 + let endPercentage = 1.0 - pivotPercentage + let animationInterval: Float = 0.01 + + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + private var drawPercentage: Float = 0.0 + private var animationTimer: Timer? + private var checkLayer: CAShapeLayer? + private var selected = false + + func setupView() { + super.setupView() + backgroundColor = UIColor.clear + drawPercentage = 1.0 + lineColor = UIColor.black + lineWidth = 1.0 + } + + func update(_ size: CGFloat) { + super.update(size) + } + + var lineWidth: CGFloat { + get { + return super.lineWidth + } + set(lineWidth) { + self.lineWidth = lineWidth + if checkLayer { + checkLayer.removeFromSuperlayer() + checkLayer = nil + updateCheckSelected(selected, animated: false) + } + } + } + + + //-------------------------------------------------- + // MARK: - Draw + //-------------------------------------------------- + + func drawCheck() { + if !checkLayer { + layoutIfNeeded() + let path = UIBezierPath() + path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) + path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) + path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) + + checkLayer = CAShapeLayer() + checkLayer.frame = bounds + layer.addSublayer(checkLayer) + checkLayer.strokeColor = lineColor.cgColor ?? UIColor.black.cgColor + checkLayer.fillColor = UIColor.clear.cgColor + checkLayer.path = path.cgPath + checkLayer.lineWidth = lineWidth + + CATransaction.begin() + CATransaction.setDisableActions(true) + checkLayer.strokeEnd = 0.0 + CATransaction.commit() + } + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + func updateCheckSelected(_ selected: Bool, animated: Bool) { + MVMCoreDispatchUtility.performBlock(onMainThread: { + self.selected = selected + + // animate this bar + self.drawCheck() + + var layer: CAShapeLayer? + if self.checkLayer.presentation() != nil && animated { + layer = self.checkLayer.presentation() + } else { + layer = self.checkLayer + } + + if animated { + let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") + animateStrokeEnd.fillMode = .both + animateStrokeEnd.isRemovedOnCompletion = false + animateStrokeEnd.duration = 0.3 + + animateStrokeEnd.fromValue = NSNumber(value: Float(layer?.strokeEnd ?? 0.0)) + if selected { + animateStrokeEnd.toValue = NSNumber(value: 1) + } else { + animateStrokeEnd.toValue = NSNumber(value: 0) + } + + animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) + layer?.add(animateStrokeEnd, forKey: "strokeEndAnimation") + } else { + layer?.removeAllAnimations() + CATransaction.begin() + CATransaction.setDisableActions(true) + if selected { + layer?.strokeEnd = 1.0 + } else { + layer?.strokeEnd = 0.0 + } + CATransaction.commit() + } + }) + } + + + func setLineColor(_ lineColor: UIColor?) { + self.lineColor = lineColor + if checkLayer { + checkLayer.strokeColor = lineColor?.cgColor + updateCheckSelected(selected, animated: false) + } + } + + func layoutSubviews() { + drawCheck() + } + + //-------------------------------------------------- + // MARK: - Molecular + //-------------------------------------------------- + + func updateView(_ size: CGFloat) { + <#code#> + } + + func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + <#code#> + } +} diff --git a/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift b/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift new file mode 100644 index 00000000..5e7c7739 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift @@ -0,0 +1,46 @@ +// +// CheckBoxWithLabel.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 9/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + + +class CheckBoxWithLabel: ViewConstrainingView { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + weak var checkMark: CheckBox? + private(set) var isSelected = false + var sizeObject: MFSizeObject? + //?????? + var checkedColor: UIColor? + var unCheckedColor: UIColor? + // Label to the right of the check box. + weak var descriptionLabel: Label? + // Setter for the descriptionLabel.text. Also sets the accessibility text. + var descriptionText: String? + var descriptionAttributedText: NSAttributedString? + // A block that is called when the switch is selected. + var switchSelected: ((_ selected: Bool) -> ())? + + //-------------------------------------------------- + // MARK: - Life Cycle + //-------------------------------------------------- + + // TODO: MVMCoreUICheckBox.m + + //-------------------------------------------------- + // MARK: - Molecular + //-------------------------------------------------- + + override func updateView(_ size: CGFloat) { + + } + + override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + + } +} From daed451c7afa9022f1ff273be08c8a29cf65a800 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Fri, 13 Sep 2019 15:25:13 -0400 Subject: [PATCH 025/131] 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 026/131] 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 bbdb0301eb612b2ee49348f91cda249fec95b3a9 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 16 Sep 2019 09:50:35 -0400 Subject: [PATCH 027/131] Further checkbox setups. --- MVMCoreUI/Atoms/Views/CheckBox.swift | 155 ++++++------ MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift | 231 ++++++++++++++++++ MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m | 2 + 3 files changed, 317 insertions(+), 71 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/CheckBox.swift b/MVMCoreUI/Atoms/Views/CheckBox.swift index a990aa90..ac6adf1a 100644 --- a/MVMCoreUI/Atoms/Views/CheckBox.swift +++ b/MVMCoreUI/Atoms/Views/CheckBox.swift @@ -8,14 +8,18 @@ import MVMCore + class CheckBox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - - var lineWidth: CGFloat = 0.0 - var lineColor: UIColor? + private var _lineColor: UIColor = .black + private var _lineWidth: CGFloat = 1.0 + + private var drawPercentage: Float = 0.0 + private var animationTimer: Timer? + private var checkLayer: CAShapeLayer? let startXOffset: Float = 42.0 / 124.0 let startYOffset: Float = 66.0 / 124.0 @@ -24,7 +28,7 @@ class CheckBox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, M let endXOffset: Float = 83.0 / 124.0 let endYOffset: Float = 46.0 / 124.0 let pivotPercentage: Float = 0.34 - let endPercentage = 1.0 - pivotPercentage + let endPercentage = 0.66 let animationInterval: Float = 0.01 @@ -32,63 +36,73 @@ class CheckBox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, M // MARK: - Lifecycle //-------------------------------------------------- - private var drawPercentage: Float = 0.0 - private var animationTimer: Timer? - private var checkLayer: CAShapeLayer? - private var selected = false + override init(frame: CGRect) { + super.init(frame: frame) - func setupView() { - super.setupView() - backgroundColor = UIColor.clear - drawPercentage = 1.0 - lineColor = UIColor.black - lineWidth = 1.0 - } - - func update(_ size: CGFloat) { - super.update(size) - } + backgroundColor = .clear + drawPercentage = 1.0 + lineColor = .black + lineWidth = 1.0 + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + fatalError("Xib File is not implemented for CheckBox.") + } var lineWidth: CGFloat { get { - return super.lineWidth + return _lineWidth } set(lineWidth) { self.lineWidth = lineWidth - if checkLayer { - checkLayer.removeFromSuperlayer() - checkLayer = nil - updateCheckSelected(selected, animated: false) - } + guard let checkLayer = checkLayer else { return } + checkLayer.removeFromSuperlayer() + checkLayer = nil + updateCheckSelected(isSelected, animated: false) } } + var lineColor: UIColor { + get { + return _lineColor + } + set(lineColor) { + _lineColor = lineColor + + if let checkLayer = checkLayer { + checkLayer.strokeColor = lineColor.cgColor + updateCheckSelected(isSelected, animated: false) + } + } + } //-------------------------------------------------- // MARK: - Draw //-------------------------------------------------- func drawCheck() { - if !checkLayer { - layoutIfNeeded() - let path = UIBezierPath() - path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) - path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) - path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) - - checkLayer = CAShapeLayer() - checkLayer.frame = bounds - layer.addSublayer(checkLayer) - checkLayer.strokeColor = lineColor.cgColor ?? UIColor.black.cgColor - checkLayer.fillColor = UIColor.clear.cgColor - checkLayer.path = path.cgPath - checkLayer.lineWidth = lineWidth - - CATransaction.begin() - CATransaction.setDisableActions(true) - checkLayer.strokeEnd = 0.0 - CATransaction.commit() - } + + guard let checkLayer = checkLayer else { return } + + layoutIfNeeded() + let path = UIBezierPath() + path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) + path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) + path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) + + checkLayer = CAShapeLayer() + checkLayer.frame = bounds + layer.addSublayer(checkLayer) + checkLayer.strokeColor = lineColor.cgColor + checkLayer.fillColor = UIColor.clear.cgColor + checkLayer.path = path.cgPath + checkLayer.lineWidth = lineWidth + + CATransaction.begin() + CATransaction.setDisableActions(true) + checkLayer.strokeEnd = 0.0 + CATransaction.commit() } //-------------------------------------------------- @@ -96,15 +110,17 @@ class CheckBox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, M //-------------------------------------------------- func updateCheckSelected(_ selected: Bool, animated: Bool) { - MVMCoreDispatchUtility.performBlock(onMainThread: { - self.selected = selected + + DispatchQueue.main.async { + + self.isSelected = selected // animate this bar self.drawCheck() var layer: CAShapeLayer? - if self.checkLayer.presentation() != nil && animated { - layer = self.checkLayer.presentation() + if self.checkLayer?.presentation() != nil && animated { + layer = self.checkLayer!.presentation() } else { layer = self.checkLayer } @@ -116,11 +132,8 @@ class CheckBox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, M animateStrokeEnd.duration = 0.3 animateStrokeEnd.fromValue = NSNumber(value: Float(layer?.strokeEnd ?? 0.0)) - if selected { - animateStrokeEnd.toValue = NSNumber(value: 1) - } else { - animateStrokeEnd.toValue = NSNumber(value: 0) - } + + animateStrokeEnd.toValue = NSNumber(value: selected ? 1 : 0) animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) layer?.add(animateStrokeEnd, forKey: "strokeEndAnimation") @@ -128,38 +141,38 @@ class CheckBox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, M layer?.removeAllAnimations() CATransaction.begin() CATransaction.setDisableActions(true) - if selected { - layer?.strokeEnd = 1.0 - } else { - layer?.strokeEnd = 0.0 - } + layer?.strokeEnd = selected ? 1.0 : 0.0 + CATransaction.commit() } - }) - } - - - func setLineColor(_ lineColor: UIColor?) { - self.lineColor = lineColor - if checkLayer { - checkLayer.strokeColor = lineColor?.cgColor - updateCheckSelected(selected, animated: false) } } - func layoutSubviews() { + override func layoutSubviews() { drawCheck() } + private func defaultState() { + + } + //-------------------------------------------------- // MARK: - Molecular //-------------------------------------------------- + open func reset() { + + } + + open func setAsMolecule() { + + } + func updateView(_ size: CGFloat) { - <#code#> + } func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - <#code#> + } } diff --git a/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift b/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift index 5e7c7739..972100d4 100644 --- a/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift +++ b/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift @@ -32,6 +32,237 @@ class CheckBoxWithLabel: ViewConstrainingView { // TODO: MVMCoreUICheckBox.m + func needsToBeConstrained() -> Bool { + return true + } + + func alignment() -> UIStackView.Alignment { + return .leading + } + + func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + + FormValidator.setupValidation(withMolecule: self, delegate: delegateObject?.formValidationProtocol) + delegate = delegateObject + fieldKey = json?.string(for: KeyFieldKey) + isRequired = json?.bool(forKey: KeyRequired) + + let checkedColorHex = json?.string("checkedColor") + let unCheckedColorHex = json?.string("unCheckedColor") + + let checkedColor = checkedColorHex != nil ? UIColor.mfGet(forHex: checkedColorHex) : UIColor.clear + let unCheckedColor = unCheckedColorHex != nil ? UIColor.mfGet(forHex: unCheckedColorHex) : UIColor.clear + + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, label: json?.dict(KeyLabel), delegateObject: delegateObject, additionalData: additionalData) + } + + class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return CGFloat(CheckBoxHeightWidth) + } + + // MARK: - convenient class methods + class func mf() -> Self { + let checkBox = self.init(frame: CGRect.zero) + checkBox.translatesAutoresizingMaskIntoConstraints = false + return checkBox + } + + class func mfCheckBoxWithRoundedRect() -> Self? { + let checkBox = self.init(roundRect: true) + checkBox.translatesAutoresizingMaskIntoConstraints = false + return checkBox + } + + class func mfCheckBox(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) -> Self { + let checkBox = self.init(checkedColor: checkedColor, unCheck: unCheckedColor, text: text) + checkBox?.translatesAutoresizingMaskIntoConstraints = false + return checkBox + } + + class func mfCheckBox(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, atributedText attributedText: NSAttributedString?) -> Self { + let checkBox = self.init(checkedColor: checkedColor, unCheck: unCheckedColor, attributedText: attributedText) + checkBox?.translatesAutoresizingMaskIntoConstraints = false + return checkBox + } + + // MARK: - FormValidationProtocol + func isValidField() -> Bool { + if isRequired { + return isSelected() + } + return true + } + + func formFieldName() -> String? { + return fieldKey + } + + func formFieldValue() -> Any? { + return NSNumber(value: isSelected()) + } + + // MARK: - inits + init() { + super.init() + setupView() + } + + init(checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { + super.init() + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: text) + addAccessibleProperties() + } + + init(checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { + super.init() + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) + descriptionAttributedText = attributedText + addAccessibleProperties() + } + + init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { + super.init() + checkMark = checkMarkView + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: text) + addAccessibleProperties() + } + + init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { + super.init() + checkMark = checkMarkView + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) + descriptionAttributedText = attributedText + addAccessibleProperties() + } + + init(roundRect isRoundRect: Bool) { + super.init() + isRoundRectCheckMark = isRoundRect + setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) + addAccessibleProperties() + setCheckMarkLayer() + } + + //default inits + required init?(coder: NSCoder) { + super.init(coder: coder) + setupView() + setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) + addAccessibleProperties() + } + + init(frame: CGRect) { + super.init(frame: frame) + setupView() + setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) + addAccessibleProperties() + } + + func awakeFromNib() { + super.awakeFromNib() + setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) + } + + func setupView() { + + let containterView = MVMCoreUICommonViewsUtility.commonView() + containterView?.isUserInteractionEnabled = false + if !sizeObject { + sizeObject = MFSizeObject(standardSize: CheckBoxHeightWidth, standardiPadPortraitSize: Int(CheckBoxHeightWidth) + 6) + } + + //checked circle + if !self.checkedSquare { + let checkedSquare = MVMCoreUICommonViewsUtility.commonView() + checkedSquare?.backgroundColor = UIColor.white + if let checkedSquare = checkedSquare { + containterView?.addSubview(checkedSquare) + } + let size = sizeObject.getValueBasedOnApplicationWidth() + let constraints = NSLayoutConstraint.constraintPinView(checkedSquare, heightConstraint: true, heightConstant: size, widthConstraint: true, widthConstant: size) + checkboxWidth = constraints[ConstraintWidth] + checkboxHeight = constraints[ConstraintHeight] + NSLayoutConstraint.constraintPinSubview(checkedSquare, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false) + + checkboxRightPinConstraint = checkedSquare?.trailingAnchor.constraintEqual(to: containterView?.trailingAnchor) + + NSLayoutConstraint.constraintPinSubview(checkedSquare, pinCenterX: false, pinCenterY: true) + self.checkedSquare = checkedSquare + self.checkBoxBorder = UIColor.black + } + + // TODO: OBJC CODE + + //check mark + if (!self.checkMark) { + MVMCoreUICheckMarkView *checkMark = [[MVMCoreUICheckMarkView alloc] initWithFrame:self.frame]; + checkMark.lineWidth = 2.0; + self.checkMark = checkMark; + self.checkMark.translatesAutoresizingMaskIntoConstraints = NO; + [self.checkedSquare addSubview:self.checkMark]; + [self.checkMark.widthAnchor constraintEqualToAnchor:self.checkedSquare.widthAnchor multiplier:.4].active = YES; + [self.checkMark.heightAnchor constraintEqualToAnchor:self.checkedSquare.heightAnchor multiplier:.4].active = YES; + [self.checkMark.centerXAnchor constraintEqualToAnchor:self.checkedSquare.centerXAnchor].active = YES; + [self.checkMark.centerYAnchor constraintEqualToAnchor:self.checkedSquare.centerYAnchor].active = YES; + } + + //label + if (!self.descriptionLabel) { + Label *descriptionLabel = [Label commonLabelB2:YES]; + [containterView addSubview:descriptionLabel]; + [NSLayoutConstraint constraintPinSubview:descriptionLabel pinCenterX:NO pinCenterY:YES]; + [NSLayoutConstraint constraintPinSubview:descriptionLabel pinTop:NO pinBottom:NO pinLeft:NO pinRight:YES]; + + self.descriptionLabelLeadingConstraint = [NSLayoutConstraint constraintWithItem:descriptionLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.checkedSquare attribute:NSLayoutAttributeTrailing multiplier:1 constant:11]; + self.descriptionLabelLeadingConstraint.active = YES; + + self.descriptionLabelLeadingConstraint.active = YES; + self.descriptionLabel = descriptionLabel; + [self setSelected:NO]; + } + + if (!self.containerView) { + [self addSubview:containterView]; + self.containerView = containterView; + [NSLayoutConstraint constraintPinSubviewToSuperview:containterView]; + } + } + + func setup(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?) { + if checkedColor != nil { + self.checkedColor = checkedColor + } + if unCheckedColor != nil { + self.unCheckedColor = unCheckedColor + } + } + + func setup(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor) + descriptionText = text ?? "" + } + + func setup(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, label labelJson: [AnyHashable : Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) { + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor) + descriptionLabel.setWithJSON(labelJson, delegateObject: delegateObject, additionalData: additionalData) + } + + @objc func updateView(_ size: CGFloat) { + MVMCoreDispatchUtility.performBlock(onMainThread: { + self.descriptionLabel.updateView(size) + if self.checkMark.responds(to: #selector(updateView(_:))) { + let width = self.sizeObject.getValueBased(onSize: size) + self.checkboxWidth.constant = width + self.checkboxHeight.constant = width + self.checkMark.updateView(size) + } + }) + } + + func setCheckMarkLayer() { + checkedSquare.layer.cornerRadius = isRoundRectCheckMark ? 5.0 : 0 + } + //-------------------------------------------------- // MARK: - Molecular //-------------------------------------------------- diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m index 2cde1bc3..d219f2f3 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUICheckBox.m @@ -306,6 +306,8 @@ static const CGFloat CheckBoxHeightWidth = 18.0; self.checkedSquare.layer.cornerRadius = self.isRoundRectCheckMark ? 5.0f : 0; } +// TODO:..................................... + #pragma mark - XIB Helpers - (instancetype)awakeAfterUsingCoder:(NSCoder *)aDecoder { From 6f4db10d33b371906dd31d3360baf2d4721fbf27 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 16 Sep 2019 10:24:18 -0400 Subject: [PATCH 028/131] beginning work on hero. --- MVMCoreUI/Atoms/Views/Label.swift | 41 +++++++++++++++++++ .../Molecules/ActionDetailWithImage.swift | 5 +++ 2 files changed, 46 insertions(+) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index ed99fcac..99576509 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -502,6 +502,47 @@ public typealias ActionBlock = () -> () let accessibleAction = customAccessibilityAction(range: range) clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1)) } + +// func rectOfCharacter() { +// +//// label.intrinsicContentSize.width +// if let range = mylabel.text?.range(of: String(describing: mylabel.text?.characters.last!)) { +// let prefix = mylabel.text?.substring(to: range.lowerBound) +// let size: CGSize = prefix!.size(attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 35.0)]) +// let position = CGPoint(x:size.width,y: 0) +// myScrollView.setContentOffset(position, animated: true) +// } +// } + + static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { + + guard let attributedText = label.attributedText else { return CGRect() } + + let paragraph = NSMutableParagraphStyle() + paragraph.alignment = label.textAlignment + + let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText) + stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count)) + + let textStorage = NSTextStorage(attributedString: stagedAttributedString) + let layoutManager = NSLayoutManager() + let textContainer = NSTextContainer(size: .zero) + + layoutManager.addTextContainer(textContainer) + textStorage.addLayoutManager(layoutManager) + + textContainer.lineFragmentPadding = 0 + textContainer.lineBreakMode = label.lineBreakMode + textContainer.maximumNumberOfLines = label.numberOfLines + textContainer.size = label.bounds.size + + var glyphRange = NSRange() + + // Convert the range for glyphs. + layoutManager.characterRange(forGlyphRange: range, actualGlyphRange: &glyphRange) + + return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer) + } } // MARK: - Atomization diff --git a/MVMCoreUI/Molecules/ActionDetailWithImage.swift b/MVMCoreUI/Molecules/ActionDetailWithImage.swift index ec91c479..8cdddb5f 100644 --- a/MVMCoreUI/Molecules/ActionDetailWithImage.swift +++ b/MVMCoreUI/Molecules/ActionDetailWithImage.swift @@ -166,5 +166,10 @@ import UIKit } else { button.isHidden = true } + + if let text = header.messageLabel.text, text.contains("frog") { + let rect = Label.boundingRect(forCharacterRange: NSRange(location: 65, length: 4), in: header.messageLabel) + print(rect) + } } } From b9e80398c16c150d68cfbdb17e16a7b151823f51 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 17 Sep 2019 09:41:14 -0400 Subject: [PATCH 029/131] Decent palce. --- MVMCoreUI/Atoms/Views/Label.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 99576509..862fd829 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -514,7 +514,7 @@ public typealias ActionBlock = () -> () // } // } - static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { + public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { guard let attributedText = label.attributedText else { return CGRect() } @@ -531,7 +531,7 @@ public typealias ActionBlock = () -> () layoutManager.addTextContainer(textContainer) textStorage.addLayoutManager(layoutManager) - textContainer.lineFragmentPadding = 0 + textContainer.lineFragmentPadding = 0.0 textContainer.lineBreakMode = label.lineBreakMode textContainer.maximumNumberOfLines = label.numberOfLines textContainer.size = label.bounds.size From fc4fcc25f6b1ebcbb578d8d3c16fa39ed5e1a157 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 17 Sep 2019 16:37:27 -0400 Subject: [PATCH 030/131] Current take on hero. Needs to be refactored and placed into a protocol. --- MVMCoreUI/Atoms/Views/Label.swift | 12 +++++ .../Templates/MoleculeListTemplate.swift | 45 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 862fd829..81159965 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -25,6 +25,7 @@ public typealias ActionBlock = () -> () /// Set this to use a custom sizing object during updateView instead of the standard. public var sizeObject: MFSizeObject? public var scaleSize: NSNumber? + public var hero: Int? // Used for scaling the font in updateView. private var originalAttributedString: NSAttributedString? @@ -562,6 +563,17 @@ extension Label { clauses = [] Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData) originalAttributedString = attributedText + + hero = json?["hero"] as? Int + } + + func noticeBounds(_ rect: CGRect, color: UIColor) -> UIView { + + let view = UIView(frame: rect) + view.layer.borderColor = color.cgColor + view.layer.borderWidth = 1.0 + + return view } public func setAsMolecule() { diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 0270cb63..0db27a35 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -88,6 +88,38 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { return cell } + open override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + + cell.layoutIfNeeded() + let heroLabel = findHeroLabel(views: cell.contentView.subviews) + let rect = Label.boundingRect(forCharacterRange: NSRange(location: heroLabel!.hero!, length: 1), in: heroLabel!) + let rectView = heroLabel?.noticeBounds(rect, color: .clear) + heroLabel?.addSubview(rectView!) + let convert = cell.contentView.convert(rectView!.center, from: heroLabel) + + cell.accessoryView?.center.y = convert.y + rectView?.removeFromSuperview() + } + + // TODO: Write recursively. + func findHeroLabel(views: [UIView]) -> Label? { + + var queue: [UIView] = views + + repeat { + for view in queue { + if let label = view as? Label, label.hero != nil { + return label + } else if !view.subviews.isEmpty { + queue.append(contentsOf: view.subviews) + } + queue.removeFirst() + } + } while (!queue.isEmpty) + + return nil + } + open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath) as? MoleculeListCellProtocol { cell.didSelectCell?(atIndex: indexPath, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil) @@ -111,11 +143,24 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { if let tableView = tableView { let point = molecule.convert(molecule.bounds.origin, to: tableView) if let indexPath = tableView.indexPathForRow(at: point), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { + //------------- tableView.beginUpdates() +// let cell = tableView.cellForRow(at: indexPath) +// cell.layoutIfNeeded() +// let heroLabel = findHeroLabel(views: cell.contentView.subviews) +// let rect = Label.boundingRect(forCharacterRange: NSRange(location: heroLabel!.hero!, length: 1), in: heroLabel!) +// let rectView = heroLabel?.noticeBounds(rect, color: .blue) +// heroLabel?.addSubview(rectView!) +// // let rect = Label.boundingRect(forCharacterRange: NSRange(location: 46, length: 6), in: self) +// let convert = cell.contentView.convert(rectView!.center, from: heroLabel) +// +// cell.accessoryView?.center.y = convert.y + /// ------------ tableView.endUpdates() } } } + 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. From decf0bf9e6ce3646711be0c6831ba225e847d1fd Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Wed, 18 Sep 2019 21:58:30 +0530 Subject: [PATCH 031/131] 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 5bc420ac05399ebf31d2310cfe932472b5b77f94 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 18 Sep 2019 14:13:33 -0400 Subject: [PATCH 032/131] accessoryVIew can now center itself in relation to the hero character. --- MVMCoreUI/Atoms/Views/Label.swift | 24 +------- .../Molecules/ActionDetailWithImage.swift | 5 -- .../Items/MoleculeTableViewCell.swift | 57 ++++++++++++++++--- .../Molecules/MVMCoreUIMoleculeViewProtocol.h | 2 + .../Templates/MoleculeListTemplate.swift | 53 ++--------------- 5 files changed, 61 insertions(+), 80 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 81159965..cab341b1 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -25,6 +25,8 @@ public typealias ActionBlock = () -> () /// Set this to use a custom sizing object during updateView instead of the standard. public var sizeObject: MFSizeObject? public var scaleSize: NSNumber? + + /// A specific text index to use as a unique marker. public var hero: Int? // Used for scaling the font in updateView. @@ -503,18 +505,7 @@ public typealias ActionBlock = () -> () let accessibleAction = customAccessibilityAction(range: range) clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1)) } - -// func rectOfCharacter() { -// -//// label.intrinsicContentSize.width -// if let range = mylabel.text?.range(of: String(describing: mylabel.text?.characters.last!)) { -// let prefix = mylabel.text?.substring(to: range.lowerBound) -// let size: CGSize = prefix!.size(attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 35.0)]) -// let position = CGPoint(x:size.width,y: 0) -// myScrollView.setContentOffset(position, animated: true) -// } -// } - + public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { guard let attributedText = label.attributedText else { return CGRect() } @@ -567,15 +558,6 @@ extension Label { hero = json?["hero"] as? Int } - func noticeBounds(_ rect: CGRect, color: UIColor) -> UIView { - - let view = UIView(frame: rect) - view.layer.borderColor = color.cgColor - view.layer.borderWidth = 1.0 - - return view - } - public func setAsMolecule() { styleB2(true) } diff --git a/MVMCoreUI/Molecules/ActionDetailWithImage.swift b/MVMCoreUI/Molecules/ActionDetailWithImage.swift index 8cdddb5f..ec91c479 100644 --- a/MVMCoreUI/Molecules/ActionDetailWithImage.swift +++ b/MVMCoreUI/Molecules/ActionDetailWithImage.swift @@ -166,10 +166,5 @@ import UIKit } else { button.isHidden = true } - - if let text = header.messageLabel.text, text.contains("frog") { - let rect = Label.boundingRect(forCharacterRange: NSRange(location: 65, length: 4), in: header.messageLabel) - print(rect) - } } } diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 641b3721..ce9fe93c 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -53,6 +53,16 @@ import UIKit } } + open override func layoutSubviews() { + super.layoutSubviews() + + if let center = heroAccessoryCenter { + accessoryView?.center.y = center.y + } + } + + var heroAccessoryCenter: CGPoint? + func styleStandard() { topMarginPadding = 24 bottomMarginPadding = 24 @@ -128,15 +138,51 @@ import UIKit } } + /// NOTE: Should only be called when displayed or about to be displayed. + public func alignAccessoryToHero() { + + 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) + let rectView = UIView(frame: rect) + heroLabel.addSubview(rectView) + accessoryView?.center.y = contentView.convert(rectView.center, from: heroLabel).y + heroAccessoryCenter = accessoryView?.center + rectView.removeFromSuperview() + } + + /** + Used to traverse 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: - MVMCoreUIMoleculeViewProtocol public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - self.json = json; + 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 @@ -161,9 +207,8 @@ import UIKit bottomSeparatorView?.setWithJSON(separator, delegateObject: delegateObject, additionalData: additionalData) } - guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { - return - } + 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) @@ -215,9 +260,7 @@ import UIKit // MARK: - Arrow /// Adds the standard mvm style caret to the accessory view public func addCaretViewAccessory() { - guard accessoryView == nil else { - return - } + guard accessoryView == nil else { return } let width: CGFloat = 6 let height: CGFloat = 10 caretView = CaretView(lineThickness: CaretView.thin) diff --git a/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h b/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h index 4c69b1f7..273329aa 100644 --- a/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h +++ b/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h @@ -24,6 +24,8 @@ /// Resets to default state before set with json is called again. - (void)reset; +/// Currently designed for UITableViewCell. Aligns the accessory view with the center of a character in a line of text. +- (void)alignAccessoryToHero; /// For the molecule list to load more efficiently. + (CGFloat)estimatedHeightForRow:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject; diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 0db27a35..84a7e64b 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -51,9 +51,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { // MARK: - table open override func registerWithTable() { super.registerWithTable() - guard let moleculesInfo = moleculesInfo else { - return - } + guard let moleculesInfo = moleculesInfo else { return } + for moleculeInfo in moleculesInfo { tableView?.register(moleculeInfo.class, forCellReuseIdentifier: moleculeInfo.identifier) } @@ -90,34 +89,9 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { open override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - cell.layoutIfNeeded() - let heroLabel = findHeroLabel(views: cell.contentView.subviews) - let rect = Label.boundingRect(forCharacterRange: NSRange(location: heroLabel!.hero!, length: 1), in: heroLabel!) - let rectView = heroLabel?.noticeBounds(rect, color: .clear) - heroLabel?.addSubview(rectView!) - let convert = cell.contentView.convert(rectView!.center, from: heroLabel) - - cell.accessoryView?.center.y = convert.y - rectView?.removeFromSuperview() - } - - // TODO: Write recursively. - func findHeroLabel(views: [UIView]) -> Label? { - - var queue: [UIView] = views - - repeat { - for view in queue { - if let label = view as? Label, label.hero != nil { - return label - } else if !view.subviews.isEmpty { - queue.append(contentsOf: view.subviews) - } - queue.removeFirst() - } - } while (!queue.isEmpty) - - return nil + if cell.accessoryView != nil, let protocolCell = cell as? (MoleculeTableViewCell & MVMCoreUIMoleculeViewProtocol) { + protocolCell.alignAccessoryToHero() + } } open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -143,31 +117,16 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { if let tableView = tableView { let point = molecule.convert(molecule.bounds.origin, to: tableView) if let indexPath = tableView.indexPathForRow(at: point), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { - //------------- tableView.beginUpdates() -// let cell = tableView.cellForRow(at: indexPath) -// cell.layoutIfNeeded() -// let heroLabel = findHeroLabel(views: cell.contentView.subviews) -// let rect = Label.boundingRect(forCharacterRange: NSRange(location: heroLabel!.hero!, length: 1), in: heroLabel!) -// let rectView = heroLabel?.noticeBounds(rect, color: .blue) -// heroLabel?.addSubview(rectView!) -// // let rect = Label.boundingRect(forCharacterRange: NSRange(location: 46, length: 6), in: self) -// let convert = cell.contentView.convert(rectView!.center, from: heroLabel) -// -// cell.accessoryView?.center.y = convert.y - /// ------------ tableView.endUpdates() } } } - 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 cell = sender as? MoleculeTableViewCell, let indexPath = self.tableView?.indexPath(for: cell) else { return } var indexPaths: [IndexPath] = [] for molecule in molecules { if let info = self.getMoleculeInfo(with: molecule) { From 4ea916bbcd47f01ba6892648bc5f048caf51517c Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 18 Sep 2019 14:14:37 -0400 Subject: [PATCH 033/131] commenting. --- MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index ce9fe93c..1f348bb5 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -151,11 +151,9 @@ import UIKit rectView.removeFromSuperview() } - /** - Used to traverse the view hierarchy for a 🦸‍♂️heroic Label. - */ + /// Used to traverse the view hierarchy for a 🦸‍♂️heroic Label. private func findHeroLabel(views: [UIView]) -> Label? { - + if views.isEmpty { return nil } @@ -169,7 +167,7 @@ import UIKit } queue.append(contentsOf: view.subviews) } - + return findHeroLabel(views: queue) } From 1c416b5653b4feb0b308b04135aeb5c1c3cf8ce1 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 18 Sep 2019 14:15:56 -0400 Subject: [PATCH 034/131] revised. --- MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 1f348bb5..840e681b 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -151,7 +151,7 @@ import UIKit rectView.removeFromSuperview() } - /// Used to traverse the view hierarchy for a 🦸‍♂️heroic Label. + /// Traverses the view hierarchy for a 🦸‍♂️heroic Label. private func findHeroLabel(views: [UIView]) -> Label? { if views.isEmpty { From 0d6b904a7f6375cc74ed854a5484aeab16b06cd8 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 19 Sep 2019 10:54:28 -0400 Subject: [PATCH 035/131] 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 036/131] 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 037/131] 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 038/131] 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 039/131] 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 040/131] 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 0f5a9be82ee19c3ce079ec2ce409acdd84e9a0a1 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Wed, 25 Sep 2019 11:23:18 -0400 Subject: [PATCH 041/131] CXTDT-25210 --- MVMCoreUI/BaseControllers/MFViewController.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MVMCoreUI/BaseControllers/MFViewController.m b/MVMCoreUI/BaseControllers/MFViewController.m index 256bf050..edbabf80 100644 --- a/MVMCoreUI/BaseControllers/MFViewController.m +++ b/MVMCoreUI/BaseControllers/MFViewController.m @@ -527,6 +527,13 @@ } } +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + if (self.selectedField) { + [self.selectedField resignFirstResponder]; + } +} + - (void)dealloc { [self stopObservingForResponseJSONUpdates]; MVMCoreLog(@"%@ deallocated", [[self class] description]); From 35f7024f8d47504bae8ddd8b0b35304a66409eed Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 25 Sep 2019 14:19:49 -0400 Subject: [PATCH 042/131] Adding two new checkbox swift classes. --- MVMCoreUI.xcodeproj/project.pbxproj | 16 +- MVMCoreUI/Atoms/Views/CheckBox.swift | 178 ----------- MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift | 277 ----------------- MVMCoreUI/Atoms/Views/Checkbox.swift | 288 +++++++++++++++++ .../Atoms/Views/CheckboxWithLabelView.swift | 289 ++++++++++++++++++ .../MVMCoreUIMoleculeMappingObject.m | 4 +- 6 files changed, 587 insertions(+), 465 deletions(-) delete mode 100644 MVMCoreUI/Atoms/Views/CheckBox.swift delete mode 100644 MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift create mode 100644 MVMCoreUI/Atoms/Views/Checkbox.swift create mode 100644 MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index b2dd623a..9692652c 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -18,8 +18,8 @@ 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, ); }; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; - 0A7BAFA1232BE61800FB8E22 /* CheckBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* CheckBox.swift */; }; - 0A7BAFA3232BE63400FB8E22 /* CheckBoxWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckBoxWithLabel.swift */; }; + 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; }; + 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; 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 */; }; @@ -205,8 +205,8 @@ 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 = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; - 0A7BAFA0232BE61800FB8E22 /* CheckBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBox.swift; sourceTree = ""; }; - 0A7BAFA2232BE63400FB8E22 /* CheckBoxWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBoxWithLabel.swift; sourceTree = ""; }; + 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; + 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; 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 = ""; }; @@ -726,8 +726,8 @@ DB891E822253FA8500022516 /* Label.swift */, 0198F7A02256A80A0066C936 /* MFRadioButton.h */, 0198F7A22256A80A0066C936 /* MFRadioButton.m */, - 0A7BAFA0232BE61800FB8E22 /* CheckBox.swift */, - 0A7BAFA2232BE63400FB8E22 /* CheckBoxWithLabel.swift */, + 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */, + 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */, ); path = Views; sourceTree = ""; @@ -1042,6 +1042,7 @@ D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */, D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */, D28B4F8B21FF967C00712C7A /* MVMCoreUIObject.m in Sources */, + 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */, D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */, D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */, D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, @@ -1049,7 +1050,7 @@ D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, - 0A7BAFA1232BE61800FB8E22 /* CheckBox.swift in Sources */, + 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */, D22479962316AF6E003FCCF9 /* HeadlineBodyTextButton.swift in Sources */, D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */, D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */, @@ -1077,7 +1078,6 @@ D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */, D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */, D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */, - 0A7BAFA3232BE63400FB8E22 /* CheckBoxWithLabel.swift in Sources */, D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, 0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Views/CheckBox.swift b/MVMCoreUI/Atoms/Views/CheckBox.swift deleted file mode 100644 index ac6adf1a..00000000 --- a/MVMCoreUI/Atoms/Views/CheckBox.swift +++ /dev/null @@ -1,178 +0,0 @@ -// -// CheckBox.swift -// MVMCoreUI -// -// Created by Kevin Christiano on 9/13/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import MVMCore - - -class CheckBox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - private var _lineColor: UIColor = .black - private var _lineWidth: CGFloat = 1.0 - - private var drawPercentage: Float = 0.0 - private var animationTimer: Timer? - private var checkLayer: CAShapeLayer? - - let startXOffset: Float = 42.0 / 124.0 - let startYOffset: Float = 66.0 / 124.0 - let pivotXOffset: Float = 58.0 / 124.0 - let pivotYOffset: Float = 80.0 / 124.0 - let endXOffset: Float = 83.0 / 124.0 - let endYOffset: Float = 46.0 / 124.0 - let pivotPercentage: Float = 0.34 - let endPercentage = 0.66 - let animationInterval: Float = 0.01 - - - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - - override init(frame: CGRect) { - super.init(frame: frame) - - backgroundColor = .clear - drawPercentage = 1.0 - lineColor = .black - lineWidth = 1.0 - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - fatalError("Xib File is not implemented for CheckBox.") - } - - var lineWidth: CGFloat { - get { - return _lineWidth - } - set(lineWidth) { - self.lineWidth = lineWidth - guard let checkLayer = checkLayer else { return } - checkLayer.removeFromSuperlayer() - checkLayer = nil - updateCheckSelected(isSelected, animated: false) - } - } - - var lineColor: UIColor { - get { - return _lineColor - } - set(lineColor) { - _lineColor = lineColor - - if let checkLayer = checkLayer { - checkLayer.strokeColor = lineColor.cgColor - updateCheckSelected(isSelected, animated: false) - } - } - } - - //-------------------------------------------------- - // MARK: - Draw - //-------------------------------------------------- - - func drawCheck() { - - guard let checkLayer = checkLayer else { return } - - layoutIfNeeded() - let path = UIBezierPath() - path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) - path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) - path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) - - checkLayer = CAShapeLayer() - checkLayer.frame = bounds - layer.addSublayer(checkLayer) - checkLayer.strokeColor = lineColor.cgColor - checkLayer.fillColor = UIColor.clear.cgColor - checkLayer.path = path.cgPath - checkLayer.lineWidth = lineWidth - - CATransaction.begin() - CATransaction.setDisableActions(true) - checkLayer.strokeEnd = 0.0 - CATransaction.commit() - } - - //-------------------------------------------------- - // MARK: - Methods - //-------------------------------------------------- - - func updateCheckSelected(_ selected: Bool, animated: Bool) { - - DispatchQueue.main.async { - - self.isSelected = selected - - // animate this bar - self.drawCheck() - - var layer: CAShapeLayer? - if self.checkLayer?.presentation() != nil && animated { - layer = self.checkLayer!.presentation() - } else { - layer = self.checkLayer - } - - if animated { - let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") - animateStrokeEnd.fillMode = .both - animateStrokeEnd.isRemovedOnCompletion = false - animateStrokeEnd.duration = 0.3 - - animateStrokeEnd.fromValue = NSNumber(value: Float(layer?.strokeEnd ?? 0.0)) - - animateStrokeEnd.toValue = NSNumber(value: selected ? 1 : 0) - - animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) - layer?.add(animateStrokeEnd, forKey: "strokeEndAnimation") - } else { - layer?.removeAllAnimations() - CATransaction.begin() - CATransaction.setDisableActions(true) - layer?.strokeEnd = selected ? 1.0 : 0.0 - - CATransaction.commit() - } - } - } - - override func layoutSubviews() { - drawCheck() - } - - private func defaultState() { - - } - - //-------------------------------------------------- - // MARK: - Molecular - //-------------------------------------------------- - - open func reset() { - - } - - open func setAsMolecule() { - - } - - func updateView(_ size: CGFloat) { - - } - - func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - - } -} diff --git a/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift b/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift deleted file mode 100644 index 972100d4..00000000 --- a/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift +++ /dev/null @@ -1,277 +0,0 @@ -// -// CheckBoxWithLabel.swift -// MVMCoreUI -// -// Created by Kevin Christiano on 9/13/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - - -class CheckBoxWithLabel: ViewConstrainingView { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - weak var checkMark: CheckBox? - private(set) var isSelected = false - var sizeObject: MFSizeObject? - //?????? - var checkedColor: UIColor? - var unCheckedColor: UIColor? - // Label to the right of the check box. - weak var descriptionLabel: Label? - // Setter for the descriptionLabel.text. Also sets the accessibility text. - var descriptionText: String? - var descriptionAttributedText: NSAttributedString? - // A block that is called when the switch is selected. - var switchSelected: ((_ selected: Bool) -> ())? - - //-------------------------------------------------- - // MARK: - Life Cycle - //-------------------------------------------------- - - // TODO: MVMCoreUICheckBox.m - - func needsToBeConstrained() -> Bool { - return true - } - - func alignment() -> UIStackView.Alignment { - return .leading - } - - func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - - FormValidator.setupValidation(withMolecule: self, delegate: delegateObject?.formValidationProtocol) - delegate = delegateObject - fieldKey = json?.string(for: KeyFieldKey) - isRequired = json?.bool(forKey: KeyRequired) - - let checkedColorHex = json?.string("checkedColor") - let unCheckedColorHex = json?.string("unCheckedColor") - - let checkedColor = checkedColorHex != nil ? UIColor.mfGet(forHex: checkedColorHex) : UIColor.clear - let unCheckedColor = unCheckedColorHex != nil ? UIColor.mfGet(forHex: unCheckedColorHex) : UIColor.clear - - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, label: json?.dict(KeyLabel), delegateObject: delegateObject, additionalData: additionalData) - } - - class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return CGFloat(CheckBoxHeightWidth) - } - - // MARK: - convenient class methods - class func mf() -> Self { - let checkBox = self.init(frame: CGRect.zero) - checkBox.translatesAutoresizingMaskIntoConstraints = false - return checkBox - } - - class func mfCheckBoxWithRoundedRect() -> Self? { - let checkBox = self.init(roundRect: true) - checkBox.translatesAutoresizingMaskIntoConstraints = false - return checkBox - } - - class func mfCheckBox(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) -> Self { - let checkBox = self.init(checkedColor: checkedColor, unCheck: unCheckedColor, text: text) - checkBox?.translatesAutoresizingMaskIntoConstraints = false - return checkBox - } - - class func mfCheckBox(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, atributedText attributedText: NSAttributedString?) -> Self { - let checkBox = self.init(checkedColor: checkedColor, unCheck: unCheckedColor, attributedText: attributedText) - checkBox?.translatesAutoresizingMaskIntoConstraints = false - return checkBox - } - - // MARK: - FormValidationProtocol - func isValidField() -> Bool { - if isRequired { - return isSelected() - } - return true - } - - func formFieldName() -> String? { - return fieldKey - } - - func formFieldValue() -> Any? { - return NSNumber(value: isSelected()) - } - - // MARK: - inits - init() { - super.init() - setupView() - } - - init(checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { - super.init() - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: text) - addAccessibleProperties() - } - - init(checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { - super.init() - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) - descriptionAttributedText = attributedText - addAccessibleProperties() - } - - init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { - super.init() - checkMark = checkMarkView - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: text) - addAccessibleProperties() - } - - init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { - super.init() - checkMark = checkMarkView - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) - descriptionAttributedText = attributedText - addAccessibleProperties() - } - - init(roundRect isRoundRect: Bool) { - super.init() - isRoundRectCheckMark = isRoundRect - setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) - addAccessibleProperties() - setCheckMarkLayer() - } - - //default inits - required init?(coder: NSCoder) { - super.init(coder: coder) - setupView() - setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) - addAccessibleProperties() - } - - init(frame: CGRect) { - super.init(frame: frame) - setupView() - setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) - addAccessibleProperties() - } - - func awakeFromNib() { - super.awakeFromNib() - setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) - } - - func setupView() { - - let containterView = MVMCoreUICommonViewsUtility.commonView() - containterView?.isUserInteractionEnabled = false - if !sizeObject { - sizeObject = MFSizeObject(standardSize: CheckBoxHeightWidth, standardiPadPortraitSize: Int(CheckBoxHeightWidth) + 6) - } - - //checked circle - if !self.checkedSquare { - let checkedSquare = MVMCoreUICommonViewsUtility.commonView() - checkedSquare?.backgroundColor = UIColor.white - if let checkedSquare = checkedSquare { - containterView?.addSubview(checkedSquare) - } - let size = sizeObject.getValueBasedOnApplicationWidth() - let constraints = NSLayoutConstraint.constraintPinView(checkedSquare, heightConstraint: true, heightConstant: size, widthConstraint: true, widthConstant: size) - checkboxWidth = constraints[ConstraintWidth] - checkboxHeight = constraints[ConstraintHeight] - NSLayoutConstraint.constraintPinSubview(checkedSquare, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false) - - checkboxRightPinConstraint = checkedSquare?.trailingAnchor.constraintEqual(to: containterView?.trailingAnchor) - - NSLayoutConstraint.constraintPinSubview(checkedSquare, pinCenterX: false, pinCenterY: true) - self.checkedSquare = checkedSquare - self.checkBoxBorder = UIColor.black - } - - // TODO: OBJC CODE - - //check mark - if (!self.checkMark) { - MVMCoreUICheckMarkView *checkMark = [[MVMCoreUICheckMarkView alloc] initWithFrame:self.frame]; - checkMark.lineWidth = 2.0; - self.checkMark = checkMark; - self.checkMark.translatesAutoresizingMaskIntoConstraints = NO; - [self.checkedSquare addSubview:self.checkMark]; - [self.checkMark.widthAnchor constraintEqualToAnchor:self.checkedSquare.widthAnchor multiplier:.4].active = YES; - [self.checkMark.heightAnchor constraintEqualToAnchor:self.checkedSquare.heightAnchor multiplier:.4].active = YES; - [self.checkMark.centerXAnchor constraintEqualToAnchor:self.checkedSquare.centerXAnchor].active = YES; - [self.checkMark.centerYAnchor constraintEqualToAnchor:self.checkedSquare.centerYAnchor].active = YES; - } - - //label - if (!self.descriptionLabel) { - Label *descriptionLabel = [Label commonLabelB2:YES]; - [containterView addSubview:descriptionLabel]; - [NSLayoutConstraint constraintPinSubview:descriptionLabel pinCenterX:NO pinCenterY:YES]; - [NSLayoutConstraint constraintPinSubview:descriptionLabel pinTop:NO pinBottom:NO pinLeft:NO pinRight:YES]; - - self.descriptionLabelLeadingConstraint = [NSLayoutConstraint constraintWithItem:descriptionLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.checkedSquare attribute:NSLayoutAttributeTrailing multiplier:1 constant:11]; - self.descriptionLabelLeadingConstraint.active = YES; - - self.descriptionLabelLeadingConstraint.active = YES; - self.descriptionLabel = descriptionLabel; - [self setSelected:NO]; - } - - if (!self.containerView) { - [self addSubview:containterView]; - self.containerView = containterView; - [NSLayoutConstraint constraintPinSubviewToSuperview:containterView]; - } - } - - func setup(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?) { - if checkedColor != nil { - self.checkedColor = checkedColor - } - if unCheckedColor != nil { - self.unCheckedColor = unCheckedColor - } - } - - func setup(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor) - descriptionText = text ?? "" - } - - func setup(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, label labelJson: [AnyHashable : Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) { - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor) - descriptionLabel.setWithJSON(labelJson, delegateObject: delegateObject, additionalData: additionalData) - } - - @objc func updateView(_ size: CGFloat) { - MVMCoreDispatchUtility.performBlock(onMainThread: { - self.descriptionLabel.updateView(size) - if self.checkMark.responds(to: #selector(updateView(_:))) { - let width = self.sizeObject.getValueBased(onSize: size) - self.checkboxWidth.constant = width - self.checkboxHeight.constant = width - self.checkMark.updateView(size) - } - }) - } - - func setCheckMarkLayer() { - checkedSquare.layer.cornerRadius = isRoundRectCheckMark ? 5.0 : 0 - } - - //-------------------------------------------------- - // MARK: - Molecular - //-------------------------------------------------- - - override func updateView(_ size: CGFloat) { - - } - - override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - - } -} diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift new file mode 100644 index 00000000..08f6227f --- /dev/null +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -0,0 +1,288 @@ +// +// Checkbox.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 9/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import MVMCore + + +@objcMembers open class Checkbox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static let defaultHeightWidth: CGFloat = 18.0 + /* + //Offsets based on the 124x124 example checkmark + let startXOffset: Float = 42.0 / 124.0 + let startYOffset: Float = 66.0 / 124.0 + let pivotXOffset: Float = 58.0 / 124.0 + let pivotYOffset: Float = 80.0 / 124.0 + let endXOffset: Float = 83.0 / 124.0 + let endYOffset: Float = 46.0 / 124.0 + let pivotPercentage: Float = 0.34 + let endPercentage = 1.0 - pivotPercentage + let animationInterval: Float = 0.01 + */ + + public var checkedColor: UIColor = .black + public var unCheckedColor: UIColor = .mfPaleGrey() + + public var hasRoundBorder = false + + private var shapeLayer: CAShapeLayer? + private var _lineColor: UIColor = .black + private var _borderColor: UIColor = .black + private var _lineWidth: CGFloat = 1.0 + private var _cornerRadius: CGFloat = 5.0 + + public func setCheckMarkLayer() { + checkedSquare.layer.cornerRadius = checkbox.isRoundRectCheckMark ? 5.0 : 0 + } + + public var lineWidth: CGFloat { + get { return _lineWidth } + set (newLineWidth) { + _lineWidth = newLineWidth + if shapeLayer != nil { + shapeLayer?.removeFromSuperlayer() + shapeLayer = nil + updateCheckSelected(isSelected, animated: false) + } + } + } + + public var lineColor: UIColor { + get { return _lineColor } + set (newLineColor) { + _lineColor = newLineColor + if let shapeLayer = shapeLayer { + shapeLayer.strokeColor = lineColor.cgColor + updateCheckSelected(isSelected, animated: false) + } + } + } + + public var borderColor: UIColor { + get { return _borderColor } + set (newBorderColor) { + _borderColor = newBorderColor + if let shapeLayer = shapeLayer { + shapeLayer.strokeColor = borderColor.cgColor + updateCheckSelected(isSelected, animated: false) + } + } + } + + override open var isSelected: Bool { + didSet { + if isSelected { + layer.addSublayer(shapeLayer!) + shapeLayer?.strokeEnd = 1 + shapeLayer?.removeAllAnimations() + shapeLayer?.add(checkedAnimation, forKey: "strokeEnd") + } else { + shapeLayer?.strokeEnd = 0 + shapeLayer?.removeAllAnimations() + shapeLayer?.add(uncheckedAnimation, forKey: "strokeEnd") + } + } + } + + lazy private var checkedAnimation: CABasicAnimation = { + let check = CABasicAnimation(keyPath: "strokeEnd") + check.timingFunction = CAMediaTimingFunction(name: .linear) + check.fillMode = .both + check.duration = 0.33 + check.fromValue = 0 + check.toValue = 1 + return check + }() + + lazy private var uncheckedAnimation: CABasicAnimation = { + let unCheck = CABasicAnimation(keyPath: "strokeEnd") + unCheck.fillMode = .both + unCheck.duration = 0.33 + unCheck.fromValue = 0 + unCheck.toValue = 1 + return unCheck + }() + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + override public init(frame: CGRect) { + super.init(frame: frame) + setupView() + } + + public convenience init() { + self.init(frame:.zero) + } + + /// There is currently no intention on using xib files. + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + fatalError("xib file is not implemented for CheckBox.") + } + + public convenience init(checkedColor: UIColor, uncheckColor: UIColor, isChecked: Bool) { + self.init(frame: .zero) + isSelected = isChecked + // TODO: define the rest.... + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + override open func layoutSubviews() { + super.layoutSubviews() + drawCheck() + } + + public func setupView() { + + translatesAutoresizingMaskIntoConstraints = false + backgroundColor = .white + lineColor = .black + lineWidth = 1.0 + + let path = UIBezierPath() + path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) + path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) + path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) + + let shapeLayer = CAShapeLayer() + self.shapeLayer = shapeLayer + shapeLayer.frame = bounds + layer.addSublayer(shapeLayer) + shapeLayer.strokeColor = lineColor.cgColor + shapeLayer.fillColor = UIColor.clear.cgColor + shapeLayer.path = path.cgPath + shapeLayer.lineJoin = .bevel + shapeLayer.lineWidth = lineWidth + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + private func drawCheck() { + + if shapeLayer == nil { + + layoutIfNeeded() + + let path = UIBezierPath() + path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) + path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) + path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) + + shapeLayer = CAShapeLayer() + shapeLayer?.frame = bounds + layer.addSublayer(shapeLayer!) + shapeLayer?.strokeColor = lineColor.cgColor + shapeLayer?.fillColor = UIColor.clear.cgColor + shapeLayer?.path = path.cgPath + shapeLayer?.lineJoin = .bevel + shapeLayer?.lineWidth = lineWidth + + CATransaction.begin() + CATransaction.setDisableActions(true) + shapeLayer?.strokeEnd = 0.0 + CATransaction.commit() + } + } + + public func updateCheckSelected(_ selected: Bool, animated: Bool) { + + DispatchQueue.main.async { + + self.isSelected = selected + + self.drawCheck() + + var layer: CAShapeLayer? + if let presentation = self.shapeLayer?.presentation(), animated { + layer = presentation + } else { + layer = self.shapeLayer + } + + if animated { + let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") + animateStrokeEnd.fillMode = .both + animateStrokeEnd.isRemovedOnCompletion = false + animateStrokeEnd.duration = 0.33 + animateStrokeEnd.fromValue = layer?.strokeEnd ?? 0.0 + animateStrokeEnd.toValue = selected ? 1 : 0 + animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) + layer?.add(animateStrokeEnd, forKey: "strokeEndAnimation") + } else { + layer?.removeAllAnimations() + CATransaction.begin() + CATransaction.setDisableActions(true) + layer?.strokeEnd = selected ? 1 : 0 + CATransaction.commit() + } + } + } + + //-------------------------------------------------- + // MARK: - Molecular + //-------------------------------------------------- + + open func needsToBeConstrained() -> Bool { + return true + } + + open func reset() { + setupView() + } + + open func setAsMolecule() { + setupView() + } + + public func updateView(_ size: CGFloat) { + + } + + public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + + guard let dictionary = json else { return } + + if let borderColor = dictionary["borderColor"] as? String { + layer.borderColor = UIColor.mfGet(forHex: borderColor).cgColor + } + + if let checkColor = dictionary["lineColor"] as? String { + _lineColor = UIColor.mfGet(forHex: checkColor) + } + + if let checkColor = dictionary["checkedColor"] as? String { + checkedColor = UIColor.mfGet(forHex: checkColor) + } + + if let unCheckedColor = dictionary["unCheckedColor"] as? String { + uncheckedColor = UIColor.mfGet(forHex: unCheckedColor) + } + + if let backroundColor = dictionary["backroundColor"] as? String { + self.backgroundColor = UIColor.mfGet(forHex: backroundColor) + } + + if let borderWidth = dictionary["borderWidth"] as? CGFloat { + _lineWidth = borderWidth + } + + if let cornerRadius = dictionary["cornerRadius"] as? CGFloat { + _cornerRadius = cornerRadius + } + } +} diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift new file mode 100644 index 00000000..609937ee --- /dev/null +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -0,0 +1,289 @@ +// +// CheckboxWithLabelView.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 9/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + + +@objcMembers open class CheckboxWithLabelView: ViewConstrainingView { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + + let checkbox = Checkbox() + let label = Label.commonLabelB2(true) + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0) + + var checkboxWidthConstraint: NSLayoutConstraint? + var checkboxHeightConstraint: NSLayoutConstraint? + + + // Setter for the descriptionLabel.text. Also sets the accessibility text. + var labelText: String? + var labelAttributedText: NSAttributedString? + + // A block that is called when the switch is selected. + public var checkBoxAction: ((_ selected: Bool) -> ())? + + func setDescriptionAttributedText(_ descriptionAttributedText: NSAttributedString?) { + descriptionLabel?.text = nil + descriptionLabel?.attributedText = descriptionAttributedText + self.valueForAccessibilityText = descriptionAttributedText?.string + } + + var descriptionText: String { + get { return super.descriptionText } + set(descriptionText) { + descriptionLabel?.attributedText = nil + descriptionLabel.text = descriptionText + self.valueForAccessibilityText = descriptionText + } + } + + var isRequired = false + var fieldKey: String? + var delegate: DelegateObject? + + + //-------------------------------------------------- + // MARK: - Life Cycle + //-------------------------------------------------- + + override open func setupView() { + super.setupView() + + guard subviews.isEmpty else { return } + + translatesAutoresizingMaskIntoConstraints = false + + addSubview(checkbox) + + let dimension = sizeObject?.getValueBasedOnApplicationWidth() ?? Checkbox.defaultHeightWidth + + checkboxWidthConstraint = checkbox.heightAnchor.constraint(equalToConstant: dimension) + checkboxWidthConstraint?.isActive = true + checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension) + checkboxHeightConstraint?.isActive = true + + NSLayoutConstraint.constraintPinSubview(checkbox, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false) + checkbox.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + checkbox.lineWidth = 2.0 + addSubview(label) + + NSLayoutConstraint.constraintPinSubview(label, pinCenterX: false, pinCenterY: true) + NSLayoutConstraint.constraintPinSubview(label, pinTop: false, pinBottom: false, pinLeft: false, pinRight: true) + + label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + //default inits + required public init?(coder: NSCoder) { + super.init(coder: coder) + fatalError("xib file is not implemented for CheckboxWithLabelView") + } + + override init(frame: CGRect) { + super.init(frame: frame) + setupView() + addAccessibleProperties() + } + + convenience init() { + self.init(frame: .zero) + } + + init(checkedColor: UIColor?, unCheckedColor: UIColor?, text: String?) { + super.init(frame: .zero) + checkbox.checkedColor = checkedColor + checkbox.unCheckedColor = unCheckedColor + label.text = text + addAccessibleProperties() + } + + init(checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { + super.init(frame: .zero) + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) + descriptionAttributedText = attributedText + addAccessibleProperties() + } + + init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { + super.init(frame: .zero) + checkMark = checkMarkView + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: text) + addAccessibleProperties() + } + + init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { + super.init(frame: .zero) + checkMark = checkMarkView + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) + descriptionAttributedText = attributedText + addAccessibleProperties() + } + + init(roundRect isRoundRect: Bool) { + super.init(frame: .zero) + + isRoundRectCheckMark = isRoundRect + setup(withCheckedColor: .white, unCheck: .white, text: nil) + addAccessibleProperties() + setCheckMarkLayer() + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return CGFloat(CheckBoxHeightWidth) + } + + // MARK: - control methods + func setSelected(_ selected: Bool) { + setSelected(selected, animated: true) + checkbox?.is + } + + func setSelected(_ selected: Bool, animated: Bool) { + setSelected(selected, animated: animated, runBlock: true) + } + + func setSelected(_ selected: Bool, animated: Bool, runBlock: Bool) { + addAccessibilityLabel(selected) + + isSelected = selected + if (switchSelected != nil) && runBlock { + switchSelected(selected) + } + + if selected { + UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { + self.checkedSquare.backgroundColor = self.checkedColor + }) + checkMark.updateCheckSelected(true, animated: animated) + } else { + UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { + self.checkedSquare.backgroundColor = self.unCheckedColor + }) + checkMark?.updateCheckSelected(false, animated: animated) + } + + if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { + let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator + formValidator?.enableByValidation() + } + } + + //-------------------------------------------------- + // MARK: - UITouch + //-------------------------------------------------- + + func touchesEnded(_ touches: Set, with event: UIEvent) { + + if touchIsOutside(touches.first) { + sendActions(for: .touchUpOutside) + } else { + self.selected = !isSelected() + sendActions(for: .touchUpInside) + } + } + + func touchIsOutside(_ touch: UITouch?) -> Bool { + let endLocation = touch?.location(in: self) + let x = endLocation?.x ?? 0.0 + let y = endLocation?.y ?? 0.0 + let faultTolerance: CGFloat = 20.0 + let widthLimit = CGFloat(frame.size.width + faultTolerance) + let heightLimt = CGFloat(frame.size.height + faultTolerance) + + return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt + } +} + +// MARK: - Molecular +extension CheckboxWithLabelView { + + @objc override open func updateView(_ size: CGFloat) { + + DispatchQueue.main.async { + self.label.updateView(size) + if self.checkbox.responds(to: #selector(self.updateView(_:))) { + if let dimension = self.sizeObject?.getValueBased(onSize: size) { + self.checkboxWidthConstraint?.constant = dimension + self.checkboxHeightConstraint?.constant = dimension + self.checkbox.updateView(size) + } + } + } + } + + override open func alignment() -> UIStackView.Alignment { + return .leading + } + + override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + delegate = delegateObject + FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) + + guard let dictionary = json else { return } + + fieldKey = dictionary.string(for: KeyFieldKey) + isRequired = dictionary.bool(forKey: KeyRequired) + + checkbox.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + label.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + } +} + +// MARK:- FormValidationProtocol +extension CheckboxWithLabelView: FormValidationProtocol { + + public func isValidField() -> Bool { + return isRequired ? checkbox.isSelected : true + } + + public func formFieldName() -> String? { + return fieldKey + } + + public func formFieldValue() -> Any? { + return NSNumber(value: checkbox.isSelected) + } + + +} + +// MARK:- Accessibility +extension CheckboxWithLabelView { + + func addAccessibleProperties() { + accessibilityTraits = .none + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") + } + + func addAccessibilityLabel(_ selected: Bool) { + let state = selected ? MVMCoreUIUtility.hardcodedString(withKey: "checkbox_checked_state") : MVMCoreUIUtility.hardcodedString(withKey: "checkbox_unchecked_state") + let description = accessibilityText.length ? accessibilityText : "" + accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state"), description, state) + } + + func hideDescriptionLabelAndPinCheckboxToRight() { + + descriptionLabel?.hidden = true + checkboxRightPinConstraint.active = true + descriptionLabelLeadingConstraint.constant = 0 + } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 945eb7bf..aeaf6b8d 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -38,11 +38,11 @@ @"caretButton": CaretButton.class, @"textField" : MFTextField.class, @"digitTextField" : MFDigitTextField.class, - @"checkbox" : MVMCoreUICheckBox.class, + @"checkbox" : Checkbox.class, @"cornerLabels" : CornerLabels.class, @"progressBar": ProgressBar.class, @"multiProgressBar": MultiProgress.class, - @"checkbox": MVMCoreUICheckBox.class, + @"checkboxWithLabelView": CheckboxWithLabelView.class, @"listItem": MoleculeTableViewCell.class, @"accordionListItem": AccordionMoleculeTableViewCell.class, @"switch": MVMCoreUISwitch.class, From 38c74acc6e86acbd8ea4e3631af9d4c1cd2ca2b4 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 25 Sep 2019 14:43:29 -0400 Subject: [PATCH 043/131] moar. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 8 +++---- .../Atoms/Views/CheckboxWithLabelView.swift | 23 +++---------------- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 08f6227f..f29940f6 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -31,16 +31,16 @@ import MVMCore public var checkedColor: UIColor = .black public var unCheckedColor: UIColor = .mfPaleGrey() - public var hasRoundBorder = false + public var hasRoundCorners = false private var shapeLayer: CAShapeLayer? private var _lineColor: UIColor = .black private var _borderColor: UIColor = .black - private var _lineWidth: CGFloat = 1.0 - private var _cornerRadius: CGFloat = 5.0 + private var _lineWidth: CGFloat = 1 + private var _cornerRadius: CGFloat = 0 public func setCheckMarkLayer() { - checkedSquare.layer.cornerRadius = checkbox.isRoundRectCheckMark ? 5.0 : 0 + layer.cornerRadius = hasRoundCorners ? 5 : 0 } public var lineWidth: CGFloat { diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index 609937ee..62589b43 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -114,29 +114,14 @@ init(checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { super.init(frame: .zero) setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) - descriptionAttributedText = attributedText + setDescriptionAttributedText = attributedText addAccessibleProperties() } - init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { - super.init(frame: .zero) - checkMark = checkMarkView - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: text) - addAccessibleProperties() - } - - init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { - super.init(frame: .zero) - checkMark = checkMarkView - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) - descriptionAttributedText = attributedText - addAccessibleProperties() - } - - init(roundRect isRoundRect: Bool) { + init(hasRoundedCheckbox: Bool) { super.init(frame: .zero) - isRoundRectCheckMark = isRoundRect + checkbox.hasRoundBorder = hasRoundRectCheckbox setup(withCheckedColor: .white, unCheck: .white, text: nil) addAccessibleProperties() setCheckMarkLayer() @@ -262,8 +247,6 @@ extension CheckboxWithLabelView: FormValidationProtocol { public func formFieldValue() -> Any? { return NSNumber(value: checkbox.isSelected) } - - } // MARK:- Accessibility From c75337bfdf03461953defbc53b36be884e10471d Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 26 Sep 2019 11:42:16 -0400 Subject: [PATCH 044/131] Getting closer to testing phase. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 154 +++++++--------- .../Atoms/Views/CheckboxWithLabelView.swift | 164 +++++++----------- 2 files changed, 130 insertions(+), 188 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index f29940f6..4408ea6d 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -15,67 +15,27 @@ import MVMCore //-------------------------------------------------- public static let defaultHeightWidth: CGFloat = 18.0 - /* - //Offsets based on the 124x124 example checkmark - let startXOffset: Float = 42.0 / 124.0 - let startYOffset: Float = 66.0 / 124.0 - let pivotXOffset: Float = 58.0 / 124.0 - let pivotYOffset: Float = 80.0 / 124.0 - let endXOffset: Float = 83.0 / 124.0 - let endYOffset: Float = 46.0 / 124.0 - let pivotPercentage: Float = 0.34 - let endPercentage = 1.0 - pivotPercentage - let animationInterval: Float = 0.01 - */ + /// The color of the box and line when checked. public var checkedColor: UIColor = .black - public var unCheckedColor: UIColor = .mfPaleGrey() + /// The color of the border when unChecked. + public var unCheckedColor: UIColor = .black + + /// If true the border of this checkbox will be circular. public var hasRoundCorners = false + // Internal values to manage the appearance of the checkbox. private var shapeLayer: CAShapeLayer? - private var _lineColor: UIColor = .black - private var _borderColor: UIColor = .black - private var _lineWidth: CGFloat = 1 - private var _cornerRadius: CGFloat = 0 - public func setCheckMarkLayer() { - layer.cornerRadius = hasRoundCorners ? 5 : 0 + public var cornerRadiusValue: CGFloat { + return bounds.height / 2.0 } - public var lineWidth: CGFloat { - get { return _lineWidth } - set (newLineWidth) { - _lineWidth = newLineWidth - if shapeLayer != nil { - shapeLayer?.removeFromSuperlayer() - shapeLayer = nil - updateCheckSelected(isSelected, animated: false) - } - } - } - - public var lineColor: UIColor { - get { return _lineColor } - set (newLineColor) { - _lineColor = newLineColor - if let shapeLayer = shapeLayer { - shapeLayer.strokeColor = lineColor.cgColor - updateCheckSelected(isSelected, animated: false) - } - } - } - - public var borderColor: UIColor { - get { return _borderColor } - set (newBorderColor) { - _borderColor = newBorderColor - if let shapeLayer = shapeLayer { - shapeLayer.strokeColor = borderColor.cgColor - updateCheckSelected(isSelected, animated: false) - } - } - } + public var lineWidth: CGFloat = 1 + public var lineColor: UIColor = .black + public var borderColor: UIColor = .black + private var checkedBackgroundColor: UIColor = .white override open var isSelected: Bool { didSet { @@ -104,6 +64,7 @@ import MVMCore lazy private var uncheckedAnimation: CABasicAnimation = { let unCheck = CABasicAnimation(keyPath: "strokeEnd") + unCheck.timingFunction = CAMediaTimingFunction(name: .linear) unCheck.fillMode = .both unCheck.duration = 0.33 unCheck.fromValue = 0 @@ -120,20 +81,21 @@ import MVMCore setupView() } - public convenience init() { - self.init(frame:.zero) - } - /// There is currently no intention on using xib files. required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) fatalError("xib file is not implemented for CheckBox.") } - public convenience init(checkedColor: UIColor, uncheckColor: UIColor, isChecked: Bool) { + public convenience init() { + self.init(frame:.zero) + } + + public convenience init(checkedColor: UIColor, unCheckedColor: UIColor, isChecked: Bool = false) { self.init(frame: .zero) isSelected = isChecked - // TODO: define the rest.... + self.checkedColor = checkedColor + self.unCheckedColor = unCheckedColor } //-------------------------------------------------- @@ -149,8 +111,6 @@ import MVMCore translatesAutoresizingMaskIntoConstraints = false backgroundColor = .white - lineColor = .black - lineWidth = 1.0 let path = UIBezierPath() path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) @@ -172,8 +132,16 @@ import MVMCore // MARK: - Methods //-------------------------------------------------- - private func drawCheck() { + open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { + print("Action initiated") + } + open override func sendActions(for controlEvents: UIControl.Event) { + print("Actions Inititaled") + } + + private func drawCheck() { + if shapeLayer == nil { layoutIfNeeded() @@ -199,12 +167,27 @@ import MVMCore } } - public func updateCheckSelected(_ selected: Bool, animated: Bool) { + /* + //Offsets based on the 124x124 example checkmark + let startXOffset: Float = 42.0 / 124.0 + let startYOffset: Float = 66.0 / 124.0 + let pivotXOffset: Float = 58.0 / 124.0 + let pivotYOffset: Float = 80.0 / 124.0 + let endXOffset: Float = 83.0 / 124.0 + let endYOffset: Float = 46.0 / 124.0 + let pivotPercentage: Float = 0.34 + let endPercentage = 1.0 - pivotPercentage + let animationInterval: Float = 0.01 + */ + + public func updateCheckboxSelection(_ selected: Bool, animated: Bool) { + + shapeLayer?.removeFromSuperlayer() + shapeLayer = nil DispatchQueue.main.async { self.isSelected = selected - self.drawCheck() var layer: CAShapeLayer? @@ -227,12 +210,13 @@ import MVMCore layer?.removeAllAnimations() CATransaction.begin() CATransaction.setDisableActions(true) + CATransaction.setAnimationDuration(0) layer?.strokeEnd = selected ? 1 : 0 CATransaction.commit() } } } - + //-------------------------------------------------- // MARK: - Molecular //-------------------------------------------------- @@ -250,39 +234,35 @@ import MVMCore } public func updateView(_ size: CGFloat) { - + // TODO: Ensure the check logic is resized. } public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { guard let dictionary = json else { return } - if let borderColor = dictionary["borderColor"] as? String { - layer.borderColor = UIColor.mfGet(forHex: borderColor).cgColor + if let borderColorHex = dictionary["borderColor"] as? String { + layer.borderColor = UIColor.mfGet(forHex: borderColorHex).cgColor } - if let checkColor = dictionary["lineColor"] as? String { - _lineColor = UIColor.mfGet(forHex: checkColor) - } - - if let checkColor = dictionary["checkedColor"] as? String { - checkedColor = UIColor.mfGet(forHex: checkColor) - } - - if let unCheckedColor = dictionary["unCheckedColor"] as? String { - uncheckedColor = UIColor.mfGet(forHex: unCheckedColor) - } - - if let backroundColor = dictionary["backroundColor"] as? String { - self.backgroundColor = UIColor.mfGet(forHex: backroundColor) - } - if let borderWidth = dictionary["borderWidth"] as? CGFloat { - _lineWidth = borderWidth + layer.borderWidth = borderWidth } - - if let cornerRadius = dictionary["cornerRadius"] as? CGFloat { - _cornerRadius = cornerRadius + + if let checkColorHex = dictionary["lineColor"] as? String { + lineColor = UIColor.mfGet(forHex: checkColorHex) + } + + if let checkColorHex = dictionary["checkedColor"] as? String { + checkedColor = UIColor.mfGet(forHex: checkColorHex) + } + + if let unCheckedColorHex = dictionary["unCheckedColor"] as? String { + unCheckedColor = UIColor.mfGet(forHex: unCheckedColorHex) + } + + if let backroundColorHex = dictionary["backroundColor"] as? String { + backgroundColor = UIColor.mfGet(forHex: backroundColorHex) } } } diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index 62589b43..93f8a894 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -24,34 +24,13 @@ var checkboxWidthConstraint: NSLayoutConstraint? var checkboxHeightConstraint: NSLayoutConstraint? - - // Setter for the descriptionLabel.text. Also sets the accessibility text. - var labelText: String? - var labelAttributedText: NSAttributedString? - // A block that is called when the switch is selected. - public var checkBoxAction: ((_ selected: Bool) -> ())? - - func setDescriptionAttributedText(_ descriptionAttributedText: NSAttributedString?) { - descriptionLabel?.text = nil - descriptionLabel?.attributedText = descriptionAttributedText - self.valueForAccessibilityText = descriptionAttributedText?.string - } - - var descriptionText: String { - get { return super.descriptionText } - set(descriptionText) { - descriptionLabel?.attributedText = nil - descriptionLabel.text = descriptionText - self.valueForAccessibilityText = descriptionText - } - } + public var checkboxAction: ((_ selected: Bool) -> ())? var isRequired = false var fieldKey: String? var delegate: DelegateObject? - //-------------------------------------------------- // MARK: - Life Cycle //-------------------------------------------------- @@ -66,7 +45,6 @@ addSubview(checkbox) let dimension = sizeObject?.getValueBasedOnApplicationWidth() ?? Checkbox.defaultHeightWidth - checkboxWidthConstraint = checkbox.heightAnchor.constraint(equalToConstant: dimension) checkboxWidthConstraint?.isActive = true checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension) @@ -75,11 +53,10 @@ NSLayoutConstraint.constraintPinSubview(checkbox, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false) checkbox.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true checkbox.lineWidth = 2.0 + addSubview(label) - - NSLayoutConstraint.constraintPinSubview(label, pinCenterX: false, pinCenterY: true) - NSLayoutConstraint.constraintPinSubview(label, pinTop: false, pinBottom: false, pinLeft: false, pinRight: true) - + label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true } @@ -87,111 +64,99 @@ // MARK: - Initializers //-------------------------------------------------- - //default inits required public init?(coder: NSCoder) { super.init(coder: coder) fatalError("xib file is not implemented for CheckboxWithLabelView") } - override init(frame: CGRect) { + override public init(frame: CGRect) { super.init(frame: frame) setupView() - addAccessibleProperties() +// addAccessibleProperties() } - convenience init() { + public convenience init() { self.init(frame: .zero) } - init(checkedColor: UIColor?, unCheckedColor: UIColor?, text: String?) { - super.init(frame: .zero) + public convenience init(checkedColor: UIColor, unCheckedColor: UIColor, text: String?, isChecked: Bool = false) { + self.init(frame: .zero) + checkbox.checkedColor = checkedColor checkbox.unCheckedColor = unCheckedColor label.text = text - addAccessibleProperties() } - init(checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { - super.init(frame: .zero) - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) - setDescriptionAttributedText = attributedText - addAccessibleProperties() - } - - init(hasRoundedCheckbox: Bool) { - super.init(frame: .zero) + public convenience init(checkedColor: UIColor, unCheck unCheckedColor: UIColor, attributedText: NSAttributedString, isChecked: Bool = false) { + self.init(frame: .zero) - checkbox.hasRoundBorder = hasRoundRectCheckbox - setup(withCheckedColor: .white, unCheck: .white, text: nil) - addAccessibleProperties() - setCheckMarkLayer() + checkbox.checkedColor = checkedColor + checkbox.unCheckedColor = unCheckedColor + label.attributedText = attributedText + } + + public convenience init(isRoundedCheckbox: Bool) { + self.init(frame: .zero) + + checkbox.hasRoundCorners = isRoundedCheckbox } //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- - override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return CGFloat(CheckBoxHeightWidth) + override open class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return CGFloat(Checkbox.defaultHeightWidth) } - // MARK: - control methods - func setSelected(_ selected: Bool) { - setSelected(selected, animated: true) - checkbox?.is - } - - func setSelected(_ selected: Bool, animated: Bool) { - setSelected(selected, animated: animated, runBlock: true) - } - - func setSelected(_ selected: Bool, animated: Bool, runBlock: Bool) { - addAccessibilityLabel(selected) + @objc public func checkboxTapped(checkbox: Checkbox) { +// addAccessibilityLabel(selected) - isSelected = selected - if (switchSelected != nil) && runBlock { - switchSelected(selected) + checkbox.isSelected.toggle() + + if let checkboxAction = checkboxAction { + checkboxAction(checkbox.isSelected) } - if selected { - UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { - self.checkedSquare.backgroundColor = self.checkedColor - }) - checkMark.updateCheckSelected(true, animated: animated) - } else { - UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { - self.checkedSquare.backgroundColor = self.unCheckedColor - }) - checkMark?.updateCheckSelected(false, animated: animated) - } +// if checkbox.isSelected { +// UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { +// self.checkbox.backgroundColor = self.checkedColor +// }) +// checkbox.updateCheckSelected(true, animated: animated) +// } else { +// UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { +// self.checkbox.backgroundColor = self.unCheckedColor +// }) +// +// checkbox.updateCheckSelected(false, animated: animated) +// } - if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { - let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator - formValidator?.enableByValidation() - } + // if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { + // let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator + // formValidator?.enableByValidation() + // } } - + //-------------------------------------------------- // MARK: - UITouch //-------------------------------------------------- func touchesEnded(_ touches: Set, with event: UIEvent) { - if touchIsOutside(touches.first) { - sendActions(for: .touchUpOutside) + if touchIsAcceptablyOutside(touches.first) { + checkbox.sendActions(for: .touchUpOutside) } else { - self.selected = !isSelected() - sendActions(for: .touchUpInside) + checkbox.sendActions(for: .touchUpInside) } } - func touchIsOutside(_ touch: UITouch?) -> Bool { + func touchIsAcceptablyOutside(_ touch: UITouch?) -> Bool { let endLocation = touch?.location(in: self) let x = endLocation?.x ?? 0.0 let y = endLocation?.y ?? 0.0 let faultTolerance: CGFloat = 20.0 - let widthLimit = CGFloat(frame.size.width + faultTolerance) - let heightLimt = CGFloat(frame.size.height + faultTolerance) + let widthLimit = CGFloat(bounds.size.width + faultTolerance) + let heightLimt = CGFloat(bounds.size.height + faultTolerance) return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt } @@ -225,8 +190,13 @@ extension CheckboxWithLabelView { guard let dictionary = json else { return } - fieldKey = dictionary.string(for: KeyFieldKey) - isRequired = dictionary.bool(forKey: KeyRequired) + if let fieldKey = dictionary[KeyFieldKey] as? String { + self.fieldKey = fieldKey + } + + if let isRequired = dictionary[KeyRequired] as? Bool { + self.isRequired = isRequired + } checkbox.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) label.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) @@ -253,20 +223,12 @@ extension CheckboxWithLabelView: FormValidationProtocol { extension CheckboxWithLabelView { func addAccessibleProperties() { - accessibilityTraits = .none - accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") +// accessibilityTraits = .none +// accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") } func addAccessibilityLabel(_ selected: Bool) { - let state = selected ? MVMCoreUIUtility.hardcodedString(withKey: "checkbox_checked_state") : MVMCoreUIUtility.hardcodedString(withKey: "checkbox_unchecked_state") - let description = accessibilityText.length ? accessibilityText : "" - accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state"), description, state) - } - - func hideDescriptionLabelAndPinCheckboxToRight() { - - descriptionLabel?.hidden = true - checkboxRightPinConstraint.active = true - descriptionLabelLeadingConstraint.constant = 0 +// let state = selected ? MVMCoreUIUtility.hardcodedString(withKey: "checkbox_checked_state") : MVMCoreUIUtility.hardcodedString(withKey: "checkbox_unchecked_state") +// accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state"), label.text ?? "", state) } } From afcf8d535ba987f9ddfd87ab32acbb7e159749e1 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 26 Sep 2019 13:56:02 -0400 Subject: [PATCH 045/131] updates. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 4408ea6d..1ab976ef 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -29,13 +29,13 @@ import MVMCore private var shapeLayer: CAShapeLayer? public var cornerRadiusValue: CGFloat { - return bounds.height / 2.0 + return bounds.height / 2 } public var lineWidth: CGFloat = 1 public var lineColor: UIColor = .black public var borderColor: UIColor = .black - private var checkedBackgroundColor: UIColor = .white + public var checkedBackgroundColor: UIColor = .white override open var isSelected: Bool { didSet { @@ -129,7 +129,7 @@ import MVMCore } //-------------------------------------------------- - // MARK: - Methods + // MARK: - Action //-------------------------------------------------- open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { @@ -140,6 +140,10 @@ import MVMCore print("Actions Inititaled") } + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + private func drawCheck() { if shapeLayer == nil { @@ -169,6 +173,7 @@ import MVMCore /* //Offsets based on the 124x124 example checkmark + let startXOffset: Float = 42.0 / 124.0 let startYOffset: Float = 66.0 / 124.0 let pivotXOffset: Float = 58.0 / 124.0 @@ -202,7 +207,7 @@ import MVMCore animateStrokeEnd.fillMode = .both animateStrokeEnd.isRemovedOnCompletion = false animateStrokeEnd.duration = 0.33 - animateStrokeEnd.fromValue = layer?.strokeEnd ?? 0.0 + animateStrokeEnd.fromValue = layer?.strokeEnd ?? 0 animateStrokeEnd.toValue = selected ? 1 : 0 animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) layer?.add(animateStrokeEnd, forKey: "strokeEndAnimation") From f5f994648aca33f941fead0178d8ed4fed149c91 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 27 Sep 2019 13:43:02 -0400 Subject: [PATCH 046/131] Decent working state with animation and touch. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 270 ++++++++++++------ .../Atoms/Views/CheckboxWithLabelView.swift | 101 ++----- 2 files changed, 212 insertions(+), 159 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 1ab976ef..d00c2463 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -16,36 +16,54 @@ import MVMCore public static let defaultHeightWidth: CGFloat = 18.0 - /// The color of the box and line when checked. - public var checkedColor: UIColor = .black + /// The color of the background when checked. + public var checkedBackgroundColor: UIColor = .white - /// The color of the border when unChecked. - public var unCheckedColor: UIColor = .black + /// The color of the background when unChecked. + public var unCheckedBackgroundColor: UIColor = .black /// If true the border of this checkbox will be circular. public var hasRoundCorners = false + // Action Block called when the switch is selected. + public var actionBlock: ActionBlock? + // Internal values to manage the appearance of the checkbox. private var shapeLayer: CAShapeLayer? public var cornerRadiusValue: CGFloat { - return bounds.height / 2 + return bounds.size.height / 2 } - public var lineWidth: CGFloat = 1 + public var lineWidth: CGFloat = 2 public var lineColor: UIColor = .black - public var borderColor: UIColor = .black - public var checkedBackgroundColor: UIColor = .white + + open var borderColor: UIColor { + get { + guard let color = layer.borderColor else { return .black } + return UIColor(cgColor: color) + } + set (newColor) { + layer.borderColor = newColor.cgColor + } + } + open var borderWidth: CGFloat { + get { return layer.borderWidth } + set (newWidth) { + layer.borderWidth = newWidth + } + } + override open var isSelected: Bool { didSet { if isSelected { - layer.addSublayer(shapeLayer!) - shapeLayer?.strokeEnd = 1 +// layer.addSublayer(shapeLayer!) +// shapeLayer?.strokeEnd = 1 shapeLayer?.removeAllAnimations() shapeLayer?.add(checkedAnimation, forKey: "strokeEnd") } else { - shapeLayer?.strokeEnd = 0 +// shapeLayer?.strokeEnd = 0 shapeLayer?.removeAllAnimations() shapeLayer?.add(uncheckedAnimation, forKey: "strokeEnd") } @@ -55,8 +73,9 @@ import MVMCore lazy private var checkedAnimation: CABasicAnimation = { let check = CABasicAnimation(keyPath: "strokeEnd") check.timingFunction = CAMediaTimingFunction(name: .linear) + check.isRemovedOnCompletion = false check.fillMode = .both - check.duration = 0.33 + check.duration = 0.3 check.fromValue = 0 check.toValue = 1 return check @@ -65,10 +84,11 @@ import MVMCore lazy private var uncheckedAnimation: CABasicAnimation = { let unCheck = CABasicAnimation(keyPath: "strokeEnd") unCheck.timingFunction = CAMediaTimingFunction(name: .linear) + unCheck.isRemovedOnCompletion = false unCheck.fillMode = .both - unCheck.duration = 0.33 - unCheck.fromValue = 0 - unCheck.toValue = 1 + unCheck.duration = 0.3 + unCheck.fromValue = 1 + unCheck.toValue = 0 return unCheck }() @@ -84,7 +104,7 @@ import MVMCore /// There is currently no intention on using xib files. required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) - fatalError("xib file is not implemented for CheckBox.") + fatalError("xib file is not implemented for Checkbox.") } public convenience init() { @@ -94,8 +114,8 @@ import MVMCore public convenience init(checkedColor: UIColor, unCheckedColor: UIColor, isChecked: Bool = false) { self.init(frame: .zero) isSelected = isChecked - self.checkedColor = checkedColor - self.unCheckedColor = unCheckedColor + self.checkedBackgroundColor = checkedColor + self.unCheckedBackgroundColor = unCheckedColor } //-------------------------------------------------- @@ -109,35 +129,31 @@ import MVMCore public func setupView() { + isUserInteractionEnabled = true translatesAutoresizingMaskIntoConstraints = false backgroundColor = .white - - let path = UIBezierPath() - path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) - path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) - path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) - - let shapeLayer = CAShapeLayer() - self.shapeLayer = shapeLayer - shapeLayer.frame = bounds - layer.addSublayer(shapeLayer) - shapeLayer.strokeColor = lineColor.cgColor - shapeLayer.fillColor = UIColor.clear.cgColor - shapeLayer.path = path.cgPath - shapeLayer.lineJoin = .bevel - shapeLayer.lineWidth = lineWidth + layer.borderWidth = 1 + layer.borderColor = UIColor.black.cgColor } //-------------------------------------------------- - // MARK: - Action + // MARK: - Actions //-------------------------------------------------- open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { - print("Action initiated") + super.sendAction(action, to: target, for: event) + + isSelected.toggle() + actionBlock?() + drawCheck() } - + open override func sendActions(for controlEvents: UIControl.Event) { - print("Actions Inititaled") + super.sendActions(for: controlEvents) + + isSelected.toggle() + actionBlock?() + drawCheck() } //-------------------------------------------------- @@ -147,54 +163,62 @@ import MVMCore private func drawCheck() { if shapeLayer == nil { - - layoutIfNeeded() - - let path = UIBezierPath() - path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) - path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) - path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) - - shapeLayer = CAShapeLayer() - shapeLayer?.frame = bounds - layer.addSublayer(shapeLayer!) - shapeLayer?.strokeColor = lineColor.cgColor - shapeLayer?.fillColor = UIColor.clear.cgColor - shapeLayer?.path = path.cgPath - shapeLayer?.lineJoin = .bevel - shapeLayer?.lineWidth = lineWidth - - CATransaction.begin() - CATransaction.setDisableActions(true) - shapeLayer?.strokeEnd = 0.0 - CATransaction.commit() + + let shapeLayer = CAShapeLayer() + self.shapeLayer = shapeLayer + shapeLayer.frame = bounds + layer.addSublayer(shapeLayer) + shapeLayer.strokeColor = lineColor.cgColor + shapeLayer.fillColor = UIColor.clear.cgColor + shapeLayer.path = checkMarkBezierPath.cgPath + shapeLayer.lineJoin = .bevel + shapeLayer.lineWidth = lineWidth + + CATransaction.withDisabledAnimations { + shapeLayer.strokeEnd = 0.0 + } } } /* - //Offsets based on the 124x124 example checkmark - - let startXOffset: Float = 42.0 / 124.0 - let startYOffset: Float = 66.0 / 124.0 - let pivotXOffset: Float = 58.0 / 124.0 - let pivotYOffset: Float = 80.0 / 124.0 - let endXOffset: Float = 83.0 / 124.0 - let endYOffset: Float = 46.0 / 124.0 + // Offsets based on the 124x124 example checkmark + let startXOffset: Float = 42.0 / 124.0 ~~ 0.33871 + let startYOffset: Float = 66.0 / 124.0 ~~ 0.53225 + let pivotXOffset: Float = 58.0 / 124.0 ~~ 0.46774 + let pivotYOffset: Float = 80.0 / 124.0 ~~ 0.64516 + let endXOffset: Float = 83.0 / 124.0 ~~ 0.66935 + let endYOffset: Float = 46.0 / 124.0 ~~ 0.37097 let pivotPercentage: Float = 0.34 let endPercentage = 1.0 - pivotPercentage let animationInterval: Float = 0.01 */ - public func updateCheckboxSelection(_ selected: Bool, animated: Bool) { + /// Returns a UIBezierPath detailing the path of a checkmark + var checkMarkBezierPath: UIBezierPath { - shapeLayer?.removeFromSuperlayer() - shapeLayer = nil + let sideLength = bounds.size.height + let startPoint = CGPoint(x: 0.33871 * sideLength, y: 0.53225 * sideLength) + let pivotOffSet = CGPoint(x: 0.46774 * sideLength, y: 0.64516 * sideLength) + let endOffset = CGPoint(x: 0.66935 * sideLength , y: 0.37097 * sideLength) + + let path = UIBezierPath() + path.move(to: startPoint) + path.addLine(to: pivotOffSet) + path.addLine(to: endOffset) + + return path + } + + public func updateSelection(_ selected: Bool, animated: Bool) { + +// shapeLayer?.removeFromSuperlayer() +// shapeLayer = nil DispatchQueue.main.async { self.isSelected = selected self.drawCheck() - + var layer: CAShapeLayer? if let presentation = self.shapeLayer?.presentation(), animated { layer = presentation @@ -204,24 +228,93 @@ import MVMCore if animated { let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") + animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) + animateStrokeEnd.duration = 0.33 animateStrokeEnd.fillMode = .both animateStrokeEnd.isRemovedOnCompletion = false - animateStrokeEnd.duration = 0.33 animateStrokeEnd.fromValue = layer?.strokeEnd ?? 0 animateStrokeEnd.toValue = selected ? 1 : 0 - animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) layer?.add(animateStrokeEnd, forKey: "strokeEndAnimation") } else { layer?.removeAllAnimations() - CATransaction.begin() - CATransaction.setDisableActions(true) - CATransaction.setAnimationDuration(0) - layer?.strokeEnd = selected ? 1 : 0 - CATransaction.commit() + CATransaction.withDisabledAnimations { + layer?.strokeEnd = selected ? 1 : 0 + } } } } + //-------------------------------------------------- + // MARK: - UITouch + //-------------------------------------------------- + + open override func touchesEnded(_ touches: Set, with event: UIEvent?) { + + if touchIsAcceptablyOutside(touches.first) { + sendActions(for: .touchUpOutside) + } else { + sendActions(for: .touchUpInside) + } + } + + func touchIsAcceptablyOutside(_ touch: UITouch?) -> Bool { + let endLocation = touch?.location(in: self) + let x = endLocation?.x ?? 0.0 + let y = endLocation?.y ?? 0.0 + let faultTolerance: CGFloat = 20.0 + let widthLimit = CGFloat(bounds.size.width + faultTolerance) + let heightLimt = CGFloat(bounds.size.height + faultTolerance) + + return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt + } + + // if checkbox.isSelected { + // UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { + // self.checkbox.backgroundColor = self.checkedColor + // }) + // checkbox.updateCheckSelected(true, animated: animated) + // } else { + // UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { + // self.checkbox.backgroundColor = self.unCheckedColor + // }) + // + // checkbox.updateCheckSelected(false, animated: animated) + // } + + // if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { + // let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator + // formValidator?.enableByValidation() + // } + + + /* + + - (void)setSelected:(BOOL)selected animated:(BOOL)animated runBlock:(BOOL)runBlock { + [self addAccessibilityLabel:selected]; + + self.isSelected = selected; + if (self.switchSelected && runBlock) { + self.switchSelected(selected); + } + if (selected) { + [UIView animateWithDuration:0.2 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{ + self.checkedSquare.backgroundColor = self.checkedColor; + } completion:nil]; + [self.checkMark updateCheckSelected:YES animated:animated]; + } else { + [UIView animateWithDuration:0.2 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{ + self.checkedSquare.backgroundColor = self.unCheckedColor; + } completion:nil]; + [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]; + } + } + */ + //-------------------------------------------------- // MARK: - Molecular //-------------------------------------------------- @@ -239,6 +332,7 @@ import MVMCore } public func updateView(_ size: CGFloat) { + // TODO: Ensure the check logic is resized. } @@ -259,15 +353,27 @@ import MVMCore } if let checkColorHex = dictionary["checkedColor"] as? String { - checkedColor = UIColor.mfGet(forHex: checkColorHex) + checkedBackgroundColor = UIColor.mfGet(forHex: checkColorHex) } if let unCheckedColorHex = dictionary["unCheckedColor"] as? String { - unCheckedColor = UIColor.mfGet(forHex: unCheckedColorHex) + unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedColorHex) } - if let backroundColorHex = dictionary["backroundColor"] as? String { - backgroundColor = UIColor.mfGet(forHex: backroundColorHex) - } +// if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { +// actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } +// } + } +} + +// TODO: Move to its own extension file. +extension CATransaction { + + /// Performs changes without activating animation actions. + static func withDisabledAnimations(_ actionBlock: ActionBlock) { + CATransaction.begin() + CATransaction.setDisableActions(true) + actionBlock() + CATransaction.commit() } } diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index 93f8a894..94cd86e2 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -21,16 +21,17 @@ var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0) - var checkboxWidthConstraint: NSLayoutConstraint? - var checkboxHeightConstraint: NSLayoutConstraint? - - // A block that is called when the switch is selected. - public var checkboxAction: ((_ selected: Bool) -> ())? - var isRequired = false var fieldKey: String? var delegate: DelegateObject? + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + var checkboxWidthConstraint: NSLayoutConstraint? + var checkboxHeightConstraint: NSLayoutConstraint? + //-------------------------------------------------- // MARK: - Life Cycle //-------------------------------------------------- @@ -43,6 +44,7 @@ translatesAutoresizingMaskIntoConstraints = false addSubview(checkbox) + addSubview(label) let dimension = sizeObject?.getValueBasedOnApplicationWidth() ?? Checkbox.defaultHeightWidth checkboxWidthConstraint = checkbox.heightAnchor.constraint(equalToConstant: dimension) @@ -50,13 +52,15 @@ checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension) checkboxHeightConstraint?.isActive = true - NSLayoutConstraint.constraintPinSubview(checkbox, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false) + checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true + layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor).isActive = true + checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true checkbox.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - checkbox.lineWidth = 2.0 - addSubview(label) + label.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true + layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true + layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true } @@ -82,89 +86,32 @@ public convenience init(checkedColor: UIColor, unCheckedColor: UIColor, text: String?, isChecked: Bool = false) { self.init(frame: .zero) - checkbox.checkedColor = checkedColor - checkbox.unCheckedColor = unCheckedColor + checkbox.checkedBackgroundColor = checkedColor + checkbox.unCheckedBackgroundColor = unCheckedColor label.text = text } public convenience init(checkedColor: UIColor, unCheck unCheckedColor: UIColor, attributedText: NSAttributedString, isChecked: Bool = false) { self.init(frame: .zero) - checkbox.checkedColor = checkedColor - checkbox.unCheckedColor = unCheckedColor + checkbox.checkedBackgroundColor = checkedColor + checkbox.unCheckedBackgroundColor = unCheckedColor label.attributedText = attributedText } public convenience init(isRoundedCheckbox: Bool) { self.init(frame: .zero) - checkbox.hasRoundCorners = isRoundedCheckbox } - - //-------------------------------------------------- - // MARK: - Methods - //-------------------------------------------------- - - override open class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return CGFloat(Checkbox.defaultHeightWidth) - } - - @objc public func checkboxTapped(checkbox: Checkbox) { -// addAccessibilityLabel(selected) - - checkbox.isSelected.toggle() - - if let checkboxAction = checkboxAction { - checkboxAction(checkbox.isSelected) - } - -// if checkbox.isSelected { -// UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { -// self.checkbox.backgroundColor = self.checkedColor -// }) -// checkbox.updateCheckSelected(true, animated: animated) -// } else { -// UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { -// self.checkbox.backgroundColor = self.unCheckedColor -// }) -// -// checkbox.updateCheckSelected(false, animated: animated) -// } - - // if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { - // let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator - // formValidator?.enableByValidation() - // } - } - - //-------------------------------------------------- - // MARK: - UITouch - //-------------------------------------------------- - - func touchesEnded(_ touches: Set, with event: UIEvent) { - - if touchIsAcceptablyOutside(touches.first) { - checkbox.sendActions(for: .touchUpOutside) - } else { - checkbox.sendActions(for: .touchUpInside) - } - } - - func touchIsAcceptablyOutside(_ touch: UITouch?) -> Bool { - let endLocation = touch?.location(in: self) - let x = endLocation?.x ?? 0.0 - let y = endLocation?.y ?? 0.0 - let faultTolerance: CGFloat = 20.0 - let widthLimit = CGFloat(bounds.size.width + faultTolerance) - let heightLimt = CGFloat(bounds.size.height + faultTolerance) - - return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt - } } // MARK: - Molecular extension CheckboxWithLabelView { + override open class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return CGFloat(Checkbox.defaultHeightWidth) + } + @objc override open func updateView(_ size: CGFloat) { DispatchQueue.main.async { @@ -198,8 +145,8 @@ extension CheckboxWithLabelView { self.isRequired = isRequired } - checkbox.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - label.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + checkbox.setWithJSON(dictionary.dictionaryForKey("checkbox"), delegateObject: delegateObject, additionalData: additionalData) + label.setWithJSON(dictionary.dictionaryForKey("label"), delegateObject: delegateObject, additionalData: additionalData) } } From b032e40400833219e9aa800ca0d3eb24ea13aaeb Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 27 Sep 2019 14:23:46 -0400 Subject: [PATCH 047/131] Checkbox in good place. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 88 ++++++++++--------- .../Atoms/Views/CheckboxWithLabelView.swift | 6 +- 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index d00c2463..29ecc3c6 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -17,13 +17,17 @@ import MVMCore public static let defaultHeightWidth: CGFloat = 18.0 /// The color of the background when checked. - public var checkedBackgroundColor: UIColor = .white + public var checkedBackgroundColor: UIColor? - /// The color of the background when unChecked. - public var unCheckedBackgroundColor: UIColor = .black + /// The color of the background when unChecked. Will change of the view's background color. + public var unCheckedBackgroundColor: UIColor = .white { + didSet (newColor) { + backgroundColor = newColor + } + } /// If true the border of this checkbox will be circular. - public var hasRoundCorners = false + public var hasRoundedCorners = false // Action Block called when the switch is selected. public var actionBlock: ActionBlock? @@ -37,7 +41,7 @@ import MVMCore public var lineWidth: CGFloat = 2 public var lineColor: UIColor = .black - + open var borderColor: UIColor { get { guard let color = layer.borderColor else { return .black } @@ -54,17 +58,23 @@ import MVMCore layer.borderWidth = newWidth } } - + override open var isSelected: Bool { didSet { + shapeLayer?.removeAllAnimations() if isSelected { -// layer.addSublayer(shapeLayer!) -// shapeLayer?.strokeEnd = 1 - shapeLayer?.removeAllAnimations() + if checkedBackgroundColor != nil { + UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { + self.backgroundColor = self.checkedBackgroundColor + }) + } shapeLayer?.add(checkedAnimation, forKey: "strokeEnd") } else { -// shapeLayer?.strokeEnd = 0 - shapeLayer?.removeAllAnimations() + if checkedBackgroundColor != nil { + UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { + self.backgroundColor = self.unCheckedBackgroundColor + }) + } shapeLayer?.add(uncheckedAnimation, forKey: "strokeEnd") } } @@ -118,6 +128,11 @@ import MVMCore self.unCheckedBackgroundColor = unCheckedColor } + public convenience init(isCircular: Bool) { + self.init(frame: .zero) + hasRoundedCorners = isCircular + } + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -125,13 +140,14 @@ import MVMCore override open func layoutSubviews() { super.layoutSubviews() drawCheck() + layer.cornerRadius = hasRoundedCorners ? cornerRadiusValue : 0 + backgroundColor = unCheckedBackgroundColor ?? .white } public func setupView() { isUserInteractionEnabled = true translatesAutoresizingMaskIntoConstraints = false - backgroundColor = .white layer.borderWidth = 1 layer.borderColor = UIColor.black.cgColor } @@ -145,15 +161,13 @@ import MVMCore isSelected.toggle() actionBlock?() - drawCheck() } - + open override func sendActions(for controlEvents: UIControl.Event) { super.sendActions(for: controlEvents) isSelected.toggle() actionBlock?() - drawCheck() } //-------------------------------------------------- @@ -163,7 +177,7 @@ import MVMCore private func drawCheck() { if shapeLayer == nil { - + let shapeLayer = CAShapeLayer() self.shapeLayer = shapeLayer shapeLayer.frame = bounds @@ -173,7 +187,7 @@ import MVMCore shapeLayer.path = checkMarkBezierPath.cgPath shapeLayer.lineJoin = .bevel shapeLayer.lineWidth = lineWidth - + CATransaction.withDisabledAnimations { shapeLayer.strokeEnd = 0.0 } @@ -181,6 +195,9 @@ import MVMCore } /* + !!! -- DO NOT REMOVE -- !!! + (Unless Design changes the appearance of the checkmark). + // Offsets based on the 124x124 example checkmark let startXOffset: Float = 42.0 / 124.0 ~~ 0.33871 let startYOffset: Float = 66.0 / 124.0 ~~ 0.53225 @@ -208,17 +225,17 @@ import MVMCore return path } - + public func updateSelection(_ selected: Bool, animated: Bool) { -// shapeLayer?.removeFromSuperlayer() -// shapeLayer = nil + // shapeLayer?.removeFromSuperlayer() + // shapeLayer = nil DispatchQueue.main.async { self.isSelected = selected self.drawCheck() - + var layer: CAShapeLayer? if let presentation = self.shapeLayer?.presentation(), animated { layer = presentation @@ -268,19 +285,6 @@ import MVMCore return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt } - // if checkbox.isSelected { - // UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { - // self.checkbox.backgroundColor = self.checkedColor - // }) - // checkbox.updateCheckSelected(true, animated: animated) - // } else { - // UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { - // self.checkbox.backgroundColor = self.unCheckedColor - // }) - // - // checkbox.updateCheckSelected(false, animated: animated) - // } - // if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { // let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator // formValidator?.enableByValidation() @@ -288,7 +292,7 @@ import MVMCore /* - + - (void)setSelected:(BOOL)selected animated:(BOOL)animated runBlock:(BOOL)runBlock { [self addAccessibilityLabel:selected]; @@ -313,8 +317,8 @@ import MVMCore [formValidator enableByValidation]; } } - */ - + */ + //-------------------------------------------------- // MARK: - Molecular //-------------------------------------------------- @@ -360,9 +364,13 @@ import MVMCore unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedColorHex) } -// if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { -// actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } -// } + if let isRound = dictionary["isRound"] as? Bool { + hasRoundedCorners = isRound + } + + // if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { + // actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } + // } } } diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index 94cd86e2..db551800 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -56,6 +56,7 @@ layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor).isActive = true checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true checkbox.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + checkbox.checkedBackgroundColor = .yellow label.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true @@ -98,11 +99,6 @@ checkbox.unCheckedBackgroundColor = unCheckedColor label.attributedText = attributedText } - - public convenience init(isRoundedCheckbox: Bool) { - self.init(frame: .zero) - checkbox.hasRoundCorners = isRoundedCheckbox - } } // MARK: - Molecular From 5f8780614f881ce8b38946cdf1ef090365bfa86c Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 27 Sep 2019 14:28:16 -0400 Subject: [PATCH 048/131] borders. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 29 ++++++++-------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 29ecc3c6..d0599043 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -8,7 +8,9 @@ import MVMCore - +/** + This class expects its height and width to be equal. + */ @objcMembers open class Checkbox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { //-------------------------------------------------- // MARK: - Properties @@ -41,23 +43,8 @@ import MVMCore public var lineWidth: CGFloat = 2 public var lineColor: UIColor = .black - - open var borderColor: UIColor { - get { - guard let color = layer.borderColor else { return .black } - return UIColor(cgColor: color) - } - set (newColor) { - layer.borderColor = newColor.cgColor - } - } - - open var borderWidth: CGFloat { - get { return layer.borderWidth } - set (newWidth) { - layer.borderWidth = newWidth - } - } + public var borderColor: UIColor = .black + public var borderWidth: CGFloat = 1 override open var isSelected: Bool { didSet { @@ -141,15 +128,15 @@ import MVMCore super.layoutSubviews() drawCheck() layer.cornerRadius = hasRoundedCorners ? cornerRadiusValue : 0 - backgroundColor = unCheckedBackgroundColor ?? .white + backgroundColor = unCheckedBackgroundColor + layer.borderWidth = borderWidth + layer.borderColor = borderColor.cgColor } public func setupView() { isUserInteractionEnabled = true translatesAutoresizingMaskIntoConstraints = false - layer.borderWidth = 1 - layer.borderColor = UIColor.black.cgColor } //-------------------------------------------------- From fe7fb2db03d2fbc2a500b5ec600b755c1cc302cb Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Mon, 30 Sep 2019 22:01:11 +0530 Subject: [PATCH 049/131] 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 77a3eb882781d9814e1268d9f1ecce85152ee6d1 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 2 Oct 2019 15:22:12 -0400 Subject: [PATCH 050/131] Added a new extension file for CATransaction. Implementation of Checkbox and CheckboxWithLabel. --- MVMCoreUI.xcodeproj/project.pbxproj | 4 + MVMCoreUI/Atoms/Views/Checkbox.swift | 323 +++++++++--------- .../Atoms/Views/CheckboxWithLabelView.swift | 204 +++++++---- .../Utility/CATransaction+Extension.swift | 21 ++ 4 files changed, 321 insertions(+), 231 deletions(-) create mode 100644 MVMCoreUI/Utility/CATransaction+Extension.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 9692652c..253371bc 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 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 */; }; 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; 948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; }; B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E142280C4CF007245F4 /* ProgressBar.swift */; }; @@ -205,6 +206,7 @@ 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 = ""; }; 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 = ""; }; 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; 948DB67D2326DCD90011F916 /* MultiProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgress.swift; sourceTree = ""; }; @@ -654,6 +656,7 @@ D29DF2A021E7AF4E003B2FB9 /* MVMCoreUIUtility.m */, D29DF2A721E7B2F9003B2FB9 /* MVMCoreUIConstants.h */, D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */, + 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */, ); path = Utility; sourceTree = ""; @@ -1095,6 +1098,7 @@ D29DF25121E6A177003B2FB9 /* MFDigitTextBox.m in Sources */, DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */, 0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */, + 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */, D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */, D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */, 0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index d0599043..9fac4910 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -8,6 +8,22 @@ import MVMCore +/* + !!! -- DO NOT REMOVE -- !!! + (Unless Design changes the appearance of the checkmark). + + // Offsets based on the 124x124 example checkmark + let startXOffset: Float = 42.0 / 124.0 ~~ 0.33871 + let startYOffset: Float = 66.0 / 124.0 ~~ 0.53225 + let pivotXOffset: Float = 58.0 / 124.0 ~~ 0.46774 + let pivotYOffset: Float = 80.0 / 124.0 ~~ 0.64516 + let endXOffset: Float = 83.0 / 124.0 ~~ 0.66935 + let endYOffset: Float = 46.0 / 124.0 ~~ 0.37097 + let pivotPercentage: Float = 0.34 + let endPercentage = 1.0 - pivotPercentage + let animationInterval: Float = 0.01 + */ + /** This class expects its height and width to be equal. */ @@ -16,85 +32,82 @@ import MVMCore // MARK: - Properties //-------------------------------------------------- + // Form Validation + var isRequired = false + var fieldKey: String? + var delegateObject: DelegateObject? + public static let defaultHeightWidth: CGFloat = 18.0 - /// The color of the background when checked. - public var checkedBackgroundColor: UIColor? + /// If true the border of this checkbox will be circular. + public var isRound: Bool = false - /// The color of the background when unChecked. Will change of the view's background color. - public var unCheckedBackgroundColor: UIColor = .white { - didSet (newColor) { - backgroundColor = newColor + /// Determined if the checkbox's UI should animated when selected. + public var isAnimated: Bool = true + + /// Disables all selection logic when setting the value of isSelected, reducing it to a stored property. + public var updateSelectionOnly: Bool = false + + /// The color of the background when checked. + public var checkedBackgroundColor: UIColor = .white { + didSet { + if isSelected { + backgroundColor = checkedBackgroundColor + } } } - /// If true the border of this checkbox will be circular. - public var hasRoundedCorners = false - - // Action Block called when the switch is selected. - public var actionBlock: ActionBlock? - - // Internal values to manage the appearance of the checkbox. - private var shapeLayer: CAShapeLayer? + /// The color of the background when unChecked. + public var unCheckedBackgroundColor: UIColor = .white { + didSet { + if !isSelected { + backgroundColor = unCheckedBackgroundColor + } + } + } public var cornerRadiusValue: CGFloat { return bounds.size.height / 2 } - public var lineWidth: CGFloat = 2 - public var lineColor: UIColor = .black - public var borderColor: UIColor = .black + /// Action Block called when the switch is selected. + public var actionBlock: ActionBlock? + + /// Manages the appearance of the checkbox. + private var shapeLayer: CAShapeLayer? + + public var checkWidth: CGFloat = 2 + public var checkColor: UIColor = .black public var borderWidth: CGFloat = 1 + public var borderColor: UIColor = .black override open var isSelected: Bool { didSet { - shapeLayer?.removeAllAnimations() - if isSelected { - if checkedBackgroundColor != nil { - UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { - self.backgroundColor = self.checkedBackgroundColor - }) + if !updateSelectionOnly { + layoutIfNeeded() + shapeLayer?.removeAllAnimations() + + updateCheckboxUI(selection: isSelected, isAnimated: isAnimated) + + if let delegate = delegateObject as? FormValidationProtocol { + delegate.formValidatorModel?()?.enableByValidation() } - shapeLayer?.add(checkedAnimation, forKey: "strokeEnd") - } else { - if checkedBackgroundColor != nil { - UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { - self.backgroundColor = self.unCheckedBackgroundColor - }) - } - shapeLayer?.add(uncheckedAnimation, forKey: "strokeEnd") + + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") } } } - lazy private var checkedAnimation: CABasicAnimation = { - let check = CABasicAnimation(keyPath: "strokeEnd") - check.timingFunction = CAMediaTimingFunction(name: .linear) - check.isRemovedOnCompletion = false - check.fillMode = .both - check.duration = 0.3 - check.fromValue = 0 - check.toValue = 1 - return check - }() - - lazy private var uncheckedAnimation: CABasicAnimation = { - let unCheck = CABasicAnimation(keyPath: "strokeEnd") - unCheck.timingFunction = CAMediaTimingFunction(name: .linear) - unCheck.isRemovedOnCompletion = false - unCheck.fillMode = .both - unCheck.duration = 0.3 - unCheck.fromValue = 1 - unCheck.toValue = 0 - return unCheck - }() - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- override public init(frame: CGRect) { super.init(frame: frame) + + accessibilityTraits = .none + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") + setupView() } @@ -108,16 +121,20 @@ import MVMCore self.init(frame:.zero) } - public convenience init(checkedColor: UIColor, unCheckedColor: UIColor, isChecked: Bool = false) { + public convenience init(isChecked: Bool) { self.init(frame: .zero) + updateSelectionOnly = true isSelected = isChecked - self.checkedBackgroundColor = checkedColor - self.unCheckedBackgroundColor = unCheckedColor + updateSelectionOnly = false } - public convenience init(isCircular: Bool) { + public convenience init(checkedBackgroundColor: UIColor, unCheckedBackgroundColor: UIColor, isChecked: Bool = false) { self.init(frame: .zero) - hasRoundedCorners = isCircular + updateSelectionOnly = true + isSelected = isChecked + updateSelectionOnly = false + self.checkedBackgroundColor = checkedBackgroundColor + self.unCheckedBackgroundColor = unCheckedBackgroundColor } //-------------------------------------------------- @@ -126,9 +143,9 @@ import MVMCore override open func layoutSubviews() { super.layoutSubviews() + drawCheck() - layer.cornerRadius = hasRoundedCorners ? cornerRadiusValue : 0 - backgroundColor = unCheckedBackgroundColor + layer.cornerRadius = isRound ? cornerRadiusValue : 0 layer.borderWidth = borderWidth layer.borderColor = borderColor.cgColor } @@ -137,6 +154,7 @@ import MVMCore isUserInteractionEnabled = true translatesAutoresizingMaskIntoConstraints = false + backgroundColor = .white } //-------------------------------------------------- @@ -161,6 +179,7 @@ import MVMCore // MARK: - Methods //-------------------------------------------------- + /// Creates the check mark used for the checkbox. private func drawCheck() { if shapeLayer == nil { @@ -169,36 +188,20 @@ import MVMCore self.shapeLayer = shapeLayer shapeLayer.frame = bounds layer.addSublayer(shapeLayer) - shapeLayer.strokeColor = lineColor.cgColor + shapeLayer.strokeColor = checkColor.cgColor shapeLayer.fillColor = UIColor.clear.cgColor - shapeLayer.path = checkMarkBezierPath.cgPath + shapeLayer.path = checkMarkPath.cgPath shapeLayer.lineJoin = .bevel - shapeLayer.lineWidth = lineWidth + shapeLayer.lineWidth = checkWidth CATransaction.withDisabledAnimations { - shapeLayer.strokeEnd = 0.0 + shapeLayer.strokeEnd = isSelected ? 1 : 0 } } } - /* - !!! -- DO NOT REMOVE -- !!! - (Unless Design changes the appearance of the checkmark). - - // Offsets based on the 124x124 example checkmark - let startXOffset: Float = 42.0 / 124.0 ~~ 0.33871 - let startYOffset: Float = 66.0 / 124.0 ~~ 0.53225 - let pivotXOffset: Float = 58.0 / 124.0 ~~ 0.46774 - let pivotYOffset: Float = 80.0 / 124.0 ~~ 0.64516 - let endXOffset: Float = 83.0 / 124.0 ~~ 0.66935 - let endYOffset: Float = 46.0 / 124.0 ~~ 0.37097 - let pivotPercentage: Float = 0.34 - let endPercentage = 1.0 - pivotPercentage - let animationInterval: Float = 0.01 - */ - /// Returns a UIBezierPath detailing the path of a checkmark - var checkMarkBezierPath: UIBezierPath { + var checkMarkPath: UIBezierPath { let sideLength = bounds.size.height let startPoint = CGPoint(x: 0.33871 * sideLength, y: 0.53225 * sideLength) @@ -213,38 +216,43 @@ import MVMCore return path } - public func updateSelection(_ selected: Bool, animated: Bool) { - - // shapeLayer?.removeFromSuperlayer() - // shapeLayer = nil + /// Programmatic means to check/uncheck the box. + /// - parameter selected: state of the check box: true = checked OR false = unchecked. + /// - parameter animated: allows the state of the checkbox to change with or without animation. + public func updateSelection(to selected: Bool, animated: Bool) { DispatchQueue.main.async { + self.updateSelectionOnly = true self.isSelected = selected + self.updateSelectionOnly = false self.drawCheck() + self.shapeLayer?.removeAllAnimations() + self.updateCheckboxUI(selection: selected, isAnimated: animated) + } + } + + func updateCheckboxUI(selection: Bool, isAnimated: Bool) { + + if isAnimated { + let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") + animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) + animateStrokeEnd.duration = 0.3 + animateStrokeEnd.fillMode = .both + animateStrokeEnd.isRemovedOnCompletion = false + animateStrokeEnd.fromValue = !selection ? 1 : 0 + animateStrokeEnd.toValue = selection ? 1 : 0 + self.shapeLayer?.add(animateStrokeEnd, forKey: "strokeEnd") - var layer: CAShapeLayer? - if let presentation = self.shapeLayer?.presentation(), animated { - layer = presentation - } else { - layer = self.shapeLayer + UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { + self.backgroundColor = selection ? self.checkedBackgroundColor : self.unCheckedBackgroundColor + }) + } else { + CATransaction.withDisabledAnimations { + self.shapeLayer?.strokeEnd = selection ? 1 : 0 } - if animated { - let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") - animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) - animateStrokeEnd.duration = 0.33 - animateStrokeEnd.fillMode = .both - animateStrokeEnd.isRemovedOnCompletion = false - animateStrokeEnd.fromValue = layer?.strokeEnd ?? 0 - animateStrokeEnd.toValue = selected ? 1 : 0 - layer?.add(animateStrokeEnd, forKey: "strokeEndAnimation") - } else { - layer?.removeAllAnimations() - CATransaction.withDisabledAnimations { - layer?.strokeEnd = selected ? 1 : 0 - } - } + self.backgroundColor = selection ? self.checkedBackgroundColor : self.unCheckedBackgroundColor } } @@ -254,11 +262,7 @@ import MVMCore open override func touchesEnded(_ touches: Set, with event: UIEvent?) { - if touchIsAcceptablyOutside(touches.first) { - sendActions(for: .touchUpOutside) - } else { - sendActions(for: .touchUpInside) - } + sendActions(for: touchIsAcceptablyOutside(touches.first) ? .touchUpOutside : .touchUpInside) } func touchIsAcceptablyOutside(_ touch: UITouch?) -> Bool { @@ -272,40 +276,6 @@ import MVMCore return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt } - // if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { - // let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator - // formValidator?.enableByValidation() - // } - - - /* - - - (void)setSelected:(BOOL)selected animated:(BOOL)animated runBlock:(BOOL)runBlock { - [self addAccessibilityLabel:selected]; - - self.isSelected = selected; - if (self.switchSelected && runBlock) { - self.switchSelected(selected); - } - if (selected) { - [UIView animateWithDuration:0.2 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{ - self.checkedSquare.backgroundColor = self.checkedColor; - } completion:nil]; - [self.checkMark updateCheckSelected:YES animated:animated]; - } else { - [UIView animateWithDuration:0.2 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{ - self.checkedSquare.backgroundColor = self.unCheckedColor; - } completion:nil]; - [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]; - } - } - */ - //-------------------------------------------------- // MARK: - Molecular //-------------------------------------------------- @@ -322,15 +292,22 @@ import MVMCore setupView() } - public func updateView(_ size: CGFloat) { - - // TODO: Ensure the check logic is resized. - } + public func updateView(_ size: CGFloat) { } public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + self.delegateObject = delegateObject + FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) guard let dictionary = json else { return } + if let fieldKey = dictionary[KeyFieldKey] as? String { + self.fieldKey = fieldKey + } + + if let isRequired = dictionary[KeyRequired] as? Bool { + self.isRequired = isRequired + } + if let borderColorHex = dictionary["borderColor"] as? String { layer.borderColor = UIColor.mfGet(forHex: borderColorHex).cgColor } @@ -339,36 +316,50 @@ import MVMCore layer.borderWidth = borderWidth } - if let checkColorHex = dictionary["lineColor"] as? String { - lineColor = UIColor.mfGet(forHex: checkColorHex) + if let isChecked = dictionary["isChecked"] as? Bool, isChecked { + updateSelectionOnly = true + isSelected = isChecked + updateSelectionOnly = false } - if let checkColorHex = dictionary["checkedColor"] as? String { - checkedBackgroundColor = UIColor.mfGet(forHex: checkColorHex) + if let checkColorHex = dictionary["checkColor"] as? String { + checkColor = UIColor.mfGet(forHex: checkColorHex) } - if let unCheckedColorHex = dictionary["unCheckedColor"] as? String { - unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedColorHex) + if let unCheckedBackgroundColorHex = dictionary["unCheckedBackgroundColor"] as? String { + unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedBackgroundColorHex) + } + + if let checkedBackgroundColorHex = dictionary["checkedBackgroundColor"] as? String { + checkedBackgroundColor = UIColor.mfGet(forHex: checkedBackgroundColorHex) + } + + if let isAnimated = dictionary["isAnimated"] as? Bool { + self.isAnimated = isAnimated } if let isRound = dictionary["isRound"] as? Bool { - hasRoundedCorners = isRound + self.isRound = isRound } - // if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { - // actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } - // } + if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { + actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } + } } } -// TODO: Move to its own extension file. -extension CATransaction { +// MARK:- FormValidationProtocol +extension Checkbox: FormValidationProtocol { - /// Performs changes without activating animation actions. - static func withDisabledAnimations(_ actionBlock: ActionBlock) { - CATransaction.begin() - CATransaction.setDisableActions(true) - actionBlock() - CATransaction.commit() + public func isValidField() -> Bool { + return isRequired ? isSelected : true + } + + public func formFieldName() -> String? { + return fieldKey + } + + public func formFieldValue() -> Any? { + return NSNumber(value: isSelected) } } diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index db551800..bf0288ae 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -12,8 +12,8 @@ // MARK: - Outlets //-------------------------------------------------- - let checkbox = Checkbox() - let label = Label.commonLabelB2(true) + public let checkbox = Checkbox() + public let label = Label.commonLabelB2(true) //-------------------------------------------------- // MARK: - Properties @@ -21,17 +21,34 @@ var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0) - var isRequired = false - var fieldKey: String? - var delegate: DelegateObject? + var checkboxPosition: CheckboxPosition = .centerLeft + + public enum CheckboxPosition: String { + case centerLeft + case topLeft + case bottomLeft + } //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- - + var checkboxWidthConstraint: NSLayoutConstraint? var checkboxHeightConstraint: NSLayoutConstraint? + var checkboxLeadingConstraint: NSLayoutConstraint? + var checkboxTrailingConstraint: NSLayoutConstraint? + var checkboxTopConstraint: NSLayoutConstraint? + var checkboxBottomConstraint: NSLayoutConstraint? + var checkboxCenterYConstraint: NSLayoutConstraint? + + var checkboxLabelConstraint: NSLayoutConstraint? + + var labelLeadingConstraint: NSLayoutConstraint? + var labelTrailingConstraint: NSLayoutConstraint? + var labelTopConstraint: NSLayoutConstraint? + var labelBottomConstraint: NSLayoutConstraint? + //-------------------------------------------------- // MARK: - Life Cycle //-------------------------------------------------- @@ -46,23 +63,24 @@ addSubview(checkbox) addSubview(label) + label.setContentCompressionResistancePriority(.required, for: .vertical) + let dimension = sizeObject?.getValueBasedOnApplicationWidth() ?? Checkbox.defaultHeightWidth checkboxWidthConstraint = checkbox.heightAnchor.constraint(equalToConstant: dimension) checkboxWidthConstraint?.isActive = true checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension) checkboxHeightConstraint?.isActive = true - checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true - layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor).isActive = true - checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true - checkbox.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - checkbox.checkedBackgroundColor = .yellow + alignSubviews(by: .centerLeft) - label.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true - layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true - label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true - label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true + label.isUserInteractionEnabled = true + let tap = UITapGestureRecognizer(target: self, action: #selector(tapped)) + tap.numberOfTapsRequired = 1 + label.addGestureRecognizer(tap) + } + + @objc func tapped() { + checkbox.updateSelection(to: !checkbox.isSelected, animated: true) } //-------------------------------------------------- @@ -77,13 +95,18 @@ override public init(frame: CGRect) { super.init(frame: frame) setupView() -// addAccessibleProperties() } public convenience init() { self.init(frame: .zero) } + public convenience init(position: CheckboxPosition) { + self.init(frame: .zero) + + alignSubviews(by: position) + } + public convenience init(checkedColor: UIColor, unCheckedColor: UIColor, text: String?, isChecked: Bool = false) { self.init(frame: .zero) @@ -99,79 +122,130 @@ checkbox.unCheckedBackgroundColor = unCheckedColor label.attributedText = attributedText } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + /// Aligns Checkbox and Label relative to the desired position of the Checkbox. + private func alignSubviews(by position: CheckboxPosition) { + checkboxPosition = position + + label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true + + switch position { + case .centerLeft: + checkboxTopConstraint = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor) + checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor) + checkboxLeadingConstraint = checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) + checkboxCenterYConstraint = checkbox.centerYAnchor.constraint(equalTo: centerYAnchor) + + labelTopConstraint = label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) + labelTrailingConstraint = layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor) + checkboxLabelConstraint = label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo) + + case .topLeft: + checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) + checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor) + checkboxLeadingConstraint = checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) + + labelTopConstraint = label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) + labelTrailingConstraint = layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor) + checkboxLabelConstraint = label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo) + + case .bottomLeft: + checkboxTopConstraint = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor) + checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor) + checkboxLeadingConstraint = checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) + + labelTopConstraint = label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) + labelTrailingConstraint = layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor) + checkboxLabelConstraint = label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo) + labelBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor) + } + + if position != .bottomLeft { + layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true + let labelBottom = label.bottomAnchor.constraint(equalTo: bottomAnchor) + labelBottomConstraint = labelBottom + labelBottom.priority = UILayoutPriority(249) + labelBottom.isActive = true + } + + toggleSubviewConstraints(isActive: true) + } + + func toggleSubviewConstraints(isActive state: Bool) { + + checkboxLeadingConstraint?.isActive = state + checkboxTrailingConstraint?.isActive = state + checkboxTopConstraint?.isActive = state + checkboxBottomConstraint?.isActive = state + checkboxCenterYConstraint?.isActive = state + labelLeadingConstraint?.isActive = state + labelTrailingConstraint?.isActive = state + labelTopConstraint?.isActive = state + labelBottomConstraint?.isActive = state + checkboxLabelConstraint?.isActive = state + + if !state { + checkboxLeadingConstraint = nil + checkboxTrailingConstraint = nil + checkboxTopConstraint = nil + checkboxBottomConstraint = nil + checkboxCenterYConstraint = nil + labelLeadingConstraint = nil + labelTrailingConstraint = nil + labelTopConstraint = nil + labelBottomConstraint = nil + checkboxLabelConstraint = nil + } + } } -// MARK: - Molecular +/// MARK: - Molecular extension CheckboxWithLabelView { - override open class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return CGFloat(Checkbox.defaultHeightWidth) } @objc override open func updateView(_ size: CGFloat) { - DispatchQueue.main.async { - self.label.updateView(size) - if self.checkbox.responds(to: #selector(self.updateView(_:))) { - if let dimension = self.sizeObject?.getValueBased(onSize: size) { - self.checkboxWidthConstraint?.constant = dimension - self.checkboxHeightConstraint?.constant = dimension - self.checkbox.updateView(size) - } + label.updateView(size) + + if self.checkbox.responds(to: #selector(self.updateView(_:))) { + if let dimension = sizeObject?.getValueBased(onSize: size) { + checkboxWidthConstraint?.constant = dimension + checkboxHeightConstraint?.constant = dimension + checkbox.updateView(size) } } + + layoutIfNeeded() } override open func alignment() -> UIStackView.Alignment { return .leading } + open override func resetConstraints() { + super.resetConstraints() + + toggleSubviewConstraints(isActive: false) + } + override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - delegate = delegateObject - FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) guard let dictionary = json else { return } - if let fieldKey = dictionary[KeyFieldKey] as? String { - self.fieldKey = fieldKey - } - - if let isRequired = dictionary[KeyRequired] as? Bool { - self.isRequired = isRequired + if let checkboxAlignment = dictionary["checkboxAlignment"] as? String, let position = CheckboxPosition(rawValue: checkboxAlignment) { + toggleSubviewConstraints(isActive: false) + alignSubviews(by: position) } checkbox.setWithJSON(dictionary.dictionaryForKey("checkbox"), delegateObject: delegateObject, additionalData: additionalData) label.setWithJSON(dictionary.dictionaryForKey("label"), delegateObject: delegateObject, additionalData: additionalData) } } - -// MARK:- FormValidationProtocol -extension CheckboxWithLabelView: FormValidationProtocol { - - public func isValidField() -> Bool { - return isRequired ? checkbox.isSelected : true - } - - public func formFieldName() -> String? { - return fieldKey - } - - public func formFieldValue() -> Any? { - return NSNumber(value: checkbox.isSelected) - } -} - -// MARK:- Accessibility -extension CheckboxWithLabelView { - - func addAccessibleProperties() { -// accessibilityTraits = .none -// accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") - } - - func addAccessibilityLabel(_ selected: Bool) { -// let state = selected ? MVMCoreUIUtility.hardcodedString(withKey: "checkbox_checked_state") : MVMCoreUIUtility.hardcodedString(withKey: "checkbox_unchecked_state") -// accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state"), label.text ?? "", state) - } -} diff --git a/MVMCoreUI/Utility/CATransaction+Extension.swift b/MVMCoreUI/Utility/CATransaction+Extension.swift new file mode 100644 index 00000000..66bb33d3 --- /dev/null +++ b/MVMCoreUI/Utility/CATransaction+Extension.swift @@ -0,0 +1,21 @@ +// +// CATransaction+Extension.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 10/2/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + + +extension CATransaction { + + /// Performs changes without activating animation actions. + static func withDisabledAnimations(_ actionBlock: ActionBlock) { + CATransaction.begin() + CATransaction.setDisableActions(true) + actionBlock() + CATransaction.commit() + } +} From 61fcd981c1312d2c27de069210b4a43ba092ccb1 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 2 Oct 2019 15:27:32 -0400 Subject: [PATCH 051/131] setting up the rest. --- .../Atoms/Views/CheckboxWithLabelView.swift | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index bf0288ae..40b91f87 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -72,15 +72,6 @@ checkboxHeightConstraint?.isActive = true alignSubviews(by: .centerLeft) - - label.isUserInteractionEnabled = true - let tap = UITapGestureRecognizer(target: self, action: #selector(tapped)) - tap.numberOfTapsRequired = 1 - label.addGestureRecognizer(tap) - } - - @objc func tapped() { - checkbox.updateSelection(to: !checkbox.isSelected, animated: true) } //-------------------------------------------------- @@ -104,25 +95,10 @@ public convenience init(position: CheckboxPosition) { self.init(frame: .zero) + toggleSubviewConstraints(isActive: false) alignSubviews(by: position) } - public convenience init(checkedColor: UIColor, unCheckedColor: UIColor, text: String?, isChecked: Bool = false) { - self.init(frame: .zero) - - checkbox.checkedBackgroundColor = checkedColor - checkbox.unCheckedBackgroundColor = unCheckedColor - label.text = text - } - - public convenience init(checkedColor: UIColor, unCheck unCheckedColor: UIColor, attributedText: NSAttributedString, isChecked: Bool = false) { - self.init(frame: .zero) - - checkbox.checkedBackgroundColor = checkedColor - checkbox.unCheckedBackgroundColor = unCheckedColor - label.attributedText = attributedText - } - //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- @@ -211,6 +187,7 @@ extension CheckboxWithLabelView { } @objc override open func updateView(_ size: CGFloat) { + super.updateView(size) label.updateView(size) From 5d1c87819d23d89a7df4aa88404cf014e56b4c1a Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 3 Oct 2019 11:14:16 -0400 Subject: [PATCH 052/131] making changes to align with comments. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 31 ++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 9fac4910..fd1fa48a 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -76,8 +76,21 @@ import MVMCore /// Manages the appearance of the checkbox. private var shapeLayer: CAShapeLayer? - public var checkWidth: CGFloat = 2 - public var checkColor: UIColor = .black + public var checkWidth: CGFloat = 2 { + didSet { + CATransaction.withDisabledAnimations { + shapeLayer?.lineWidth = checkWidth + } + } + } + public var checkColor: UIColor = .black { + didSet { + CATransaction.withDisabledAnimations { + shapeLayer?.strokeColor = checkColor.cgColor + } + } + } + public var borderWidth: CGFloat = 1 public var borderColor: UIColor = .black @@ -93,7 +106,9 @@ import MVMCore delegate.formValidatorModel?()?.enableByValidation() } - accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") + if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") { + accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state) + } } } } @@ -105,8 +120,11 @@ import MVMCore override public init(frame: CGRect) { super.init(frame: frame) - accessibilityTraits = .none + accessibilityTraits = .button accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") + if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") { + accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state) + } setupView() } @@ -276,6 +294,11 @@ import MVMCore return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt } + override open func accessibilityActivate() -> Bool { + sendActions(for: .touchUpInside) + return true + } + //-------------------------------------------------- // MARK: - Molecular //-------------------------------------------------- From 44e420218db627b5de1e6eab3311b4a47624fdba Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 3 Oct 2019 13:22:15 -0400 Subject: [PATCH 053/131] Constraints mended. more commenting. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 79 ++++++++---- .../Atoms/Views/CheckboxWithLabelView.swift | 121 ++++++------------ 2 files changed, 90 insertions(+), 110 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index fd1fa48a..77a1f342 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -66,6 +66,7 @@ import MVMCore } } + /// Retrieves ideeal radius value to curve square into a circle. public var cornerRadiusValue: CGFloat { return bounds.size.height / 2 } @@ -76,6 +77,7 @@ import MVMCore /// Manages the appearance of the checkbox. private var shapeLayer: CAShapeLayer? + /// Width of the check mark. public var checkWidth: CGFloat = 2 { didSet { CATransaction.withDisabledAnimations { @@ -83,6 +85,8 @@ import MVMCore } } } + + /// Color of the check mark. public var checkColor: UIColor = .black { didSet { CATransaction.withDisabledAnimations { @@ -91,24 +95,35 @@ import MVMCore } } - public var borderWidth: CGFloat = 1 - public var borderColor: UIColor = .black + /// Border width of the checkbox + public var borderWidth: CGFloat = 1 { + didSet { + layer.borderWidth = borderWidth + } + } + /// border color of the Checkbox + public var borderColor: UIColor = .black { + didSet { + layer.borderColor = borderColor.cgColor + } + } + + /// The represented state of the Checkbox. + /// If updateSelectionOnly = true, then this will behave only as a stored property. override open var isSelected: Bool { didSet { if !updateSelectionOnly { layoutIfNeeded() shapeLayer?.removeAllAnimations() - updateCheckboxUI(selection: isSelected, isAnimated: isAnimated) + updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) if let delegate = delegateObject as? FormValidationProtocol { delegate.formValidatorModel?()?.enableByValidation() } - if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") { - accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state) - } + updateAccessibilityLabel() } } } @@ -122,9 +137,7 @@ import MVMCore accessibilityTraits = .button accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") - if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") { - accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state) - } + updateAccessibilityLabel() setupView() } @@ -181,13 +194,16 @@ import MVMCore open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { super.sendAction(action, to: target, for: event) - - isSelected.toggle() - actionBlock?() + toggleAndAction() } open override func sendActions(for controlEvents: UIControl.Event) { super.sendActions(for: controlEvents) + toggleAndAction() + } + + /// This will toggle the state of the Checkbox and execute the actionBlock if provided. + public func toggleAndAction() { isSelected.toggle() actionBlock?() @@ -208,7 +224,7 @@ import MVMCore layer.addSublayer(shapeLayer) shapeLayer.strokeColor = checkColor.cgColor shapeLayer.fillColor = UIColor.clear.cgColor - shapeLayer.path = checkMarkPath.cgPath + shapeLayer.path = checkMarkPath() shapeLayer.lineJoin = .bevel shapeLayer.lineWidth = checkWidth @@ -219,19 +235,19 @@ import MVMCore } /// Returns a UIBezierPath detailing the path of a checkmark - var checkMarkPath: UIBezierPath { + func checkMarkPath() -> CGPath { let sideLength = bounds.size.height let startPoint = CGPoint(x: 0.33871 * sideLength, y: 0.53225 * sideLength) let pivotOffSet = CGPoint(x: 0.46774 * sideLength, y: 0.64516 * sideLength) let endOffset = CGPoint(x: 0.66935 * sideLength , y: 0.37097 * sideLength) - let path = UIBezierPath() - path.move(to: startPoint) - path.addLine(to: pivotOffSet) - path.addLine(to: endOffset) + let bezierPath = UIBezierPath() + bezierPath.move(to: startPoint) + bezierPath.addLine(to: pivotOffSet) + bezierPath.addLine(to: endOffset) - return path + return bezierPath.cgPath } /// Programmatic means to check/uncheck the box. @@ -246,11 +262,14 @@ import MVMCore self.updateSelectionOnly = false self.drawCheck() self.shapeLayer?.removeAllAnimations() - self.updateCheckboxUI(selection: selected, isAnimated: animated) + self.updateCheckboxUI(isSelected: selected, isAnimated: animated) } } - func updateCheckboxUI(selection: Bool, isAnimated: Bool) { + /// updates the visuals of the check mark and background. + /// - parameter isSelection: the check state of the checkbox. + /// - parameter isAnimated: determines of the changes should animate or immediately refelect. + public func updateCheckboxUI(isSelected: Bool, isAnimated: Bool) { if isAnimated { let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") @@ -258,19 +277,27 @@ import MVMCore animateStrokeEnd.duration = 0.3 animateStrokeEnd.fillMode = .both animateStrokeEnd.isRemovedOnCompletion = false - animateStrokeEnd.fromValue = !selection ? 1 : 0 - animateStrokeEnd.toValue = selection ? 1 : 0 + animateStrokeEnd.fromValue = !isSelected ? 1 : 0 + animateStrokeEnd.toValue = isSelected ? 1 : 0 self.shapeLayer?.add(animateStrokeEnd, forKey: "strokeEnd") UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { - self.backgroundColor = selection ? self.checkedBackgroundColor : self.unCheckedBackgroundColor + self.backgroundColor = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor }) } else { CATransaction.withDisabledAnimations { - self.shapeLayer?.strokeEnd = selection ? 1 : 0 + self.shapeLayer?.strokeEnd = isSelected ? 1 : 0 } - self.backgroundColor = selection ? self.checkedBackgroundColor : self.unCheckedBackgroundColor + self.backgroundColor = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor + } + } + + /// Adjust accessibility label based on state of Checkbox. + func updateAccessibilityLabel() { + // Attention: This needs to be addressed with the accessibility team. + if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") { + accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state) } } diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index 40b91f87..e9aa0d0e 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -21,12 +21,12 @@ var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0) - var checkboxPosition: CheckboxPosition = .centerLeft + var checkboxPosition: CheckboxPosition = .center public enum CheckboxPosition: String { - case centerLeft - case topLeft - case bottomLeft + case center + case top + case bottom } //-------------------------------------------------- @@ -35,20 +35,10 @@ var checkboxWidthConstraint: NSLayoutConstraint? var checkboxHeightConstraint: NSLayoutConstraint? - - var checkboxLeadingConstraint: NSLayoutConstraint? - var checkboxTrailingConstraint: NSLayoutConstraint? var checkboxTopConstraint: NSLayoutConstraint? var checkboxBottomConstraint: NSLayoutConstraint? var checkboxCenterYConstraint: NSLayoutConstraint? - var checkboxLabelConstraint: NSLayoutConstraint? - - var labelLeadingConstraint: NSLayoutConstraint? - var labelTrailingConstraint: NSLayoutConstraint? - var labelTopConstraint: NSLayoutConstraint? - var labelBottomConstraint: NSLayoutConstraint? - //-------------------------------------------------- // MARK: - Life Cycle //-------------------------------------------------- @@ -71,7 +61,27 @@ checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension) checkboxHeightConstraint?.isActive = true - alignSubviews(by: .centerLeft) + checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true + + let generalTop = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor) + generalTop.priority = UILayoutPriority(rawValue: 750) + generalTop.isActive = true + + let generalBottom = checkbox.bottomAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.bottomAnchor) + generalBottom.priority = UILayoutPriority(rawValue: 750) + generalBottom.isActive = true + + // Allows various positions of checkbox. + checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor) + checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) + checkboxCenterYConstraint = checkbox.centerYAnchor.constraint(equalTo: centerYAnchor) + + label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true + layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true + label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true + layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true + + alignSubviews(by: .center) } //-------------------------------------------------- @@ -95,7 +105,6 @@ public convenience init(position: CheckboxPosition) { self.init(frame: .zero) - toggleSubviewConstraints(isActive: false) alignSubviews(by: position) } @@ -107,74 +116,21 @@ private func alignSubviews(by position: CheckboxPosition) { checkboxPosition = position - label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true - switch position { - case .centerLeft: - checkboxTopConstraint = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor) - checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor) - checkboxLeadingConstraint = checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) - checkboxCenterYConstraint = checkbox.centerYAnchor.constraint(equalTo: centerYAnchor) + case .center: + checkboxCenterYConstraint?.isActive = true + checkboxBottomConstraint?.isActive = false + checkboxTopConstraint?.isActive = false - labelTopConstraint = label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) - labelTrailingConstraint = layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor) - checkboxLabelConstraint = label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo) + case .top: + checkboxBottomConstraint?.isActive = false + checkboxTopConstraint?.isActive = true + checkboxCenterYConstraint?.isActive = false - case .topLeft: - checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) - checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor) - checkboxLeadingConstraint = checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) - - labelTopConstraint = label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) - labelTrailingConstraint = layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor) - checkboxLabelConstraint = label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo) - - case .bottomLeft: - checkboxTopConstraint = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor) - checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor) - checkboxLeadingConstraint = checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) - - labelTopConstraint = label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) - labelTrailingConstraint = layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor) - checkboxLabelConstraint = label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo) - labelBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor) - } - - if position != .bottomLeft { - layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true - let labelBottom = label.bottomAnchor.constraint(equalTo: bottomAnchor) - labelBottomConstraint = labelBottom - labelBottom.priority = UILayoutPriority(249) - labelBottom.isActive = true - } - - toggleSubviewConstraints(isActive: true) - } - - func toggleSubviewConstraints(isActive state: Bool) { - - checkboxLeadingConstraint?.isActive = state - checkboxTrailingConstraint?.isActive = state - checkboxTopConstraint?.isActive = state - checkboxBottomConstraint?.isActive = state - checkboxCenterYConstraint?.isActive = state - labelLeadingConstraint?.isActive = state - labelTrailingConstraint?.isActive = state - labelTopConstraint?.isActive = state - labelBottomConstraint?.isActive = state - checkboxLabelConstraint?.isActive = state - - if !state { - checkboxLeadingConstraint = nil - checkboxTrailingConstraint = nil - checkboxTopConstraint = nil - checkboxBottomConstraint = nil - checkboxCenterYConstraint = nil - labelLeadingConstraint = nil - labelTrailingConstraint = nil - labelTopConstraint = nil - labelBottomConstraint = nil - checkboxLabelConstraint = nil + case .bottom: + checkboxBottomConstraint?.isActive = true + checkboxTopConstraint?.isActive = false + checkboxCenterYConstraint?.isActive = false } } } @@ -208,8 +164,6 @@ extension CheckboxWithLabelView { open override func resetConstraints() { super.resetConstraints() - - toggleSubviewConstraints(isActive: false) } override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { @@ -218,7 +172,6 @@ extension CheckboxWithLabelView { guard let dictionary = json else { return } if let checkboxAlignment = dictionary["checkboxAlignment"] as? String, let position = CheckboxPosition(rawValue: checkboxAlignment) { - toggleSubviewConstraints(isActive: false) alignSubviews(by: position) } From 5043964c290074d31cc47c9043a8e36510358e3e Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 3 Oct 2019 16:43:58 -0400 Subject: [PATCH 054/131] revised alignment for case when no label. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 5 ++- .../Atoms/Views/CheckboxWithLabelView.swift | 35 ++++++++++++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 77a1f342..bf8407be 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -335,7 +335,10 @@ import MVMCore } open func reset() { - setupView() + + backgroundColor = nil + shapeLayer = nil + isSelected = false } open func setAsMolecule() { diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index e9aa0d0e..55339e61 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -53,7 +53,7 @@ addSubview(checkbox) addSubview(label) - label.setContentCompressionResistancePriority(.required, for: .vertical) + label.text = "" let dimension = sizeObject?.getValueBasedOnApplicationWidth() ?? Checkbox.defaultHeightWidth checkboxWidthConstraint = checkbox.heightAnchor.constraint(equalToConstant: dimension) @@ -64,13 +64,17 @@ checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true let generalTop = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor) - generalTop.priority = UILayoutPriority(rawValue: 750) + generalTop.priority = UILayoutPriority(800) generalTop.isActive = true let generalBottom = checkbox.bottomAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.bottomAnchor) - generalBottom.priority = UILayoutPriority(rawValue: 750) + generalBottom.priority = UILayoutPriority(800) generalBottom.isActive = true + let checboxBottom = checkbox.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor) + checboxBottom.priority = UILayoutPriority(249) + checboxBottom.isActive = true + // Allows various positions of checkbox. checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor) checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) @@ -79,9 +83,17 @@ label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true - layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true - alignSubviews(by: .center) + let bottomLabelConstraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor) + bottomLabelConstraint.priority = UILayoutPriority(500) + bottomLabelConstraint.isActive = true + + // layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true + // let labelBottom = label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor) + // labelBottom.priority = UILayoutPriority(249) + // labelBottom.isActive = true + + alignCheckbox(.center) } //-------------------------------------------------- @@ -105,7 +117,7 @@ public convenience init(position: CheckboxPosition) { self.init(frame: .zero) - alignSubviews(by: position) + alignCheckbox(position) } //-------------------------------------------------- @@ -113,7 +125,7 @@ //-------------------------------------------------- /// Aligns Checkbox and Label relative to the desired position of the Checkbox. - private func alignSubviews(by position: CheckboxPosition) { + private func alignCheckbox(_ position: CheckboxPosition) { checkboxPosition = position switch position { @@ -166,13 +178,20 @@ extension CheckboxWithLabelView { super.resetConstraints() } + open override func reset() { + super.reset() + + label.text = "" + checkbox.reset() + } + override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) guard let dictionary = json else { return } if let checkboxAlignment = dictionary["checkboxAlignment"] as? String, let position = CheckboxPosition(rawValue: checkboxAlignment) { - alignSubviews(by: position) + alignCheckbox(position) } checkbox.setWithJSON(dictionary.dictionaryForKey("checkbox"), delegateObject: delegateObject, additionalData: additionalData) From 1f815fa102f03e335e232834b776e2b7bc6f441f Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 7 Oct 2019 12:05:56 -0400 Subject: [PATCH 055/131] remove armv7 --- MVMCoreUI.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 253371bc..a4a3e406 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -1283,6 +1283,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 arm64e"; }; name = Debug; }; @@ -1312,6 +1313,7 @@ SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 arm64e"; }; name = Release; }; From 7d6289463d796eab35a8dd4cd606061f552deea9 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 7 Oct 2019 16:30:14 -0400 Subject: [PATCH 056/131] Improved molecular logic of checkbox. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 40 ++++++++++++++----- .../Atoms/Views/CheckboxWithLabelView.swift | 32 +++++++-------- .../MVMCoreUIMoleculeMappingObject.m | 2 +- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index bf8407be..6253b30d 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -80,8 +80,10 @@ import MVMCore /// Width of the check mark. public var checkWidth: CGFloat = 2 { didSet { - CATransaction.withDisabledAnimations { - shapeLayer?.lineWidth = checkWidth + if shapeLayer != nil { + CATransaction.withDisabledAnimations { + shapeLayer?.lineWidth = checkWidth + } } } } @@ -89,8 +91,10 @@ import MVMCore /// Color of the check mark. public var checkColor: UIColor = .black { didSet { - CATransaction.withDisabledAnimations { - shapeLayer?.strokeColor = checkColor.cgColor + if shapeLayer != nil { + CATransaction.withDisabledAnimations { + shapeLayer?.strokeColor = checkColor.cgColor + } } } } @@ -109,8 +113,11 @@ import MVMCore } } - /// The represented state of the Checkbox. - /// If updateSelectionOnly = true, then this will behave only as a stored property. + /** + The represented state of the Checkbox. + + Setting updateSelectionOnly to true bypasses the animation logic inherent with setting this property. + */ override open var isSelected: Bool { didSet { if !updateSelectionOnly { @@ -181,7 +188,7 @@ import MVMCore layer.borderColor = borderColor.cgColor } - public func setupView() { + open func setupView() { isUserInteractionEnabled = true translatesAutoresizingMaskIntoConstraints = false @@ -225,7 +232,7 @@ import MVMCore shapeLayer.strokeColor = checkColor.cgColor shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.path = checkMarkPath() - shapeLayer.lineJoin = .bevel + shapeLayer.lineJoin = .miter shapeLayer.lineWidth = checkWidth CATransaction.withDisabledAnimations { @@ -237,7 +244,7 @@ import MVMCore /// Returns a UIBezierPath detailing the path of a checkmark func checkMarkPath() -> CGPath { - let sideLength = bounds.size.height + let sideLength = max(bounds.size.height, bounds.size.width) let startPoint = CGPoint(x: 0.33871 * sideLength, y: 0.53225 * sideLength) let pivotOffSet = CGPoint(x: 0.46774 * sideLength, y: 0.64516 * sideLength) let endOffset = CGPoint(x: 0.66935 * sideLength , y: 0.37097 * sideLength) @@ -336,16 +343,27 @@ import MVMCore open func reset() { - backgroundColor = nil + shapeLayer?.removeAllAnimations() + shapeLayer?.removeFromSuperlayer() shapeLayer = nil + backgroundColor = nil + borderColor = .black + borderWidth = 1.0 + checkColor = .black + checkWidth = 2.0 + updateSelectionOnly = true isSelected = false + updateSelectionOnly = false } open func setAsMolecule() { setupView() } - public func updateView(_ size: CGFloat) { } + public func updateView(_ size: CGFloat) { + + layoutIfNeeded() + } public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { self.delegateObject = delegateObject diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index 55339e61..f148863b 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -38,6 +38,7 @@ var checkboxTopConstraint: NSLayoutConstraint? var checkboxBottomConstraint: NSLayoutConstraint? var checkboxCenterYConstraint: NSLayoutConstraint? + var centerLabelCheckboxConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Life Cycle @@ -62,14 +63,8 @@ checkboxHeightConstraint?.isActive = true checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true - - let generalTop = checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor) - generalTop.priority = UILayoutPriority(800) - generalTop.isActive = true - - let generalBottom = checkbox.bottomAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.bottomAnchor) - generalBottom.priority = UILayoutPriority(800) - generalBottom.isActive = true + checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true + layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor).isActive = true let checboxBottom = checkbox.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor) checboxBottom.priority = UILayoutPriority(249) @@ -79,20 +74,17 @@ checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor) checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) checkboxCenterYConstraint = checkbox.centerYAnchor.constraint(equalTo: centerYAnchor) + centerLabelCheckboxConstraint = label.centerYAnchor.constraint(equalTo: checkbox.centerYAnchor) label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true - let bottomLabelConstraint = layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor) - bottomLabelConstraint.priority = UILayoutPriority(500) + layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true + let bottomLabelConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor) + bottomLabelConstraint.priority = UILayoutPriority(249) bottomLabelConstraint.isActive = true - // layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true - // let labelBottom = label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor) - // labelBottom.priority = UILayoutPriority(249) - // labelBottom.isActive = true - alignCheckbox(.center) } @@ -130,19 +122,22 @@ switch position { case .center: - checkboxCenterYConstraint?.isActive = true checkboxBottomConstraint?.isActive = false checkboxTopConstraint?.isActive = false + checkboxCenterYConstraint?.isActive = true + centerLabelCheckboxConstraint?.isActive = true case .top: checkboxBottomConstraint?.isActive = false checkboxTopConstraint?.isActive = true checkboxCenterYConstraint?.isActive = false + centerLabelCheckboxConstraint?.isActive = false case .bottom: checkboxBottomConstraint?.isActive = true checkboxTopConstraint?.isActive = false checkboxCenterYConstraint?.isActive = false + centerLabelCheckboxConstraint?.isActive = false } } } @@ -176,6 +171,11 @@ extension CheckboxWithLabelView { open override func resetConstraints() { super.resetConstraints() + + checkboxCenterYConstraint?.isActive = false + checkboxBottomConstraint?.isActive = false + checkboxTopConstraint?.isActive = false + centerLabelCheckboxConstraint?.isActive = false } open override func reset() { diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index aeaf6b8d..64dbe2e6 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -39,10 +39,10 @@ @"textField" : MFTextField.class, @"digitTextField" : MFDigitTextField.class, @"checkbox" : Checkbox.class, + @"checkboxWithLabelView" : CheckboxWithLabelView.class, @"cornerLabels" : CornerLabels.class, @"progressBar": ProgressBar.class, @"multiProgressBar": MultiProgress.class, - @"checkboxWithLabelView": CheckboxWithLabelView.class, @"listItem": MoleculeTableViewCell.class, @"accordionListItem": AccordionMoleculeTableViewCell.class, @"switch": MVMCoreUISwitch.class, From 66e45378f461e753f678ff853aca24de577fd659 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 7 Oct 2019 16:33:39 -0400 Subject: [PATCH 057/131] shapelayer . --- MVMCoreUI/Atoms/Views/Checkbox.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 6253b30d..6f308651 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -80,9 +80,9 @@ import MVMCore /// Width of the check mark. public var checkWidth: CGFloat = 2 { didSet { - if shapeLayer != nil { + if let shapeLayer = shapeLayer { CATransaction.withDisabledAnimations { - shapeLayer?.lineWidth = checkWidth + shapeLayer.lineWidth = checkWidth } } } @@ -91,9 +91,9 @@ import MVMCore /// Color of the check mark. public var checkColor: UIColor = .black { didSet { - if shapeLayer != nil { + if let shapeLayer = shapeLayer { CATransaction.withDisabledAnimations { - shapeLayer?.strokeColor = checkColor.cgColor + shapeLayer.strokeColor = checkColor.cgColor } } } From 1d93f0b4524378b7dee09f46628be35b2f071ee0 Mon Sep 17 00:00:00 2001 From: panxi Date: Mon, 7 Oct 2019 16:58:21 -0400 Subject: [PATCH 058/131] add new mvmanimation xc framework --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++- .../xcshareddata/xcschemes/MVMCoreUI.xcscheme | 67 +++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 MVMCoreUI.xcodeproj/xcshareddata/xcschemes/MVMCoreUI.xcscheme diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 253371bc..39b9b9f1 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ @@ -21,6 +21,7 @@ 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; }; 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; + 94620EDB234BDB4B006ED207 /* MVMAnimation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94620EDA234BDB4B006ED207 /* MVMAnimation.xcframework */; }; 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 */; }; @@ -153,7 +154,6 @@ D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF14721E68728003B2FB9 /* MFSizeThreshold.m */; }; D29DF2CE21E7C104003B2FB9 /* MFLoadingViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF2CC21E7C104003B2FB9 /* MFLoadingViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF2CD21E7C104003B2FB9 /* MFLoadingViewController.m */; }; - D29DF2D121E7C1C8003B2FB9 /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29DF2D021E7C1C8003B2FB9 /* MVMAnimationFramework.framework */; }; D29DF2E121E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF2E021E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF2EE21ECEADF003B2FB9 /* MFFonts.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF14D21E693AD003B2FB9 /* MFFonts.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF14C21E693AD003B2FB9 /* MFFonts.m */; }; @@ -209,6 +209,7 @@ 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 = ""; }; 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; + 94620EDA234BDB4B006ED207 /* MVMAnimation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMAnimation.xcframework; path = /Users/ryan/Documents/mvmrc_ios/mvmrc_ios/../SharedFrameworks/MVMAnimation.xcframework; 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 = ""; }; @@ -393,7 +394,7 @@ buildActionMask = 2147483647; files = ( D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */, - D29DF2D121E7C1C8003B2FB9 /* MVMAnimationFramework.framework in Frameworks */, + 94620EDB234BDB4B006ED207 /* MVMAnimation.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -539,6 +540,7 @@ isa = PBXGroup; children = ( D29DF2D021E7C1C8003B2FB9 /* MVMAnimationFramework.framework */, + 94620EDA234BDB4B006ED207 /* MVMAnimation.xcframework */, D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */, ); name = Frameworks; diff --git a/MVMCoreUI.xcodeproj/xcshareddata/xcschemes/MVMCoreUI.xcscheme b/MVMCoreUI.xcodeproj/xcshareddata/xcschemes/MVMCoreUI.xcscheme new file mode 100644 index 00000000..c686e971 --- /dev/null +++ b/MVMCoreUI.xcodeproj/xcshareddata/xcschemes/MVMCoreUI.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + From 473ec08b07745f196d6be058f91d0639695e1e0b Mon Sep 17 00:00:00 2001 From: panxi Date: Mon, 7 Oct 2019 17:02:06 -0400 Subject: [PATCH 059/131] remove testing scheme --- .../xcshareddata/xcschemes/MVMCoreUI.xcscheme | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 MVMCoreUI.xcodeproj/xcshareddata/xcschemes/MVMCoreUI.xcscheme diff --git a/MVMCoreUI.xcodeproj/xcshareddata/xcschemes/MVMCoreUI.xcscheme b/MVMCoreUI.xcodeproj/xcshareddata/xcschemes/MVMCoreUI.xcscheme deleted file mode 100644 index c686e971..00000000 --- a/MVMCoreUI.xcodeproj/xcshareddata/xcschemes/MVMCoreUI.xcscheme +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - From bbdb2181f22e96db7ca96f9c8b1536158cc564a4 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 8 Oct 2019 11:30:25 -0400 Subject: [PATCH 060/131] aligning with invision. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 6f308651..f82d6bed 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -244,10 +244,17 @@ import MVMCore /// Returns a UIBezierPath detailing the path of a checkmark func checkMarkPath() -> CGPath { - let sideLength = max(bounds.size.height, bounds.size.width) - let startPoint = CGPoint(x: 0.33871 * sideLength, y: 0.53225 * sideLength) - let pivotOffSet = CGPoint(x: 0.46774 * sideLength, y: 0.64516 * sideLength) - let endOffset = CGPoint(x: 0.66935 * sideLength , y: 0.37097 * sideLength) + let length = max(bounds.size.height, bounds.size.width) + let xInsetLeft = length * 0.25 + let xInsetRight = length * 0.25 + let yInsetTop = length * 0.3 + let yInsetBottom = length * 0.35 + let innerWidth = length - (xInsetLeft + xInsetRight) + let innerHeight = length - (yInsetTop + yInsetBottom) + + let startPoint = CGPoint(x: xInsetLeft, y: yInsetTop + (innerHeight / 2)) + let pivotOffSet = CGPoint(x: xInsetLeft + (innerWidth * 0.33), y: yInsetTop + innerHeight) + let endOffset = CGPoint(x: xInsetLeft + innerWidth, y: yInsetTop) let bezierPath = UIBezierPath() bezierPath.move(to: startPoint) From ec190a28bdf137a1134762400fda080ebdd90328 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 8 Oct 2019 11:47:08 -0400 Subject: [PATCH 061/131] Further updates made. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 32 ++++++---------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index f82d6bed..187d9d7c 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -8,22 +8,6 @@ import MVMCore -/* - !!! -- DO NOT REMOVE -- !!! - (Unless Design changes the appearance of the checkmark). - - // Offsets based on the 124x124 example checkmark - let startXOffset: Float = 42.0 / 124.0 ~~ 0.33871 - let startYOffset: Float = 66.0 / 124.0 ~~ 0.53225 - let pivotXOffset: Float = 58.0 / 124.0 ~~ 0.46774 - let pivotYOffset: Float = 80.0 / 124.0 ~~ 0.64516 - let endXOffset: Float = 83.0 / 124.0 ~~ 0.66935 - let endYOffset: Float = 46.0 / 124.0 ~~ 0.37097 - let pivotPercentage: Float = 0.34 - let endPercentage = 1.0 - pivotPercentage - let animationInterval: Float = 0.01 - */ - /** This class expects its height and width to be equal. */ @@ -182,7 +166,7 @@ import MVMCore override open func layoutSubviews() { super.layoutSubviews() - drawCheck() + drawShapeLayer() layer.cornerRadius = isRound ? cornerRadiusValue : 0 layer.borderWidth = borderWidth layer.borderColor = borderColor.cgColor @@ -220,8 +204,8 @@ import MVMCore // MARK: - Methods //-------------------------------------------------- - /// Creates the check mark used for the checkbox. - private func drawCheck() { + /// Creates the check mark layer. + private func drawShapeLayer() { if shapeLayer == nil { @@ -241,16 +225,14 @@ import MVMCore } } - /// Returns a UIBezierPath detailing the path of a checkmark + /// - returns: The CGPath of a UIBezierPath detailing the path of a checkmark func checkMarkPath() -> CGPath { let length = max(bounds.size.height, bounds.size.width) let xInsetLeft = length * 0.25 - let xInsetRight = length * 0.25 let yInsetTop = length * 0.3 - let yInsetBottom = length * 0.35 - let innerWidth = length - (xInsetLeft + xInsetRight) - let innerHeight = length - (yInsetTop + yInsetBottom) + let innerWidth = length - (xInsetLeft + length * 0.25) // + Right X Inset + let innerHeight = length - (yInsetTop + length * 0.35) // + Bottom Y Inset let startPoint = CGPoint(x: xInsetLeft, y: yInsetTop + (innerHeight / 2)) let pivotOffSet = CGPoint(x: xInsetLeft + (innerWidth * 0.33), y: yInsetTop + innerHeight) @@ -274,7 +256,7 @@ import MVMCore self.updateSelectionOnly = true self.isSelected = selected self.updateSelectionOnly = false - self.drawCheck() + self.drawShapeLayer() self.shapeLayer?.removeAllAnimations() self.updateCheckboxUI(isSelected: selected, isAnimated: animated) } From 03f12b8a7a014d4a2533dcc440a9e4b9eff23c04 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 8 Oct 2019 13:15:10 -0400 Subject: [PATCH 062/131] added isEnabled func. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 36 ++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 187d9d7c..1f6a429a 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -62,7 +62,7 @@ import MVMCore private var shapeLayer: CAShapeLayer? /// Width of the check mark. - public var checkWidth: CGFloat = 2 { + public var checkWidth: CGFloat = 2.3 { didSet { if let shapeLayer = shapeLayer { CATransaction.withDisabledAnimations { @@ -75,11 +75,7 @@ import MVMCore /// Color of the check mark. public var checkColor: UIColor = .black { didSet { - if let shapeLayer = shapeLayer { - CATransaction.withDisabledAnimations { - shapeLayer.strokeColor = checkColor.cgColor - } - } + setshapeLayerStrokeColor(checkColor) } } @@ -285,7 +281,7 @@ import MVMCore self.shapeLayer?.strokeEnd = isSelected ? 1 : 0 } - self.backgroundColor = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor + backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor } } @@ -297,6 +293,32 @@ import MVMCore } } + func isEnabled(_ enabled: Bool) { + + isUserInteractionEnabled = enabled + + if enabled { + layer.borderColor = borderColor.cgColor + backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor + alpha = 1.0 + setshapeLayerStrokeColor(checkColor) + } else { + layer.borderColor = UIColor.mfSilver().cgColor + backgroundColor = .white + alpha = DisableOppacity + setshapeLayerStrokeColor(UIColor.mfSilver()) + } + } + + private func setshapeLayerStrokeColor(_ color: UIColor) { + + if let shapeLayer = shapeLayer { + CATransaction.withDisabledAnimations { + shapeLayer.strokeColor = color.cgColor + } + } + } + //-------------------------------------------------- // MARK: - UITouch //-------------------------------------------------- From cfdf44379d9b250a283c63b3ed4f99d5cdef77ec Mon Sep 17 00:00:00 2001 From: panxi Date: Tue, 8 Oct 2019 13:44:02 -0400 Subject: [PATCH 063/131] update framework relate to group --- MVMCoreUI.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 39b9b9f1..a5774155 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -209,7 +209,7 @@ 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 = ""; }; 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; - 94620EDA234BDB4B006ED207 /* MVMAnimation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMAnimation.xcframework; path = /Users/ryan/Documents/mvmrc_ios/mvmrc_ios/../SharedFrameworks/MVMAnimation.xcframework; sourceTree = ""; }; + 94620EDA234BDB4B006ED207 /* MVMAnimation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMAnimation.xcframework; path = ../SharedFrameworks/MVMAnimation.xcframework; 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 = ""; }; From 104f45d0b6a4d8d87c4a97fb50d453c758ab51ff Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 8 Oct 2019 14:11:31 -0400 Subject: [PATCH 064/131] default arch --- MVMCoreUI.xcodeproj/project.pbxproj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index a4a3e406..adb09309 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -1271,7 +1271,7 @@ FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../SharedFrameworks"; INFOPLIST_FILE = MVMCoreUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1283,7 +1283,6 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = "arm64 arm64e"; }; name = Debug; }; @@ -1302,7 +1301,7 @@ FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../SharedFrameworks"; INFOPLIST_FILE = MVMCoreUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1313,7 +1312,6 @@ SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = "arm64 arm64e"; }; name = Release; }; From 1618583acce693418af22bb1cd758875f9bb2b88 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 8 Oct 2019 15:19:12 -0400 Subject: [PATCH 065/131] corrected comment. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 1f6a429a..0d2c3e75 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -259,7 +259,7 @@ import MVMCore } /// updates the visuals of the check mark and background. - /// - parameter isSelection: the check state of the checkbox. + /// - parameter isSelected: the check state of the checkbox. /// - parameter isAnimated: determines of the changes should animate or immediately refelect. public func updateCheckboxUI(isSelected: Bool, isAnimated: Bool) { From 9922ef467422d6b007f9b6b6213eeb9a1855ed6c Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 8 Oct 2019 16:45:34 -0400 Subject: [PATCH 066/131] added enabled molecule logic. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 0d2c3e75..2ece7d4a 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -354,6 +354,7 @@ import MVMCore open func reset() { + isEnabled(true) shapeLayer?.removeAllAnimations() shapeLayer?.removeFromSuperlayer() shapeLayer = nil @@ -424,9 +425,15 @@ import MVMCore self.isRound = isRound } + if let enabled = dictionary["isEnabled"] as? Bool { + isEnabled(enabled) + } + if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } + +// layoutIfNeeded() } } From 3606ce4c8255dd1e0fe748a70ef38f495c431670 Mon Sep 17 00:00:00 2001 From: "Khan, Arshad" Date: Wed, 9 Oct 2019 13:53:10 +0530 Subject: [PATCH 067/131] 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 0f6a243e7bb5b78721c00f167b3ad45d033cfc21 Mon Sep 17 00:00:00 2001 From: panxi Date: Wed, 9 Oct 2019 10:10:57 -0400 Subject: [PATCH 068/131] change back to animaitonframework --- MVMCoreUI.xcodeproj/project.pbxproj | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index a5774155..3878406b 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -21,7 +21,7 @@ 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; }; 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; - 94620EDB234BDB4B006ED207 /* MVMAnimation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94620EDA234BDB4B006ED207 /* MVMAnimation.xcframework */; }; + 948B34AC234E1F400005B989 /* MVMAnimationFramework.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 948B34AB234E1F400005B989 /* MVMAnimationFramework.xcframework */; }; 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 */; }; @@ -209,7 +209,7 @@ 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 = ""; }; 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; - 94620EDA234BDB4B006ED207 /* MVMAnimation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMAnimation.xcframework; path = ../SharedFrameworks/MVMAnimation.xcframework; sourceTree = ""; }; + 948B34AB234E1F400005B989 /* MVMAnimationFramework.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMAnimationFramework.xcframework; path = ../SharedFrameworks/MVMAnimationFramework.xcframework; 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 = ""; }; @@ -346,7 +346,6 @@ D29DF2C321E7BF57003B2FB9 /* MFTabBarInteractor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFTabBarInteractor.m; sourceTree = ""; }; D29DF2CC21E7C104003B2FB9 /* MFLoadingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFLoadingViewController.h; sourceTree = ""; }; D29DF2CD21E7C104003B2FB9 /* MFLoadingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFLoadingViewController.m; sourceTree = ""; }; - D29DF2D021E7C1C8003B2FB9 /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = ""; }; D29DF2E021E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPanelProtocol.h; sourceTree = ""; }; D29DF31621ECECC0003B2FB9 /* NHaasGroteskDSStd-45Lt.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NHaasGroteskDSStd-45Lt.otf"; sourceTree = ""; }; D29DF31721ECECC0003B2FB9 /* OCRAExtended.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = OCRAExtended.ttf; sourceTree = ""; }; @@ -393,8 +392,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 948B34AC234E1F400005B989 /* MVMAnimationFramework.xcframework in Frameworks */, D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */, - 94620EDB234BDB4B006ED207 /* MVMAnimation.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -539,9 +538,8 @@ D29DF0E421E4F3C7003B2FB9 /* Frameworks */ = { isa = PBXGroup; children = ( - D29DF2D021E7C1C8003B2FB9 /* MVMAnimationFramework.framework */, - 94620EDA234BDB4B006ED207 /* MVMAnimation.xcframework */, D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */, + 948B34AB234E1F400005B989 /* MVMAnimationFramework.xcframework */, ); name = Frameworks; sourceTree = ""; From 960d9e0e7ca847191819cde67a49080c59530000 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 9 Oct 2019 10:18:18 -0400 Subject: [PATCH 069/131] removed comment. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 2ece7d4a..c43accb2 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -432,8 +432,6 @@ import MVMCore if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } - -// layoutIfNeeded() } } From 5ef9754e2cb8a362f098dd74941f6dc17caebed7 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 9 Oct 2019 12:45:18 -0400 Subject: [PATCH 070/131] defaulting to clear. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index c43accb2..c3879f96 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -33,7 +33,7 @@ import MVMCore public var updateSelectionOnly: Bool = false /// The color of the background when checked. - public var checkedBackgroundColor: UIColor = .white { + public var checkedBackgroundColor: UIColor = .clear { didSet { if isSelected { backgroundColor = checkedBackgroundColor @@ -42,7 +42,7 @@ import MVMCore } /// The color of the background when unChecked. - public var unCheckedBackgroundColor: UIColor = .white { + public var unCheckedBackgroundColor: UIColor = .clear { didSet { if !isSelected { backgroundColor = unCheckedBackgroundColor @@ -62,7 +62,7 @@ import MVMCore private var shapeLayer: CAShapeLayer? /// Width of the check mark. - public var checkWidth: CGFloat = 2.3 { + public var checkWidth: CGFloat = 2 { didSet { if let shapeLayer = shapeLayer { CATransaction.withDisabledAnimations { @@ -172,7 +172,7 @@ import MVMCore isUserInteractionEnabled = true translatesAutoresizingMaskIntoConstraints = false - backgroundColor = .white + backgroundColor = .clear } //-------------------------------------------------- @@ -304,7 +304,7 @@ import MVMCore setshapeLayerStrokeColor(checkColor) } else { layer.borderColor = UIColor.mfSilver().cgColor - backgroundColor = .white + backgroundColor = .clear alpha = DisableOppacity setshapeLayerStrokeColor(UIColor.mfSilver()) } @@ -358,7 +358,7 @@ import MVMCore shapeLayer?.removeAllAnimations() shapeLayer?.removeFromSuperlayer() shapeLayer = nil - backgroundColor = nil + backgroundColor = .clear borderColor = .black borderWidth = 1.0 checkColor = .black From f37ee3c9ba5f626bfafa3666f44c4475ef186001 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 9 Oct 2019 13:04:04 -0400 Subject: [PATCH 071/131] os version --- 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 3878406b..f505de3a 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -1185,7 +1185,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.1; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -1244,7 +1244,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.1; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -1271,7 +1271,7 @@ FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../SharedFrameworks"; INFOPLIST_FILE = MVMCoreUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1301,7 +1301,7 @@ FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../SharedFrameworks"; INFOPLIST_FILE = MVMCoreUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", From db583609ac6afb8fd983b37d051757d76477c998 Mon Sep 17 00:00:00 2001 From: panxi Date: Thu, 10 Oct 2019 06:49:31 -0400 Subject: [PATCH 072/131] change to 8.0 --- MVMCoreUI.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index f505de3a..47eed952 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -966,7 +966,7 @@ }; }; buildConfigurationList = D29DF0C621E404D4003B2FB9 /* Build configuration list for PBXProject "MVMCoreUI" */; - compatibilityVersion = "Xcode 9.3"; + compatibilityVersion = "Xcode 8.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( From 93733fb7b613b695e6b54de6ac796a85359cedc9 Mon Sep 17 00:00:00 2001 From: panxi Date: Thu, 10 Oct 2019 12:48:27 -0400 Subject: [PATCH 073/131] add framework back --- MVMCoreUI.xcodeproj/project.pbxproj | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 47eed952..11c20e13 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 48; objects = { /* Begin PBXBuildFile section */ @@ -21,7 +21,7 @@ 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; }; 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; - 948B34AC234E1F400005B989 /* MVMAnimationFramework.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 948B34AB234E1F400005B989 /* MVMAnimationFramework.xcframework */; }; + 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 */; }; @@ -209,7 +209,7 @@ 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 = ""; }; 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; - 948B34AB234E1F400005B989 /* MVMAnimationFramework.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMAnimationFramework.xcframework; path = ../SharedFrameworks/MVMAnimationFramework.xcframework; 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 = ""; }; @@ -392,8 +392,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 948B34AC234E1F400005B989 /* MVMAnimationFramework.xcframework in Frameworks */, D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */, + 9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -539,7 +539,7 @@ isa = PBXGroup; children = ( D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */, - 948B34AB234E1F400005B989 /* MVMAnimationFramework.xcframework */, + 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */, ); name = Frameworks; sourceTree = ""; @@ -962,6 +962,7 @@ D29DF0CB21E404D4003B2FB9 = { CreatedOnToolsVersion = 10.1; LastSwiftMigration = 1010; + ProvisioningStyle = Automatic; }; }; }; @@ -1248,8 +1249,7 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -1272,11 +1272,7 @@ INFOPLIST_FILE = MVMCoreUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCoreUI; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -1302,11 +1298,7 @@ INFOPLIST_FILE = MVMCoreUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCoreUI; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; From eaad6469bbb0c58c9ac7bb10e91d17838b6e0dd3 Mon Sep 17 00:00:00 2001 From: "Hedden, Kyle Matthew" Date: Fri, 11 Oct 2019 13:03:41 -0400 Subject: [PATCH 074/131] Move self.presentingViewController access onto the main thread. --- MVMCoreUI/BaseControllers/MFViewController.m | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/BaseControllers/MFViewController.m b/MVMCoreUI/BaseControllers/MFViewController.m index edbabf80..bc8208dd 100644 --- a/MVMCoreUI/BaseControllers/MFViewController.m +++ b/MVMCoreUI/BaseControllers/MFViewController.m @@ -67,11 +67,13 @@ } - (void)dismiss { - if (self.presentingViewController) { - [[MVMCoreNavigationHandler sharedNavigationHandler] dismissViewController:self animated:YES]; - } else if (self.navigationController) { - [[MVMCoreNavigationHandler sharedNavigationHandler] popViewController:self animated:YES]; - } + [MVMCoreDispatchUtility performBlockOnMainThread:^{ + if (self.presentingViewController) { + [[MVMCoreNavigationHandler sharedNavigationHandler] dismissViewController:self animated:YES]; + } else if (self.navigationController) { + [[MVMCoreNavigationHandler sharedNavigationHandler] popViewController:self animated:YES]; + } + }]; } - (BOOL)isVisibleViewController { From bb51fba0e59f95c99967597d6b237773fb8ecde5 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 17 Oct 2019 10:31:34 -0400 Subject: [PATCH 075/131] remove legacy os code --- MVMCoreUI/Atoms/TextFields/MFTextField.m | 8 ++-- MVMCoreUI/Atoms/Views/MFTextView.m | 9 ++--- MVMCoreUI/Atoms/Views/MFView.m | 4 +- .../MFProgrammaticTableViewController.m | 8 +--- .../MFScrollingViewController.m | 5 +-- .../ThreeLayerTableViewController.swift | 10 ++--- .../ThreeLayerViewController.swift | 14 +++---- ...MVMCoreUITabBarPageControlViewController.m | 10 ----- ...abelsAndBottomButtonsTableViewController.m | 20 ++++------ .../TopLabelsAndBottomButtonsViewController.m | 27 +++++--------- .../Items/MoleculeCollectionViewCell.swift | 8 ++-- .../Items/MoleculeTableViewCell.swift | 37 ++++++------------- .../Utility/MVMCoreUICommonViewsUtility.m | 25 ++++--------- MVMCoreUI/Utility/MVMCoreUIUtility.m | 21 +++-------- 14 files changed, 62 insertions(+), 144 deletions(-) diff --git a/MVMCoreUI/Atoms/TextFields/MFTextField.m b/MVMCoreUI/Atoms/TextFields/MFTextField.m index ba68c0ed..e4709844 100644 --- a/MVMCoreUI/Atoms/TextFields/MFTextField.m +++ b/MVMCoreUI/Atoms/TextFields/MFTextField.m @@ -83,11 +83,9 @@ self.enabled = YES; // Disable SmartQuotes - if (@available(iOS 11.0, *)) { - self.textField.smartQuotesType = UITextSmartQuotesTypeNo; - self.textField.smartDashesType = UITextSmartDashesTypeNo; - self.textField.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo; - } + self.textField.smartQuotesType = UITextSmartQuotesTypeNo; + self.textField.smartDashesType = UITextSmartDashesTypeNo; + self.textField.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo; } } diff --git a/MVMCoreUI/Atoms/Views/MFTextView.m b/MVMCoreUI/Atoms/Views/MFTextView.m index c120d964..424c8117 100644 --- a/MVMCoreUI/Atoms/Views/MFTextView.m +++ b/MVMCoreUI/Atoms/Views/MFTextView.m @@ -173,11 +173,10 @@ view.placeHolderLabel.textColor = [UIColor mfLightGrayColor]; // Disable SmartQuotes - if (@available(iOS 11.0, *)) { - view.textView.smartQuotesType = UITextSmartQuotesTypeNo; - view.textView.smartDashesType = UITextSmartDashesTypeNo; - view.textView.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo; - } + view.textView.smartQuotesType = UITextSmartQuotesTypeNo; + view.textView.smartDashesType = UITextSmartDashesTypeNo; + view.textView.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo; + [view didSetFont:view.textView.font]; view.hideBorder = YES; return view; diff --git a/MVMCoreUI/Atoms/Views/MFView.m b/MVMCoreUI/Atoms/Views/MFView.m index 4e0d71ab..6d939fc8 100644 --- a/MVMCoreUI/Atoms/Views/MFView.m +++ b/MVMCoreUI/Atoms/Views/MFView.m @@ -48,9 +48,7 @@ - (void)setAsMolecule { self.translatesAutoresizingMaskIntoConstraints = NO; - if (@available(iOS 11.0, *)) { - self.insetsLayoutMarginsFromSafeArea = NO; - } + self.insetsLayoutMarginsFromSafeArea = NO; } - (void)reset { diff --git a/MVMCoreUI/BaseControllers/MFProgrammaticTableViewController.m b/MVMCoreUI/BaseControllers/MFProgrammaticTableViewController.m index 98d2b5ef..1d0d2a3b 100644 --- a/MVMCoreUI/BaseControllers/MFProgrammaticTableViewController.m +++ b/MVMCoreUI/BaseControllers/MFProgrammaticTableViewController.m @@ -61,12 +61,8 @@ tableView.separatorStyle = UITableViewCellSeparatorStyleNone; tableView.delegate = self; tableView.dataSource = self; - if (@available(iOS 11.0, *)) { - tableView.insetsContentViewsToSafeArea = NO; - } - if ([tableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) { - tableView.cellLayoutMarginsFollowReadableWidth = NO; - } + tableView.insetsContentViewsToSafeArea = NO; + tableView.cellLayoutMarginsFollowReadableWidth = NO; return tableView; } diff --git a/MVMCoreUI/BaseControllers/MFScrollingViewController.m b/MVMCoreUI/BaseControllers/MFScrollingViewController.m index 5486b54a..56019cb7 100644 --- a/MVMCoreUI/BaseControllers/MFScrollingViewController.m +++ b/MVMCoreUI/BaseControllers/MFScrollingViewController.m @@ -91,10 +91,7 @@ static NSTimeInterval const HandScrollAnimationTiming = 7.f; - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; - BOOL automaticInset = NO; - if (@available(iOS 11.0, *)) { - automaticInset = self.navigationController && self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic; - } + BOOL automaticInset = self.navigationController && self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic; // Takes into account the navigation bar. if (!automaticInset && (self.edgesForExtendedLayout & UIRectEdgeTop)) { diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift index 0293e860..d1bfa0db 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -190,13 +190,9 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { footerView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true footerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true view.rightAnchor.constraint(equalTo: footerView.rightAnchor).isActive = true - if #available(iOS 11.0, *) { - view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true - safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view) - safeAreaView?.backgroundColor = bottomView?.backgroundColor - } else { - view.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true - } + view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true + safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view) + safeAreaView?.backgroundColor = bottomView?.backgroundColor } else { bottomConstraint?.isActive = true var y: CGFloat? diff --git a/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift index dae861dd..ea6923c5 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift @@ -45,7 +45,7 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController { return } - if #available(iOS 11.0, *), scrollView.contentInsetAdjustmentBehavior == UIScrollView.ContentInsetAdjustmentBehavior.automatic { + if scrollView.contentInsetAdjustmentBehavior == UIScrollView.ContentInsetAdjustmentBehavior.automatic { heightConstraint?.constant = -scrollView.adjustedContentInset.top - scrollView.adjustedContentInset.bottom } else { heightConstraint?.constant = -scrollView.contentInset.top - scrollView.contentInset.bottom @@ -233,14 +233,10 @@ extension ThreeLayerViewController { view.topAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true - if #available(iOS 11.0, *) { - parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true - if let safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: parentView) { - safeAreaView.backgroundColor = bottomView?.backgroundColor - self.safeAreaView = safeAreaView - } - } else { - NSLayoutConstraint.pinViewBottom(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true + parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + if let safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: parentView) { + safeAreaView.backgroundColor = bottomView?.backgroundColor + self.safeAreaView = safeAreaView } } } diff --git a/MVMCoreUI/Containers/TabBarController/MVMCoreUITabBarPageControlViewController.m b/MVMCoreUI/Containers/TabBarController/MVMCoreUITabBarPageControlViewController.m index 1d8481d6..cb48eb54 100644 --- a/MVMCoreUI/Containers/TabBarController/MVMCoreUITabBarPageControlViewController.m +++ b/MVMCoreUI/Containers/TabBarController/MVMCoreUITabBarPageControlViewController.m @@ -283,16 +283,6 @@ // So we will update titles. [self newDataBuildScreen]; - - // Fix for right bar button item with custom view which disappears when user navigates to top tabbar page controller - if (@available(iOS 11.0, *)) { - } else { - NSMutableArray *buttonItems = [[NSMutableArray alloc] init]; - UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:self action:nil]; - [buttonItems addObject:space]; - [buttonItems addObjectsFromArray:self.navigationItem.rightBarButtonItems]; - self.navigationItem.rightBarButtonItems = buttonItems; - } } - (void)viewWillDisappear:(BOOL)animated { diff --git a/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsTableViewController.m b/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsTableViewController.m index ca75b778..1c90d9e0 100644 --- a/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsTableViewController.m +++ b/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsTableViewController.m @@ -325,19 +325,13 @@ NSLayoutConstraint *bottomViewTop = [NSLayoutConstraint constraintWithItem:footerView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:tableView attribute:NSLayoutAttributeBottom multiplier:1 constant:0]; bottomViewTop.active = YES; - NSLayoutConstraint *bottomViewBot = nil; - if (@available(iOS 11.0, *)) { - bottomViewBot = [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:footerView.bottomAnchor]; - - UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view]; - safeAreaView.backgroundColor = footerView.backgroundColor; - self.safeAreaView = safeAreaView; - } else { - // Fallback on earlier versions - bottomViewBot = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:footerView attribute:NSLayoutAttributeBottom multiplier:1 constant:0]; - } - bottomViewBot.priority = 900; - bottomViewBot.active = YES; + NSLayoutConstraint *bottomViewBot = [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:footerView.bottomAnchor]; + bottomViewBot.priority = 900; + bottomViewBot.active = YES; + + UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view]; + safeAreaView.backgroundColor = footerView.backgroundColor; + self.safeAreaView = safeAreaView; [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[footerView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(footerView)]]; } else { diff --git a/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsViewController.m b/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsViewController.m index 77472e11..0c9d1d5d 100644 --- a/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsViewController.m +++ b/MVMCoreUI/LegacyControllers/TopLabelsAndBottomButtonsViewController.m @@ -272,13 +272,8 @@ - (void)updateViewConstraints { [super updateViewConstraints]; - // Updates for ios 11 - if (@available(iOS 11.0, *)) { - if (self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) { - self.heightConstraint.constant = -self.scrollView.adjustedContentInset.top - self.scrollView.adjustedContentInset.bottom; - } else { - self.heightConstraint.constant = -self.scrollView.contentInset.top - self.scrollView.contentInset.bottom; - } + if (self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) { + self.heightConstraint.constant = -self.scrollView.adjustedContentInset.top - self.scrollView.adjustedContentInset.bottom; } else { self.heightConstraint.constant = -self.scrollView.contentInset.top - self.scrollView.contentInset.bottom; } @@ -322,17 +317,13 @@ [self.view addSubview:bottomView]; UIScrollView *scrollview = self.scrollView; - if (@available(iOS 11.0, *)) { - [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]]; - [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:bottomView.bottomAnchor].active = YES; - - UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view]; - safeAreaView.backgroundColor = bottomView.backgroundColor; - self.safeAreaView = safeAreaView; - } else { - // Fallback on earlier versions - [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]]; - } + [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]]; + [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:bottomView.bottomAnchor].active = YES; + + UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view]; + safeAreaView.backgroundColor = bottomView.backgroundColor; + self.safeAreaView = safeAreaView; + [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[bottomView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(bottomView)]]; } diff --git a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift index 42a5e5cd..685d2e7a 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift @@ -37,11 +37,9 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi } isAccessibilityElement = false contentView.isAccessibilityElement = false - if #available(iOS 11.0, *) { - insetsLayoutMarginsFromSafeArea = false - contentView.insetsLayoutMarginsFromSafeArea = false - contentView.preservesSuperviewLayoutMargins = false - } + insetsLayoutMarginsFromSafeArea = false + contentView.insetsLayoutMarginsFromSafeArea = false + contentView.preservesSuperviewLayoutMargins = false // Covers the card when peaking. peakingCover.backgroundColor = .white diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 641b3721..3c20402e 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -87,29 +87,16 @@ import UIKit // MARK: - MFViewProtocol public func updateView(_ size: CGFloat) { MFStyler.setMarginsFor(self, size: size, defaultHorizontal: updateViewHorizontalDefaults, top: topMarginPadding, bottom: bottomMarginPadding) - if #available(iOS 11.0, *) { - 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) + if accessoryView != nil { + // Smaller left margin if accessory view. + var margin = directionalLayoutMargins + margin.trailing = 16 + contentView.directionalLayoutMargins = margin } else { - if accessoryView != nil { - // Smaller left margin if accessory view. - var margin = layoutMargins - margin.right = 16 - contentView.layoutMargins = margin - } else { - contentView.layoutMargins = layoutMargins - } - topSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left) - bottomSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left) + 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 { @@ -121,11 +108,9 @@ import UIKit public func setupView() { selectionStyle = .none - if #available(iOS 11.0, *) { - insetsLayoutMarginsFromSafeArea = false - contentView.insetsLayoutMarginsFromSafeArea = false - contentView.preservesSuperviewLayoutMargins = false - } + insetsLayoutMarginsFromSafeArea = false + contentView.insetsLayoutMarginsFromSafeArea = false + contentView.preservesSuperviewLayoutMargins = false } // MARK: - MVMCoreUIMoleculeViewProtocol diff --git a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m index 94c6ca85..fd5c9b21 100644 --- a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m @@ -198,13 +198,8 @@ static const CGFloat VertialShadowOffset = 6; [view.rightAnchor constraintEqualToAnchor:button.rightAnchor constant:PaddingTwo].active = YES; [view.centerYAnchor constraintEqualToAnchor:button.centerYAnchor].active = YES; } else { - if (@available(iOS 11.0, *)) { - [button.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.topAnchor constant:PaddingOne].active = YES; - [view.safeAreaLayoutGuide.trailingAnchor constraintEqualToAnchor:button.trailingAnchor constant:PaddingTwo].active = YES; - } else { - [NSLayoutConstraint constraintPinSubview:button pinTop:YES topConstant:PaddingOne pinBottom:NO bottomConstant:0 pinLeft:NO leftConstant:0 pinRight:YES rightConstant:PaddingTwo]; - } - + [button.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.topAnchor constant:PaddingOne].active = YES; + [view.safeAreaLayoutGuide.trailingAnchor constraintEqualToAnchor:button.trailingAnchor constant:PaddingTwo].active = YES; } } return button; @@ -249,16 +244,12 @@ static const CGFloat VertialShadowOffset = 6; } + (nullable UIView *)getAndSetupSafeAreaViewOnView:(nonnull UIView *)view { - if (@available(iOS 11.0, *)) { - UIView *safeAreaView = [MVMCoreUICommonViewsUtility commonView]; - [view addSubview:safeAreaView]; - [safeAreaView.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.bottomAnchor].active = YES; - [view.bottomAnchor constraintEqualToAnchor:safeAreaView.bottomAnchor].active = YES; - [NSLayoutConstraint constraintPinSubview:safeAreaView pinTop:NO topConstant:0 pinBottom:NO bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0]; - return safeAreaView; - } else { - return nil; - } + UIView *safeAreaView = [MVMCoreUICommonViewsUtility commonView]; + [view addSubview:safeAreaView]; + [safeAreaView.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.bottomAnchor].active = YES; + [view.bottomAnchor constraintEqualToAnchor:safeAreaView.bottomAnchor].active = YES; + [NSLayoutConstraint constraintPinSubview:safeAreaView pinTop:NO topConstant:0 pinBottom:NO bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0]; + return safeAreaView; } #pragma mark - shadows diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index 4f150783..257014f5 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -58,21 +58,13 @@ } + (UIEdgeInsets)getMarginsForView:(nullable UIView *)view { - if (@available(iOS 11.0, *)) { - return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing); - } else { - return view.layoutMargins; - } + return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing) } #pragma mark - Setters + (void)setMarginsForView:(nullable UIView *)view leading:(CGFloat)leading top:(CGFloat)top trailing:(CGFloat)trailing bottom:(CGFloat)bottom { - if (@available(iOS 11.0, *)) { - view.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(top, leading, bottom, trailing); - } else { - view.layoutMargins = UIEdgeInsetsMake(top, leading, bottom, trailing); - } + view.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(top, leading, bottom, trailing); } #pragma mark - Formatting @@ -152,12 +144,9 @@ CGFloat topInset = scrollview.contentInset.top; CGFloat bottomInset = scrollview.contentInset.bottom; - // Updates for ios 11 - if (@available(iOS 11.0, *)) { - if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) { - topInset = scrollview.adjustedContentInset.top; - bottomInset = scrollview.adjustedContentInset.bottom; - } + if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) { + topInset = scrollview.adjustedContentInset.top; + bottomInset = scrollview.adjustedContentInset.bottom; } CGFloat remainingSpace = frameHeight - contentSizeHeight - topInset - bottomInset; From 38beb2c7faa28b3c0c7e41688ff6dec9027fadf0 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 17 Oct 2019 11:21:42 -0400 Subject: [PATCH 076/131] typo fix --- MVMCoreUI/Utility/MVMCoreUIUtility.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index 257014f5..48a7a6a1 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -58,7 +58,7 @@ } + (UIEdgeInsets)getMarginsForView:(nullable UIView *)view { - return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing) + return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing); } #pragma mark - Setters From f267b98438dbd5711e8d0124a3959f499476aea1 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 17 Oct 2019 11:57:30 -0400 Subject: [PATCH 077/131] constraints added. changes made. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 35 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index c3879f96..a568622b 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -75,7 +75,7 @@ import MVMCore /// Color of the check mark. public var checkColor: UIColor = .black { didSet { - setshapeLayerStrokeColor(checkColor) + setShapeLayerStrokeColor(checkColor) } } @@ -115,6 +115,21 @@ import MVMCore } } + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + + private var heightConstraint: NSLayoutConstraint? + private var widthConstraint: NSLayoutConstraint? + + /// Updates the height and width anchors of the Checkbox with the assigned value. + public var heigthWidthConstant: Int = Checkbox.defaultHeightWidth { + didSet { + heightConstraint?.constant = heigthWidthConstant + widthConstraint?.constant = heigthWidthConstant + } + } + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -170,9 +185,15 @@ import MVMCore open func setupView() { + guard constraints.isEmpty else { return } + isUserInteractionEnabled = true translatesAutoresizingMaskIntoConstraints = false backgroundColor = .clear + + widthConstraint = widthAnchor.constraint(equalToConstant: Checkbox.defaultHeightWidth) + heightConstraint = heightAnchor.constraint(equalToConstant: Checkbox.defaultHeightWidth) + heightWidthIsActive(true) } //-------------------------------------------------- @@ -301,16 +322,16 @@ import MVMCore layer.borderColor = borderColor.cgColor backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor alpha = 1.0 - setshapeLayerStrokeColor(checkColor) + setShapeLayerStrokeColor(checkColor) } else { layer.borderColor = UIColor.mfSilver().cgColor backgroundColor = .clear alpha = DisableOppacity - setshapeLayerStrokeColor(UIColor.mfSilver()) + setShapeLayerStrokeColor(UIColor.mfSilver()) } } - private func setshapeLayerStrokeColor(_ color: UIColor) { + private func setShapeLayerStrokeColor(_ color: UIColor) { if let shapeLayer = shapeLayer { CATransaction.withDisabledAnimations { @@ -319,6 +340,12 @@ import MVMCore } } + public func heightWidthIsActive(_ isActive: Bool) { + + heightConstraint?.isActive = isActive + widthConstraint?.isActive = isActive + } + //-------------------------------------------------- // MARK: - UITouch //-------------------------------------------------- From 2fdc4795f40859be9cd9ade4d4f99a10f25998f2 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 17 Oct 2019 12:00:44 -0400 Subject: [PATCH 078/131] correcting type. --- MVMCoreUI/Atoms/Views/Checkbox.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index a568622b..d139f130 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -123,7 +123,7 @@ import MVMCore private var widthConstraint: NSLayoutConstraint? /// Updates the height and width anchors of the Checkbox with the assigned value. - public var heigthWidthConstant: Int = Checkbox.defaultHeightWidth { + public var heigthWidthConstant: CGFloat = Checkbox.defaultHeightWidth { didSet { heightConstraint?.constant = heigthWidthConstant widthConstraint?.constant = heigthWidthConstant @@ -140,7 +140,6 @@ import MVMCore accessibilityTraits = .button accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") updateAccessibilityLabel() - setupView() } From b5dcf68c5d4344e346d71479dbc2d7b4228c99e5 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 17 Oct 2019 12:17:14 -0400 Subject: [PATCH 079/131] refactoring taxtcontainer. --- MVMCoreUI/Atoms/Views/Label.swift | 44 ++++++++++++------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index cab341b1..cf1ed6f4 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -505,10 +505,12 @@ public typealias ActionBlock = () -> () let accessibleAction = customAccessibilityAction(range: range) clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1)) } - - public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { - - guard let attributedText = label.attributedText else { return CGRect() } + + /// Provides a text container in memory of how the text would appear on screen. + func abstractTextContainer() -> NSTextContainer? { + + // Must configure the attributed string to translate what would appear on screen to accurately analyze. + guard let attributedText = attributedText else { return nil } let paragraph = NSMutableParagraphStyle() paragraph.alignment = label.textAlignment @@ -524,9 +526,16 @@ public typealias ActionBlock = () -> () textStorage.addLayoutManager(layoutManager) textContainer.lineFragmentPadding = 0.0 - textContainer.lineBreakMode = label.lineBreakMode - textContainer.maximumNumberOfLines = label.numberOfLines - textContainer.size = label.bounds.size + textContainer.lineBreakMode = lineBreakMode + textContainer.maximumNumberOfLines = numberOfLines + textContainer.size = bounds.size + + return textContainer + } + + public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { + + let textContainer = abstractTextContainer() var glyphRange = NSRange() @@ -669,26 +678,7 @@ extension UITapGestureRecognizer { return true } - // Must configure the attributed string to translate what would appear on screen to accurately analyze. - guard let attributedText = label.attributedText else { return false } - - let paragraph = NSMutableParagraphStyle() - paragraph.alignment = label.textAlignment - - let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText) - stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count)) - - let textStorage = NSTextStorage(attributedString: stagedAttributedString) - let layoutManager = NSLayoutManager() - let textContainer = NSTextContainer(size: .zero) - - layoutManager.addTextContainer(textContainer) - textStorage.addLayoutManager(layoutManager) - - textContainer.lineFragmentPadding = 0.0 - textContainer.lineBreakMode = label.lineBreakMode - textContainer.maximumNumberOfLines = label.numberOfLines - textContainer.size = label.bounds.size + guard let textContainer = label.abstractTextContainer() else { return false } let indexOfGlyph = layoutManager.glyphIndex(for: location(in: label), in: textContainer) From 73ac4b932bb37dc413b829296d40a017d63ef599 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 17 Oct 2019 12:39:05 -0400 Subject: [PATCH 080/131] refacgtorerd. --- MVMCoreUI/Atoms/Views/Label.swift | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index cf1ed6f4..fefd686d 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -506,14 +506,17 @@ public typealias ActionBlock = () -> () clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1)) } - /// Provides a text container in memory of how the text would appear on screen. - func abstractTextContainer() -> NSTextContainer? { + /** + Provides a text container and layout manager of how the text would appear on screen. + They are used in tandem to derive low-level TextKit results of the label. + */ + public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager)? { // Must configure the attributed string to translate what would appear on screen to accurately analyze. guard let attributedText = attributedText else { return nil } let paragraph = NSMutableParagraphStyle() - paragraph.alignment = label.textAlignment + paragraph.alignment = textAlignment let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText) stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count)) @@ -530,12 +533,14 @@ public typealias ActionBlock = () -> () textContainer.maximumNumberOfLines = numberOfLines textContainer.size = bounds.size - return textContainer + return (textContainer, layoutManager) } public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { - let textContainer = abstractTextContainer() + guard let abstractContainer = label.abstractTextContainer() else { return CGRect() } + let textContainer = abstractContainer.0 + let layoutManager = abstractContainer.1 var glyphRange = NSRange() @@ -678,7 +683,9 @@ extension UITapGestureRecognizer { return true } - guard let textContainer = label.abstractTextContainer() else { return false } + guard let abstractContainer = label.abstractTextContainer() else { return false } + let textContainer = abstractContainer.0 + let layoutManager = abstractContainer.1 let indexOfGlyph = layoutManager.glyphIndex(for: location(in: label), in: textContainer) From 6d52663b4f66bd8ee7ae6760e2ff4b84c6d371c7 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 17 Oct 2019 14:06:01 -0400 Subject: [PATCH 081/131] fixed small error in refactor. --- MVMCoreUI/Atoms/Views/Label.swift | 4 ++-- MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index fefd686d..6fc496bd 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -510,7 +510,7 @@ public typealias ActionBlock = () -> () Provides a text container and layout manager of how the text would appear on screen. They are used in tandem to derive low-level TextKit results of the label. */ - public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager)? { + public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager, NSTextStorage)? { // Must configure the attributed string to translate what would appear on screen to accurately analyze. guard let attributedText = attributedText else { return nil } @@ -533,7 +533,7 @@ public typealias ActionBlock = () -> () textContainer.maximumNumberOfLines = numberOfLines textContainer.size = bounds.size - return (textContainer, layoutManager) + return (textContainer, layoutManager, textStorage) } public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 568a551c..2e8b9bfc 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -126,14 +126,12 @@ import UIKit /// 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 dinmensions 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) - let rectView = UIView(frame: rect) - heroLabel.addSubview(rectView) - accessoryView?.center.y = contentView.convert(rectView.center, from: heroLabel).y + accessoryView?.center.y = contentView.convert(UIView(frame: rect).center, from: heroLabel).y heroAccessoryCenter = accessoryView?.center - rectView.removeFromSuperview() } /// Traverses the view hierarchy for a 🦸‍♂️heroic Label. From 720d17d7517bb795446d2621c59a67473ac96720 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Thu, 17 Oct 2019 15:35:14 -0400 Subject: [PATCH 082/131] revisions made. --- MVMCoreUI.xcodeproj/project.pbxproj | 6 +-- .../Buttons/HeadlineBodyPrimaryButton.swift | 53 ------------------- MVMCoreUI/Molecules/HeadlineBodyButton.swift | 28 +++++----- 3 files changed, 16 insertions(+), 71 deletions(-) delete mode 100644 MVMCoreUI/Atoms/Buttons/HeadlineBodyPrimaryButton.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index ed3c634d..0eca06db 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -18,9 +18,9 @@ 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, ); }; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; - 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.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 */; }; + 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; }; 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 */; }; @@ -207,9 +207,8 @@ 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 = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; - 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = ""; }; - 0A804A3C2316CB79009A8656 /* HeadlineBodyPrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyPrimaryButton.swift; sourceTree = ""; }; 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = ""; }; + 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = ""; }; 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = ""; }; @@ -682,7 +681,6 @@ DBC4391A224421A0001AB423 /* CaretButton.swift */, D29DF25821E6A22D003B2FB9 /* MFButtonProtocol.h */, D29DF16B21E69E1F003B2FB9 /* ButtonDelegateProtocol.h */, - 0A804A3C2316CB79009A8656 /* HeadlineBodyPrimaryButton.swift */, D29DF16A21E69E1F003B2FB9 /* MFCustomButton.h */, D29DF17021E69E1F003B2FB9 /* MFCustomButton.m */, D29DF16C21E69E1F003B2FB9 /* PrimaryButton.h */, diff --git a/MVMCoreUI/Atoms/Buttons/HeadlineBodyPrimaryButton.swift b/MVMCoreUI/Atoms/Buttons/HeadlineBodyPrimaryButton.swift deleted file mode 100644 index 029a42c0..00000000 --- a/MVMCoreUI/Atoms/Buttons/HeadlineBodyPrimaryButton.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// HeadlineBodyPrimaryButton.swift -// MVMCoreUI -// -// Created by Kevin Christiano on 8/28/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation - - -@objcMembers open class HeadlineBodyPrimaryButton: ViewConstrainingView { - //------------------------------------------------------ - // MARK: - Outlets - //------------------------------------------------------ - - - - //------------------------------------------------------ - // MARK: - Properties - //------------------------------------------------------ - - - - //------------------------------------------------------ - // MARK: - Lifecycle - //------------------------------------------------------ - - open override func setupView() { - super.setupView() - - guard subviews.count == 0 else { return } - - } - - open override func updateView(_ size: CGFloat) { - super.updateView(size) - } - - //------------------------------------------------------ - // MARK: - JSON - //------------------------------------------------------ - - - - open override func reset() { - - } - - open override func setAsMolecule() { - super.setAsMolecule() - } -} diff --git a/MVMCoreUI/Molecules/HeadlineBodyButton.swift b/MVMCoreUI/Molecules/HeadlineBodyButton.swift index feb0f751..2922f0f2 100644 --- a/MVMCoreUI/Molecules/HeadlineBodyButton.swift +++ b/MVMCoreUI/Molecules/HeadlineBodyButton.swift @@ -14,7 +14,7 @@ import UIKit // MARK: - Outlets //------------------------------------------------------ - let headline = HeadlineBody(frame: .zero) + let headlineBody = HeadlineBody(frame: .zero) let button = PrimaryButton.primaryTinyButton(false)! //------------------------------------------------------ @@ -61,14 +61,14 @@ import UIKit defaultState() - addSubview(headline) + addSubview(headlineBody) addSubview(button) - headline.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true - headline.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true - layoutMarginsGuide.trailingAnchor.constraint(equalTo: headline.trailingAnchor).isActive = true + headlineBody.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true + headlineBody.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true + layoutMarginsGuide.trailingAnchor.constraint(equalTo: headlineBody.trailingAnchor).isActive = true - buttonTopConstraint = button.topAnchor.constraint(equalTo: headline.bottomAnchor, constant: buttonHeadlinePadding) + buttonTopConstraint = button.topAnchor.constraint(equalTo: headlineBody.bottomAnchor, constant: buttonHeadlinePadding) buttonTopConstraint?.isActive = true button.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true @@ -79,17 +79,17 @@ import UIKit override open func updateView(_ size: CGFloat) { super.updateView(size) - headline.updateView(size) + headlineBody.updateView(size) button.updateView(size) - buttonTopConstraint?.constant = headline.hasText() ? buttonHeadlinePadding : 0 + buttonTopConstraint?.constant = headlineBody.hasText() && !button.isHidden ? buttonHeadlinePadding : 0 } private func defaultState() { - headline.headlineLabel.font = MFStyler.fontH3() - headline.messageLabel.font = MFStyler.fontB3() - button.setAsSecondaryCustom() + headlineBody.headlineLabel.font = MFStyler.fontH3() + headlineBody.messageLabel.font = MFStyler.fontB3() + button.setAsTiny(true) button.isHidden = false buttonHeadlinePadding = PaddingTwo } @@ -101,7 +101,7 @@ import UIKit override open func reset() { super.reset() - headline.reset() + headlineBody.reset() button.reset() defaultState() } @@ -109,7 +109,7 @@ import UIKit open override func setAsMolecule() { super.setAsMolecule() - headline.setAsMolecule() + headlineBody.setAsMolecule() button.setAsMolecule() defaultState() } @@ -123,7 +123,7 @@ import UIKit buttonHeadlinePadding = padding } - headline.setWithJSON(dictionary.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData) + headlineBody.setWithJSON(dictionary.optionalDictionaryForKey("headlineBodyBody"), delegateObject: delegateObject, additionalData: additionalData) if let buttonDictionary = dictionary.optionalDictionaryForKey("button") { button.setWithJSON(buttonDictionary, delegateObject: delegateObject, additionalData: additionalData) From 034610af5ee8a2999f947d03444e10a3ef15bd2a Mon Sep 17 00:00:00 2001 From: panxi Date: Thu, 17 Oct 2019 15:50:25 -0400 Subject: [PATCH 083/131] 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 084/131] 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 085/131] 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 086/131] 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 f067a20ba97734c8c82a044fe4630aaeac014dbb Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 18 Oct 2019 08:47:14 -0400 Subject: [PATCH 087/131] hero stuff. --- MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift | 10 ++++++++-- MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h | 3 --- MVMCoreUI/Templates/MoleculeListCellProtocol.h | 3 +++ MVMCoreUI/Templates/MoleculeListTemplate.swift | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 2e8b9bfc..2ae52775 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -56,6 +56,7 @@ import UIKit 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 } @@ -83,6 +84,11 @@ import UIKit bottomSeparatorView?.hide() } + public func willDisplay() { + + alignAccessoryToHero() + } + // MARK: - Inits public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) @@ -126,7 +132,7 @@ import UIKit /// 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 dinmensions of subviews. + // 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) @@ -240,7 +246,7 @@ import UIKit // MARK: - Arrow /// Adds the standard mvm style caret to the accessory view - public func addCaretViewAccessory() { + @objc public func addCaretViewAccessory() { guard accessoryView == nil else { return } let width: CGFloat = 6 let height: CGFloat = 10 diff --git a/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h b/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h index 273329aa..a499eb7c 100644 --- a/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h +++ b/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h @@ -24,9 +24,6 @@ /// Resets to default state before set with json is called again. - (void)reset; -/// Currently designed for UITableViewCell. Aligns the accessory view with the center of a character in a line of text. -- (void)alignAccessoryToHero; - /// For the molecule list to load more efficiently. + (CGFloat)estimatedHeightForRow:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject; diff --git a/MVMCoreUI/Templates/MoleculeListCellProtocol.h b/MVMCoreUI/Templates/MoleculeListCellProtocol.h index 7603c820..8eae2e58 100644 --- a/MVMCoreUI/Templates/MoleculeListCellProtocol.h +++ b/MVMCoreUI/Templates/MoleculeListCellProtocol.h @@ -17,4 +17,7 @@ /// Handle action - (void)didSelectCellAtIndex:(nonnull NSIndexPath *)indexPath delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData; +/// Currently designed for UITableViewCell. Aligns the accessory view with the center of a character in a line of text. +- (void)willDisplay; + @end diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 84a7e64b..b400f0a9 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -89,8 +89,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { open override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - if cell.accessoryView != nil, let protocolCell = cell as? (MoleculeTableViewCell & MVMCoreUIMoleculeViewProtocol) { - protocolCell.alignAccessoryToHero() + if let protocolCell = cell as? MoleculeListCellProtocol { + protocolCell.willDisplay?() } } From 0bb1debc77500bfa1b4d833b4800db334ab3ff40 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 18 Oct 2019 10:28:22 -0400 Subject: [PATCH 088/131] no comment. --- MVMCoreUI/Templates/MoleculeListCellProtocol.h | 1 - 1 file changed, 1 deletion(-) diff --git a/MVMCoreUI/Templates/MoleculeListCellProtocol.h b/MVMCoreUI/Templates/MoleculeListCellProtocol.h index 8eae2e58..3fa19a02 100644 --- a/MVMCoreUI/Templates/MoleculeListCellProtocol.h +++ b/MVMCoreUI/Templates/MoleculeListCellProtocol.h @@ -17,7 +17,6 @@ /// Handle action - (void)didSelectCellAtIndex:(nonnull NSIndexPath *)indexPath delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData; -/// Currently designed for UITableViewCell. Aligns the accessory view with the center of a character in a line of text. - (void)willDisplay; @end From a3663ee1b5f52bc40c443b394ba819329c7f66cf Mon Sep 17 00:00:00 2001 From: "Hedden, Kyle Matthew" Date: Sat, 19 Oct 2019 15:14:31 -0400 Subject: [PATCH 089/131] Add current page type to session object. --- MVMCoreUI/BaseControllers/MFViewController.m | 2 ++ MVMCoreUI/OtherHandlers/MVMCoreUISession.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/MVMCoreUI/BaseControllers/MFViewController.m b/MVMCoreUI/BaseControllers/MFViewController.m index edbabf80..5e9f3c6a 100644 --- a/MVMCoreUI/BaseControllers/MFViewController.m +++ b/MVMCoreUI/BaseControllers/MFViewController.m @@ -518,6 +518,7 @@ // Don't track page state if there is a tab bar page control, it will be handled later. if (!self.manager) { + [MVMCoreUISession sharedGlobal].currentPageType = self.pageType; [self adobeTrackPageState]; } @@ -819,6 +820,7 @@ if (self.initialLoadFinished) { [self updateNavigationBarUI:self.manager.navigationController]; } + [MVMCoreUISession sharedGlobal].currentPageType = self.pageType; [self adobeTrackPageState]; } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUISession.h b/MVMCoreUI/OtherHandlers/MVMCoreUISession.h index a5ce493c..8adb32cd 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUISession.h +++ b/MVMCoreUI/OtherHandlers/MVMCoreUISession.h @@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN @property (weak, nonatomic, nullable) MVMCoreUINavigationController *navigationController; @property (weak, nonatomic, nullable) MFLoadingViewController *loadingViewController; +/// Tracks the current page type the user is currently viewing. KVO compliant. +@property (nonatomic, strong, nullable) NSString *currentPageType; + // for handscroll Animation on subclasses of MFScrollingViewController @property (assign, nonatomic) BOOL enableHandScrollAnimation; From f1c319244a1e7c04e9c79e60362714b209f4cfba Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Mon, 21 Oct 2019 17:26:33 -0400 Subject: [PATCH 090/131] 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 091/131] 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 092/131] 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 b79b999910969344b908d81fa5e2d070cc94879c Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 22 Oct 2019 11:04:22 -0400 Subject: [PATCH 093/131] added secondary. --- MVMCoreUI/Molecules/HeadlineBodyButton.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/MVMCoreUI/Molecules/HeadlineBodyButton.swift b/MVMCoreUI/Molecules/HeadlineBodyButton.swift index 2922f0f2..e3281d35 100644 --- a/MVMCoreUI/Molecules/HeadlineBodyButton.swift +++ b/MVMCoreUI/Molecules/HeadlineBodyButton.swift @@ -90,6 +90,7 @@ import UIKit headlineBody.headlineLabel.font = MFStyler.fontH3() headlineBody.messageLabel.font = MFStyler.fontB3() button.setAsTiny(true) + button.setAsSecondaryCustom() button.isHidden = false buttonHeadlinePadding = PaddingTwo } From 2248b36ccbd9fd2d51d0389bf359356501a09d7c Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 22 Oct 2019 11:10:06 -0400 Subject: [PATCH 094/131] one less body. --- MVMCoreUI/Molecules/HeadlineBodyButton.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Molecules/HeadlineBodyButton.swift b/MVMCoreUI/Molecules/HeadlineBodyButton.swift index e3281d35..87efc9b6 100644 --- a/MVMCoreUI/Molecules/HeadlineBodyButton.swift +++ b/MVMCoreUI/Molecules/HeadlineBodyButton.swift @@ -124,7 +124,7 @@ import UIKit buttonHeadlinePadding = padding } - headlineBody.setWithJSON(dictionary.optionalDictionaryForKey("headlineBodyBody"), delegateObject: delegateObject, additionalData: additionalData) + headlineBody.setWithJSON(dictionary.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData) if let buttonDictionary = dictionary.optionalDictionaryForKey("button") { button.setWithJSON(buttonDictionary, delegateObject: delegateObject, additionalData: additionalData) From 252beb311a35837f657111f8803f163cca7c7403 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 22 Oct 2019 11:40:50 -0400 Subject: [PATCH 095/131] 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 9b86b4449fda763349a289f4c54de908ac3b14d4 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 22 Oct 2019 11:54:13 -0400 Subject: [PATCH 096/131] dcsds --- MVMCoreUI/Molecules/ActionDetailWithImage.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Molecules/ActionDetailWithImage.swift b/MVMCoreUI/Molecules/ActionDetailWithImage.swift index 76dde8e6..86cee41b 100644 --- a/MVMCoreUI/Molecules/ActionDetailWithImage.swift +++ b/MVMCoreUI/Molecules/ActionDetailWithImage.swift @@ -101,8 +101,8 @@ import UIKit private func setDefaultState() { - headlineBodyButton.headline.headlineLabel.font = MFStyler.fontH3() - headlineBodyButton.headline.messageLabel.font = MFStyler.fontB3() + headlineBodyButton.headlineBody.headlineLabel.font = MFStyler.fontH3() + headlineBodyButton.headlineBody.messageLabel.font = MFStyler.fontB3() imageLoader.imageView.contentMode = .scaleAspectFit imageLoader.addSizeConstraintsForAspectRatio = true buttonHeaderPadding = PaddingTwo From 5ee34fb027c26b6d69e099a7b9a8ed001ab8e0d7 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Tue, 22 Oct 2019 15:11:16 -0400 Subject: [PATCH 097/131] 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 b8bdf4829ec0efead7ed0f07efe1fde498ca0aa6 Mon Sep 17 00:00:00 2001 From: "Chintakrinda, Arun Kumar (Arun)" Date: Wed, 23 Oct 2019 18:21:38 +0530 Subject: [PATCH 098/131] Fix for label and molecule stack --- MVMCoreUI/Atoms/Views/Label.swift | 2 +- MVMCoreUI/Organisms/MoleculeStackView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 6fc496bd..52ee43d1 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -308,7 +308,7 @@ 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) + let actionBlock = actionLabel.createActionBlockFrom(actionMap: attribute, additionalData: additionalData, delegateObject: delegate) actionLabel.appendActionableClause(range: range, actionBlock: actionBlock) default: diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 14ad9e70..95855f52 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -190,7 +190,7 @@ public class MoleculeStackView: ViewConstrainingView { return "stack<>" } var name = "stack<" - for case let item as [AnyHashable: AnyHashable] in molecules { + for case let item as [AnyHashable: Any] in molecules { if let molecule = item.optionalDictionaryForKey(KeyMolecule), let moleculeName = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.name?(forReuse: molecule, delegateObject: delegateObject) ?? molecule.optionalStringForKey(KeyMoleculeName) { name.append(moleculeName + ",") } From 75632696804d4ebb44e84e07a2d6e1ca659a47b3 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 23 Oct 2019 10:36:32 -0400 Subject: [PATCH 099/131] 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 100/131] 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 101/131] 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 102/131] 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 103/131] 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 104/131] 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 105/131] 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 106/131] 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 107/131] 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 108/131] 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 109/131] 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 110/131] 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 111/131] 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 112/131] 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 113/131] 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 114/131] 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 115/131] 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 116/131] 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 117/131] 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 118/131] 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 119/131] 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 120/131] 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 121/131] 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 122/131] 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 123/131] 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 124/131] 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 125/131] 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 126/131] 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 127/131] 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 128/131] 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 129/131] 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 130/131] 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 131/131] 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.