diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 5fff92e6..bb63fd1d 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -88,6 +88,8 @@ 0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682A32434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift */; }; 0A6682AA2435125F00AD3CA1 /* Styler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682A92435125F00AD3CA1 /* Styler.swift */; }; 0A6682AC243531C300AD3CA1 /* Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682AB243531C300AD3CA1 /* Padding.swift */; }; + 0A6682B5243769C700AD3CA1 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682B3243769C700AD3CA1 /* TextView.swift */; }; + 0A6682B6243769C700AD3CA1 /* TextViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682B4243769C700AD3CA1 /* TextViewModel.swift */; }; 0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */; }; 0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */; }; 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; }; @@ -213,6 +215,8 @@ AAC6F167243332E400F295C1 /* RadioSwatchesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */; }; BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */; }; BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */; }; + BB2BF0EA2452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2BF0E92452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift */; }; + BB2BF0EC2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2BF0EB2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift */; }; BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */; }; BB2C969224330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */; }; BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */; }; @@ -313,6 +317,10 @@ D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */; }; D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */; }; D282AACB2243C61700C46919 /* ButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AACA2243C61700C46919 /* ButtonView.swift */; }; + D28764AA2458980300CB882D /* ThreeLayerFillMiddleTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28764A92458980300CB882D /* ThreeLayerFillMiddleTemplate.swift */; }; + D28764AC245898A400CB882D /* ThreeLayerFillMiddleTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28764AB245898A400CB882D /* ThreeLayerFillMiddleTemplateModel.swift */; }; + D28764F9245A327200CB882D /* TwoLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28764F8245A327200CB882D /* TwoLinkView.swift */; }; + D28764FB245A33A500CB882D /* TwoLinkViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28764FA245A33A500CB882D /* TwoLinkViewModel.swift */; }; D28A837923C7D5BC00DFE4FC /* PageModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A837823C7D5BC00DFE4FC /* PageModelProtocol.swift */; }; D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A837A23C928DA00DFE4FC /* MoleculeListCellProtocol.swift */; }; D28A837D23CCA86A00DFE4FC /* TabsListItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A837C23CCA86A00DFE4FC /* TabsListItemModel.swift */; }; @@ -544,6 +552,8 @@ 0A6682A32434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonBodyTextModel.swift; sourceTree = ""; }; 0A6682A92435125F00AD3CA1 /* Styler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Styler.swift; sourceTree = ""; }; 0A6682AB243531C300AD3CA1 /* Padding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Padding.swift; sourceTree = ""; }; + 0A6682B3243769C700AD3CA1 /* TextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = ""; }; + 0A6682B4243769C700AD3CA1 /* TextViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextViewModel.swift; sourceTree = ""; }; 0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyRequiredModel.swift; sourceTree = ""; }; 0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDropdownEntryField.swift; sourceTree = ""; }; 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = ""; }; @@ -671,6 +681,8 @@ AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchesModel.swift; sourceTree = ""; }; BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonMediumModel.swift; sourceTree = ""; }; BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonMedium.swift; sourceTree = ""; }; + BB2BF0E92452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonSmall.swift; sourceTree = ""; }; + BB2BF0EB2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonSmallModel.swift; sourceTree = ""; }; BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableTextLinkAllTextAndLinksModel.swift; sourceTree = ""; }; BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableTextLinkAllTextAndLinks.swift; sourceTree = ""; }; BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextDividerSubsectionModel.swift; sourceTree = ""; }; @@ -770,6 +782,10 @@ D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFLoadImageView.swift; sourceTree = ""; }; D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFTransparentGIFView.swift; sourceTree = ""; }; D282AACA2243C61700C46919 /* ButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonView.swift; sourceTree = ""; }; + D28764A92458980300CB882D /* ThreeLayerFillMiddleTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerFillMiddleTemplate.swift; sourceTree = ""; }; + D28764AB245898A400CB882D /* ThreeLayerFillMiddleTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerFillMiddleTemplateModel.swift; sourceTree = ""; }; + D28764F8245A327200CB882D /* TwoLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoLinkView.swift; sourceTree = ""; }; + D28764FA245A33A500CB882D /* TwoLinkViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoLinkViewModel.swift; sourceTree = ""; }; D28A837823C7D5BC00DFE4FC /* PageModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageModelProtocol.swift; sourceTree = ""; }; D28A837A23C928DA00DFE4FC /* MoleculeListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListCellProtocol.swift; sourceTree = ""; }; D28A837C23CCA86A00DFE4FC /* TabsListItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsListItemModel.swift; sourceTree = ""; }; @@ -1208,6 +1224,8 @@ D20FFFB42451E32100A31DA2 /* Device */ = { isa = PBXGroup; children = ( + BB2BF0EB2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift */, + BB2BF0E92452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift */, BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */, BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */, AA617AB12453012400910B8F /* ListDeviceComplexLinkSmallModel.swift */, @@ -1349,6 +1367,8 @@ 94F6516C2437954100631BF9 /* Tabs.swift */, 011D9625240EBB16000E3791 /* RadioButtonLabelModel.swift */, 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */, + D28764FA245A33A500CB882D /* TwoLinkViewModel.swift */, + D28764F8245A327200CB882D /* TwoLinkView.swift */, ); path = HorizontalCombinationViews; sourceTree = ""; @@ -1605,6 +1625,8 @@ D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */, D2092350244F7BE80044AD09 /* ThreeLayerCenterTemplateModel.swift */, D209234E244F77FD0044AD09 /* ThreeLayerCenterTemplate.swift */, + D28764AB245898A400CB882D /* ThreeLayerFillMiddleTemplateModel.swift */, + D28764A92458980300CB882D /* ThreeLayerFillMiddleTemplate.swift */, D264FAA4243F66A500D98315 /* CollectionTemplateItemProtocol.swift */, D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */, D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */, @@ -1945,6 +1967,8 @@ D2B18B7D236090D500A9AEDC /* BaseClasses */ = { isa = PBXGroup; children = ( + 0A6682B4243769C700AD3CA1 /* TextViewModel.swift */, + 0A6682B3243769C700AD3CA1 /* TextView.swift */, C003506023AA94CD00B6AC29 /* Button.swift */, D2B18B7E2360913400A9AEDC /* Control.swift */, D2B18B802360945C00A9AEDC /* View.swift */, @@ -2168,13 +2192,16 @@ 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */, AA11A41F23F15D3100D7962F /* ListRightVariablePayments.swift in Sources */, + D28764AA2458980300CB882D /* ThreeLayerFillMiddleTemplate.swift in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, 0116A4E5228B19640094F3ED /* RadioButtonSelectionHelper.swift in Sources */, D2092353244F7D630044AD09 /* MVMCoreUIViewControllerMappingObject+Extension.swift in Sources */, 017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, 94382086243238D100B43AF3 /* WebViewModel.swift in Sources */, + D28764F9245A327200CB882D /* TwoLinkView.swift in Sources */, D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */, + 0A6682B5243769C700AD3CA1 /* TextView.swift in Sources */, D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */, D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */, 0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */, @@ -2188,6 +2215,7 @@ D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, D2B18B7F2360913400A9AEDC /* Control.swift in Sources */, 011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */, + BB2BF0EA2452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift in Sources */, D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */, 0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */, D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */, @@ -2218,6 +2246,7 @@ AA617AB22453012400910B8F /* ListDeviceComplexLinkSmallModel.swift in Sources */, D260106323D0C05000764D80 /* StackItemModel.swift in Sources */, D2E2A99823D8D63C000B42E6 /* ActionDetailWithImageModel.swift in Sources */, + D28764AC245898A400CB882D /* ThreeLayerFillMiddleTemplateModel.swift in Sources */, BBBBC87D24374A4900B0F079 /* ListThreeColumnBillChangesDividerModel.swift in Sources */, D2E2A99D23DA3217000B42E6 /* UIStackViewAlignment+Extension.swift in Sources */, 01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */, @@ -2264,6 +2293,7 @@ AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */, 011D9626240EBB16000E3791 /* RadioButtonLabelModel.swift in Sources */, 8DDD6C1D244D90B8006A2232 /* ListThreeColumnDataUsage.swift in Sources */, + D28764FB245A33A500CB882D /* TwoLinkViewModel.swift in Sources */, AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */, D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */, 944589232385DA9600DE9FD4 /* ImageViewModel.swift in Sources */, @@ -2376,6 +2406,7 @@ D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */, 012A88B1238C880100FE3DA1 /* CarouselPagingModelProtocol.swift in Sources */, D29DF2C921E7BFC6003B2FB9 /* MFSizeObject.m in Sources */, + 0A6682B6243769C700AD3CA1 /* TextViewModel.swift in Sources */, 9445890E2385C3F800DE9FD4 /* MultiProgressModel.swift in Sources */, 011D95A5240455DC000E3791 /* FormGroupRule.swift in Sources */, D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */, @@ -2453,6 +2484,7 @@ AA85236C244435A20059CC1E /* RadioSwatchCollectionViewCell.swift in Sources */, 52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */, D26C5A6B23F4A40D007AEECE /* ListItemModel.swift in Sources */, + BB2BF0EC2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift in Sources */, 0A21DB8D235E06EF00C160A2 /* MFDigitTextField.m in Sources */, 94AF4A4323E9D19E00676048 /* MFCaretView.m in Sources */, 943784F6236B77BB006A1E82 /* WheelAnimationHandler.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryFieldModel.swift index 496625dc..e34183de 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryFieldModel.swift @@ -6,7 +6,7 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -@objcMembers public class BaseDropdownEntryFieldModel: TextEntryFieldModel { +@objcMembers open class BaseDropdownEntryFieldModel: TextEntryFieldModel { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryFieldModel.swift index 86c072dd..96ec3488 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryFieldModel.swift @@ -6,7 +6,7 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -@objcMembers public class DateDropdownEntryFieldModel: BaseDropdownEntryFieldModel { +@objcMembers open class DateDropdownEntryFieldModel: BaseDropdownEntryFieldModel { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/DigitBox.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DigitBox.swift index 9e860116..ba04f482 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/DigitBox.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/DigitBox.swift @@ -13,7 +13,7 @@ import UIKit } -@objcMembers open class DigitBox: EntryFieldContainer, UITextFieldDelegate, TextFieldDidDeleteProtocol { +@objcMembers open class DigitBox: EntryFieldContainer, UITextFieldDelegate, TextInputDidDeleteProtocol { //-------------------------------------------------- // MARK: - Outlets //-------------------------------------------------- @@ -125,7 +125,7 @@ import UIKit updateView(MVMCoreUIUtility.getWidth()) } - @objc public func textFieldDidDelete() { + @objc public func textInputDidDelete() { digitBoxDelegate?.digitFieldDidDelete?(digitField) } diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryFieldModel.swift index b906244d..4132a15e 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryFieldModel.swift @@ -7,7 +7,7 @@ // -@objcMembers public class DigitEntryFieldModel: TextEntryFieldModel { +@objcMembers open class DigitEntryFieldModel: TextEntryFieldModel { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift index e902ae22..f7886848 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift @@ -9,7 +9,7 @@ import Foundation -@objcMembers public class EntryFieldModel: MoleculeModelProtocol, FormFieldProtocol, FormRuleWatcherFieldProtocol, EnableableModelProtocol { +@objcMembers open class EntryFieldModel: MoleculeModelProtocol, FormFieldProtocol, FormRuleWatcherFieldProtocol, EnableableModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryFieldModel.swift index 2105120c..979fac4d 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryFieldModel.swift @@ -6,7 +6,7 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -@objcMembers public class ItemDropdownEntryFieldModel: BaseDropdownEntryFieldModel { +@objcMembers open class ItemDropdownEntryFieldModel: BaseDropdownEntryFieldModel { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryFieldModel.swift index ea367447..0541de8d 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryFieldModel.swift @@ -6,7 +6,7 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -@objcMembers public class MdnEntryFieldModel: TextEntryFieldModel { +@objcMembers open class MdnEntryFieldModel: TextEntryFieldModel { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift index 57c9dae9..894fadd4 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift @@ -134,7 +134,7 @@ import UIKit } } - /// If you're using a MFViewController, you must set this to it + /// If you're using a ViewController, you must set this to it public weak var uiTextFieldDelegate: UITextFieldDelegate? { get { return textField.delegate } set { textField.delegate = newValue } diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift index 491e9891..d47b9802 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift @@ -7,7 +7,7 @@ // -@objcMembers public class TextEntryFieldModel: EntryFieldModel { +@objcMembers open class TextEntryFieldModel: EntryFieldModel { //-------------------------------------------------- // MARK: - Types //-------------------------------------------------- @@ -17,6 +17,7 @@ case secure case number case email + case text } //-------------------------------------------------- @@ -37,7 +38,6 @@ //-------------------------------------------------- private enum CodingKeys: String, CodingKey { - case moleculeName case placeholder case enabledTextColor case disabledTextColor @@ -66,7 +66,6 @@ public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(placeholder, forKey: .placeholder) try container.encode(enabledTextColor, forKey: .enabledTextColor) try container.encode(disabledTextColor, forKey: .disabledTextColor) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Line.swift b/MVMCoreUI/Atomic/Atoms/Views/Line.swift index 0429c1c6..62550503 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Line.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Line.swift @@ -14,23 +14,36 @@ import UIKit } public var heightConstraint: NSLayoutConstraint? + public var widthConstraint: NSLayoutConstraint? + open func updateLineConstraints(constant: CGFloat) { + if let useVerticalLine = lineModel?.useVerticalLine, useVerticalLine { + heightConstraint?.isActive = false + widthConstraint?.isActive = true + widthConstraint?.constant = constant + } else { + widthConstraint?.isActive = false + heightConstraint?.isActive = true + heightConstraint?.constant = constant + } + } + open func setStyle(_ style: LineModel.Style) { switch style { case .standard: - heightConstraint?.constant = 1 - backgroundColor = .mfSilver() + updateLineConstraints(constant: 1) + backgroundColor = lineModel?.backgroundColor?.uiColor ?? .mfSilver() case .thin: - heightConstraint?.constant = 1 - backgroundColor = .black + updateLineConstraints(constant: 1) + backgroundColor = lineModel?.backgroundColor?.uiColor ?? .black case .medium: - heightConstraint?.constant = 2 - backgroundColor = .black + updateLineConstraints(constant: 2) + backgroundColor = lineModel?.backgroundColor?.uiColor ?? .black case .heavy: - heightConstraint?.constant = 4 - backgroundColor = .black + updateLineConstraints(constant: 4) + backgroundColor = lineModel?.backgroundColor?.uiColor ?? .black case .none: - heightConstraint?.constant = 0 + updateLineConstraints(constant: 0) } } @@ -51,15 +64,17 @@ import UIKit super.setupView() heightConstraint = heightAnchor.constraint(equalToConstant: 1) heightConstraint?.isActive = true + widthConstraint = widthAnchor.constraint(equalToConstant: 1) + widthConstraint?.isActive = false setStyle(.standard) } // MARK: - MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) if let lineModel = model as? LineModel { setStyle(lineModel.type) } - super.set(with: model, delegateObject, additionalData) } open override func reset() { diff --git a/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift index 53bc1f4c..7412230d 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift @@ -48,8 +48,13 @@ import UIKit // public var color: Color? public var backgroundColor: Color? + // Use this to show vertical line + // Default is false + public var useVerticalLine: Bool? + public init(type: Style) { self.type = type + self.useVerticalLine = false } private enum CodingKeys: String, CodingKey { @@ -58,6 +63,7 @@ import UIKit case backgroundColor case color case frequency + case useVerticalLine } required public init(from decoder: Decoder) throws { @@ -69,6 +75,7 @@ import UIKit self.frequency = frequency } backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + useVerticalLine = try typeContainer.decodeIfPresent(Bool.self, forKey: .useVerticalLine) } public func encode(to encoder: Encoder) throws { @@ -77,5 +84,6 @@ import UIKit try container.encode(type, forKey: .type) try container.encodeIfPresent(frequency, forKey: .frequency) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(useVerticalLine, forKey: .useVerticalLine) } } diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index d3c854f0..0badfdcc 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -51,6 +51,9 @@ import Foundation try? ModelRegistry.register(LabelAttributeStrikeThroughModel.self) try? ModelRegistry.register(LabelAttributeActionModel.self) + // TextView + MoleculeObjectMapping.shared()?.register(viewClass: TextView.self, viewModelClass: TextViewModel.self) + // Buttons MoleculeObjectMapping.shared()?.register(viewClass: PillButton.self, viewModelClass: ButtonModel.self) MoleculeObjectMapping.shared()?.register(viewClass: TwoButtonView.self, viewModelClass: TwoButtonViewModel.self) @@ -89,6 +92,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: StringAndMoleculeView.self, viewModelClass: StringAndMoleculeModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ImageHeadlineBody.self, viewModelClass: ImageHeadlineBodyModel.self) MoleculeObjectMapping.shared()?.register(viewClass: Tabs.self, viewModelClass: TabsModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: TwoLinkView.self, viewModelClass: TwoLinkViewModel.self) // Vertical Combination Molecules MoleculeObjectMapping.shared()?.register(viewClass: HeadlineBody.self, viewModelClass: HeadlineBodyModel.self) @@ -174,12 +178,11 @@ import Foundation // Device Items MoleculeObjectMapping.shared()?.register(viewClass: ListDeviceComplexButtonMedium.self, viewModelClass: ListDeviceComplexButtonMediumModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListDeviceComplexButtonSmall.self, viewModelClass: ListDeviceComplexButtonSmallModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListDeviceComplexLinkSmall.self, viewModelClass: ListDeviceComplexLinkSmallModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListDeviceComplexLinkMedium.self, viewModelClass: ListDeviceComplexLinkMediumModel.self) - // TODO: Need View - try? ModelRegistry.register(TabsModel.self) - // Helper models try? ModelRegistry.register(RuleRequiredModel.self) try? ModelRegistry.register(RuleAnyRequiredModel.self) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift index f8ab918d..72dd7034 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift @@ -21,15 +21,16 @@ import Foundation // MARK: - Initializers public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { rightImageView.addSizeConstraintsForAspectRatio = true + rightImageView.imageView.contentMode = .scaleAspectFit rightImageView.heightAnchor.constraint(equalToConstant: 116.0).isActive = true rightImageView.widthAnchor.constraint(equalToConstant: 116.0).isActive = true verticalStack = Stack.createStack(with: [(view: eyebrow, model: StackItemModel(horizontalAlignment: .leading)), (view: headline, model: StackItemModel(horizontalAlignment: .leading)), (view: body, model: StackItemModel(horizontalAlignment: .leading)), (view: body2, model: StackItemModel(horizontalAlignment: .leading)), - (view: button, model: StackItemModel(spacing:16, horizontalAlignment: .leading))], + (view: button, model: StackItemModel(spacing: 16, horizontalAlignment: .leading))], axis: .vertical, spacing: 0) - stack = Stack.createStack(with: [(view: verticalStack, model: StackItemModel(horizontalAlignment: .leading, verticalAlignment: .leading)), + stack = Stack.createStack(with: [(view: verticalStack, model: StackItemModel(horizontalAlignment: .leading, verticalAlignment: .leading)), (view: rightImageView, model: StackItemModel(horizontalAlignment: .fill, verticalAlignment: .center))], axis: .horizontal) super.init(style: style, reuseIdentifier: reuseIdentifier) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift new file mode 100644 index 00000000..670ed65e --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift @@ -0,0 +1,80 @@ +// +// ListDeviceComplexButtonSmall.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 24/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +@objcMembers open class ListDeviceComplexButtonSmall: TableViewCell { + + public var verticalStack: Stack + public let eyebrow = Label.createLabelRegularMicro(true) + public let headline = Label.createLabelBoldTitleMedium(true) + public let body = Label.createLabelRegularBodySmall(true) + public let body2 = Label.createLabelRegularBodySmall(true) + public let button = PillButton(frame: .zero) + public let rightImageView = MFLoadImageView() + public var stack: Stack + + // MARK: - Initializers + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + rightImageView.addSizeConstraintsForAspectRatio = true + rightImageView.imageView.contentMode = .scaleAspectFit + rightImageView.heightAnchor.constraint(equalToConstant: 71.0).isActive = true + rightImageView.widthAnchor.constraint(equalToConstant: 71.0).isActive = true + verticalStack = Stack.createStack(with: [(view: eyebrow, model: StackItemModel(horizontalAlignment: .leading)), + (view: headline, model: StackItemModel(horizontalAlignment: .leading)), + (view: body, model: StackItemModel(horizontalAlignment: .leading)), + (view: body2, model: StackItemModel(horizontalAlignment: .leading)), + (view: button, model: StackItemModel(spacing: 16, horizontalAlignment: .leading))], + axis: .vertical, spacing: 0) + stack = Stack.createStack(with: [(view: verticalStack, model: StackItemModel(horizontalAlignment: .leading, verticalAlignment: .leading)), + (view: rightImageView, model: StackItemModel(horizontalAlignment: .fill, verticalAlignment: .center))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - MFViewProtocol + open override func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + verticalStack.restack() + } + + // MARK: - ModelMoleculeViewProtocol + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListDeviceComplexButtonSmallModel else { return } + verticalStack.updateContainedMolecules(with: [model.eyebrow, + model.headline, + model.body, + model.body2, + model.button], + delegateObject, additionalData) + rightImageView.set(with: model.image, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 120 + } + + public func setDefault() { + eyebrow.styleRegularMicro(true) + headline.styleBoldTitleMedium(true) + body.styleRegularBodySmall(true) + body2.styleRegularBodySmall(true) + eyebrow.textColor = .mvmCoolGray6 + } + + open override func reset() { + super.reset() + setDefault() + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmallModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmallModel.swift new file mode 100644 index 00000000..25de6eab --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmallModel.swift @@ -0,0 +1,68 @@ +// +// ListDeviceComplexButtonSmallModel.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 24/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +public class ListDeviceComplexButtonSmallModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "listDvcBtnS" + public var eyebrow: LabelModel? + public var headline: LabelModel? + public var body: LabelModel? + public var body2: LabelModel? + public var button: ButtonModel + public var image: ImageViewModel + + public init(eyebrow: LabelModel, headline:LabelModel, body: LabelModel, body2: LabelModel, button: ButtonModel, image: ImageViewModel) { + self.eyebrow = eyebrow + self.headline = headline + self.body = body + self.body2 = body2 + self.button = button + self.image = image + super.init() + } + + /// Defaults to set + override public func setDefaults() { + super.setDefaults() + button.size = .tiny + button.style = .secondary + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case eyebrow + case headline + case body + case body2 + case button + case image + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + eyebrow = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .eyebrow) + headline = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .headline) + body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) + body2 = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body2) + button = try typeContainer.decode(ButtonModel.self, forKey: .button) + image = try typeContainer.decode(ImageViewModel.self, forKey: .image) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(eyebrow, forKey: .eyebrow) + try container.encodeIfPresent(headline, forKey: .headline) + try container.encodeIfPresent(body, forKey: .body) + try container.encodeIfPresent(body2, forKey: .body2) + try container.encode(button, forKey: .button) + try container.encode(image, forKey: .image) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift index f6e18184..037ca83d 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift @@ -16,7 +16,7 @@ import Foundation public let headline = Label.createLabelBoldTitleMedium(true) public let body = Label.createLabelRegularBodySmall(true) public let body2 = Label.createLabelRegularBodySmall(true) - public let link = Link() + public let twoLinkView = TwoLinkView() public let rightImage = MFLoadImageView() let verticalStack: Stack public let stack: Stack @@ -26,11 +26,14 @@ import Foundation //------------------------------------------------------ public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { rightImage.addSizeConstraintsForAspectRatio = true + rightImage.imageView.contentMode = .scaleAspectFit rightImage.heightAnchor.constraint(equalToConstant: 116.0).isActive = true rightImage.widthAnchor.constraint(equalToConstant: 116.0).isActive = true - verticalStack = Stack.createStack(with: [(view: eyebrow, model: StackItemModel(horizontalAlignment: .leading)), (view: headline, model: StackItemModel(horizontalAlignment: .leading)), (view: body, model: StackItemModel(horizontalAlignment: .leading)), (view: body2, model: StackItemModel(horizontalAlignment: .leading)), (view: link, model: StackItemModel(spacing: 16, horizontalAlignment: .leading))], axis: .vertical, spacing: 0) + verticalStack = Stack.createStack(with: [(view: eyebrow, model: StackItemModel(horizontalAlignment: .leading)), (view: headline, model: StackItemModel(horizontalAlignment: .leading)), (view: body, model: StackItemModel(horizontalAlignment: .leading)), (view: body2, model: StackItemModel(horizontalAlignment: .leading)), (view: twoLinkView, model: StackItemModel(spacing: 16, horizontalAlignment: .leading))], axis: .vertical, spacing: 0) - stack = Stack.createStack(with: [(view: verticalStack, model: StackItemModel(horizontalAlignment: .leading)), (view: rightImage, model: StackItemModel(verticalAlignment: .center))], axis: .horizontal) + stack = Stack.createStack(with: [(view: verticalStack, model: StackItemModel(horizontalAlignment: .leading, verticalAlignment: .leading)), + (view: rightImage, model: StackItemModel(verticalAlignment: .center))], + axis: .horizontal) super.init(style: style, reuseIdentifier: reuseIdentifier) } @@ -54,7 +57,7 @@ import Foundation open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? ListDeviceComplexLinkMediumModel else { return } - verticalStack.updateContainedMolecules(with: [model.eyebrow, model.headline, model.body, model.body2, model.link], delegateObject, additionalData) + verticalStack.updateContainedMolecules(with: [model.eyebrow, model.headline, model.body, model.body2, model.twoLinkView], delegateObject, additionalData) rightImage.set(with: model.image, delegateObject, additionalData) } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMediumModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMediumModel.swift index c07d7788..0e33d2bf 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMediumModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMediumModel.swift @@ -13,15 +13,15 @@ public class ListDeviceComplexLinkMediumModel: ListItemModel, MoleculeModelProto public var headline: LabelModel? public var body: LabelModel? public var body2: LabelModel? - public var link: LinkModel + public var twoLinkView: TwoLinkViewModel public var image: ImageViewModel - public init(eyebrow: LabelModel, headline: LabelModel, body: LabelModel, body2: LabelModel, link: LinkModel, image: ImageViewModel) { + public init(eyebrow: LabelModel, headline: LabelModel, body: LabelModel, body2: LabelModel, twoLinkView: TwoLinkViewModel, image: ImageViewModel) { self.eyebrow = eyebrow self.headline = headline self.body = body self.body2 = body2 - self.link = link + self.twoLinkView = twoLinkView self.image = image super.init() } @@ -32,7 +32,7 @@ public class ListDeviceComplexLinkMediumModel: ListItemModel, MoleculeModelProto case headline case body case body2 - case link + case twoLinkView case image } @@ -42,7 +42,7 @@ public class ListDeviceComplexLinkMediumModel: ListItemModel, MoleculeModelProto headline = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .headline) body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) body2 = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body2) - link = try typeContainer.decode(LinkModel.self, forKey: .link) + twoLinkView = try typeContainer.decode(TwoLinkViewModel.self, forKey: .twoLinkView) image = try typeContainer.decode(ImageViewModel.self, forKey: .image) try super.init(from: decoder) } @@ -55,7 +55,7 @@ public class ListDeviceComplexLinkMediumModel: ListItemModel, MoleculeModelProto try container.encodeIfPresent(headline, forKey: .headline) try container.encodeIfPresent(body, forKey: .body) try container.encodeIfPresent(body2, forKey: .body2) - try container.encode(link, forKey: .link) + try container.encode(twoLinkView, forKey: .twoLinkView) try container.encode(image, forKey: .image) } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift index c57b3b18..1650ec20 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift @@ -16,7 +16,7 @@ import Foundation public let headline = Label.createLabelBoldTitleMedium(true) public let body = Label.createLabelRegularBodySmall(true) public let body2 = Label.createLabelRegularBodySmall(true) - public let link = Link() + public let twoLinkView = TwoLinkView() public let rightImage = MFLoadImageView() let verticalStack: Stack public let stack: Stack @@ -26,11 +26,14 @@ import Foundation //------------------------------------------------------ public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { rightImage.addSizeConstraintsForAspectRatio = true + rightImage.imageView.contentMode = .scaleAspectFit rightImage.heightAnchor.constraint(equalToConstant: 71.0).isActive = true rightImage.widthAnchor.constraint(equalToConstant: 71.0).isActive = true - verticalStack = Stack.createStack(with: [(view: eyebrow, model: StackItemModel(horizontalAlignment: .leading)), (view: headline, model: StackItemModel(horizontalAlignment: .leading)), (view: body, model: StackItemModel(horizontalAlignment: .leading)), (view: body2, model: StackItemModel(horizontalAlignment: .leading)), (view: link, model: StackItemModel(spacing: 16, horizontalAlignment: .leading))], axis: .vertical, spacing: 0) + verticalStack = Stack.createStack(with: [(view: eyebrow, model: StackItemModel(horizontalAlignment: .leading)), (view: headline, model: StackItemModel(horizontalAlignment: .leading)), (view: body, model: StackItemModel(horizontalAlignment: .leading)), (view: body2, model: StackItemModel(horizontalAlignment: .leading)), (view: twoLinkView, model: StackItemModel(spacing: 16, horizontalAlignment: .leading))], axis: .vertical, spacing: 0) - stack = Stack.createStack(with: [(view: verticalStack, model: StackItemModel(horizontalAlignment: .leading)), (view: rightImage, model: StackItemModel(verticalAlignment: .center))], axis: .horizontal) + stack = Stack.createStack(with: [(view: verticalStack, model: StackItemModel(horizontalAlignment: .leading, verticalAlignment: .leading)), + (view: rightImage, model: StackItemModel(verticalAlignment: .center))], + axis: .horizontal) super.init(style: style, reuseIdentifier: reuseIdentifier) } @@ -54,7 +57,7 @@ import Foundation open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? ListDeviceComplexLinkSmallModel else { return } - verticalStack.updateContainedMolecules(with: [model.eyebrow, model.headline, model.body, model.body2, model.link], delegateObject, additionalData) + verticalStack.updateContainedMolecules(with: [model.eyebrow, model.headline, model.body, model.body2, model.twoLinkView], delegateObject, additionalData) rightImage.set(with: model.image, delegateObject, additionalData) } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmallModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmallModel.swift index a0b3047b..f8784320 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmallModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmallModel.swift @@ -13,15 +13,15 @@ public class ListDeviceComplexLinkSmallModel: ListItemModel, MoleculeModelProtoc public var headline: LabelModel? public var body: LabelModel? public var body2: LabelModel? - public var link: LinkModel + public var twoLinkView: TwoLinkViewModel public var image: ImageViewModel - public init(eyebrow: LabelModel, headline: LabelModel, body: LabelModel, body2: LabelModel, link: LinkModel, image: ImageViewModel) { + public init(eyebrow: LabelModel, headline: LabelModel, body: LabelModel, body2: LabelModel, twoLinkView: TwoLinkViewModel, image: ImageViewModel) { self.eyebrow = eyebrow self.headline = headline self.body = body self.body2 = body2 - self.link = link + self.twoLinkView = twoLinkView self.image = image super.init() } @@ -32,7 +32,7 @@ public class ListDeviceComplexLinkSmallModel: ListItemModel, MoleculeModelProtoc case headline case body case body2 - case link + case twoLinkView case image } @@ -42,7 +42,7 @@ public class ListDeviceComplexLinkSmallModel: ListItemModel, MoleculeModelProtoc headline = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .headline) body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) body2 = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body2) - link = try typeContainer.decode(LinkModel.self, forKey: .link) + twoLinkView = try typeContainer.decode(TwoLinkViewModel.self, forKey: .twoLinkView) image = try typeContainer.decode(ImageViewModel.self, forKey: .image) try super.init(from: decoder) } @@ -55,7 +55,7 @@ public class ListDeviceComplexLinkSmallModel: ListItemModel, MoleculeModelProtoc try container.encodeIfPresent(headline, forKey: .headline) try container.encodeIfPresent(body, forKey: .body) try container.encodeIfPresent(body2, forKey: .body2) - try container.encode(link, forKey: .link) + try container.encode(twoLinkView, forKey: .twoLinkView) try container.encode(image, forKey: .image) } } diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoLinkView.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoLinkView.swift new file mode 100644 index 00000000..ed2ff2fb --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoLinkView.swift @@ -0,0 +1,109 @@ +// +// TwoLinkView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/29/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class TwoLinkView: View, MVMCoreUIViewConstrainingProtocol { + open var leftLink = Link() + open var rightLink = Link() + private var stack = UIStackView() + + public init() { + super.init(frame: .zero) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + // MARK: - MVMCoreViewProtocol + open override func updateView(_ size: CGFloat) { + super.updateView(size) + stack.updateView(size) + } + + open override func setupView() { + super.setupView() + + stack.translatesAutoresizingMaskIntoConstraints = false + addSubview(stack) + stack.addArrangedSubview(leftLink) + stack.addArrangedSubview(rightLink) + NSLayoutConstraint.constraintPinSubview(toSuperview: stack) + stack.axis = .horizontal + stack.spacing = 8 + } + + // MARK: - Stack Manipulation + public func showRightLink() { + if !stack.arrangedSubviews.contains(rightLink) { + stack.addArrangedSubview(rightLink) + rightLink.isHidden = false + } + } + + public func showLeftLink() { + if !stack.arrangedSubviews.contains(leftLink) { + stack.insertArrangedSubview(leftLink, at: 0) + leftLink.isHidden = false + } + } + + public func hideRightLink() { + if rightLink.superview != nil { + stack.removeArrangedSubview(rightLink) + rightLink.isHidden = true + } + } + + public func hideLeftLink() { + if leftLink.superview != nil { + stack.removeArrangedSubview(leftLink) + leftLink.isHidden = true + } + } + + // MARK: - MoleculeViewProtocol + open override func reset() { + super.reset() + stack.reset() + } + + // MARK: - MVMCoreUIViewConstrainingProtocol + open func horizontalAlignment() -> UIStackView.Alignment { + return .center + } + + // MARK: - MoleculeViewProtocol + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 16 + } + + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + + guard let model = model as? TwoLinkViewModel else { return } + + if let model = model.leftLink { + showLeftLink() + leftLink.set(with: model, delegateObject, additionalData) + } else { + hideLeftLink() + } + if let model = model.rightLink { + showRightLink() + rightLink.set(with: model, delegateObject, additionalData) + } else { + hideRightLink() + } + } +} diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoLinkViewModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoLinkViewModel.swift new file mode 100644 index 00000000..57962c09 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoLinkViewModel.swift @@ -0,0 +1,43 @@ +// +// TwoLinkViewModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/29/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class TwoLinkViewModel: MoleculeModelProtocol { + public static var identifier: String = "twoLinkView" + public var backgroundColor: Color? + public var rightLink: LinkModel? + public var leftLink: LinkModel? + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case rightLink + case leftLink + } + + public init(_ rightLink: LinkModel?, _ leftLink: LinkModel?) { + self.rightLink = rightLink + self.leftLink = leftLink + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + rightLink = try typeContainer.decodeIfPresent(LinkModel.self, forKey: .rightLink) + leftLink = try typeContainer.decodeIfPresent(LinkModel.self, forKey: .leftLink) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(rightLink, forKey: .rightLink) + try container.encodeIfPresent(leftLink, forKey: .leftLink) + } +} diff --git a/MVMCoreUI/Atomic/Templates/ThreeLayerFillMiddleTemplate.swift b/MVMCoreUI/Atomic/Templates/ThreeLayerFillMiddleTemplate.swift new file mode 100644 index 00000000..50e55a45 --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/ThreeLayerFillMiddleTemplate.swift @@ -0,0 +1,21 @@ +// +// ThreeLayerFillMiddleTemplate.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/28/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + + +@objcMembers open class ThreeLayerFillMiddleTemplate: ThreeLayerTemplate { + open override func spaceBetweenMiddleAndBottom() -> CGFloat? { + return 0 + } + + open override func handleNewData() { + super.handleNewData() + heightConstraint?.isActive = true + } +} diff --git a/MVMCoreUI/Atomic/Templates/ThreeLayerFillMiddleTemplateModel.swift b/MVMCoreUI/Atomic/Templates/ThreeLayerFillMiddleTemplateModel.swift new file mode 100644 index 00000000..7ea1aa70 --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/ThreeLayerFillMiddleTemplateModel.swift @@ -0,0 +1,15 @@ +// +// ThreeLayerFillMiddleTemplateModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/28/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class ThreeLayerFillMiddleTemplateModel: ThreeLayerPageTemplateModel { + public override class var identifier: String { + return "threeLayerFillMiddle" + } +} diff --git a/MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift b/MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift index 4884d5b1..c393297b 100644 --- a/MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift @@ -49,4 +49,8 @@ import UIKit open override func spaceBetweenTopAndMiddle() -> CGFloat? { return 0 } + + open override func spaceBetweenMiddleAndBottom() -> CGFloat? { + return nil + } } diff --git a/MVMCoreUI/BaseClasses/TextField.swift b/MVMCoreUI/BaseClasses/TextField.swift index c571d52d..8795054b 100644 --- a/MVMCoreUI/BaseClasses/TextField.swift +++ b/MVMCoreUI/BaseClasses/TextField.swift @@ -8,8 +8,8 @@ import UIKit -public protocol TextFieldDidDeleteProtocol: class { - func textFieldDidDelete() +public protocol TextInputDidDeleteProtocol: class { + func textInputDidDelete() } @@ -17,6 +17,7 @@ public protocol TextFieldDidDeleteProtocol: class { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- + private var initialSetupPerformed = false /// Set to true to hide the blinking textField cursor. @@ -27,7 +28,7 @@ public protocol TextFieldDidDeleteProtocol: class { //-------------------------------------------------- /// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well. - public weak var didDeleteDelegate: TextFieldDidDeleteProtocol? + public weak var didDeleteDelegate: TextInputDidDeleteProtocol? //-------------------------------------------------- // MARK: - Initialization @@ -50,7 +51,7 @@ public protocol TextFieldDidDeleteProtocol: class { public func initialSetup() { if !initialSetupPerformed { - tintColor = .black + tintColor = .mvmBlack initialSetupPerformed = true setupView() } @@ -68,7 +69,7 @@ public protocol TextFieldDidDeleteProtocol: class { open override func deleteBackward() { super.deleteBackward() - didDeleteDelegate?.textFieldDidDelete() + didDeleteDelegate?.textInputDidDelete() } } @@ -86,7 +87,9 @@ extension TextField: MVMCoreViewProtocol { /// MARK:- MoleculeViewProtocol extension TextField: MoleculeViewProtocol { - open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + + open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + if let color = model.backgroundColor?.uiColor { backgroundColor = color } diff --git a/MVMCoreUI/BaseClasses/TextView.swift b/MVMCoreUI/BaseClasses/TextView.swift new file mode 100644 index 00000000..191806bb --- /dev/null +++ b/MVMCoreUI/BaseClasses/TextView.swift @@ -0,0 +1,490 @@ +// +// TextView.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/1/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + + +@objc open class TextView: UITextView, UITextViewDelegate, MVMCoreViewProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + open var model: MoleculeModelProtocol? + + private var initialSetupPerformed = false + + /// If true then text textView is currently displaying the stored placeholder text as there is not content to display. + public var isShowingPlaceholder: Bool = false { + didSet { textViewModel?.showsPlaceholder = isShowingPlaceholder } + } + + /// Set to true to hide the blinking textField cursor. + public var hideBlinkingCaret = false + + public var textViewModel: TextViewModel? { + return model as? TextViewModel + } + + //-------------------------------------------------- + // MARK: - Drawing Properties + //-------------------------------------------------- + + private(set) var fieldState: FieldState = .original { + didSet (oldState) { + // Will not update if new state is the same as old. + if fieldState != oldState { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + + self.fieldState.setStateUI(for: self) + } + } + } + } + + /// Determines if the top, left, and right borders should be drawn. + private var hideBorders = false + + public var borderStrokeColor: UIColor = .mvmCoolGray3 + public var bottomStrokeColor: UIColor = .mvmBlack + private var borderPath: UIBezierPath = UIBezierPath() + private var bottomPath: UIBezierPath = UIBezierPath() + + //-------------------------------------------------- + // MARK: - Property Observers + //-------------------------------------------------- + + private var _isEnabled: Bool = true + private var _showError: Bool = false + private var _isSelected: Bool = false + + public var isEnabled: Bool { + get { return _isEnabled } + set (enabled) { + + _isEnabled = enabled + _isSelected = false + _showError = false + + fieldState = enabled ? .original : .disabled + } + } + + public var showError: Bool { + get { return _showError } + set (error) { + + _showError = error + _isEnabled = true + _isSelected = false + + fieldState = error ? .error : .original + } + } + + public var isSelected: Bool { + get { return _isSelected } + set (selected) { + + _isSelected = selected + _isEnabled = true + + if _showError { + fieldState = selected ? .selectedError : .error + } else { + fieldState = selected ? .selected : .original + } + } + } + + //-------------------------------------------------- + // MARK: - Delegate + //-------------------------------------------------- + + /// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well. + public weak var didDeleteDelegate: TextInputDidDeleteProtocol? + + /// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well. + private weak var proprietorTextDelegate: UITextViewDelegate? + + /// If you're using a ViewController, you must set this to it. + public weak var uiTextViewDelegate: UITextViewDelegate? { + get { return delegate } + set { + delegate = self + proprietorTextDelegate = newValue + } + } + + var delegateObject: MVMCoreUIDelegateObject? + + //-------------------------------------------------- + // MARK: - Constraint + //-------------------------------------------------- + + public var heightConstraint: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Initialization + //-------------------------------------------------- + + public override init(frame: CGRect, textContainer: NSTextContainer?) { + super.init(frame: .zero, textContainer: nil) + initialSetup() + } + + public convenience init() { + self.init(frame: .zero, textContainer: nil) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + convenience init(delegate: UITextViewDelegate) { + self.init(frame: .zero, textContainer: nil) + self.delegate = delegate + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + public func initialSetup() { + + if !initialSetupPerformed { + tintColor = .mvmBlack + initialSetupPerformed = true + setupView() + } + } + + open func updateView(_ size: CGFloat) { + + setNeedsDisplay() + } + + /// Will be called only once. + open func setupView() { + + translatesAutoresizingMaskIntoConstraints = false + initialConfiguration() + } + + public func initialConfiguration() { + + insetsLayoutMarginsFromSafeArea = false + showsVerticalScrollIndicator = false + showsHorizontalScrollIndicator = false + isSecureTextEntry = false + textContainerInset = UIEdgeInsets(top: Padding.Three, left: Padding.Three, bottom: Padding.Three, right: Padding.Three) + backgroundColor = .mvmWhite + clipsToBounds = true + smartQuotesType = .no + smartDashesType = .no + smartInsertDeleteType = .no + inputAccessoryView = nil + font = textViewModel?.fontStyle.getFont() + isEditable = true + isOpaque = false + } + + open func reset() { + + text = "" + inputAccessoryView?.removeFromSuperview() + inputAccessoryView = nil + initialConfiguration() + } + + open override func layoutSubviews() { + super.layoutSubviews() + + setNeedsDisplay() + } + + //-------------------------------------------------- + // MARK: - Draw + //-------------------------------------------------- + + /// This handles the top, left, and right border lines. + open override func draw(_ rect: CGRect) { + super.draw(rect) + + borderPath.removeAllPoints() + bottomPath.removeAllPoints() + + if !hideBorders { + // Brings the other half of the line inside the view to prevent line cropping. + let origin = bounds.origin + let size = frame.size + let insetLean: CGFloat = 0.5 + borderPath.lineWidth = 1 + + // Drawing begins and ends from the bottom left. + borderPath.move(to: CGPoint(x: origin.x + insetLean, y: origin.y + size.height)) + borderPath.addLine(to: CGPoint(x: origin.x + insetLean, y: origin.y + insetLean)) + borderPath.addLine(to: CGPoint(x: origin.x + size.width - insetLean, y: origin.y + insetLean)) + borderPath.addLine(to: CGPoint(x: origin.x + size.width - insetLean, y: origin.y + size.height)) + + borderStrokeColor.setStroke() + borderPath.stroke() + + let lineWidth: CGFloat = showError || isSelected ? 4 : 1 + bottomPath.lineWidth = lineWidth + bottomPath.move(to: CGPoint(x: origin.x + size.width, y: origin.y + size.height - (lineWidth / 2))) + bottomPath.addLine(to: CGPoint(x: origin.x, y: origin.y + size.height - (lineWidth / 2))) + + bottomStrokeColor.setStroke() + bottomPath.stroke() + } + } + + //-------------------------------------------------- + // MARK: - Draw States + //-------------------------------------------------- + + public enum FieldState { + case original + case error + case selectedError + case selected + case disabled + + public func setStateUI(for inputField: TextView) { + + switch self { + case .original: + inputField.originalUI() + + case .error: + inputField.errorUI() + + case .selectedError: + inputField.selectedErrorUI() + + case .selected: + inputField.selectedUI() + + case .disabled: + inputField.disabledUI() + } + + inputField.setNeedsDisplay() + } + } + + open func originalUI() { + + isEditable = textViewModel?.editable ?? true + isUserInteractionEnabled = true + hideBorders = textViewModel?.hideBorders ?? false + borderStrokeColor = .mvmCoolGray3 + bottomStrokeColor = .mvmBlack + textColor = isShowingPlaceholder ? textViewModel?.placeholderTextColor.uiColor : textViewModel?.enabledTextColor.uiColor + } + + open func errorUI() { + + isEditable = textViewModel?.editable ?? true + isUserInteractionEnabled = true + hideBorders = textViewModel?.hideBorders ?? false + borderStrokeColor = .mvmOrange + bottomStrokeColor = .mvmOrange + textColor = textViewModel?.enabledTextColor.uiColor + } + + open func selectedErrorUI() { + + isEditable = textViewModel?.editable ?? true + isUserInteractionEnabled = true + hideBorders = textViewModel?.hideBorders ?? false + borderStrokeColor = .mvmBlack + bottomStrokeColor = .mvmOrange + textColor = textViewModel?.enabledTextColor.uiColor + } + + open func selectedUI() { + + isEditable = textViewModel?.editable ?? true + isUserInteractionEnabled = true + hideBorders = textViewModel?.hideBorders ?? false + borderStrokeColor = .mvmBlack + bottomStrokeColor = .mvmBlack + textColor = textViewModel?.enabledTextColor.uiColor + } + + open func disabledUI() { + + isEditable = textViewModel?.editable ?? false + isUserInteractionEnabled = false + hideBorders = textViewModel?.hideBorders ?? false + borderStrokeColor = .mvmCoolGray3 + bottomStrokeColor = .mvmCoolGray3 + textColor = textViewModel?.disabledTextColor.uiColor + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + /// Alters the blinking caret line as per design standards. + open override func caretRect(for position: UITextPosition) -> CGRect { + + if hideBlinkingCaret { + return .zero + } + + let caretRect = super.caretRect(for: position) + return CGRect(origin: caretRect.origin, size: CGSize(width: 1, height: caretRect.height)) + } + + open override func deleteBackward() { + super.deleteBackward() + didDeleteDelegate?.textInputDidDelete() + } + + public func setTextAppearance() { + + if isShowingPlaceholder { + setTextContentTraits() + } + } + + public func setPlaceholderIfAvailable() { + + if let placeholder = textViewModel?.placeholder, !placeholder.isEmpty && text.isEmpty { + setPlaceholderContentTraits() + } + } + + public func setTextContentTraits() { + + isShowingPlaceholder = false + text = "" + font = textViewModel?.fontStyle.getFont() + textColor = textViewModel?.enabledTextColor.uiColor + } + + public func setPlaceholderContentTraits() { + + isShowingPlaceholder = true + textColor = textViewModel?.placeholderTextColor.uiColor + font = textViewModel?.placeholderFontStyle.getFont() + text = textViewModel?.placeholder + } + + @objc func dismissFieldInput(_ sender: TextView) { + + resignFirstResponder() + } + + //-------------------------------------------------- + // MARK: - UITextViewDelegate + //-------------------------------------------------- + + @objc public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { + + return proprietorTextDelegate?.textViewShouldBeginEditing?(textView) ?? true + } + + @objc public func textViewDidBeginEditing(_ textView: UITextView) { + + setTextAppearance() + isSelected = true + proprietorTextDelegate?.textViewDidBeginEditing?(textView) + } + + @objc public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + + return proprietorTextDelegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text) ?? true + } + + @objc public func textViewDidChange(_ textView: UITextView) { + + textViewModel?.text = textView.text + proprietorTextDelegate?.textViewDidChange?(textView) + } + + @objc public func textViewShouldEndEditing(_ textView: UITextView) -> Bool { + + return proprietorTextDelegate?.textViewShouldEndEditing?(textView) ?? true + } + + @objc public func textViewDidEndEditing(_ textView: UITextView) { + + setPlaceholderIfAvailable() + isSelected = false + proprietorTextDelegate?.textViewDidEndEditing?(textView) + } +} + +// MARK:- MoleculeViewProtocol +extension TextView: MoleculeViewProtocol { + + open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + self.model = model + self.delegateObject = delegateObject + + if let color = model.backgroundColor?.uiColor { + backgroundColor = color + } + + guard let model = model as? TextViewModel else { return } + + heightConstraint?.isActive = false + if let height = model.height { + heightConstraint = heightAnchor.constraint(equalToConstant: height) + heightConstraint?.isActive = true + } + + isEditable = model.editable + textAlignment = model.textAlignment + textColor = model.enabledTextColor.uiColor + hideBorders = model.hideBorders + text = model.text + uiTextViewDelegate = delegateObject?.uiTextViewDelegate + + if let accessibilityText = model.accessibilityText { + accessibilityLabel = accessibilityText + } + + switch model.type { + case .secure, .password: + isSecureTextEntry = true + case .number: + keyboardType = .numberPad + case .email: + keyboardType = .emailAddress + default: + break + } + + font = model.fontStyle.getFont() + setPlaceholderIfAvailable() + + if isEditable { + FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) + let observingDelegate = delegateObject?.uiTextViewDelegate ?? self + inputAccessoryView = UIToolbar.getToolbarWithDoneButton(delegate: observingDelegate, + action: #selector(dismissFieldInput)) + + if (model.selected ?? false) && !model.wasInitiallySelected { + model.wasInitiallySelected = true + DispatchQueue.main.async { + self.becomeFirstResponder() + } + } + } + + if !model.enabled { + isEnabled = false + } + } +} diff --git a/MVMCoreUI/BaseClasses/TextViewModel.swift b/MVMCoreUI/BaseClasses/TextViewModel.swift new file mode 100644 index 00000000..49ac664c --- /dev/null +++ b/MVMCoreUI/BaseClasses/TextViewModel.swift @@ -0,0 +1,95 @@ +// +// TextViewModel.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/2/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + + +open class TextViewModel: TextEntryFieldModel { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public override class var identifier: String { + return "textView" + } + + public var accessibilityText: String? + public var fontStyle: Styler.Font = Styler.Font.RegularBodySmall + public var textAlignment: NSTextAlignment = .left + public var height: CGFloat? + public var placeholderTextColor: Color = Color(uiColor: .mvmCoolGray3) + public var placeholderFontStyle: Styler.Font = Styler.Font.RegularMicro + public var showsPlaceholder: Bool = false + public var hideBorders: Bool = false + public var editable: Bool = true + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case text + case accessibilityText + case fontStyle + case textAlignment + case height + case hideBorders + case placeholderFontStyle + case placeholderTextColor + case editable + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + try super.init(from: decoder) + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + if let placeholderFontStyle = try typeContainer.decodeIfPresent(Styler.Font.self, forKey: .placeholderFontStyle) { + self.placeholderFontStyle = placeholderFontStyle + } + + if let placeholderTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .placeholderTextColor) { + self.placeholderTextColor = placeholderTextColor + } + + if let textAlignment = try typeContainer.decodeIfPresent(NSTextAlignment.self, forKey: .textAlignment) { + self.textAlignment = textAlignment + } + + if let hideBorders = try typeContainer.decodeIfPresent(Bool.self, forKey: .hideBorders) { + self.hideBorders = hideBorders + } + + if let editable = try typeContainer.decodeIfPresent(Bool.self, forKey: .editable) { + self.editable = editable + } + + if let fontStyle = try typeContainer.decodeIfPresent(Styler.Font.self, forKey: .fontStyle) { + self.fontStyle = fontStyle + } + + accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) + height = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .height) + } + + public override func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) + try container.encodeIfPresent(height, forKey: .height) + try container.encode(fontStyle, forKey: .fontStyle) + try container.encode(hideBorders, forKey: .hideBorders) + try container.encode(text, forKey: .text) + try container.encode(placeholderFontStyle, forKey: .placeholderFontStyle) + try container.encode(placeholderTextColor, forKey: .placeholderTextColor) + try container.encode(textAlignment, forKey: .textAlignment) + try container.encode(editable, forKey: .editable) + } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIDelegateObject.swift b/MVMCoreUI/OtherHandlers/MVMCoreUIDelegateObject.swift index c9d5d34c..c63c4a87 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIDelegateObject.swift +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIDelegateObject.swift @@ -8,10 +8,13 @@ import UIKit + open class MVMCoreUIDelegateObject: DelegateObject { + public weak var formHolderDelegate: FormHolderProtocol? public weak var buttonDelegate: ButtonDelegateProtocol? public weak var uiTextFieldDelegate: UITextFieldDelegate? + public weak var uiTextViewDelegate: UITextViewDelegate? public weak var observingTextFieldDelegate: ObservingTextFieldDelegate? public weak var moleculeDelegate: MoleculeDelegateProtocol? @@ -20,6 +23,7 @@ open class MVMCoreUIDelegateObject: DelegateObject { formHolderDelegate = delegate as? FormHolderProtocol buttonDelegate = delegate as? ButtonDelegateProtocol uiTextFieldDelegate = delegate as? UITextFieldDelegate + uiTextViewDelegate = delegate as? UITextViewDelegate observingTextFieldDelegate = delegate as? ObservingTextFieldDelegate moleculeDelegate = delegate as? MoleculeDelegateProtocol } @@ -28,4 +32,3 @@ open class MVMCoreUIDelegateObject: DelegateObject { return controller?.delegateObject?() as? MVMCoreUIDelegateObject } } - diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift index 5cb2abdb..36f13be0 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift @@ -23,6 +23,7 @@ public extension MVMCoreUIViewControllerMappingObject { register(template: ThreeLayerTemplate.self) register(template: ThreeLayerCenterTemplate.self) + register(template: ThreeLayerFillMiddleTemplate.self) register(template: CollectionTemplate.self) } diff --git a/MVMCoreUI/Styles/Styler.swift b/MVMCoreUI/Styles/Styler.swift index 509ad107..863d4703 100644 --- a/MVMCoreUI/Styles/Styler.swift +++ b/MVMCoreUI/Styles/Styler.swift @@ -142,7 +142,7 @@ open class Styler { } /// Returns the font based on the declared enum case. - public func getFont(_ genericScaling: Bool = true) -> UIFont? { + public func getFont(_ genericScaling: Bool = true) -> UIFont { let size = genericScaling ? sizeFontGeneric(forCurrentDevice: pointSize()) : pointSize()