diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index d0d68469..9079fc39 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -92,6 +92,7 @@ 0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86223D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift */; }; 0A7EF86523D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */; }; 0A7EF86723D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift */; }; + 0A849EFE246F1775009F277F /* RuleEqualsIgnoreCaseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A849EFD246F1775009F277F /* RuleEqualsIgnoreCaseModel.swift */; }; 0A9D091D2433796500D2E6C0 /* BarsCarouselIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D09172433796500D2E6C0 /* BarsCarouselIndicatorModel.swift */; }; 0A9D091E2433796500D2E6C0 /* NumericCarouselIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D09182433796500D2E6C0 /* NumericCarouselIndicatorModel.swift */; }; 0A9D091F2433796500D2E6C0 /* NumericIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9D09192433796500D2E6C0 /* NumericIndicatorView.swift */; }; @@ -109,8 +110,12 @@ 0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB423FF18D2004C5109 /* Arrow.swift */; }; 0AE98BB723FF18E9004C5109 /* ArrowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB623FF18E9004C5109 /* ArrowModel.swift */; }; 279B1569242BBC2F00921D6C /* ActionModelAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279B1568242BBC2F00921D6C /* ActionModelAdapter.swift */; }; + 27F973532466074500CAB5C5 /* PageBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27F973522466074500CAB5C5 /* PageBehavior.swift */; }; + 27F9736A246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */; }; 31BE15CB23D8924D00452370 /* CheckboxLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31BE15C923D8924C00452370 /* CheckboxLabelModel.swift */; }; 31BE15CC23D8924D00452370 /* CheckboxModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31BE15CA23D8924C00452370 /* CheckboxModel.swift */; }; + 32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */; }; + 32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */; }; 522679C123FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */; }; 522679C223FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */; }; 52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */; }; @@ -147,8 +152,6 @@ 8DE5BECF2456F7B100772E76 /* ListTwoColumnDropdownSelectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DE5BECE2456F7B100772E76 /* ListTwoColumnDropdownSelectors.swift */; }; 8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DEFA95B243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift */; }; 8DEFA95E243DAC2F000D27E5 /* ListThreeColumnDataUsageDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DEFA95D243DAC2F000D27E5 /* ListThreeColumnDataUsageDivider.swift */; }; - 942C372E241149170066E45E /* NHaasGroteskDSStd-75Bd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 942C372C241149170066E45E /* NHaasGroteskDSStd-75Bd.otf */; }; - 942C372F241149170066E45E /* NHaasGroteskDSStd-55Rg.otf in Resources */ = {isa = PBXBuildFile; fileRef = 942C372D241149170066E45E /* NHaasGroteskDSStd-55Rg.otf */; }; 942C378C2412F4FA0066E45E /* ModalMoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 942C378B2412F4FA0066E45E /* ModalMoleculeListTemplate.swift */; }; 942C378E2412F5B60066E45E /* ModalMoleculeStackTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 942C378D2412F5B60066E45E /* ModalMoleculeStackTemplate.swift */; }; 9432A79F23DB47BA00719041 /* EntryFieldContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9432A79E23DB47BA00719041 /* EntryFieldContainer.swift */; }; @@ -197,8 +200,12 @@ AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69AAF52445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift */; }; AA69AAF82445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69AAF72445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift */; }; AA85236C244435A20059CC1E /* RadioSwatchCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */; }; + AA9972502475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA99724F2475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift */; }; + AA997252247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA997251247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift */; }; AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */; }; AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */; }; + AAB7EDEF246ADA1600E54929 /* ListProgressBarThinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB7EDEE246ADA1600E54929 /* ListProgressBarThinModel.swift */; }; + AAB7EDF1246ADA2A00E54929 /* ListProgressBarThin.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB7EDF0246ADA2A00E54929 /* ListProgressBarThin.swift */; }; AAB9C10824346F4B00151545 /* RadioSwatches.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C10724346F4B00151545 /* RadioSwatches.swift */; }; AAB9C10A243496DD00151545 /* RadioSwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C109243496DD00151545 /* RadioSwatch.swift */; }; AAC6F167243332E400F295C1 /* RadioSwatchesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */; }; @@ -248,7 +255,7 @@ D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */; }; D20923592450ECE00044AD09 /* TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20923582450ECE00044AD09 /* TableView.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; - D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */; }; + D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; }; D21B7F71243BAC1600051ABF /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */; }; @@ -266,7 +273,11 @@ D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */; }; D236E5B5241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B3241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift */; }; D236E5B7242007C500C38625 /* MVMControllerModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B6242007C500C38625 /* MVMControllerModelProtocol.swift */; }; + D23EA7E82473654300D60C34 /* PanelNavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA7E72473654300D60C34 /* PanelNavigationItemModelProtocol.swift */; }; + D23EA7FB2475F09800D60C34 /* CarouselItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */; }; D243859923A16B1800332775 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = D243859823A16B1800332775 /* Container.swift */; }; + D2509ED12472ED9B001BFB9D /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */; }; + D2509ED62472EE2F001BFB9D /* NavigationItemButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED52472EE2F001BFB9D /* NavigationItemButtonModel.swift */; }; D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106423D0CEA700764D80 /* StackModel.swift */; }; D253BB9C245874F8002DE544 /* BGImageMolecule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D253BB9B245874F8002DE544 /* BGImageMolecule.swift */; }; D253BB9E2458751F002DE544 /* BGImageMoleculeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D253BB9D2458751F002DE544 /* BGImageMoleculeModel.swift */; }; @@ -290,6 +301,7 @@ D268C70C2386DFFD007F2C1C /* MoleculeStackItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */; }; D268C70E238C22D7007F2C1C /* DropDownFilterTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */; }; D26C5A6B23F4A40D007AEECE /* ListItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D26C5A6A23F4A40D007AEECE /* ListItemModel.swift */; }; + D272F5F92473163100BD1A8F /* BarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D272F5F82473163100BD1A8F /* BarButtonItem.swift */; }; D274CA332236A78900B01B62 /* FooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D274CA322236A78900B01B62 /* FooterView.swift */; }; D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2755D7A23689C7500485468 /* TableViewCell.swift */; }; D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */; }; @@ -505,6 +517,7 @@ 0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDropdownEntryFieldModel.swift; sourceTree = ""; }; 0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateDropdownEntryFieldModel.swift; sourceTree = ""; }; 0A8321AE2355FE9500CB7F00 /* DigitBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitBox.swift; sourceTree = ""; }; + 0A849EFD246F1775009F277F /* RuleEqualsIgnoreCaseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleEqualsIgnoreCaseModel.swift; sourceTree = ""; }; 0A9D09172433796500D2E6C0 /* BarsCarouselIndicatorModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarsCarouselIndicatorModel.swift; sourceTree = ""; }; 0A9D09182433796500D2E6C0 /* NumericCarouselIndicatorModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumericCarouselIndicatorModel.swift; sourceTree = ""; }; 0A9D09192433796500D2E6C0 /* NumericIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumericIndicatorView.swift; sourceTree = ""; }; @@ -523,8 +536,12 @@ 0AE98BB423FF18D2004C5109 /* Arrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arrow.swift; sourceTree = ""; }; 0AE98BB623FF18E9004C5109 /* ArrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowModel.swift; sourceTree = ""; }; 279B1568242BBC2F00921D6C /* ActionModelAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionModelAdapter.swift; sourceTree = ""; }; + 27F973522466074500CAB5C5 /* PageBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageBehavior.swift; sourceTree = ""; }; + 27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenBrightnessModifierBehavior.swift; sourceTree = ""; }; 31BE15C923D8924C00452370 /* CheckboxLabelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxLabelModel.swift; sourceTree = ""; }; 31BE15CA23D8924C00452370 /* CheckboxModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxModel.swift; sourceTree = ""; }; + 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableNumberedListAllTextAndLinksModel.swift; sourceTree = ""; }; + 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableNumberedListAllTextAndLinks.swift; sourceTree = ""; }; 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinks.swift; sourceTree = ""; }; 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinksModel.swift; sourceTree = ""; }; 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextAllTextAndLinks.swift; sourceTree = ""; }; @@ -562,8 +579,6 @@ 8DEFA95B243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnDataUsageDividerModel.swift; sourceTree = ""; }; 8DEFA95D243DAC2F000D27E5 /* ListThreeColumnDataUsageDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnDataUsageDivider.swift; sourceTree = ""; }; 9402C34F23A2CEA3004B974C /* LeftRightLabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftRightLabelModel.swift; sourceTree = ""; }; - 942C372C241149170066E45E /* NHaasGroteskDSStd-75Bd.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NHaasGroteskDSStd-75Bd.otf"; sourceTree = ""; }; - 942C372D241149170066E45E /* NHaasGroteskDSStd-55Rg.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NHaasGroteskDSStd-55Rg.otf"; sourceTree = ""; }; 942C378B2412F4FA0066E45E /* ModalMoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalMoleculeListTemplate.swift; sourceTree = ""; }; 942C378D2412F5B60066E45E /* ModalMoleculeStackTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalMoleculeStackTemplate.swift; sourceTree = ""; }; 9432A79E23DB47BA00719041 /* EntryFieldContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntryFieldContainer.swift; sourceTree = ""; }; @@ -611,8 +626,12 @@ AA69AAF52445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxBodyText.swift; sourceTree = ""; }; AA69AAF72445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxBodyTextModel.swift; sourceTree = ""; }; AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchCollectionViewCell.swift; sourceTree = ""; }; + AA99724F2475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconAllTextLinksModel.swift; sourceTree = ""; }; + AA997251247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconAllTextLinks.swift; sourceTree = ""; }; AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyText.swift; sourceTree = ""; }; AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyTextModel.swift; sourceTree = ""; }; + AAB7EDEE246ADA1600E54929 /* ListProgressBarThinModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListProgressBarThinModel.swift; sourceTree = ""; }; + AAB7EDF0246ADA2A00E54929 /* ListProgressBarThin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListProgressBarThin.swift; sourceTree = ""; }; AAB9C10724346F4B00151545 /* RadioSwatches.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatches.swift; sourceTree = ""; }; AAB9C109243496DD00151545 /* RadioSwatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatch.swift; sourceTree = ""; }; AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchesModel.swift; sourceTree = ""; }; @@ -662,7 +681,7 @@ D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerModelBase.swift; sourceTree = ""; }; D20923582450ECE00044AD09 /* TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableView.swift; sourceTree = ""; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; - D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = ""; }; + D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = ""; }; D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = ""; }; @@ -680,7 +699,11 @@ D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTwoColumnPriceDescription.swift; sourceTree = ""; }; D236E5B3241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTwoColumnPriceDescriptionModel.swift; sourceTree = ""; }; D236E5B6242007C500C38625 /* MVMControllerModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMControllerModelProtocol.swift; sourceTree = ""; }; + D23EA7E72473654300D60C34 /* PanelNavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanelNavigationItemModelProtocol.swift; sourceTree = ""; }; + D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselItemProtocol.swift; sourceTree = ""; }; D243859823A16B1800332775 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; + D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = ""; }; + D2509ED52472EE2F001BFB9D /* NavigationItemButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationItemButtonModel.swift; sourceTree = ""; }; D253BB9B245874F8002DE544 /* BGImageMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageMolecule.swift; sourceTree = ""; }; D253BB9D2458751F002DE544 /* BGImageMoleculeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageMoleculeModel.swift; sourceTree = ""; }; D256E9922412880000360572 /* Header.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = ""; }; @@ -703,6 +726,7 @@ D264FAAB2441009400D98315 /* RadioBoxCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioBoxCollectionViewCell.swift; sourceTree = ""; }; D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDownFilterTableViewCell.swift; sourceTree = ""; }; D26C5A6A23F4A40D007AEECE /* ListItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListItemModel.swift; sourceTree = ""; }; + D272F5F82473163100BD1A8F /* BarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarButtonItem.swift; sourceTree = ""; }; D274CA322236A78900B01B62 /* FooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooterView.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 = ""; }; @@ -850,6 +874,7 @@ children = ( D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */, 014AA72323C501E2006F3E93 /* ContainerModelProtocol.swift */, + D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */, 012A88C3238D86E600FE3DA1 /* CarouselItemModelProtocol.swift */, 012A88B0238C880100FE3DA1 /* CarouselPagingModelProtocol.swift */, 01EB3683236097C0006832FA /* MoleculeModelProtocol.swift */, @@ -858,6 +883,8 @@ 011B58EF23A2AA980085F53C /* ListItemModelProtocol.swift */, D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */, D2092354244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift */, + D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */, + D23EA7E72473654300D60C34 /* PanelNavigationItemModelProtocol.swift */, ); path = ModelProtocols; sourceTree = ""; @@ -873,6 +900,7 @@ 011D95A0240453D0000E3791 /* RuleEqualsModel.swift */, 011D95A2240453F8000E3791 /* RuleRegexModel.swift */, 0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */, + 0A849EFD246F1775009F277F /* RuleEqualsIgnoreCaseModel.swift */, ); name = Rules; path = Rules/Rules; @@ -954,6 +982,15 @@ path = Adapters; sourceTree = ""; }; + 27F973512466071600CAB5C5 /* Behaviors */ = { + isa = PBXGroup; + children = ( + 27F973522466074500CAB5C5 /* PageBehavior.swift */, + 27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */, + ); + path = Behaviors; + sourceTree = ""; + }; 5206F150241144A900658DC5 /* Headers */ = { isa = PBXGroup; children = ( @@ -1248,6 +1285,8 @@ D20492F12434CB5F00A5EED6 /* FourColumn */, AA4FC2A323F4F69600E251DB /* RightVariable */, D22B38EB23F4E0AE00490EF6 /* LeftVariable */, + AAB7EDEE246ADA1600E54929 /* ListProgressBarThinModel.swift */, + AAB7EDF0246ADA2A00E54929 /* ListProgressBarThin.swift */, ); path = List; sourceTree = ""; @@ -1265,6 +1304,10 @@ 8D24041023E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift */, 0A6682A32434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift */, 0A6682A12434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift */, + AA99724F2475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift */, + AA997251247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift */, + 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */, + 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */, ); path = LeftVariable; sourceTree = ""; @@ -1321,6 +1364,15 @@ path = TwoColumn; sourceTree = ""; }; + D2509ED42472EE0B001BFB9D /* NavigationBar */ = { + isa = PBXGroup; + children = ( + D2509ED52472EE2F001BFB9D /* NavigationItemButtonModel.swift */, + D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */, + ); + path = NavigationBar; + sourceTree = ""; + }; D253BB9A24587023002DE544 /* OtherContainers */ = { isa = PBXGroup; children = ( @@ -1391,6 +1443,7 @@ D29DF0CE21E404D4003B2FB9 /* MVMCoreUI */ = { isa = PBXGroup; children = ( + 27F973512466071600CAB5C5 /* Behaviors */, D2C78CD324252F4E00B69FDE /* Atomic */, 012A88EF23985E0100FE3DA1 /* CustomPrimitives */, D2B18B7D236090D500A9AEDC /* BaseClasses */, @@ -1461,6 +1514,7 @@ D29DF10E21E67A77003B2FB9 /* Molecules */ = { isa = PBXGroup; children = ( + D2509ED42472EE0B001BFB9D /* NavigationBar */, D253BB9A24587023002DE544 /* OtherContainers */, D22B38E923F4E07800490EF6 /* DesignedComponents */, D22479912316A9EF003FCCF9 /* Items */, @@ -1474,7 +1528,6 @@ 012A88EB238F084D00FE3DA1 /* FooterModel.swift */, D274CA322236A78900B01B62 /* FooterView.swift */, D260105723CF9CC500764D80 /* Doughnut */, - D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */, ); path = Molecules; sourceTree = ""; @@ -1718,8 +1771,6 @@ 94CA227924058533002D6750 /* VerizonNHGeDS-Regular.otf */, 94CA227824058533002D6750 /* VerizonNHGeTX-Bold.otf */, 94CA227B24058533002D6750 /* VerizonNHGeTX-Regular.otf */, - 942C372D241149170066E45E /* NHaasGroteskDSStd-55Rg.otf */, - 942C372C241149170066E45E /* NHaasGroteskDSStd-75Bd.otf */, D29DF31721ECECC0003B2FB9 /* OCRAExtended.ttf */, ); path = Fonts; @@ -1769,6 +1820,7 @@ D264FAA92440F97600D98315 /* CollectionView.swift */, 0A5D59C323AD488600EFD9E9 /* Protocols */, 0A7918F423F5E7EA00772FF4 /* ImageView.swift */, + D272F5F82473163100BD1A8F /* BarButtonItem.swift */, ); path = BaseClasses; sourceTree = ""; @@ -1905,8 +1957,6 @@ D29DF32C21EE8736003B2FB9 /* Localizable.strings in Resources */, 94CA227D24058534002D6750 /* VerizonNHGeDS-Regular.otf in Resources */, D29DF32E21EE8C3D003B2FB9 /* Media.xcassets in Resources */, - 942C372E241149170066E45E /* NHaasGroteskDSStd-75Bd.otf in Resources */, - 942C372F241149170066E45E /* NHaasGroteskDSStd-55Rg.otf in Resources */, 94CA227E24058534002D6750 /* VerizonNHGeDS-Bold.otf in Resources */, D287651A245B338E00CB882D /* VerizonNHGeTX-Regular.otf in Resources */, D29DF31B21ECECC0003B2FB9 /* OCRAExtended.ttf in Resources */, @@ -1929,6 +1979,8 @@ 8D3BA9BF2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift in Sources */, 94C661DA23CCF4FB00D9FE5B /* UIColor+Extension.swift in Sources */, D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */, + D2509ED62472EE2F001BFB9D /* NavigationItemButtonModel.swift in Sources */, + 32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */, DBC4391822442197001AB423 /* CaretView.swift in Sources */, C07065C42395677300FBF997 /* Link.swift in Sources */, 0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */, @@ -1955,6 +2007,7 @@ D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */, 0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */, AA2AD116244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift in Sources */, + D272F5F92473163100BD1A8F /* BarButtonItem.swift in Sources */, 0A9D09202433796500D2E6C0 /* BarsIndicatorView.swift in Sources */, D2E2A99423D8CCBC000B42E6 /* HeadlineBodyLinkModel.swift in Sources */, 01004F3022721C3800991ECC /* RadioButton.swift in Sources */, @@ -2040,6 +2093,7 @@ AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */, 011D9626240EBB16000E3791 /* RadioButtonLabelModel.swift in Sources */, 8DDD6C1D244D90B8006A2232 /* ListThreeColumnDataUsage.swift in Sources */, + 0A849EFE246F1775009F277F /* RuleEqualsIgnoreCaseModel.swift in Sources */, D28764FB245A33A500CB882D /* TwoLinkViewModel.swift in Sources */, AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */, D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */, @@ -2070,6 +2124,7 @@ 01509D952327ED1900EF99AA /* HeadlineBodyLinkToggle.swift in Sources */, 31BE15CB23D8924D00452370 /* CheckboxLabelModel.swift in Sources */, D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */, + D23EA7E82473654300D60C34 /* PanelNavigationItemModelProtocol.swift in Sources */, 94F6516D2437954100631BF9 /* Tabs.swift in Sources */, 5248BFEC23F12E350059236A /* ListThreeColumnPlanDataDivider.swift in Sources */, 0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */, @@ -2129,15 +2184,19 @@ 014AA72423C501E2006F3E93 /* MoleculeContainerModel.swift in Sources */, D29DF28321E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.m in Sources */, 011B58F223A2AE2C0085F53C /* DropDownListItemModel.swift in Sources */, + D2509ED12472ED9B001BFB9D /* NavigationItemModelProtocol.swift in Sources */, 8D448E5524050A46006211BB /* ListOneColumnFullWidthTextAllTextAndLinksModel.swift in Sources */, 94C2D9842386F3F80006CF46 /* LabelAttributeModel.swift in Sources */, 944589212385D6E900DE9FD4 /* DashLineModel.swift in Sources */, D2E2A99623D8CF85000B42E6 /* HeadlineBodyLinkToggleModel.swift in Sources */, C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */, + 32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */, 011D958524042432000E3791 /* RulesProtocol.swift in Sources */, + AA9972502475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift in Sources */, AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */, D264FAA3243E632F00D98315 /* ProgrammaticCollectionViewController.swift in Sources */, D29DF27A21E7A533003B2FB9 /* MVMCoreUISession.m in Sources */, + 27F9736A246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift in Sources */, D2A5146B2214905000345BFB /* ThreeLayerViewController.swift in Sources */, 526A265E240D200500B0D828 /* ListTwoColumnCompareChanges.swift in Sources */, 8D24041523E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift in Sources */, @@ -2169,7 +2228,7 @@ D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, 525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */, 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */, - D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */, + D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */, AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */, DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */, D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */, @@ -2189,6 +2248,7 @@ C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */, 01EB3684236097C0006832FA /* MoleculeModelProtocol.swift in Sources */, D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, + AAB7EDF1246ADA2A00E54929 /* ListProgressBarThin.swift in Sources */, 8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */, D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */, D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, @@ -2226,9 +2286,12 @@ 948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */, 013F801923FB4A8E00AD8013 /* UIContentMode+Extension.swift in Sources */, 525239C22407BD1000454969 /* ListTwoColumnPriceDetails.swift in Sources */, + AA997252247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift in Sources */, D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */, + D23EA7FB2475F09800D60C34 /* CarouselItemProtocol.swift in Sources */, D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */, D29DF11821E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.m in Sources */, + 27F973532466074500CAB5C5 /* PageBehavior.swift in Sources */, 94C2D9A323872C110006CF46 /* LabelAttributeStrikeThroughModel.swift in Sources */, D28A838523CCCA8900DFE4FC /* ScrollerModel.swift in Sources */, D29DF26C21E6AA0B003B2FB9 /* FLAnimatedImage.m in Sources */, @@ -2276,6 +2339,7 @@ EA5124FD243601600051A3A4 /* BGImageHeadlineBodyButton.swift in Sources */, 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */, 01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */, + AAB7EDEF246ADA1600E54929 /* ListProgressBarThinModel.swift in Sources */, D21B7F75243BAC8900051ABF /* CarouselItem.swift in Sources */, C695A69823C990C200BFB94E /* DoughnutChartView.swift in Sources */, 8D3BA9BD2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift index 628c7cde..18f61b2d 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift @@ -322,7 +322,7 @@ import UIKit @objc override open func resignFirstResponder() -> Bool { if validateWhenDoneEditing { - validateTextField() + validateText() } selectedDigitBox?.isSelected = false @@ -440,7 +440,7 @@ extension DigitEntryField { selectedDigitBox = nil if !switchFieldsAutomatically && validateWhenDoneEditing { - validateTextField() + validateText() } proprietorTextDelegate?.textFieldDidEndEditing?(textField) diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift index e54cb80f..65b13861 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift @@ -49,6 +49,9 @@ import UIKit public var isValid: Bool = false + /// Validate on each entry in the textField. Default: true + public var validateEachCharacter: Bool = true + //-------------------------------------------------- // MARK: - Computed Properties //-------------------------------------------------- @@ -229,6 +232,48 @@ import UIKit entryFieldContainer.updateView(size) } + //-------------------------------------------------- + // MARK: - Validation + //-------------------------------------------------- + + /// Validates the text of the entry field. + @objc public func validateText() { + if let isValid = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) { + self.isValid = isValid + } + } + + /// Executes on .textDidBeginEditingNotification + @objc func startEditing() { + isSelected = true + } + + /// Executes on .textDidChangeNotification (each character entry) + @objc func valueChanged() { + guard validateEachCharacter else { return } + } + + /// Executes on .textDidEndEditingNotification + @objc func endInputing() { + isSelected = false + resignFirstResponder() + } + + @objc public func updateValidation(_ isValid: Bool) { + let previousValidity = self.isValid + self.isValid = isValid + + if previousValidity && !isValid { + shouldShowError(true) + } else if (!previousValidity && isValid) { + shouldShowError(false) + } + } + + func shouldShowError(_ showError: Bool) { + self.showError = showError + } + //-------------------------------------------------- // MARK: - MoleculeViewProtocol //-------------------------------------------------- @@ -255,6 +300,18 @@ import UIKit entryFieldContainer.set(with: model, delegateObject, additionalData) + model.updateUI = { [weak self] in + MVMCoreDispatchUtility.performBlock(onMainThread: { + guard let self = self else { return } + + if self.isSelected { + self.updateValidation(model.isValid ?? true) + } else if model.isValid ?? true && self.showError { + self.showError = false + } + }) + } + title = model.title feedback = model.feedback isEnabled = model.enabled diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift index 1d6e9221..dd2a3c62 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift @@ -71,12 +71,13 @@ import Foundation } public func setValidity(_ valid: Bool, rule: RulesProtocol) { - if let fieldKey = fieldKey, - let ruleErrorMessage = rule.errorMessage?[fieldKey] { + + if let fieldKey = fieldKey, let ruleErrorMessage = rule.errorMessage?[fieldKey] { self.errorMessage = ruleErrorMessage } + self.isValid = valid - + updateUI?() } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift index 9d7c8927..2daee169 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift @@ -51,9 +51,6 @@ import UIKit private var observingForChange: Bool = false - /// Validate on each entry in the textField. Default: true - public var validateEachCharacter: Bool = true - /// Validate when user resigns editing. Default: true public var validateWhenDoneEditing: Bool = true @@ -226,7 +223,7 @@ import UIKit @discardableResult @objc override open func resignFirstResponder() -> Bool { if validateWhenDoneEditing { - validateTextField() + validateText() } textField.resignFirstResponder() isSelected = false @@ -234,56 +231,37 @@ import UIKit } /// Validates the text of the entry field. - @objc public func validateTextField() { + @objc public override func validateText() { text = textField.text - _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + super.validateText() } - @objc public func updateValidation(_ isValid: Bool) { - let previousValidity = self.isValid - self.isValid = isValid - - if previousValidity && !isValid { - shouldShowError(true) - } else if (!previousValidity && isValid) { - shouldShowError(false) - } - } - - func shouldShowError(_ showError: Bool) { - self.showError = showError - if showError { - observingTextFieldDelegate?.isValid?(textfield: self) - entryFieldContainer.originalUI() - } else { - observingTextFieldDelegate?.isInvalid?(textfield: self) - } - } /// Executes on UITextField.textDidBeginEditingNotification - @objc func startEditing() { - isSelected = true + @objc override func startEditing() { + super.startEditing() textField.becomeFirstResponder() } /// Executes on UITextField.textDidChangeNotification (each character entry) - @objc func valueChanged() { - guard validateEachCharacter else { return } - isSelected = true - validateTextField() + @objc override func valueChanged() { + super.valueChanged() + validateText() } /// Executes on UITextField.textDidEndEditingNotification - @objc func endInputing() { - resignFirstResponder() - + @objc override func endInputing() { + super.endInputing() + // Don't show error till user starts typing. guard text?.count ?? 0 != 0 else { + showError = false return } - if let isValid = (model as? TextEntryFieldModel)?.isValid { + if let isValid = textEntryFieldModel?.isValid { self.isValid = isValid } + shouldShowError(!isValid) } @@ -311,6 +289,16 @@ import UIKit } } + override func shouldShowError(_ showError: Bool) { + super.shouldShowError(showError) + + if showError { + observingTextFieldDelegate?.isValid?(textfield: self) + } else { + observingTextFieldDelegate?.isInvalid?(textfield: self) + } + } + //-------------------------------------------------- // MARK: - MoleculeViewProtocol //-------------------------------------------------- @@ -320,16 +308,6 @@ import UIKit guard let model = model as? TextEntryFieldModel else { return } - model.updateUI = { [weak self] in - MVMCoreDispatchUtility.performBlock(onMainThread: { - guard let self = self else { return } - - if self.isSelected { - self.updateValidation(model.isValid ?? true) - } - }) - } - self.delegateObject = delegateObject FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) text = model.text diff --git a/MVMCoreUI/Atomic/Atoms/TextFields/TextViewEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/TextViewEntryField.swift index 546b5c30..e08f3843 100644 --- a/MVMCoreUI/Atomic/Atoms/TextFields/TextViewEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/TextViewEntryField.swift @@ -24,9 +24,6 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele // MARK: - Properties //-------------------------------------------------- - /// Validate on each entry in the textView. Default: true - public var validateEachCharacter: Bool = true - private var observingForChange: Bool = false //-------------------------------------------------- @@ -71,7 +68,7 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele /// The text of this textView. open override var text: String? { - get { return textView.text } + get { return textViewEntryFieldModel?.text } set { textView.text = newValue textViewEntryFieldModel?.text = newValue @@ -186,29 +183,37 @@ class TextViewEntryField: EntryField, UITextViewDelegate, ObservingTextFieldDele //-------------------------------------------------- /// Validates the text of the entry field. - @objc public func validateTextView() { + @objc public override func validateText() { text = textView.text - if let isValid = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) { - self.isValid = isValid - } + super.validateText() } /// Executes on UITextView.textDidBeginEditingNotification - @objc func startEditing() { - isSelected = true + @objc override func startEditing() { + super.startEditing() _ = textView.becomeFirstResponder() } /// Executes on UITextView.textDidChangeNotification (each character entry) - @objc func valueChanged() { - guard validateEachCharacter else { return } - validateTextView() + @objc override func valueChanged() { + super.valueChanged() + validateText() } /// Executes on UITextView.textDidEndEditingNotification - @objc func endInputing() { - resignFirstResponder() - isSelected = false + @objc override func endInputing() { + super.endInputing() + + // Don't show error till user starts typing. + guard text?.count ?? 0 != 0 else { + showError = false + return + } + + if let isValid = textViewEntryFieldModel?.isValid { + self.isValid = isValid + } + showError = !isValid } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 087e893c..598f3f4e 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -677,6 +677,9 @@ public typealias ActionBlock = () -> () @objc public func updateView(_ size: CGFloat) { scaleSize = size as NSNumber + // This fixes a defect for when there are multiple labels stacked in a list item. Sometime some labels will not fill their available space. + preferredMaxLayoutWidth = size + if let originalAttributedString = originalAttributedString { let attributedString = NSMutableAttributedString(attributedString: originalAttributedString) attributedString.removeAttribute(.font, range: NSRange(location: 0, length: attributedString.length)) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Line.swift b/MVMCoreUI/Atomic/Atoms/Views/Line.swift index d0890540..7e05e2fa 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Line.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Line.swift @@ -84,6 +84,15 @@ import UIKit //-------------------------------------------------- // MARK: - MoleculeViewProtocol //-------------------------------------------------- + public convenience init(pinTo view: UIView, edge: UIRectEdge, useMargin: Bool) { + self.init(frame: .zero) + addLine(to: view, edge: edge, useMargin: useMargin) + } + + open func addLine(to view: UIView, edge: UIRectEdge, useMargin: Bool) { + view.addSubview(self) + NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: self, useMargins: useMargin, pinTop: edge != .bottom, pinBottom: edge != .top, pinLeft: edge != .right, pinRight: edge != .left).values)) + } open override func setupView() { super.setupView() diff --git a/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift b/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift index a8d0ba9a..baa63ee8 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift @@ -11,7 +11,8 @@ import UIKit @objcMembers open class LoadImageView: View { public let loadingSpinner = MFLoadingSpinner(frame: .zero) public let imageView = MFTransparentGIFView(frame: .zero) - public var addSizeConstraintsForAspectRatio = false + public var addSizeConstraintsForAspectRatio = true + public var shouldNotifyDelegateOnUpdate = true var centerX: NSLayoutConstraint? var centerY: NSLayoutConstraint? var widthConstraint: NSLayoutConstraint? @@ -172,7 +173,6 @@ import UIKit } else { heightConstraint?.isActive = false heightConstraint = imageView.heightAnchor.constraint(equalToConstant: height) - heightConstraint?.priority = UILayoutPriority(rawValue: 900) } heightConstraint?.isActive = true } @@ -182,7 +182,6 @@ import UIKit widthConstraint.constant = width } else { widthConstraint = imageView.widthAnchor.constraint(equalToConstant: width) - widthConstraint?.priority = UILayoutPriority(rawValue: 900) } widthConstraint?.isActive = true } @@ -264,16 +263,15 @@ import UIKit } let finishedLoadingBlock: MVMCoreGetImageBlock = {[weak self] (image, data, isFallbackImage) in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in - guard let loadingImageName = self?.currentImageName, loadingImageName == imageName else { - return - } - self?.isFallbackImage = isFallbackImage - self?.loadingSpinner.pause() - let layoutWillChange = self?.layoutWillChange(width: self?.currentImageWidth, height: self?.currentImageHeight, size: image?.size) ?? false - self?.addConstraints(width: width, height: height, size: image?.size) - self?.loadingSpinnerHeightConstraint?.constant = 0 + guard let self = self, + let loadingImageName = self.currentImageName, loadingImageName == imageName else { return } + self.isFallbackImage = isFallbackImage + self.loadingSpinner.pause() + let layoutWillChange = self.shouldNotifyDelegateOnUpdate ? self.layoutWillChange(width: self.currentImageWidth, height: self.currentImageHeight, size: image?.size) : false + self.addConstraints(width: width, height: height, size: image?.size) + self.loadingSpinnerHeightConstraint?.constant = 0 if layoutWillChange { - self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self!) + self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) } completionBlock(image,data,isFallbackImage) })} diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index 4b3119f2..18b3fee7 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -146,6 +146,8 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableRadioButtonAndPaymentMethod.self, viewModelClass: ListLeftVariableRadioButtonAndPaymentMethodModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableRadioButtonBodyText.self, viewModelClass: ListLeftVariableRadioButtonBodyTextModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableCheckboxBodyText.self, viewModelClass: ListLeftVariableCheckboxBodyTextModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableIconAllTextLinks.self, viewModelClass: ListLeftVariableIconAllTextLinksModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableNumberedListAllTextAndLinks.self, viewModelClass: ListLeftVariableNumberedListAllTextAndLinksModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRVWheel.self, viewModelClass: ListRVWheelModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariablePayments.self, viewModelClass: ListRightVariablePaymentsModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariableTotalData.self, viewModelClass: ListRightVariableTotalDataModel.self) @@ -162,6 +164,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnInternationalData.self, viewModelClass: ListThreeColumnInternationalDataModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnDataUsage.self, viewModelClass: ListThreeColumnDataUsageModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListFourColumnDataUsageListItem.self, viewModelClass: ListFourColumnDataUsageListItemModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListProgressBarThin.self, viewModelClass: ListProgressBarThinModel.self) // Designed Section Dividers MoleculeObjectMapping.shared()?.register(viewClass: ListFourColumnDataUsageDivider.self, viewModelClass: ListFourColumnDataUsageDividerModel.self) @@ -193,12 +196,16 @@ import Foundation try? ModelRegistry.register(RuleAnyValueChangedModel.self) try? ModelRegistry.register(RuleAllValueChangedModel.self) try? ModelRegistry.register(RuleEqualsModel.self) + try? ModelRegistry.register(RuleEqualsIgnoreCaseModel.self) try? ModelRegistry.register(RuleRegexModel.self) // Actions try? ModelRegistry.register(ActionTopAlertModel.self) try? ModelRegistry.register(ActionCollapseNotificationModel.self) try? ModelRegistry.register(ActionOpenPanelModel.self) + + // Behaviors + try? ModelRegistry.register(ScreenBrightnessModifierBehavior.self) } /// Convenience function to get required modules for a give model diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift index c39bce65..e6e226cb 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift @@ -43,6 +43,7 @@ import Foundation // MARK: - MFViewProtocol open override func setupView() { super.setupView() + rightImageView.shouldNotifyDelegateOnUpdate = false addMolecule(stack) stack.restack() verticalStack.restack() diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift index 77a189c2..e8436620 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift @@ -43,6 +43,7 @@ import Foundation // MARK: - MFViewProtocol open override func setupView() { super.setupView() + rightImageView.shouldNotifyDelegateOnUpdate = false addMolecule(stack) stack.restack() verticalStack.restack() diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift index f53da09c..573335c0 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift @@ -46,6 +46,7 @@ import Foundation //----------------------------------------------------- open override func setupView() { super.setupView() + rightImage.shouldNotifyDelegateOnUpdate = false addMolecule(stack) stack.restack() verticalStack.restack() diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift index 5ba8ac97..49173067 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift @@ -46,6 +46,7 @@ import Foundation //----------------------------------------------------- open override func setupView() { super.setupView() + rightImage.shouldNotifyDelegateOnUpdate = false addMolecule(stack) stack.restack() verticalStack.restack() diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconAllTextLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconAllTextLinks.swift new file mode 100644 index 00000000..f53134a1 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconAllTextLinks.swift @@ -0,0 +1,54 @@ +// +// ListLeftVariableIconAllTextLinks.swift +// MVMCoreUI +// +// Created by Lekshmi S on 20/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +@objcMembers open class ListLeftVariableIconAllTextLinks: TableViewCell { + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + public let leftImage = LoadImageView() + public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink(frame: .zero) + public var stack: Stack + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: leftImage, model: StackItemModel(horizontalAlignment: .fill)), (view: eyebrowHeadlineBodyLink, model: StackItemModel(horizontalAlignment: .leading))], axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //-------------------------------------------------- + // MARK: - Life Cycle + //-------------------------------------------------- + override open func setupView() { + super.setupView() + leftImage.addSizeConstraintsForAspectRatio = true + leftImage.imageView.contentMode = .scaleAspectFit + addMolecule(stack) + stack.restack() + } + + //------------------------------------------------------ + // MARK: - Molecule + //------------------------------------------------------ + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListLeftVariableIconAllTextLinksModel else { return } + leftImage.set(with: model.image, delegateObject, additionalData) + eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 140 + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconAllTextLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconAllTextLinksModel.swift new file mode 100644 index 00000000..8e35984a --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconAllTextLinksModel.swift @@ -0,0 +1,49 @@ +// +// ListLeftVariableIconAllTextLinksModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 20/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +public class ListLeftVariableIconAllTextLinksModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "listLVImgAll" + public var image: ImageViewModel + public var eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel + + override public func setDefaults() { + super.setDefaults() + if image.width == nil, image.height == nil { + image.width = 30 + image.height = 30 + } + } + + public init(image: ImageViewModel, eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel) { + self.image = image + self.eyebrowHeadlineBodyLink = eyebrowHeadlineBodyLink + super.init() + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case image + case eyebrowHeadlineBodyLink + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + image = try typeContainer.decode(ImageViewModel.self, forKey: .image) + eyebrowHeadlineBodyLink = try typeContainer.decode(EyebrowHeadlineBodyLinkModel.self, forKey: .eyebrowHeadlineBodyLink) + 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.encode(image, forKey: .image) + try container.encode(eyebrowHeadlineBodyLink, forKey: .eyebrowHeadlineBodyLink) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinks.swift new file mode 100644 index 00000000..e7a35cc6 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinks.swift @@ -0,0 +1,60 @@ +// +// ListLeftVariableNumberedListAllTextAndLinks.swift +// MVMCoreUI +// +// Created by Subhankar Acharya on 21/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ListLeftVariableNumberedListAllTextAndLinks: TableViewCell { + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + public let leftLabel = Label.createLabelTitle2XLarge(true) + public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink(frame: .zero) + public var stack: Stack + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(horizontalAlignment: .fill)), + (view: eyebrowHeadlineBodyLink, model: StackItemModel(horizontalAlignment: .leading))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //-------------------------------------------------- + // MARK: - Life Cycle + //-------------------------------------------------- + override open func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + //------------------------------------------------------ + // MARK: - Molecule + //------------------------------------------------------ + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListLeftVariableNumberedListAllTextAndLinksModel else { return } + leftLabel.text = String(model.number) + eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 140 + } + + open override func reset() { + super.reset() + leftLabel.styleTitle2XLarge(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinksModel.swift new file mode 100644 index 00000000..78a85a8e --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinksModel.swift @@ -0,0 +1,42 @@ +// +// ListLeftVariableNumberedListAllTextAndLinksModel.swift +// MVMCoreUI +// +// Created by Subhankar Acharya on 21/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListLeftVariableNumberedListAllTextAndLinksModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "listLVNAll" + public var number: Int + public var eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel + + public init(number: Int, eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel) { + self.number = number + self.eyebrowHeadlineBodyLink = eyebrowHeadlineBodyLink + super.init() + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case number + case eyebrowHeadlineBodyLink + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + number = try typeContainer.decode(Int.self, forKey: .number) + eyebrowHeadlineBodyLink = try typeContainer.decode(EyebrowHeadlineBodyLinkModel.self, forKey: .eyebrowHeadlineBodyLink) + 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.encode(number, forKey: .number) + try container.encode(eyebrowHeadlineBodyLink, forKey: .eyebrowHeadlineBodyLink) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift new file mode 100644 index 00000000..072be6bb --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift @@ -0,0 +1,99 @@ +// +// ListProgressBarThin.swift +// MVMCoreUI +// +// Created by Lekshmi S on 12/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +@objcMembers open class ListProgressBarThin: TableViewCell { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + public let progressBar = ProgressBar() + public let leftHeadline = Label.commonLabelB1(true) + public let leftBody = Label.commonLabelB2(true) + public let rightBar = Line() + public let rightLabel = Label.commonLabelB2(true) + private let barStackItem: StackItem + private let rightLabelStackItem: StackItem + public var labelStack: Stack + public var horizontalStack: Stack + public var stack: Stack + + //------------------------------------------------------ + // MARK: - Initializers + //------------------------------------------------------ + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + //vertical stack with leftHeadline, leftBody + labelStack = Stack.createStack(with: [leftHeadline, leftBody], axis: .vertical, spacing: 2) + + //horizontal stack with leftHeadline, leftBody, bar, rightLabel + barStackItem = StackItem(andContain: rightBar) + rightLabelStackItem = StackItem(andContain: rightLabel) + let horizontalStackItems = [StackItem(andContain: labelStack), barStackItem, rightLabelStackItem] + let horizontalStackModel = StackModel(molecules: [StackItemModel(horizontalAlignment: .leading), StackItemModel(horizontalAlignment: .fill), StackItemModel(spacing: 5, horizontalAlignment: .fill)], + axis: .horizontal) + horizontalStack = Stack(with: horizontalStackModel, stackItems: horizontalStackItems) + + //stack with all components + stack = Stack.createStack(with: [horizontalStack, progressBar], axis: .vertical, spacing: PaddingDefaultVerticalSpacing3) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + open override func alignAccessoryToHero() -> CGPoint? { + // Ensures that the right items are centered with the arrow. + let heroCenter = super.alignAccessoryToHero() + if let heroCenter = heroCenter { + let convertedPoint = horizontalStack.convert(heroCenter, from: self) + barStackItem.containerHelper.alignCenterVerticalConstraint?.constant = convertedPoint.y - horizontalStack.bounds.midY + rightLabelStackItem.containerHelper.alignCenterVerticalConstraint?.constant = convertedPoint.y - horizontalStack.bounds.midY + } + return heroCenter + } + + //------------------------------------------------------- + // MARK: - View Lifecycle + //------------------------------------------------------- + open override func setupView() { + super.setupView() + rightBar.widthAnchor.constraint(equalToConstant: 20).isActive = true + rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) + rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) + rightLabel.numberOfLines = 1 + addMolecule(stack) + stack.restack() + horizontalStack.restack() + labelStack.restack() + } + + //------------------------------------------------------ + // MARK: - Molecule + //------------------------------------------------------ + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListProgressBarThinModel else { return } + labelStack.updateContainedMolecules(with: [model.leftHeadline, + model.leftBody], delegateObject, additionalData) + progressBar.set(with: model.progressBar, delegateObject, additionalData) + rightBar.set(with: model.rightBar, delegateObject, additionalData) + rightLabel.set(with: model.rightLabel, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 120 + } + + open override func reset() { + super.reset() + leftHeadline.styleB1(true) + leftBody.styleB2(true) + rightLabel.styleB2(true) + rightBar.setStyle(.medium) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift new file mode 100644 index 00000000..acd8e013 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift @@ -0,0 +1,65 @@ +// +// ListProgressBarThinModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 12/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +public class ListProgressBarThinModel: ListItemModel, MoleculeModelProtocol { + public static var identifier = "listPrgBarThin" + public var progressBar: ProgressBarModel + public var leftHeadline: LabelModel + public var leftBody: LabelModel? + public var rightBar: LineModel + public var rightLabel: LabelModel + + public init(progressBar: ProgressBarModel, leftHeadline: LabelModel, leftBody: LabelModel? = nil, rightBar: LineModel, rightLabel: LabelModel) { + self.progressBar = progressBar + self.leftHeadline = leftHeadline + self.leftBody = leftBody + self.rightBar = rightBar + self.rightLabel = rightLabel + super.init() + } + + override public func setDefaults() { + super.setDefaults() + rightBar.type = .medium + if rightBar.backgroundColor == nil { + rightBar.backgroundColor = Color(uiColor: .gray) + } + leftHeadline.hero = 0 + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case progressBar + case leftHeadline + case leftBody + case rightBar + case rightLabel + } + + public required init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + progressBar = try typeContainer.decode(ProgressBarModel.self, forKey:.progressBar) + leftHeadline = try typeContainer.decode(LabelModel.self, forKey: .leftHeadline) + leftBody = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .leftBody) + rightBar = try typeContainer.decode(LineModel.self, forKey: .rightBar) + rightLabel = try typeContainer.decode(LabelModel.self, forKey: .rightLabel) + 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.encode(progressBar, forKey: .progressBar) + try container.encode(leftHeadline, forKey: .leftHeadline) + try container.encodeIfPresent(leftBody, forKey: .leftBody) + try container.encode(rightBar, forKey: .rightBar) + try container.encode(rightLabel, forKey: .rightLabel) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift b/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift index 3089596a..a37fe016 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift @@ -8,7 +8,7 @@ import Foundation -open class CarouselItem: MoleculeCollectionViewCell { +open class CarouselItem: MoleculeCollectionViewCell, CarouselItemProtocol { open var allowsPeaking = false var peakingLeftArrow = UIImageView(image: MVMCoreUIUtility.imageNamed("peakingRightArrow")?.withRenderingMode(.alwaysTemplate)) diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemButtonModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemButtonModel.swift new file mode 100644 index 00000000..26be6952 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemButtonModel.swift @@ -0,0 +1,41 @@ +// +// NavigationItemButtonModel.swift +// +// +// Created by Scott Pfeil on 5/18/20. +// + +import Foundation + +public class NavigationItemButtonModel: Codable { + var imageName: String + var action: ActionModelProtocol + + public init(with imageName: String, action: ActionModelProtocol) { + self.imageName = imageName + self.action = action + } + + private enum CodingKeys: String, CodingKey { + case imageName + case action + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + imageName = try typeContainer.decode(String.self, forKey: .imageName) + action = try typeContainer.decodeModel(codingKey: .action) + } + + open func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(imageName, forKey: .imageName) + try container.encodeModel(action, forKey: .action) + } + + /// Convenience function that creates a BarButtonItem for the model. + public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> BarButtonItem { + let image = UIImage(named: imageName, in: MVMCoreCache.shared()?.bundleToUseForImages(), compatibleWith: nil) + return BarButtonItem.create(with: image, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/NavigationItemModelProtocol.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift similarity index 70% rename from MVMCoreUI/Atomic/Molecules/NavigationItemModelProtocol.swift rename to MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index f15a847a..38dee0d2 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationItemModelProtocol.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -8,48 +8,7 @@ import Foundation -public protocol NavigationItemModelProtocol { - var title: String? { get set } - var hidden: Bool { get set } - var backgroundColor: Color? { get set } - var translucent: Bool { get set } - var tintColor: Color { get set } - var line: LineModel? { get set } - var showLeftPanelButton: Bool { get set } - var showRightPanelButton: Bool { get set } - var backButton: NavigationItemButtonModel? { get set } - var additionalLeftButtons: [NavigationItemButtonModel]? { get set } - var additionalRightButtons: [NavigationItemButtonModel]? { get set } -} - -public class NavigationItemButtonModel: Codable { - var imageName: String - var action: ActionModelProtocol - - public init(with imageName: String, action: ActionModelProtocol) { - self.imageName = imageName - self.action = action - } - - private enum CodingKeys: String, CodingKey { - case imageName - case action - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - imageName = try typeContainer.decode(String.self, forKey: .imageName) - action = try typeContainer.decodeModel(codingKey: .action) - } - - open func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(imageName, forKey: .imageName) - try container.encodeModel(action, forKey: .action) - } -} - -public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { +public class NavigationItemModel: NavigationItemModelProtocol, PanelNavigationItemModelProtocol, MoleculeModelProtocol { public class var identifier: String { return "navigationBar" } @@ -60,12 +19,14 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt public var translucent: Bool public var tintColor: Color public var line: LineModel? - public var showLeftPanelButton: Bool - public var showRightPanelButton: Bool public var backButton: NavigationItemButtonModel? public var additionalLeftButtons: [NavigationItemButtonModel]? public var additionalRightButtons: [NavigationItemButtonModel]? + // Legacy, will remove once menu is gone. + public var showLeftPanelButton: Bool + public var showRightPanelButton: Bool + public init() { hidden = false translucent = false diff --git a/MVMCoreUI/Atomic/Organisms/Carousel.swift b/MVMCoreUI/Atomic/Organisms/Carousel.swift index 4cf65f1b..9fa72b5d 100644 --- a/MVMCoreUI/Atomic/Organisms/Carousel.swift +++ b/MVMCoreUI/Atomic/Organisms/Carousel.swift @@ -36,7 +36,7 @@ open class Carousel: View { open var numberOfPages = 0 /// The models for the molecules. - var molecules: [MoleculeModelProtocol]? + var molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]? /// The horizontal alignment of the cell in the collection view. Only noticeable if the itemWidthPercent is less than 100%. public var itemAlignment = UICollectionView.ScrollPosition.left @@ -173,14 +173,12 @@ open class Carousel: View { numberOfPages = newMolecules.count molecules = newMolecules - if carouselModel?.loop ?? false && newMolecules.count > 2 { + if carouselModel?.loop ?? false && newMolecules.count > 1 { // Sets up the row data with buffer cells on each side (for illusion of endless scroll... also has one more buffer cell on each side in case we can peek that cell). loop = true - - molecules?.insert(newMolecules.last!, at: 0) - molecules?.insert(newMolecules[(newMolecules.count - 2)], at: 0) - molecules?.append(newMolecules.first!) - molecules?.append(newMolecules[1]) + + molecules?.insert(contentsOf: newMolecules.suffix(2), at: 0) + molecules?.append(contentsOf: newMolecules.prefix(2)) } pageIndex = 0 @@ -259,15 +257,15 @@ open class Carousel: View { let visibleItemsPaths = collectionView.indexPathsForVisibleItems.sorted { $0.row < $1.row } if let firstItem = visibleItemsPaths.first, firstItem.row != currentIndex { - (collectionView.cellForItem(at: firstItem) as? CarouselItem)?.setPeaking(true, animated: true) + (collectionView.cellForItem(at: firstItem) as? CarouselItemProtocol)?.setPeaking(true, animated: true) } if let lastItem = visibleItemsPaths.last, lastItem.row != currentIndex { - (collectionView.cellForItem(at: lastItem) as? CarouselItem)?.setPeaking(true, animated: true) + (collectionView.cellForItem(at: lastItem) as? CarouselItemProtocol)?.setPeaking(true, animated: true) } } else { // Hide peaking. for item in collectionView.visibleCells { - (item as? CarouselItem)?.setPeaking(false, animated: true) + (item as? CarouselItemProtocol)?.setPeaking(false, animated: true) } } } @@ -301,7 +299,7 @@ extension Carousel: UICollectionViewDelegateFlowLayout { } open func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - (cell as? CarouselItem)?.setPeaking(false, animated: false) + (cell as? CarouselItemProtocol)?.setPeaking(false, animated: false) } } diff --git a/MVMCoreUI/Atomic/Organisms/CarouselModel.swift b/MVMCoreUI/Atomic/Organisms/CarouselModel.swift index 857901e8..5ff753aa 100644 --- a/MVMCoreUI/Atomic/Organisms/CarouselModel.swift +++ b/MVMCoreUI/Atomic/Organisms/CarouselModel.swift @@ -19,7 +19,7 @@ import UIKit } public var backgroundColor: Color? - public var molecules: [CarouselItemModel] + public var molecules: [MoleculeModelProtocol & CarouselItemModelProtocol] public var index: Int = 0 public var spacing: Float? public var border: Bool? @@ -29,7 +29,7 @@ import UIKit public var itemAlignment: UICollectionView.ScrollPosition? public var pagingMolecule: (CarouselPagingModelProtocol & MoleculeModelProtocol)? - public init(molecules: [CarouselItemModel]) { + public init(molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]) { self.molecules = molecules } @@ -57,7 +57,7 @@ import UIKit required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules) + molecules = try typeContainer.decodeModels(codingKey: .molecules) index = try typeContainer.decodeIfPresent(Int.self, forKey: .index) ?? 0 backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) spacing = try typeContainer.decodeIfPresent(Float.self, forKey: .spacing) @@ -73,7 +73,7 @@ import UIKit var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) - try container.encode(molecules, forKey: .molecules) + try container.encodeModels(molecules, forKey: .molecules) try container.encode(spacing, forKey: .spacing) try container.encode(border, forKey: .border) try container.encode(loop, forKey: .loop) diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift index 093c9337..198cf80d 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift @@ -9,7 +9,5 @@ import Foundation -public protocol CarouselItemModelProtocol: ContainerModelProtocol, MoleculeModelProtocol { - var peakingUI: Bool? { get } - var peakingArrowColor: Color? { get } +public protocol CarouselItemModelProtocol: ContainerModelProtocol { } diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemProtocol.swift new file mode 100644 index 00000000..4f49e5f7 --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemProtocol.swift @@ -0,0 +1,15 @@ +// +// CarouselItemProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 5/20/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol CarouselItemProtocol: UICollectionViewCell { + + /// Notifies the cell if it is peaking or not. + func setPeaking(_ peaking: Bool, animated: Bool) +} diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift new file mode 100644 index 00000000..634539d4 --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift @@ -0,0 +1,21 @@ +// +// NavigationItemModelProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 5/18/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol NavigationItemModelProtocol { + var title: String? { get set } + var hidden: Bool { get set } + var backgroundColor: Color? { get set } + var translucent: Bool { get set } + var tintColor: Color { get set } + var line: LineModel? { get set } + var backButton: NavigationItemButtonModel? { get set } + var additionalLeftButtons: [NavigationItemButtonModel]? { get set } + var additionalRightButtons: [NavigationItemButtonModel]? { get set } +} diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/PanelNavigationItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/PanelNavigationItemModelProtocol.swift new file mode 100644 index 00000000..8f599be4 --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/PanelNavigationItemModelProtocol.swift @@ -0,0 +1,14 @@ +// +// PanelNavigationItemModelProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 5/18/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol PanelNavigationItemModelProtocol { + var showLeftPanelButton: Bool { get set } + var showRightPanelButton: Bool { get set } +} diff --git a/MVMCoreUI/Atomic/Templates/TemplateModel.swift b/MVMCoreUI/Atomic/Templates/TemplateModel.swift index 3dcf3964..e8992e58 100644 --- a/MVMCoreUI/Atomic/Templates/TemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/TemplateModel.swift @@ -28,6 +28,7 @@ import Foundation public var screenHeading: String? public var navigationItem: (NavigationItemModelProtocol & MoleculeModelProtocol)? public var formRules: [FormGroupRule]? + public var behaviors: [PageBehaviorProtocol]? //-------------------------------------------------- // MARK: - Initializer @@ -47,6 +48,7 @@ import Foundation case screenHeading case backgroundColor case formRules + case behaviors case navigationItem } @@ -60,6 +62,7 @@ import Foundation screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) formRules = try typeContainer.decodeIfPresent([FormGroupRule].self, forKey: .formRules) + behaviors = try typeContainer.decodeModelsIfPresent(codingKey: .behaviors) navigationItem = try typeContainer.decodeModelIfPresent(codingKey: .navigationItem) } diff --git a/MVMCoreUI/BaseClasses/BarButtonItem.swift b/MVMCoreUI/BaseClasses/BarButtonItem.swift new file mode 100644 index 00000000..ac26c040 --- /dev/null +++ b/MVMCoreUI/BaseClasses/BarButtonItem.swift @@ -0,0 +1,82 @@ +// +// BarButtonItem.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 5/18/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +public typealias BarButtonAction = (BarButtonItem) -> () + +@objc fileprivate class ActionDelegate: NSObject { + fileprivate var buttonAction: BarButtonAction? + @objc fileprivate func callActionBlock(_ sender: BarButtonItem) { + buttonAction?(sender) + } +} + +@objcMembers open class BarButtonItem: UIBarButtonItem, MFButtonProtocol { + + //-------------------------------------------------- + // MARK: - Delegate + //-------------------------------------------------- + + open weak var buttonDelegate: ButtonDelegateProtocol? + private var actionDelegate: ActionDelegate? + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + public static func create(with image: UIImage?) -> Self { + let actionObject = ActionDelegate() + let button = self.init(image: image, style: .plain, target: actionObject, action: #selector(actionObject.callActionBlock(_:))) + button.actionDelegate = actionObject + return button + } + + /// Creates the item with the passed in action. + public static func create(with image: UIImage?, actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { + let button = create(with: image) + button.set(with: actionModel, delegateObject: delegateObject, additionalData: additionalData) + return button + } + + /// Creates the item with the passed in action map. + public static func create(with image: UIImage?, actionMap: [AnyHashable : Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { + let button = create(with: image) + button.set(with: actionMap, delegateObject: delegateObject, additionalData: additionalData) + return button + } + + /// Creates the item with the passed in action. + public static func create(with image: UIImage?, action: @escaping BarButtonAction) -> Self { + let button = create(with: image) + button.actionDelegate?.buttonAction = action + return button + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + open func set(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + buttonDelegate = delegateObject?.buttonDelegate + actionDelegate?.buttonAction = { sender in + if let data = try? actionModel.encode(using: JSONEncoder()), + let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any], + delegateObject?.buttonDelegate?.button?(sender, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } + } + + open func set(with actionMap: [AnyHashable : Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + buttonDelegate = delegateObject?.buttonDelegate + actionDelegate?.buttonAction = { sender in + guard delegateObject?.buttonDelegate?.button?(sender, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true else { return } + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } +} + diff --git a/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift b/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift index 579a612e..64242fc3 100644 --- a/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift +++ b/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift @@ -5,9 +5,10 @@ // Created by Scott Pfeil on 3/16/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // +// A convenience aggregate for common MVM templates. import Foundation -public protocol MVMControllerModelProtocol: TemplateModelProtocol, FormHolderModelProtocol { +public protocol MVMControllerModelProtocol: TemplateModelProtocol, FormHolderModelProtocol, PageBehaviorsTemplateProtocol { } diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index a10e0767..782deae2 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -158,18 +158,27 @@ import UIKit needsUpdateUI = true view.setNeedsLayout() } - - /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, - open func handleNewData() { - // TODO: remove legacy. Temporary, convert legacy to navigation model. - var navigationModel = pageModel?.navigationItem ?? NavigationItemModel() + + /// Creates a legacy navigation model. + open func createDefaultLegacyNavigationModel() -> NavigationItemModel { + let navigationModel = NavigationItemModel() navigationModel.title = pageModel?.screenHeading navigationModel.showLeftPanelButton = isMasterInitiallyAccessible() navigationModel.showRightPanelButton = isSupportInitiallyAccessible() if /*(self as? MVMCoreUITabBarPageControlViewController) != nil ||*/ manager != nil || loadObject?.requestParameters?.tabWasPressed ?? false == true { navigationModel.line = LineModel(type: .none) } - pageModel?.navigationItem = navigationModel + return navigationModel + } + + /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, + open func handleNewData() { + // TODO: remove legacy. Temporary, convert legacy to navigation model. + if pageModel?.navigationItem == nil { + let navigationItem = createDefaultLegacyNavigationModel() + pageModel?.navigationItem = navigationItem + } + if formValidator == nil { let rules = pageModel?.formRules formValidator = FormValidator(rules) @@ -310,6 +319,18 @@ import UIKit MVMCoreUISession.sharedGlobal()?.currentPageType = pageType MVMCoreUILoggingHandler.shared()?.defaultLogPageState(forController: self) } + + executeBehaviors { (behavior: PageVisibilityBehavior) in + behavior.onPageShown() + } + } + + open override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + executeBehaviors { (behavior: PageVisibilityBehavior) in + behavior.onPageHidden() + } } deinit { @@ -424,4 +445,9 @@ import UIKit selectedField = nil } } + + // MARK: - Behavior Execution + func executeBehaviors(_ behaviorBlock:(_ behavior:T)->Void) { + pageModel?.behaviors?.compactMap({ $0 as? T }).forEach({ behaviorBlock($0) }) + } } diff --git a/MVMCoreUI/Behaviors/PageBehavior.swift b/MVMCoreUI/Behaviors/PageBehavior.swift new file mode 100644 index 00000000..d8fd99a3 --- /dev/null +++ b/MVMCoreUI/Behaviors/PageBehavior.swift @@ -0,0 +1,44 @@ +// +// PageBehaviors.swift +// MVMCoreUI +// +// Created by Kyle on 5/8/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol PageBehaviorProtocol: ModelProtocol { + + // The type of rule + var behaviorName: String { get } + +} + +public extension PageBehaviorProtocol { + + var behaviorName: String { + get { return Self.identifier } + } + + static var categoryCodingKey: String { + return "behaviorName" + } + + static var categoryName: String { + return "\(PageBehaviorProtocol.self)" + } +} + +public protocol PageVisibilityBehavior: PageBehaviorProtocol { + + func onPageShown() + func onPageHidden() + +} + +public protocol PageBehaviorsTemplateProtocol { + + var behaviors: [PageBehaviorProtocol]? { get } + +} diff --git a/MVMCoreUI/Behaviors/ScreenBrightnessModifierBehavior.swift b/MVMCoreUI/Behaviors/ScreenBrightnessModifierBehavior.swift new file mode 100644 index 00000000..b6ea9f6d --- /dev/null +++ b/MVMCoreUI/Behaviors/ScreenBrightnessModifierBehavior.swift @@ -0,0 +1,68 @@ +// +// ScreenBrightnessModifierBehavior.swift +// MVMCoreUI +// +// Created by Kyle on 5/9/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +public class ScreenBrightnessModifierBehavior: PageVisibilityBehavior { + + public static var identifier = "screenBrightnessModifier" + + @Clamping(range: 0...1) var screenBrightness: CGFloat + + var originalScreenBrightness: CGFloat? + + //MARK:- PageVisibilityBehavior + + public func onPageShown() { + changeScreenBrightness() + } + + public func onPageHidden() { + restoreScreenBrightness() + } + + //MARK:- Behavior + + func changeScreenBrightness() { + guard originalScreenBrightness == nil else { return } + originalScreenBrightness = UIScreen.main.brightness + UIScreen.main.brightness = screenBrightness + NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: UIApplication.willResignActiveNotification, object: nil) + } + + func restoreScreenBrightness() { + guard let originalScreenBrightness = originalScreenBrightness else { return } + UIScreen.main.brightness = originalScreenBrightness + self.originalScreenBrightness = nil + NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil) + } + + @objc func willResignActive() { + restoreScreenBrightness() + NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) + } + + @objc func didBecomeActive() { + NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil) + changeScreenBrightness() + } + + //MARK:- Codable + + private enum CodingKeys: String, CodingKey { + case screenBrightness + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + screenBrightness = try typeContainer.decode(CGFloat.self, forKey: .screenBrightness) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(screenBrightness, forKey: .screenBrightness) + } +} diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index f278236c..3f7846a0 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -11,35 +11,61 @@ import UIKit @objcMembers open class NavigationController: UINavigationController { public var separatorView: Line? + /// Getter for the main navigation controller public static func navigationController() -> Self? { return MVMCoreActionUtility.initializerClassCheck(MVMCoreUISession.sharedGlobal()?.navigationController, classToVerify: self) as? Self } - public static func style(_ navigationBar: UINavigationBar) { + /// Provides MVM styling to the navigation bar. Returns a reference to the line. + public static func style(_ navigationBar: UINavigationBar) -> Line { UIColor.mfSetBackgroundColor(forNavigationBar: .white, navigationBar: navigationBar, transparent: false) navigationBar.shadowImage = UIImage() navigationBar.isOpaque = true navigationBar.tintColor = .black navigationBar.titleTextAttributes = [NSAttributedString.Key.font: MFStyler.fontBoldBodySmall(false)]; + return Line(pinTo: navigationBar, edge: .bottom, useMargin: false) } + /// Sets up the application with a navigation controller public static func setupNavigationController() -> Self? { let navigationController = self.init() - style(navigationController.navigationBar) - navigationController.separatorView = Line(pinTo: navigationController.navigationBar, edge: .bottom, useMargin: false) - navigationController.separatorView?.setStyle(.standard) + navigationController.separatorView = style(navigationController.navigationBar) MVMCoreUISession.sharedGlobal()?.navigationController = navigationController MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController MVMCoreNavigationHandler.shared()?.navigationController = navigationController return navigationController } + /// Sets up the application with a navigation controller as the main container. public static func setupNavigationControllerAsMainController() -> Self? { guard let navigationController = setupNavigationController() else { return nil } MVMCoreUISession.sharedGlobal()?.setup(asStandardLoadViewDelegate: navigationController) return navigationController } + /// Convenience function for setting the navigation buttons. + public static func setNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject + var items: [UIBarButtonItem] = [] + if let backButtonModel = navigationItemModel.backButton, + navigationController.viewControllers.count > 1 { + items.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } + if let leftItemModels = navigationItemModel.additionalLeftButtons { + for item in leftItemModels { + items.append(item.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } + viewController.navigationItem.leftBarButtonItems = items + } + if let rightItemModels = navigationItemModel.additionalRightButtons { + for item in rightItemModels { + items.append(item.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } + viewController.navigationItem.rightBarButtonItems = items + } + } + + /// Convenience function for setting navigation bar with model. public static func set(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { viewController.navigationItem.title = navigationItemModel.title viewController.navigationItem.accessibilityLabel = navigationItemModel.title @@ -54,19 +80,26 @@ import UIKit // Have the navigation title match the tint color navigationController.navigationBar.titleTextAttributes?.updateValue(tint, forKey: .foregroundColor) - // Update icons if main navigation controller. - if navigationController == MVMCoreUISession.sharedGlobal()?.navigationController, - navigationController.topViewController == viewController { - // Update line. - MVMCoreUISession.sharedGlobal()?.navigationController?.separatorView?.setStyle(navigationItemModel.line?.type ?? .standard) + // Update line. + if let navigationController = navigationController as? NavigationController { + navigationController.separatorView?.isHidden = navigationItemModel.line?.type ?? .standard == .none } - if navigationController == MVMCoreUISplitViewController.main()?.navigationController, - navigationController.topViewController == viewController { - // Update Panels - MVMCoreUISplitViewController.main()?.setLeftPanelIsAccessible(navigationItemModel.showLeftPanelButton , for: viewController) - MVMCoreUISplitViewController.main()?.setRightPanelIsAccessible(navigationItemModel.showRightPanelButton , for: viewController) - MVMCoreUISession.sharedGlobal()?.splitViewController?.setNavigationIconColor(tint) + // Let legacy splitview controller handle buttons for now. + guard navigationController == MVMCoreUISplitViewController.main()?.navigationController, + navigationController.topViewController == viewController else { + // Not the main split view controller, add buttons. + setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + return + } + + // Update icons if main navigation controller. + MVMCoreUISession.sharedGlobal()?.splitViewController?.setNavigationIconColor(tint) + + // Update Panels + if let model = navigationItemModel as? PanelNavigationItemModelProtocol { + MVMCoreUISplitViewController.main()?.setLeftPanelIsAccessible(model.showLeftPanelButton, for: viewController) + MVMCoreUISplitViewController.main()?.setRightPanelIsAccessible(model.showRightPanelButton, for: viewController) } } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h index 0c252a08..e70a8b8b 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h @@ -124,8 +124,8 @@ typedef NS_ENUM(NSInteger, MFNumberOfDrawers) { - (nullable UIImage *)imageForBackButton; // Can overide to provide other global buttons to be on the navigation bar. -- (nullable NSArray *)additionalLeftButtons; -- (nullable NSArray *)additionalRightButtons; +- (nullable NSArray *)additionalLeftButtonsForViewController:(nonnull UIViewController *)viewController; +- (nullable NSArray *)additionalRightButtonsForViewController:(nonnull UIViewController *)viewController; // The width of the panel when it is permanently extended. Default 320. - (CGFloat)leftPanelExtendedWidth; diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index e93ad613..d9814a1f 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -12,6 +12,7 @@ @import MVMCore.MVMCoreViewManagerProtocol; @import MVMCore.MVMCoreObject; @import MVMCore.MVMCoreActionUtility; +@import MVMCore.NSDictionary_MFConvenience; #import "MVMCoreUIUtility.h" #import "UIColor+MFConvenience.h" #import "NSLayoutConstraint+MFConvenience.h" @@ -106,12 +107,29 @@ CGFloat const PanelAnimationDuration = 0.2; return (width > 2000 ? MFTwoDrawer : (width > 1000 ? MFOneDrawer : MFNoDrawer)); } -- (nullable NSArray *)additionalLeftButtons { - return nil; +- (nullable UIBarButtonItem *)getBackButtonForViewController:(nonnull UIViewController *)viewController { + if (![viewController conformsToProtocol:@protocol(MVMCoreViewControllerProtocol)]) { return self.backButton; } + UIViewController *controller = (UIViewController *)viewController; + NSDictionary *item = [controller.loadObject.pageJSON dictWithChainOfKeysOrIndexes:@[@"navigationItem",@"backButton"]]; + if (!item) { return self.backButton; } + DelegateObject *delegate = [controller delegateObject]; + return [self createNavigationItemButtonFrom:item delegateObject:[delegate isKindOfClass:[MVMCoreUIDelegateObject class]] ? (MVMCoreUIDelegateObject *)delegate : nil]; } -- (nullable NSArray *)additionalRightButtons { - return nil; +- (nullable NSArray *)additionalLeftButtonsForViewController:(nonnull UIViewController *)viewController { + if (![viewController conformsToProtocol:@protocol(MVMCoreViewControllerProtocol)]) { return nil; } + UIViewController *controller = (UIViewController *)viewController; + NSArray *items = [controller.loadObject.pageJSON arrayForChainOfKeysOrIndexes:@[@"navigationItem",@"additionalLeftButtons"]]; + DelegateObject *delegate = [controller delegateObject]; + return [self createNavigationItemButtonsFrom:items delegateObject:[delegate isKindOfClass:[MVMCoreUIDelegateObject class]] ? (MVMCoreUIDelegateObject *)delegate : nil]; +} + +- (nullable NSArray *)additionalRightButtonsForViewController:(nonnull UIViewController *)viewController { + if (![viewController conformsToProtocol:@protocol(MVMCoreViewControllerProtocol)]) { return nil; } + UIViewController *controller = (UIViewController *)viewController; + NSArray *items = [controller.loadObject.pageJSON arrayForChainOfKeysOrIndexes:@[@"navigationItem",@"additionalRightButtons"]]; + DelegateObject *delegate = [controller delegateObject]; + return [self createNavigationItemButtonsFrom:items delegateObject:[delegate isKindOfClass:[MVMCoreUIDelegateObject class]] ? (MVMCoreUIDelegateObject *)delegate : nil]; } - (CGFloat)leftPanelExtendedWidth { @@ -234,12 +252,18 @@ CGFloat const PanelAnimationDuration = 0.2; - (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended { NSMutableArray *leftBarButtonItems = [NSMutableArray array]; if ([viewController.navigationController.viewControllers count] > 1) { - [leftBarButtonItems addObject:self.backButton]; + UIBarButtonItem *button = [self getBackButtonForViewController:viewController]; + if (button) { + viewController.navigationItem.hidesBackButton = YES; + [leftBarButtonItems addObject:button]; + } else { + viewController.navigationItem.hidesBackButton = NO; + } } if ((accessible && !extended) && self.leftPanelButton) { [leftBarButtonItems addObject:self.leftPanelButton]; } - NSArray *extraButtons = [self additionalLeftButtons]; + NSArray *extraButtons = [self additionalLeftButtonsForViewController:viewController]; if (extraButtons) { [leftBarButtonItems addObjectsFromArray:extraButtons]; } @@ -410,7 +434,7 @@ CGFloat const PanelAnimationDuration = 0.2; if ((accessible && !extended) && self.rightPanelButton) { [navigationItems addObject:self.rightPanelButton]; } - NSArray *extraButtons = [self additionalRightButtons]; + NSArray *extraButtons = [self additionalRightButtonsForViewController:viewController]; if (extraButtons) { [navigationItems addObjectsFromArray:extraButtons]; } @@ -601,6 +625,24 @@ CGFloat const PanelAnimationDuration = 0.2; #pragma mark - Other Panel Functions +/// Convenience function, creates a BarButtonItem from button json +- (nonnull UIBarButtonItem *)createNavigationItemButtonFrom:(nonnull NSDictionary *)JSON delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject { + UIImage *image = [UIImage imageNamed:[JSON string:@"imageName"] inBundle:[[MVMCoreCache sharedCache] bundleToUseForImages] compatibleWithTraitCollection:nil]; + BarButtonItem *item = [BarButtonItem createWith:image actionMap:[JSON dict:@"action"] delegateObject:delegateObject additionalData:nil]; + return item; +} + +/// Convenience function, creates a list of BarButtonItems list of json +- (nullable NSArray *)createNavigationItemButtonsFrom:(nullable NSArray *)JSONlist delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject { + if (JSONlist.count == 0) { return nil; } + NSMutableArray *items = [NSMutableArray arrayWithCapacity:JSONlist.count]; + for (NSDictionary *itemData in JSONlist) { + UIBarButtonItem *item = [self createNavigationItemButtonFrom:itemData delegateObject:delegateObject]; + [items addObject:item]; + } + return items; +} + - (void)forceHideBothDrawers { [self hideBothDrawersShouldForceHide:YES]; } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsIgnoreCaseModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsIgnoreCaseModel.swift new file mode 100644 index 00000000..5e014cff --- /dev/null +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsIgnoreCaseModel.swift @@ -0,0 +1,57 @@ +// +// RuleEqualsIgnoreCaseModel.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 5/15/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + + +public class RuleEqualsIgnoreCaseModel: RulesProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "equalsIgnoreCase" + public var type: String = RuleEqualsIgnoreCaseModel.identifier + public var fields: [String] + public var errorMessage: [String: String]? + + //-------------------------------------------------- + // MARK: - Validation + //-------------------------------------------------- + + public func isValid(_ formField: FormFieldProtocol) -> Bool { + return false + } + + public func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { + var valid = false + var compareText: String? + + for formKey in fields { + guard let formField = fieldMolecules[formKey] else { continue } + + guard let compareString = compareText else { + compareText = formField.formFieldValue() as? String + continue + } + + if let fieldValue = formField.formFieldValue() as? String, + compareString.caseInsensitiveCompare(fieldValue) == .orderedSame { + valid = true + for formKey in fields { + guard let formField = fieldMolecules[formKey] else { continue } + (formField as? FormRuleWatcherFieldProtocol)?.setValidity(true, rule: self) + } + break + } + + (formField as? FormRuleWatcherFieldProtocol)?.setValidity(valid, rule: self) + } + + return valid + } +} diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift index c9e7d9f7..77cdf254 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift @@ -24,6 +24,7 @@ public class RuleRequiredModel: RulesProtocol { //-------------------------------------------------- public func isValid(_ formField: FormFieldProtocol) -> Bool { + guard let value = formField.formFieldValue() else { return false } var valid = true diff --git a/MVMCoreUI/SupportingFiles/Fonts/NHaasGroteskDSStd-55Rg.otf b/MVMCoreUI/SupportingFiles/Fonts/NHaasGroteskDSStd-55Rg.otf deleted file mode 100644 index f56ed7aa..00000000 Binary files a/MVMCoreUI/SupportingFiles/Fonts/NHaasGroteskDSStd-55Rg.otf and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Fonts/NHaasGroteskDSStd-75Bd.otf b/MVMCoreUI/SupportingFiles/Fonts/NHaasGroteskDSStd-75Bd.otf deleted file mode 100644 index fbcba354..00000000 Binary files a/MVMCoreUI/SupportingFiles/Fonts/NHaasGroteskDSStd-75Bd.otf and /dev/null differ diff --git a/MVMCoreUI/Utility/MFFonts.h b/MVMCoreUI/Utility/MFFonts.h index fb4bbe92..46947581 100644 --- a/MVMCoreUI/Utility/MFFonts.h +++ b/MVMCoreUI/Utility/MFFonts.h @@ -14,9 +14,6 @@ extern NSString * _Nonnull const DSBold; extern NSString * _Nonnull const DSRegular; extern NSString * _Nonnull const TXBold; extern NSString * _Nonnull const TXRegular; -//2.0 font -extern NSString * _Nonnull const DS75Bd; -extern NSString * _Nonnull const DS55Rg; @interface MFFonts : NSObject diff --git a/MVMCoreUI/Utility/MFFonts.m b/MVMCoreUI/Utility/MFFonts.m index 150cffa7..d16f15ec 100644 --- a/MVMCoreUI/Utility/MFFonts.m +++ b/MVMCoreUI/Utility/MFFonts.m @@ -17,11 +17,6 @@ NSString * const DSRegular = @"VerizonNHGeDS-Regular"; NSString * const TXBold = @"VerizonNHGeTX-Bold"; NSString * const TXRegular = @"VerizonNHGeTX-Regular"; -//2.0 font -NSString * const DS75Bd = @"NHaasGroteskDSStd-75Bd"; -NSString * const DS55Rg = @"NHaasGroteskDSStd-55Rg"; - - @implementation MFFonts + (void)loadMVMFonts { @@ -31,8 +26,6 @@ NSString * const DS55Rg = @"NHaasGroteskDSStd-55Rg"; [MFFonts loadFont:DSRegular type:@"otf"]; [MFFonts loadFont:TXBold type:@"otf"]; [MFFonts loadFont:TXRegular type:@"otf"]; - [MFFonts loadFont:DS75Bd type:@"otf"]; - [MFFonts loadFont:DS55Rg type:@"otf"]; [MFFonts loadFont:@"OCRAExtended" type:@"ttf"]; }); }