diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 4b5e5103..bd526826 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -15,31 +15,29 @@ 011B58F223A2AE2C0085F53C /* DropDownListItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011B58F123A2AE2C0085F53C /* DropDownListItemModel.swift */; }; 011D958524042432000E3791 /* RulesProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D958424042432000E3791 /* RulesProtocol.swift */; }; 011D958724042492000E3791 /* FormFieldProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D958624042492000E3791 /* FormFieldProtocol.swift */; }; - 011D95892404249B000E3791 /* FormProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95882404249B000E3791 /* FormProtocol.swift */; }; + 011D95892404249B000E3791 /* FormHolderModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95882404249B000E3791 /* FormHolderModelProtocol.swift */; }; 011D959B240451E3000E3791 /* RuleRequiredModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D959A240451E3000E3791 /* RuleRequiredModel.swift */; }; 011D959D2404536F000E3791 /* RuleAnyValueChangedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D959C2404536F000E3791 /* RuleAnyValueChangedModel.swift */; }; 011D959F240453A1000E3791 /* RuleAllValueChangedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D959E240453A1000E3791 /* RuleAllValueChangedModel.swift */; }; 011D95A1240453D0000E3791 /* RuleEqualsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95A0240453D0000E3791 /* RuleEqualsModel.swift */; }; 011D95A3240453F8000E3791 /* RuleRegexModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95A2240453F8000E3791 /* RuleRegexModel.swift */; }; 011D95A5240455DC000E3791 /* FormGroupRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95A4240455DC000E3791 /* FormGroupRule.swift */; }; - 011D95A924057AC7000E3791 /* FormActionFieldProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95A824057AC7000E3791 /* FormActionFieldProtocol.swift */; }; + 011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95A824057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift */; }; 011D95AB2405C553000E3791 /* FormItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95AA2405C553000E3791 /* FormItemProtocol.swift */; }; 011D95AD2406BB57000E3791 /* FormHolderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95AC2406BB57000E3791 /* FormHolderProtocol.swift */; }; 011D95AF2407266E000E3791 /* RadioButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95AE2407266E000E3791 /* RadioButtonModel.swift */; }; - 011D9602240DA20A000E3791 /* ValidProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D9601240DA20A000E3791 /* ValidProtocol.swift */; }; + 011D9602240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D9601240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift */; }; 011D9626240EBB16000E3791 /* RadioButtonLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D9625240EBB16000E3791 /* RadioButtonLabelModel.swift */; }; - 011D9628240EFA1E000E3791 /* MFViewController+Form.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D9627240EFA1E000E3791 /* MFViewController+Form.swift */; }; 012A889C23889E8400FE3DA1 /* TemplateModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A889B23889E8400FE3DA1 /* TemplateModelProtocol.swift */; }; 012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */; }; 012A88B1238C880100FE3DA1 /* CarouselPagingModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88B0238C880100FE3DA1 /* CarouselPagingModelProtocol.swift */; }; 012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88C1238D7BCA00FE3DA1 /* CarouselItemModel.swift */; }; 012A88C4238D86E600FE3DA1 /* CarouselItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88C3238D86E600FE3DA1 /* CarouselItemModelProtocol.swift */; }; 012A88C6238DA34000FE3DA1 /* ModuleMoleculeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88C5238DA34000FE3DA1 /* ModuleMoleculeModel.swift */; }; - 012A88C8238DB02000FE3DA1 /* ModelMoleculeDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88C7238DB02000FE3DA1 /* ModelMoleculeDelegateProtocol.swift */; }; + 012A88C8238DB02000FE3DA1 /* MoleculeDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88C7238DB02000FE3DA1 /* MoleculeDelegateProtocol.swift */; }; 012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88AE238C626E00FE3DA1 /* CarouselModel.swift */; }; 012A88EC238F084D00FE3DA1 /* FooterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88EB238F084D00FE3DA1 /* FooterModel.swift */; }; 012A88F123985E0100FE3DA1 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88F023985E0100FE3DA1 /* Color.swift */; }; - 012CA99E2385A2D3003F810F /* MFView+ModelExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012CA99D2385A2D3003F810F /* MFView+ModelExtension.swift */; }; 013F801923FB4A8E00AD8013 /* UIContentMode+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 013F801823FB4A8E00AD8013 /* UIContentMode+Extension.swift */; }; 014AA72423C501E2006F3E93 /* MoleculeContainerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 014AA72123C501E2006F3E93 /* MoleculeContainerModel.swift */; }; 014AA72523C501E2006F3E93 /* ContainerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 014AA72223C501E2006F3E93 /* ContainerModel.swift */; }; @@ -53,11 +51,7 @@ 01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D922327ECFB00EF99AA /* ProgressBar.swift */; }; 01509D952327ED1900EF99AA /* HeadlineBodyLinkToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D942327ED1900EF99AA /* HeadlineBodyLinkToggle.swift */; }; 017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */; }; - 017BEB3C2361EA1D0024EF95 /* MFViewController+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB3B2361EA1D0024EF95 /* MFViewController+Model.swift */; }; - 017BEB4023620A230024EF95 /* TextFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB3F23620A230024EF95 /* TextFieldModel.swift */; }; - 017BEB442362192F0024EF95 /* MVMCoreUIMoleculeMappingObject+ModelExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB432362192F0024EF95 /* MVMCoreUIMoleculeMappingObject+ModelExtension.swift */; }; 017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */; }; - 017BEB4A236235BA0024EF95 /* ModelMoleculeViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB49236235BA0024EF95 /* ModelMoleculeViewProtocol.swift */; }; 017BEB7B236763000024EF95 /* LineModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB7A236763000024EF95 /* LineModel.swift */; }; 017BEB7F23676E870024EF95 /* MoleculeObjectMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB7E23676E870024EF95 /* MoleculeObjectMapping.swift */; }; 0198F7A62256A80B0066C936 /* MFRadioButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 0198F7A02256A80A0066C936 /* MFRadioButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -91,10 +85,16 @@ 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; 0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */; }; 0A5D59C223AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */; }; + 0A6682A22434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682A12434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift */; }; + 0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682A32434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift */; }; + 0A6682AA2435125F00AD3CA1 /* Styler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682A92435125F00AD3CA1 /* Styler.swift */; }; + 0A6682AC243531C300AD3CA1 /* Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6682AB243531C300AD3CA1 /* Padding.swift */; }; 0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */; }; 0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */; }; 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; }; 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; + 0A7ECC5D243CE85300C828E8 /* DoughnutChartItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7ECC5C243CE85300C828E8 /* DoughnutChartItemModel.swift */; }; + 0A7ECC5F243CEB1200C828E8 /* ColorViewWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7ECC5E243CEB1200C828E8 /* ColorViewWithLabel.swift */; }; 0A7EF85B23D8A52800B2AAD1 /* EntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85A23D8A52800B2AAD1 /* EntryFieldModel.swift */; }; 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */; }; 0A7EF85F23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */; }; @@ -110,6 +110,7 @@ 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */; }; 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 */; }; 31BE15CB23D8924D00452370 /* CheckboxLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31BE15C923D8924C00452370 /* CheckboxLabelModel.swift */; }; 31BE15CC23D8924D00452370 /* CheckboxModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31BE15CA23D8924C00452370 /* CheckboxModel.swift */; }; 522679C123FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */; }; @@ -133,7 +134,15 @@ 8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D084AD12410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift */; }; 8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24041023E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift */; }; 8D24041523E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24041423E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift */; }; + 8D3BA9BD2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3BA9BC2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift */; }; + 8D3BA9BF2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3BA9BE2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift */; }; 8D448E5524050A46006211BB /* ListOneColumnFullWidthTextAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D448E5424050A46006211BB /* ListOneColumnFullWidthTextAllTextAndLinksModel.swift */; }; + 8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4687E1242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift */; }; + 8D4687E4242E2DF300802879 /* ListFourColumnDataUsageListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4687E3242E2DF300802879 /* ListFourColumnDataUsageListItem.swift */; }; + 8DD1E36E243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DD1E36D243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift */; }; + 8DD1E370243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DD1E36F243B3D0500D8F2DF /* ListThreeColumnInternationalData.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 */; }; @@ -141,6 +150,8 @@ 9432A79F23DB47BA00719041 /* EntryFieldContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9432A79E23DB47BA00719041 /* EntryFieldContainer.swift */; }; 943784F5236B77BB006A1E82 /* GraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F3236B77BB006A1E82 /* GraphView.swift */; }; 943784F6236B77BB006A1E82 /* GraphViewAnimationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */; }; + 943820842432382400B43AF3 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943820832432382400B43AF3 /* WebView.swift */; }; + 94382086243238D100B43AF3 /* WebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94382085243238D100B43AF3 /* WebViewModel.swift */; }; 9445890C2385BCE300DE9FD4 /* ProgressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9445890B2385BCE300DE9FD4 /* ProgressBarModel.swift */; }; 9445890E2385C3F800DE9FD4 /* MultiProgressModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9445890D2385C3F800DE9FD4 /* MultiProgressModel.swift */; }; 9445891F2385D2E900DE9FD4 /* CaretViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9445891E2385D2E900DE9FD4 /* CaretViewModel.swift */; }; @@ -154,6 +165,8 @@ 94AF4A3F23E9D13900676048 /* MFCaretButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AF4A3D23E9D13900676048 /* MFCaretButton.m */; }; 94AF4A4223E9D19E00676048 /* MFCaretView.h in Headers */ = {isa = PBXBuildFile; fileRef = 94AF4A4023E9D19E00676048 /* MFCaretView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 94AF4A4323E9D19E00676048 /* MFCaretView.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AF4A4123E9D19E00676048 /* MFCaretView.m */; }; + 94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C0150924215643005811A9 /* ActionTopAlertModel.swift */; }; + 94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C0150B2421564A005811A9 /* ActionCollapseNotificationModel.swift */; }; 94C2D9842386F3F80006CF46 /* LabelAttributeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9832386F3F80006CF46 /* LabelAttributeModel.swift */; }; 94C2D9A123872BCC0006CF46 /* LabelAttributeUnderlineModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9A023872BCC0006CF46 /* LabelAttributeUnderlineModel.swift */; }; 94C2D9A323872C110006CF46 /* LabelAttributeStrikeThroughModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9A223872C110006CF46 /* LabelAttributeStrikeThroughModel.swift */; }; @@ -173,12 +186,29 @@ 94FB966323D797DA003D482B /* MFTextButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 94FB966123D797DA003D482B /* MFTextButton.m */; }; AA11A41F23F15D3100D7962F /* ListRightVariablePayments.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA11A41E23F15D3100D7962F /* ListRightVariablePayments.swift */; }; AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */; }; + AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1EC59624373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift */; }; + AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */; }; + AA56A20F243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */; }; + AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */; }; AA3CA8D0241627CE008BF379 /* HeadersH1Bill.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3CA8CF241627CE008BF379 /* HeadersH1Bill.swift */; }; AA3CA8D2241627DB008BF379 /* HeadersH1BillModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3CA8D1241627DB008BF379 /* HeadersH1BillModel.swift */; }; AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */; }; AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */; }; + BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */; }; + BB2C969224330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */; }; BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */; }; BB47A588241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A587241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift */; }; + BB54C5202434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB54C51E2434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift */; }; + BB54C5212434D92F0038326C /* ListRightVariableButtonAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB54C51F2434D92F0038326C /* ListRightVariableButtonAllTextAndLinksModel.swift */; }; + BB6C6AC0242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB6C6ABE242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTallModel.swift */; }; + BB6C6AC1242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB6C6ABF242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift */; }; + BB6C6AC824225290005F7224 /* ListOneColumnTextWithWhitespaceDividerShort.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB6C6AC62422528F005F7224 /* ListOneColumnTextWithWhitespaceDividerShort.swift */; }; + BB6C6AC924225290005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB6C6AC72422528F005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift */; }; + BBAA4F03243D8E3B005AAD5F /* RadioBoxes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAA4EFF243D8E3B005AAD5F /* RadioBoxes.swift */; }; + BBAA4F04243D8E3B005AAD5F /* RadioBoxModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAA4F00243D8E3B005AAD5F /* RadioBoxModel.swift */; }; + BBAA4F05243D8E3B005AAD5F /* RadioBoxesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAA4F01243D8E3B005AAD5F /* RadioBoxesModel.swift */; }; + BBBBC87C24374A4900B0F079 /* ListThreeColumnBillChangesDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBBBC87A24374A4900B0F079 /* ListThreeColumnBillChangesDivider.swift */; }; + BBBBC87D24374A4900B0F079 /* ListThreeColumnBillChangesDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBBBC87B24374A4900B0F079 /* ListThreeColumnBillChangesDividerModel.swift */; }; C003506123AA94CD00B6AC29 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = C003506023AA94CD00B6AC29 /* Button.swift */; }; C07065C42395677300FBF997 /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07065C32395677300FBF997 /* Link.swift */; }; C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A67E23C9830600BFB94E /* UnOrderedListModel.swift */; }; @@ -192,8 +222,15 @@ C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */; }; C7F8012123E8303200396FBD /* ListRVWheel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F8012023E8303200396FBD /* ListRVWheel.swift */; }; C7F8012323E846C300396FBD /* ListRVWheelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F8012223E846C300396FBD /* ListRVWheelModel.swift */; }; + D202AFE4242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift */; }; + D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D202AFE5242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; + D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; + D21B7F71243BAC1600051ABF /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */; }; + D21B7F73243BAC6800051ABF /* CollectionItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */; }; + D21B7F75243BAC8900051ABF /* CarouselItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F74243BAC8900051ABF /* CarouselItem.swift */; }; + D21B7F77243BB70700051ABF /* MoleculeCollectionItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F76243BB70700051ABF /* MoleculeCollectionItemModel.swift */; }; D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */; }; D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22479892314445E003FCCF9 /* LabelToggle.swift */; }; D224798C231450C8003FCCF9 /* HeadlineBodyToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224798B231450C8003FCCF9 /* HeadlineBodyToggle.swift */; }; @@ -208,13 +245,14 @@ D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = D22D1F45220496A30077CEC0 /* MVMCoreUISwitch.m */; }; D22D1F562204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D22D1F542204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; D22D1F572204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D22D1F552204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m */; }; + D22D8393241C27B100D3DF69 /* TemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8392241C27B100D3DF69 /* TemplateModel.swift */; }; D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8394241FB41200D3DF69 /* UIStackView+Extension.swift */; }; 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 */; }; D243859923A16B1800332775 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = D243859823A16B1800332775 /* Container.swift */; }; D256E9932412880000360572 /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = D256E9922412880000360572 /* Header.swift */; }; D260105323CEA61600764D80 /* ToggleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105223CEA61600764D80 /* ToggleModel.swift */; }; - D260105523CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */; }; D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105823D0A92900764D80 /* ContainerProtocol.swift */; }; D260105B23D0BB7100764D80 /* StackModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105A23D0BB7100764D80 /* StackModelProtocol.swift */; }; D260105D23D0BCD400764D80 /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260105C23D0BCD400764D80 /* Stack.swift */; }; @@ -225,6 +263,15 @@ D260D7B122D65BDD007E7233 /* MVMCoreUIPageControl.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */; }; D260D7B622D68514007E7233 /* MVMCoreUIPagingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */; }; + D264FA8E243BCD9A00D98315 /* CollectionTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */; }; + D264FA90243BCE6800D98315 /* ThreeLayerCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FA8F243BCE6800D98315 /* ThreeLayerCollectionViewController.swift */; }; + D264FAA1243CF66B00D98315 /* ContainerCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FAA0243CF66B00D98315 /* ContainerCollectionReusableView.swift */; }; + D264FAA3243E632F00D98315 /* ProgrammaticCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FAA2243E632F00D98315 /* ProgrammaticCollectionViewController.swift */; }; + D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FAA4243F66A500D98315 /* CollectionTemplateItemProtocol.swift */; }; + D264FAA7243FE13B00D98315 /* RadioBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FAA6243FE13B00D98315 /* RadioBox.swift */; }; + D264FAAA2440F97600D98315 /* CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FAA92440F97600D98315 /* CollectionView.swift */; }; + D264FAAC2441009400D98315 /* RadioBoxCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FAAB2441009400D98315 /* RadioBoxCollectionViewCell.swift */; }; 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 */; }; @@ -235,7 +282,6 @@ D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */; }; D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */; }; D282AACB2243C61700C46919 /* ButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AACA2243C61700C46919 /* ButtonView.swift */; }; - D28A837723C79FC600DFE4FC /* MFCustomButton+ActionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A837623C79FC600DFE4FC /* MFCustomButton+ActionModel.swift */; }; D28A837923C7D5BC00DFE4FC /* PageModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A837823C7D5BC00DFE4FC /* PageModelProtocol.swift */; }; D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A837A23C928DA00DFE4FC /* MoleculeListCellProtocol.swift */; }; D28A837D23CCA86A00DFE4FC /* TabsListItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A837C23CCA86A00DFE4FC /* TabsListItemModel.swift */; }; @@ -245,7 +291,6 @@ D28A838523CCCA8900DFE4FC /* ScrollerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838423CCCA8900DFE4FC /* ScrollerModel.swift */; }; D28A838923CCCFCB00DFE4FC /* LinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838823CCCFCB00DFE4FC /* LinkModel.swift */; }; D28A838B23CCDA6B00DFE4FC /* ButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838A23CCDA6B00DFE4FC /* ButtonModel.swift */; }; - D28A838D23CCDCC200DFE4FC /* PrimaryButton+MoleculeProtocolExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838C23CCDCC200DFE4FC /* PrimaryButton+MoleculeProtocolExtension.swift */; }; D28A838F23CCDEDE00DFE4FC /* TwoButtonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */; }; D28A839123CD4FD400DFE4FC /* CornerLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839023CD4FD400DFE4FC /* CornerLabelsModel.swift */; }; D28A839323CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */; }; @@ -259,6 +304,7 @@ D29770FC21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29770FA21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m */; }; D29770FD21F7C77400B2F0D0 /* MVMCoreUITextFieldView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29770FB21F7C77400B2F0D0 /* MVMCoreUITextFieldView.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */; }; + D29C94D5242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */; }; D29DF0D121E404D4003B2FB9 /* MVMCoreUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */; }; D29DF11521E6805F003B2FB9 /* UIColor+MFConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF11121E6805F003B2FB9 /* UIColor+MFConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -303,11 +349,11 @@ D29DF28421E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF28221E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF28B21E7AC2B003B2FB9 /* ViewConstrainingView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF28721E7AC2B003B2FB9 /* ViewConstrainingView.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF28821E7AC2B003B2FB9 /* ViewConstrainingView.m */; }; - D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF28D21E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m */; }; + D29DF29521E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF28D21E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.m */; }; D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF28E21E7ADB8003B2FB9 /* StackableViewController.m */; }; D29DF29721E7ADB8003B2FB9 /* MFScrollingViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF28F21E7ADB8003B2FB9 /* MFScrollingViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF29821E7ADB8003B2FB9 /* MFScrollingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF29021E7ADB8003B2FB9 /* MFScrollingViewController.m */; }; - D29DF29921E7ADB8003B2FB9 /* ProgrammaticScrollViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF29121E7ADB8003B2FB9 /* ProgrammaticScrollViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D29DF29921E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF29121E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF29A21E7ADB8003B2FB9 /* MFProgrammaticTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF29221E7ADB8003B2FB9 /* MFProgrammaticTableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF29B21E7ADB9003B2FB9 /* StackableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF29321E7ADB8003B2FB9 /* StackableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF29421E7ADB8003B2FB9 /* MFProgrammaticTableViewController.m */; }; @@ -349,9 +395,6 @@ D29E28D823D21AB800ACEA85 /* StringAndMoleculeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29E28D723D21AB800ACEA85 /* StringAndMoleculeView.swift */; }; D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29E28D923D21AFA00ACEA85 /* StringAndMoleculeModel.swift */; }; D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29E28DC23D7404C00ACEA85 /* ContainerHelper.swift */; }; - D2A514582211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A514562211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D2A514592211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2A514572211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m */; }; - D2A5145D2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */; }; D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A5146022121FBF00345BFB /* MoleculeStackTemplate.swift */; }; D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A514622213643100345BFB /* MoleculeStackCenteredTemplate.swift */; }; @@ -360,6 +403,9 @@ D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A638FC22CA98280052ED1F /* HeadlineBody.swift */; }; D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A6390022CBB1820052ED1F /* Carousel.swift */; }; D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */; }; + D2A92882241AAB67004E01C6 /* ScrollingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A92881241AAB67004E01C6 /* ScrollingViewController.swift */; }; + D2A92884241ACB25004E01C6 /* ProgrammaticScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A92883241ACB25004E01C6 /* ProgrammaticScrollViewController.swift */; }; + D2A92886241ACD99004E01C6 /* ProgrammaticTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A92885241ACD99004E01C6 /* ProgrammaticTableViewController.swift */; }; D2B18B7F2360913400A9AEDC /* Control.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B7E2360913400A9AEDC /* Control.swift */; }; D2B18B812360945C00A9AEDC /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B802360945C00A9AEDC /* View.swift */; }; D2B18B922361E65A00A9AEDC /* CoreUIObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B912361E65A00A9AEDC /* CoreUIObject.swift */; }; @@ -368,6 +414,7 @@ D2C5001821F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; }; D2C521A923EDE79E00CA2634 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C521A823EDE79E00CA2634 /* ViewController.swift */; }; + D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */; }; D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */; }; D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */; }; D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */; }; @@ -393,6 +440,8 @@ DBC4391B224421A0001AB423 /* CaretLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391A224421A0001AB423 /* CaretLink.swift */; }; DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */; }; DBEFFA04225A829700230692 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB891E822253FA8500022516 /* Label.swift */; }; + EA5124FD243601600051A3A4 /* BGImageHeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */; }; + EA5124FF2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -404,20 +453,19 @@ 011B58F123A2AE2C0085F53C /* DropDownListItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropDownListItemModel.swift; sourceTree = ""; }; 011D958424042432000E3791 /* RulesProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RulesProtocol.swift; sourceTree = ""; }; 011D958624042492000E3791 /* FormFieldProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormFieldProtocol.swift; sourceTree = ""; }; - 011D95882404249B000E3791 /* FormProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormProtocol.swift; sourceTree = ""; }; + 011D95882404249B000E3791 /* FormHolderModelProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormHolderModelProtocol.swift; sourceTree = ""; }; 011D959A240451E3000E3791 /* RuleRequiredModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleRequiredModel.swift; sourceTree = ""; }; 011D959C2404536F000E3791 /* RuleAnyValueChangedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyValueChangedModel.swift; sourceTree = ""; }; 011D959E240453A1000E3791 /* RuleAllValueChangedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAllValueChangedModel.swift; sourceTree = ""; }; 011D95A0240453D0000E3791 /* RuleEqualsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleEqualsModel.swift; sourceTree = ""; }; 011D95A2240453F8000E3791 /* RuleRegexModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleRegexModel.swift; sourceTree = ""; }; 011D95A4240455DC000E3791 /* FormGroupRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormGroupRule.swift; sourceTree = ""; }; - 011D95A824057AC7000E3791 /* FormActionFieldProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormActionFieldProtocol.swift; sourceTree = ""; }; + 011D95A824057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormGroupWatcherFieldProtocol.swift; sourceTree = ""; }; 011D95AA2405C553000E3791 /* FormItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormItemProtocol.swift; sourceTree = ""; }; 011D95AC2406BB57000E3791 /* FormHolderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormHolderProtocol.swift; sourceTree = ""; }; 011D95AE2407266E000E3791 /* RadioButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonModel.swift; sourceTree = ""; }; - 011D9601240DA20A000E3791 /* ValidProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidProtocol.swift; sourceTree = ""; }; + 011D9601240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormRuleWatcherFieldProtocol.swift; sourceTree = ""; }; 011D9625240EBB16000E3791 /* RadioButtonLabelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonLabelModel.swift; sourceTree = ""; }; - 011D9627240EFA1E000E3791 /* MFViewController+Form.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MFViewController+Form.swift"; sourceTree = ""; }; 012A889B23889E8400FE3DA1 /* TemplateModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModelProtocol.swift; sourceTree = ""; }; 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateProtocol.swift; sourceTree = ""; }; 012A88AE238C626E00FE3DA1 /* CarouselModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselModel.swift; sourceTree = ""; }; @@ -425,10 +473,9 @@ 012A88C1238D7BCA00FE3DA1 /* CarouselItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselItemModel.swift; sourceTree = ""; }; 012A88C3238D86E600FE3DA1 /* CarouselItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselItemModelProtocol.swift; sourceTree = ""; }; 012A88C5238DA34000FE3DA1 /* ModuleMoleculeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleMoleculeModel.swift; sourceTree = ""; }; - 012A88C7238DB02000FE3DA1 /* ModelMoleculeDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelMoleculeDelegateProtocol.swift; sourceTree = ""; }; + 012A88C7238DB02000FE3DA1 /* MoleculeDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeDelegateProtocol.swift; sourceTree = ""; }; 012A88EB238F084D00FE3DA1 /* FooterModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooterModel.swift; sourceTree = ""; }; 012A88F023985E0100FE3DA1 /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; - 012CA99D2385A2D3003F810F /* MFView+ModelExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MFView+ModelExtension.swift"; sourceTree = ""; }; 013F801823FB4A8E00AD8013 /* UIContentMode+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIContentMode+Extension.swift"; sourceTree = ""; }; 014AA72123C501E2006F3E93 /* MoleculeContainerModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeContainerModel.swift; sourceTree = ""; }; 014AA72223C501E2006F3E93 /* ContainerModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContainerModel.swift; sourceTree = ""; }; @@ -442,11 +489,7 @@ 01509D922327ECFB00EF99AA /* ProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = ""; }; 01509D942327ED1900EF99AA /* HeadlineBodyLinkToggle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeadlineBodyLinkToggle.swift; sourceTree = ""; }; 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonLabel.swift; sourceTree = ""; }; - 017BEB3B2361EA1D0024EF95 /* MFViewController+Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MFViewController+Model.swift"; sourceTree = ""; }; - 017BEB3F23620A230024EF95 /* TextFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldModel.swift; sourceTree = ""; }; - 017BEB432362192F0024EF95 /* MVMCoreUIMoleculeMappingObject+ModelExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUIMoleculeMappingObject+ModelExtension.swift"; sourceTree = ""; }; 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeViewProtocol.swift; sourceTree = ""; }; - 017BEB49236235BA0024EF95 /* ModelMoleculeViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelMoleculeViewProtocol.swift; sourceTree = ""; }; 017BEB7A236763000024EF95 /* LineModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineModel.swift; sourceTree = ""; }; 017BEB7E23676E870024EF95 /* MoleculeObjectMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeObjectMapping.swift; sourceTree = ""; }; 0198F7A02256A80A0066C936 /* MFRadioButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFRadioButton.h; sourceTree = ""; }; @@ -468,11 +511,17 @@ 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = ""; }; 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = ""; }; 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleGuidelinesProtocol.swift; sourceTree = ""; }; + 0A6682A12434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonBodyText.swift; sourceTree = ""; }; + 0A6682A32434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonBodyTextModel.swift; sourceTree = ""; }; + 0A6682A92435125F00AD3CA1 /* Styler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Styler.swift; sourceTree = ""; }; + 0A6682AB243531C300AD3CA1 /* Padding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Padding.swift; sourceTree = ""; }; 0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyRequiredModel.swift; sourceTree = ""; }; 0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDropdownEntryField.swift; sourceTree = ""; }; 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = ""; }; 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxLabel.swift; sourceTree = ""; }; + 0A7ECC5C243CE85300C828E8 /* DoughnutChartItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoughnutChartItemModel.swift; sourceTree = ""; }; + 0A7ECC5E243CEB1200C828E8 /* ColorViewWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorViewWithLabel.swift; sourceTree = ""; }; 0A7EF85A23D8A52800B2AAD1 /* EntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryFieldModel.swift; sourceTree = ""; }; 0A7EF85C23D8A95600B2AAD1 /* TextEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryFieldModel.swift; sourceTree = ""; }; 0A7EF85E23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MdnEntryFieldModel.swift; sourceTree = ""; }; @@ -490,6 +539,7 @@ 0AE98BB223FF0934004C5109 /* ExternalLinkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalLinkModel.swift; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinks.swift; sourceTree = ""; }; @@ -513,7 +563,15 @@ 8D084AD12410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextBodyText.swift; sourceTree = ""; }; 8D24041023E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconWithRightCaret.swift; sourceTree = ""; }; 8D24041423E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconWithRightCaretModel.swift; sourceTree = ""; }; + 8D3BA9BC2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnInternationalDataDividerModel.swift; sourceTree = ""; }; + 8D3BA9BE2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnInternationalDataDivider.swift; sourceTree = ""; }; 8D448E5424050A46006211BB /* ListOneColumnFullWidthTextAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextAllTextAndLinksModel.swift; sourceTree = ""; }; + 8D4687E1242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListFourColumnDataUsageListItemModel.swift; sourceTree = ""; }; + 8D4687E3242E2DF300802879 /* ListFourColumnDataUsageListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListFourColumnDataUsageListItem.swift; sourceTree = ""; }; + 8DD1E36D243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnInternationalDataModel.swift; sourceTree = ""; }; + 8DD1E36F243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnInternationalData.swift; sourceTree = ""; }; + 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 = ""; }; @@ -522,6 +580,8 @@ 9432A79E23DB47BA00719041 /* EntryFieldContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntryFieldContainer.swift; sourceTree = ""; }; 943784F3236B77BB006A1E82 /* GraphView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphView.swift; sourceTree = ""; }; 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphViewAnimationHandler.swift; sourceTree = ""; }; + 943820832432382400B43AF3 /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = ""; }; + 94382085243238D100B43AF3 /* WebViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewModel.swift; sourceTree = ""; }; 9445890B2385BCE300DE9FD4 /* ProgressBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBarModel.swift; sourceTree = ""; }; 9445890D2385C3F800DE9FD4 /* MultiProgressModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgressModel.swift; sourceTree = ""; }; 9445891E2385D2E900DE9FD4 /* CaretViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaretViewModel.swift; sourceTree = ""; }; @@ -535,6 +595,8 @@ 94AF4A3D23E9D13900676048 /* MFCaretButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFCaretButton.m; sourceTree = ""; }; 94AF4A4023E9D19E00676048 /* MFCaretView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFCaretView.h; sourceTree = ""; }; 94AF4A4123E9D19E00676048 /* MFCaretView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFCaretView.m; sourceTree = ""; }; + 94C0150924215643005811A9 /* ActionTopAlertModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionTopAlertModel.swift; sourceTree = ""; }; + 94C0150B2421564A005811A9 /* ActionCollapseNotificationModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionCollapseNotificationModel.swift; sourceTree = ""; }; 94C2D9832386F3F80006CF46 /* LabelAttributeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeModel.swift; sourceTree = ""; }; 94C2D9A023872BCC0006CF46 /* LabelAttributeUnderlineModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeUnderlineModel.swift; sourceTree = ""; }; 94C2D9A223872C110006CF46 /* LabelAttributeStrikeThroughModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeStrikeThroughModel.swift; sourceTree = ""; }; @@ -552,12 +614,29 @@ 94FB966123D797DA003D482B /* MFTextButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFTextButton.m; sourceTree = ""; }; AA11A41E23F15D3100D7962F /* ListRightVariablePayments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariablePayments.swift; sourceTree = ""; }; AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariablePaymentsModel.swift; sourceTree = ""; }; + AA1EC59624373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnSpeedTestDividerModel.swift; sourceTree = ""; }; + AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnSpeedTestDivider.swift; sourceTree = ""; }; + AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDividerModel.swift; sourceTree = ""; }; + AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDivider.swift; sourceTree = ""; }; AA3CA8CF241627CE008BF379 /* HeadersH1Bill.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH1Bill.swift; sourceTree = ""; }; AA3CA8D1241627DB008BF379 /* HeadersH1BillModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH1BillModel.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 = ""; }; + BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableTextLinkAllTextAndLinksModel.swift; sourceTree = ""; }; + BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableTextLinkAllTextAndLinks.swift; sourceTree = ""; }; BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextDividerSubsectionModel.swift; sourceTree = ""; }; BB47A587241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextDividerSubsection.swift; sourceTree = ""; }; + BB54C51E2434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableButtonAllTextAndLinks.swift; sourceTree = ""; }; + BB54C51F2434D92F0038326C /* ListRightVariableButtonAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableButtonAllTextAndLinksModel.swift; sourceTree = ""; }; + BB6C6ABE242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTallModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListOneColumnTextWithWhitespaceDividerTallModel.swift; sourceTree = ""; }; + BB6C6ABF242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListOneColumnTextWithWhitespaceDividerTall.swift; sourceTree = ""; }; + BB6C6AC62422528F005F7224 /* ListOneColumnTextWithWhitespaceDividerShort.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListOneColumnTextWithWhitespaceDividerShort.swift; sourceTree = ""; }; + BB6C6AC72422528F005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListOneColumnTextWithWhitespaceDividerShortModel.swift; sourceTree = ""; }; + BBAA4EFF243D8E3B005AAD5F /* RadioBoxes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioBoxes.swift; sourceTree = ""; }; + BBAA4F00243D8E3B005AAD5F /* RadioBoxModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioBoxModel.swift; sourceTree = ""; }; + BBAA4F01243D8E3B005AAD5F /* RadioBoxesModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioBoxesModel.swift; sourceTree = ""; }; + BBBBC87A24374A4900B0F079 /* ListThreeColumnBillChangesDivider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListThreeColumnBillChangesDivider.swift; sourceTree = ""; }; + BBBBC87B24374A4900B0F079 /* ListThreeColumnBillChangesDividerModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListThreeColumnBillChangesDividerModel.swift; sourceTree = ""; }; C003506023AA94CD00B6AC29 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; C07065C32395677300FBF997 /* Link.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Link.swift; sourceTree = ""; }; C695A67E23C9830600BFB94E /* UnOrderedListModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnOrderedListModel.swift; sourceTree = ""; }; @@ -571,8 +650,15 @@ C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadLineBodyCaretLinkImage.swift; sourceTree = ""; }; C7F8012023E8303200396FBD /* ListRVWheel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRVWheel.swift; sourceTree = ""; }; C7F8012223E846C300396FBD /* ListRVWheelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRVWheelModel.swift; sourceTree = ""; }; + D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSTextAlignment+Extension.swift"; sourceTree = ""; }; + D202AFE5242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionViewScrollPosition+Extension.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 = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; + D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = ""; }; + D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionItemModelProtocol.swift; sourceTree = ""; }; + D21B7F74243BAC8900051ABF /* CarouselItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselItem.swift; sourceTree = ""; }; + D21B7F76243BB70700051ABF /* MoleculeCollectionItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeCollectionItemModel.swift; sourceTree = ""; }; D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLayoutConstraintAxis+Extension.swift"; sourceTree = ""; }; D22479892314445E003FCCF9 /* LabelToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelToggle.swift; sourceTree = ""; }; D224798B231450C8003FCCF9 /* HeadlineBodyToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyToggle.swift; sourceTree = ""; }; @@ -587,13 +673,14 @@ D22D1F45220496A30077CEC0 /* MVMCoreUISwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUISwitch.m; sourceTree = ""; }; D22D1F542204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIStackableViewController.h; sourceTree = ""; }; D22D1F552204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIStackableViewController.m; sourceTree = ""; }; + D22D8392241C27B100D3DF69 /* TemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModel.swift; sourceTree = ""; }; D22D8394241FB41200D3DF69 /* UIStackView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Extension.swift"; sourceTree = ""; }; 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 = ""; }; D243859823A16B1800332775 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; D256E9922412880000360572 /* Header.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = ""; }; D260105223CEA61600764D80 /* ToggleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleModel.swift; sourceTree = ""; }; - D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUISwitch+Model.swift"; sourceTree = ""; }; D260105823D0A92900764D80 /* ContainerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerProtocol.swift; sourceTree = ""; }; D260105A23D0BB7100764D80 /* StackModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackModelProtocol.swift; sourceTree = ""; }; D260105C23D0BCD400764D80 /* Stack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; @@ -604,6 +691,15 @@ D260D7AF22D65BDD007E7233 /* MVMCoreUIPageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPageControl.h; sourceTree = ""; }; D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIPageControl.m; sourceTree = ""; }; D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPagingProtocol.h; sourceTree = ""; }; + D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionTemplateModel.swift; sourceTree = ""; }; + D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionTemplate.swift; sourceTree = ""; }; + D264FA8F243BCE6800D98315 /* ThreeLayerCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerCollectionViewController.swift; sourceTree = ""; }; + D264FAA0243CF66B00D98315 /* ContainerCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerCollectionReusableView.swift; sourceTree = ""; }; + D264FAA2243E632F00D98315 /* ProgrammaticCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgrammaticCollectionViewController.swift; sourceTree = ""; }; + D264FAA4243F66A500D98315 /* CollectionTemplateItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionTemplateItemProtocol.swift; sourceTree = ""; }; + D264FAA6243FE13B00D98315 /* RadioBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBox.swift; sourceTree = ""; }; + D264FAA92440F97600D98315 /* CollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionView.swift; sourceTree = ""; }; + 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 = ""; }; D274CA322236A78900B01B62 /* FooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooterView.swift; sourceTree = ""; }; @@ -613,7 +709,6 @@ D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFLoadImageView.swift; sourceTree = ""; }; D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFTransparentGIFView.swift; sourceTree = ""; }; D282AACA2243C61700C46919 /* ButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonView.swift; sourceTree = ""; }; - D28A837623C79FC600DFE4FC /* MFCustomButton+ActionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MFCustomButton+ActionModel.swift"; sourceTree = ""; }; D28A837823C7D5BC00DFE4FC /* PageModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageModelProtocol.swift; sourceTree = ""; }; D28A837A23C928DA00DFE4FC /* MoleculeListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListCellProtocol.swift; sourceTree = ""; }; D28A837C23CCA86A00DFE4FC /* TabsListItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsListItemModel.swift; sourceTree = ""; }; @@ -623,7 +718,6 @@ D28A838423CCCA8900DFE4FC /* ScrollerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollerModel.swift; sourceTree = ""; }; D28A838823CCCFCB00DFE4FC /* LinkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkModel.swift; sourceTree = ""; }; D28A838A23CCDA6B00DFE4FC /* ButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModel.swift; sourceTree = ""; }; - D28A838C23CCDCC200DFE4FC /* PrimaryButton+MoleculeProtocolExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrimaryButton+MoleculeProtocolExtension.swift"; sourceTree = ""; }; D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonViewModel.swift; sourceTree = ""; }; D28A839023CD4FD400DFE4FC /* CornerLabelsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerLabelsModel.swift; sourceTree = ""; }; D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyCaretLinkImageModel.swift; sourceTree = ""; }; @@ -637,6 +731,7 @@ D29770FA21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUITextFieldView.m; sourceTree = ""; }; D29770FB21F7C77400B2F0D0 /* MVMCoreUITextFieldView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUITextFieldView.h; sourceTree = ""; }; D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleMolecule.swift; sourceTree = ""; }; + D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUICommonViewsUtility+Extension.swift"; sourceTree = ""; }; D29DF0CC21E404D4003B2FB9 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUI.h; sourceTree = ""; }; D29DF0D021E404D4003B2FB9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -702,11 +797,11 @@ D29DF28221E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUICommonViewsUtility.h; sourceTree = ""; }; D29DF28721E7AC2B003B2FB9 /* ViewConstrainingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewConstrainingView.h; sourceTree = ""; }; D29DF28821E7AC2B003B2FB9 /* ViewConstrainingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewConstrainingView.m; sourceTree = ""; }; - D29DF28D21E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProgrammaticScrollViewController.m; sourceTree = ""; }; + D29DF28D21E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFProgrammaticScrollViewController.m; sourceTree = ""; }; D29DF28E21E7ADB8003B2FB9 /* StackableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StackableViewController.m; sourceTree = ""; }; D29DF28F21E7ADB8003B2FB9 /* MFScrollingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFScrollingViewController.h; sourceTree = ""; }; D29DF29021E7ADB8003B2FB9 /* MFScrollingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFScrollingViewController.m; sourceTree = ""; }; - D29DF29121E7ADB8003B2FB9 /* ProgrammaticScrollViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProgrammaticScrollViewController.h; sourceTree = ""; }; + D29DF29121E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFProgrammaticScrollViewController.h; sourceTree = ""; }; D29DF29221E7ADB8003B2FB9 /* MFProgrammaticTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFProgrammaticTableViewController.h; sourceTree = ""; }; D29DF29321E7ADB8003B2FB9 /* StackableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StackableViewController.h; sourceTree = ""; }; D29DF29421E7ADB8003B2FB9 /* MFProgrammaticTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFProgrammaticTableViewController.m; sourceTree = ""; }; @@ -742,9 +837,6 @@ D29E28D723D21AB800ACEA85 /* StringAndMoleculeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringAndMoleculeView.swift; sourceTree = ""; }; D29E28D923D21AFA00ACEA85 /* StringAndMoleculeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringAndMoleculeModel.swift; sourceTree = ""; }; D29E28DC23D7404C00ACEA85 /* ContainerHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerHelper.swift; sourceTree = ""; }; - D2A514562211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIMoleculeMappingObject.h; sourceTree = ""; }; - D2A514572211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIMoleculeMappingObject.m; sourceTree = ""; }; - D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIMoleculeViewProtocol.h; sourceTree = ""; }; D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = ""; }; D2A5146022121FBF00345BFB /* MoleculeStackTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackTemplate.swift; sourceTree = ""; }; D2A514622213643100345BFB /* MoleculeStackCenteredTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackCenteredTemplate.swift; sourceTree = ""; }; @@ -753,6 +845,9 @@ D2A638FC22CA98280052ED1F /* HeadlineBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBody.swift; sourceTree = ""; }; D2A6390022CBB1820052ED1F /* Carousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Carousel.swift; sourceTree = ""; }; D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeCollectionViewCell.swift; sourceTree = ""; }; + D2A92881241AAB67004E01C6 /* ScrollingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollingViewController.swift; sourceTree = ""; }; + D2A92883241ACB25004E01C6 /* ProgrammaticScrollViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgrammaticScrollViewController.swift; sourceTree = ""; }; + D2A92885241ACD99004E01C6 /* ProgrammaticTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgrammaticTableViewController.swift; sourceTree = ""; }; D2B18B7E2360913400A9AEDC /* Control.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Control.swift; sourceTree = ""; }; D2B18B802360945C00A9AEDC /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; D2B18B912361E65A00A9AEDC /* CoreUIObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreUIObject.swift; sourceTree = ""; }; @@ -761,6 +856,7 @@ D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = ""; }; D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = ""; }; D2C521A823EDE79E00CA2634 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionOpenPanelModel.swift; sourceTree = ""; }; D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scroller.swift; sourceTree = ""; }; D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplate.swift; sourceTree = ""; }; D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeHeaderModel.swift; sourceTree = ""; }; @@ -785,6 +881,8 @@ DBC4391722442197001AB423 /* DashLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashLine.swift; sourceTree = ""; }; DBC4391A224421A0001AB423 /* CaretLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretLink.swift; sourceTree = ""; }; DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelWithInternalButton.swift; sourceTree = ""; }; + EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButton.swift; sourceTree = ""; }; + EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButtonModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -803,6 +901,7 @@ 011B58EE23A2AA850085F53C /* ModelProtocols */ = { isa = PBXGroup; children = ( + D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */, 014AA72323C501E2006F3E93 /* ContainerModelProtocol.swift */, 012A88C3238D86E600FE3DA1 /* CarouselItemModelProtocol.swift */, 012A88B0238C880100FE3DA1 /* CarouselPagingModelProtocol.swift */, @@ -818,13 +917,13 @@ 011D958A24042794000E3791 /* Rules */ = { isa = PBXGroup; children = ( + 011D95A4240455DC000E3791 /* FormGroupRule.swift */, 011D958424042432000E3791 /* RulesProtocol.swift */, 011D959A240451E3000E3791 /* RuleRequiredModel.swift */, 011D959C2404536F000E3791 /* RuleAnyValueChangedModel.swift */, 011D959E240453A1000E3791 /* RuleAllValueChangedModel.swift */, 011D95A0240453D0000E3791 /* RuleEqualsModel.swift */, 011D95A2240453F8000E3791 /* RuleRegexModel.swift */, - 011D95A4240455DC000E3791 /* FormGroupRule.swift */, 0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */, ); name = Rules; @@ -839,23 +938,15 @@ path = CustomPrimitives; sourceTree = ""; }; - 01509D96232803B200EF99AA /* Models */ = { - isa = PBXGroup; - children = ( - 011B58EE23A2AA850085F53C /* ModelProtocols */, - ); - path = Models; - sourceTree = ""; - }; 01C74D87224298E2009C25A3 /* FormUIHelpers */ = { isa = PBXGroup; children = ( - 011D95882404249B000E3791 /* FormProtocol.swift */, + 011D95882404249B000E3791 /* FormHolderModelProtocol.swift */, 011D95AC2406BB57000E3791 /* FormHolderProtocol.swift */, 011D95AA2405C553000E3791 /* FormItemProtocol.swift */, 011D958624042492000E3791 /* FormFieldProtocol.swift */, - 011D95A824057AC7000E3791 /* FormActionFieldProtocol.swift */, - 011D9601240DA20A000E3791 /* ValidProtocol.swift */, + 011D95A824057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift */, + 011D9601240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift */, 0105618A224BBE7700E1557D /* FormValidator.swift */, 011D958A24042794000E3791 /* Rules */, ); @@ -865,6 +956,7 @@ 0A5D59C323AD488600EFD9E9 /* Protocols */ = { isa = PBXGroup; children = ( + D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */, 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */, ); path = Protocols; @@ -893,6 +985,14 @@ path = Link; sourceTree = ""; }; + 279B1565242BBB8500921D6C /* Adapters */ = { + isa = PBXGroup; + children = ( + 279B1568242BBC2F00921D6C /* ActionModelAdapter.swift */, + ); + path = Adapters; + sourceTree = ""; + }; 5206F150241144A900658DC5 /* Headers */ = { isa = PBXGroup; children = ( @@ -936,17 +1036,33 @@ 525665CB24211FD5007BF25F /* OneColumn */ = { isa = PBXGroup; children = ( + BB6C6AC72422528F005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift */, + BB6C6AC62422528F005F7224 /* ListOneColumnTextWithWhitespaceDividerShort.swift */, + BB6C6ABE242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTallModel.swift */, + BB6C6ABF242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift */, BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */, BB47A587241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift */, ); path = OneColumn; sourceTree = ""; }; - 526A265A240D1FCE00B0D828 /* TwoColumn */ = { + 8DD1E36C243B3CD900D8F2DF /* ThreeColumn */ = { isa = PBXGroup; children = ( + 8DD1E36D243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift */, + 8DD1E36F243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift */, ); - path = TwoColumn; + path = ThreeColumn; + sourceTree = ""; + }; + 94C01508242155FE005811A9 /* Actions */ = { + isa = PBXGroup; + children = ( + 94C0150924215643005811A9 /* ActionTopAlertModel.swift */, + 94C0150B2421564A005811A9 /* ActionCollapseNotificationModel.swift */, + D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */, + ); + path = Actions; sourceTree = ""; }; 94C2D9822386F3E30006CF46 /* Label */ = { @@ -975,6 +1091,10 @@ AA4FC2A323F4F69600E251DB /* RightVariable */ = { isa = PBXGroup; children = ( + BB54C51F2434D92F0038326C /* ListRightVariableButtonAllTextAndLinksModel.swift */, + BB54C51E2434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift */, + BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */, + BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */, AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */, AA11A41E23F15D3100D7962F /* ListRightVariablePayments.swift */, 8D070BAF241B56530099AC56 /* ListRightVariableTotalDataModel.swift */, @@ -985,11 +1105,51 @@ path = RightVariable; sourceTree = ""; }; + AA56A20D243C5EDA00303286 /* TwoColumn */ = { + isa = PBXGroup; + children = ( + AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */, + AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */, + ); + path = TwoColumn; + sourceTree = ""; + }; + D202AFE2242A5F1400E5BEDF /* Extensions */ = { + isa = PBXGroup; + children = ( + D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift */, + 0A209CD223A7E2810068F8B0 /* UIStackViewAlignment+Extension.swift */, + D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */, + D202AFE5242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift */, + 013F801823FB4A8E00AD8013 /* UIContentMode+Extension.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + D20492F12434CB5F00A5EED6 /* FourColumn */ = { + isa = PBXGroup; + children = ( + 8D4687E1242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift */, + 8D4687E3242E2DF300802879 /* ListFourColumnDataUsageListItem.swift */, + ); + path = FourColumn; + sourceTree = ""; + }; D213347423842FE3008E41B3 /* Controllers */ = { isa = PBXGroup; children = ( + D29DF16021E69996003B2FB9 /* MFViewController.h */, + D29DF15F21E69996003B2FB9 /* MFViewController.m */, + D29DF28F21E7ADB8003B2FB9 /* MFScrollingViewController.h */, + D29DF29021E7ADB8003B2FB9 /* MFScrollingViewController.m */, + D29DF29121E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.h */, + D29DF28D21E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.m */, D29DF29321E7ADB8003B2FB9 /* StackableViewController.h */, D29DF28E21E7ADB8003B2FB9 /* StackableViewController.m */, + D29DF29221E7ADB8003B2FB9 /* MFProgrammaticTableViewController.h */, + D29DF29421E7ADB8003B2FB9 /* MFProgrammaticTableViewController.m */, + D22D1F542204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h */, + D22D1F552204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m */, D29770F021F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsViewController.h */, D29770F121F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsViewController.m */, D29770EF21F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.h */, @@ -1001,6 +1161,7 @@ D213347523842FF5008E41B3 /* Views */ = { isa = PBXGroup; children = ( + DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */, 94FB966023D797DA003D482B /* MFTextButton.h */, 94FB966123D797DA003D482B /* MFTextButton.m */, D29DF17E21E69E2E003B2FB9 /* MFView.h */, @@ -1048,7 +1209,6 @@ D29DF24321E6A176003B2FB9 /* MFDigitTextField.h */, D29DF24821E6A177003B2FB9 /* MFDigitTextField.m */, D29DF24A21E6A177003B2FB9 /* MFDigitTextField.xib */, - D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */, D29770C721F7C4AE00B2F0D0 /* TopLabelsView.h */, D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */, 94F217B423E0BF6100A47C06 /* PrimaryButtonView.h */, @@ -1089,6 +1249,8 @@ C7192E7C23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift */, D2E2A99923D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift */, 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */, + EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */, + EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */, ); path = VerticalCombinationViews; sourceTree = ""; @@ -1101,6 +1263,8 @@ D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */, D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */, D28A837E23CCA96400DFE4FC /* TabsModel.swift */, + 011D9625240EBB16000E3791 /* RadioButtonLabelModel.swift */, + 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */, ); path = HorizontalCombinationViews; sourceTree = ""; @@ -1137,8 +1301,6 @@ D26C5A6A23F4A40D007AEECE /* ListItemModel.swift */, 01EB368923609801006832FA /* MoleculeListItemModel.swift */, 01509D8E2327EC6F00EF99AA /* MoleculeTableViewCell.swift */, - 012A88C1238D7BCA00FE3DA1 /* CarouselItemModel.swift */, - D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */, D28A838023CCB0D800DFE4FC /* AccordionListItemModel.swift */, D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */, D28A837C23CCA86A00DFE4FC /* TabsListItemModel.swift */, @@ -1150,6 +1312,10 @@ D260105E23D0BFFC00764D80 /* StackItem.swift */, 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */, D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */, + D21B7F76243BB70700051ABF /* MoleculeCollectionItemModel.swift */, + D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */, + 012A88C1238D7BCA00FE3DA1 /* CarouselItemModel.swift */, + D21B7F74243BAC8900051ABF /* CarouselItem.swift */, ); path = Items; sourceTree = ""; @@ -1168,8 +1334,10 @@ D22B38EA23F4E08B00490EF6 /* List */ = { isa = PBXGroup; children = ( - D22D8396241FDE4700D3DF69 /* TwoColumn */, 52267A0523FFE0A900906CBA /* OneColumn */, + D22D8396241FDE4700D3DF69 /* TwoColumn */, + 8DD1E36C243B3CD900D8F2DF /* ThreeColumn */, + D20492F12434CB5F00A5EED6 /* FourColumn */, AA4FC2A323F4F69600E251DB /* RightVariable */, D22B38EB23F4E0AE00490EF6 /* LeftVariable */, ); @@ -1185,6 +1353,8 @@ 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */, 8D24041423E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift */, 8D24041023E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift */, + 0A6682A32434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift */, + 0A6682A12434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift */, ); path = LeftVariable; sourceTree = ""; @@ -1193,9 +1363,9 @@ isa = PBXGroup; children = ( 525665CB24211FD5007BF25F /* OneColumn */, - 526A265A240D1FCE00B0D828 /* TwoColumn */, - 525019E3240684E500EED91C /* FourColumn */, + AA56A20D243C5EDA00303286 /* TwoColumn */, D22B38ED23F4E11100490EF6 /* ThreeColumn */, + 525019E3240684E500EED91C /* FourColumn */, ); path = SectionDividers; sourceTree = ""; @@ -1203,8 +1373,16 @@ D22B38ED23F4E11100490EF6 /* ThreeColumn */ = { isa = PBXGroup; children = ( + BBBBC87B24374A4900B0F079 /* ListThreeColumnBillChangesDividerModel.swift */, + BBBBC87A24374A4900B0F079 /* ListThreeColumnBillChangesDivider.swift */, 5248BFEB23F12E350059236A /* ListThreeColumnPlanDataDividerModel.swift */, 5248BFEA23F12E350059236A /* ListThreeColumnPlanDataDivider.swift */, + 8D3BA9BC2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift */, + 8D3BA9BE2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift */, + AA1EC59624373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift */, + AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */, + 8DEFA95B243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift */, + 8DEFA95D243DAC2F000D27E5 /* ListThreeColumnDataUsageDivider.swift */, ); path = ThreeColumn; sourceTree = ""; @@ -1212,6 +1390,7 @@ D22D1F582204D2590077CEC0 /* Legacy */ = { isa = PBXGroup; children = ( + 279B1565242BBB8500921D6C /* Adapters */, D213347523842FF5008E41B3 /* Views */, D213347423842FE3008E41B3 /* Controllers */, ); @@ -1234,13 +1413,32 @@ D260105723CF9CC500764D80 /* Doughnut */ = { isa = PBXGroup; children = ( + 0A7ECC5C243CE85300C828E8 /* DoughnutChartItemModel.swift */, C695A69323C9909000BFB94E /* DoughnutChartModel.swift */, C695A69523C990BC00BFB94E /* DoughnutChart.swift */, C695A69723C990C200BFB94E /* DoughnutChartView.swift */, + 0A7ECC5E243CEB1200C828E8 /* ColorViewWithLabel.swift */, ); path = Doughnut; sourceTree = ""; }; + D264FAA8243FE17A00D98315 /* Selectors */ = { + isa = PBXGroup; + children = ( + D264FAAB2441009400D98315 /* RadioBoxCollectionViewCell.swift */, + BBAA4F01243D8E3B005AAD5F /* RadioBoxesModel.swift */, + BBAA4EFF243D8E3B005AAD5F /* RadioBoxes.swift */, + BBAA4F00243D8E3B005AAD5F /* RadioBoxModel.swift */, + D264FAA6243FE13B00D98315 /* RadioBox.swift */, + 0116A4E4228B19640094F3ED /* RadioButtonSelectionHelper.swift */, + 011D95AE2407266E000E3791 /* RadioButtonModel.swift */, + 01004F2F22721C3800991ECC /* RadioButton.swift */, + 31BE15CA23D8924C00452370 /* CheckboxModel.swift */, + 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */, + ); + path = Selectors; + sourceTree = ""; + }; D29DF0C221E404D4003B2FB9 = { isa = PBXGroup; children = ( @@ -1262,7 +1460,7 @@ D29DF0CE21E404D4003B2FB9 /* MVMCoreUI */ = { isa = PBXGroup; children = ( - 01509D96232803B200EF99AA /* Models */, + D2C78CD324252F4E00B69FDE /* Atomic */, 012A88EF23985E0100FE3DA1 /* CustomPrimitives */, D2B18B7D236090D500A9AEDC /* BaseClasses */, 01C74D87224298E2009C25A3 /* FormUIHelpers */, @@ -1275,10 +1473,7 @@ D22D1F582204D2590077CEC0 /* Legacy */, D29DF10F21E67A7D003B2FB9 /* BaseControllers */, D29DF11E21E6851E003B2FB9 /* TopAlert */, - D29DF10D21E67A70003B2FB9 /* Atoms */, - D29DF10E21E67A77003B2FB9 /* Molecules */, - D22479902316A9CB003FCCF9 /* Organisms */, - D29DF0DF21E418B2003B2FB9 /* Templates */, + 94C01508242155FE005811A9 /* Actions */, D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */, D29DF0D021E404D4003B2FB9 /* Info.plist */, ); @@ -1288,7 +1483,7 @@ D29DF0DF21E418B2003B2FB9 /* Templates */ = { isa = PBXGroup; children = ( - 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */, + D22D8392241C27B100D3DF69 /* TemplateModel.swift */, 014AA72823C5059B006F3E93 /* StackPageTemplateModel.swift */, D2A5146022121FBF00345BFB /* MoleculeStackTemplate.swift */, 942C378D2412F5B60066E45E /* ModalMoleculeStackTemplate.swift */, @@ -1300,6 +1495,9 @@ 942C378B2412F4FA0066E45E /* ModalMoleculeListTemplate.swift */, 014AA72A23C5059B006F3E93 /* ThreeLayerPageTemplateModel.swift */, D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */, + D264FAA4243F66A500D98315 /* CollectionTemplateItemProtocol.swift */, + D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */, + D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */, ); path = Templates; sourceTree = ""; @@ -1316,6 +1514,7 @@ D29DF10D21E67A70003B2FB9 /* Atoms */ = { isa = PBXGroup; children = ( + D264FAA8243FE17A00D98315 /* Selectors */, D29DF22B21E6A0FA003B2FB9 /* TextFields */, D29DF17D21E69E26003B2FB9 /* Views */, D29DF16821E69E1F003B2FB9 /* Buttons */, @@ -1337,16 +1536,12 @@ D2A514662213885800345BFB /* MoleculeHeaderView.swift */, 012A88EB238F084D00FE3DA1 /* FooterModel.swift */, D274CA322236A78900B01B62 /* FooterView.swift */, - 0116A4E4228B19640094F3ED /* RadioButtonSelectionHelper.swift */, 012A88C5238DA34000FE3DA1 /* ModuleMoleculeModel.swift */, D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */, D28A838423CCCA8900DFE4FC /* ScrollerModel.swift */, D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */, - 011D9625240EBB16000E3791 /* RadioButtonLabelModel.swift */, - 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */, - 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, - 017BEB49236235BA0024EF95 /* ModelMoleculeViewProtocol.swift */, D260105723CF9CC500764D80 /* Doughnut */, + D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */, ); path = Molecules; sourceTree = ""; @@ -1354,23 +1549,18 @@ D29DF10F21E67A7D003B2FB9 /* BaseControllers */ = { isa = PBXGroup; children = ( - D29DF16021E69996003B2FB9 /* MFViewController.h */, - D29DF15F21E69996003B2FB9 /* MFViewController.m */, - 017BEB3B2361EA1D0024EF95 /* MFViewController+Model.swift */, - 011D9627240EFA1E000E3791 /* MFViewController+Form.swift */, - D29DF28F21E7ADB8003B2FB9 /* MFScrollingViewController.h */, - D29DF29021E7ADB8003B2FB9 /* MFScrollingViewController.m */, - D29DF29121E7ADB8003B2FB9 /* ProgrammaticScrollViewController.h */, - D29DF28D21E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m */, - D29DF29221E7ADB8003B2FB9 /* MFProgrammaticTableViewController.h */, - D29DF29421E7ADB8003B2FB9 /* MFProgrammaticTableViewController.m */, - D22D1F542204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h */, - D22D1F552204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m */, + D236E5B6242007C500C38625 /* MVMControllerModelProtocol.swift */, D29DF2CC21E7C104003B2FB9 /* MFLoadingViewController.h */, D29DF2CD21E7C104003B2FB9 /* MFLoadingViewController.m */, D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */, D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */, + D264FAA0243CF66B00D98315 /* ContainerCollectionReusableView.swift */, + D264FA8F243BCE6800D98315 /* ThreeLayerCollectionViewController.swift */, D2C521A823EDE79E00CA2634 /* ViewController.swift */, + D2A92881241AAB67004E01C6 /* ScrollingViewController.swift */, + D2A92883241ACB25004E01C6 /* ProgrammaticScrollViewController.swift */, + D2A92885241ACD99004E01C6 /* ProgrammaticTableViewController.swift */, + D264FAA2243E632F00D98315 /* ProgrammaticCollectionViewController.swift */, ); path = BaseControllers; sourceTree = ""; @@ -1378,7 +1568,6 @@ D29DF11021E6805F003B2FB9 /* Categories */ = { isa = PBXGroup; children = ( - 013F801823FB4A8E00AD8013 /* UIContentMode+Extension.swift */, D29DF11121E6805F003B2FB9 /* UIColor+MFConvenience.h */, D29DF11321E6805F003B2FB9 /* UIColor+MFConvenience.m */, D29DF11221E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.h */, @@ -1423,6 +1612,8 @@ children = ( D29DF13821E68636003B2FB9 /* MFStyler.h */, D29DF13921E68637003B2FB9 /* MFStyler.m */, + 0A6682A92435125F00AD3CA1 /* Styler.swift */, + 0A6682AB243531C300AD3CA1 /* Padding.swift */, ); path = Styles; sourceTree = ""; @@ -1433,6 +1624,7 @@ D29DF13B21E6870B003B2FB9 /* Sizing */, D29DF28221E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.h */, D29DF28121E7AB23003B2FB9 /* MVMCoreUICommonViewsUtility.m */, + D29C94D4242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift */, D29DF14D21E693AD003B2FB9 /* MFFonts.h */, D29DF14C21E693AD003B2FB9 /* MFFonts.m */, 9458C3152406C8FD00930963 /* UIFont+FontWrapping.h */, @@ -1442,8 +1634,6 @@ D29DF2A721E7B2F9003B2FB9 /* MVMCoreUIConstants.h */, D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */, 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */, - 0A209CD223A7E2810068F8B0 /* UIStackViewAlignment+Extension.swift */, - D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */, ); path = Utility; sourceTree = ""; @@ -1467,8 +1657,6 @@ DBC4391A224421A0001AB423 /* CaretLink.swift */, D28A838A23CCDA6B00DFE4FC /* ButtonModel.swift */, D2E2A99E23E07F8A000B42E6 /* PillButton.swift */, - D28A838C23CCDCC200DFE4FC /* PrimaryButton+MoleculeProtocolExtension.swift */, - D28A837623C79FC600DFE4FC /* MFCustomButton+ActionModel.swift */, ); path = Buttons; sourceTree = ""; @@ -1491,23 +1679,18 @@ D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */, 017BEB7A236763000024EF95 /* LineModel.swift */, D213347623843825008E41B3 /* Line.swift */, - DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */, 94C2D9822386F3E30006CF46 /* Label */, - 31BE15CA23D8924C00452370 /* CheckboxModel.swift */, - 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */, 31BE15C923D8924C00452370 /* CheckboxLabelModel.swift */, 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */, - 011D95AE2407266E000E3791 /* RadioButtonModel.swift */, - 01004F2F22721C3800991ECC /* RadioButton.swift */, D28A838223CCBD3F00DFE4FC /* CircleProgressModel.swift */, 943784F3236B77BB006A1E82 /* GraphView.swift */, 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */, D260105223CEA61600764D80 /* ToggleModel.swift */, 0AA33B392398524F0067DD0F /* Toggle.swift */, - D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */, - 012CA99D2385A2D3003F810F /* MFView+ModelExtension.swift */, 0AE98BB623FF18E9004C5109 /* ArrowModel.swift */, 0AE98BB423FF18D2004C5109 /* Arrow.swift */, + 94382085243238D100B43AF3 /* WebViewModel.swift */, + 943820832432382400B43AF3 /* WebView.swift */, ); path = Views; sourceTree = ""; @@ -1530,7 +1713,6 @@ 0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */, 0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */, 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */, - 017BEB3F23620A230024EF95 /* TextFieldModel.swift */, ); path = TextFields; sourceTree = ""; @@ -1566,11 +1748,6 @@ D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */, D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */, D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */, - 012A88C7238DB02000FE3DA1 /* ModelMoleculeDelegateProtocol.swift */, - D2A514562211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h */, - D2A514572211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m */, - 017BEB432362192F0024EF95 /* MVMCoreUIMoleculeMappingObject+ModelExtension.swift */, - 017BEB7E23676E870024EF95 /* MoleculeObjectMapping.swift */, ); path = OtherHandlers; sourceTree = ""; @@ -1671,17 +1848,43 @@ D2B18B7D236090D500A9AEDC /* BaseClasses */ = { isa = PBXGroup; children = ( - D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */, C003506023AA94CD00B6AC29 /* Button.swift */, D2B18B7E2360913400A9AEDC /* Control.swift */, D2B18B802360945C00A9AEDC /* View.swift */, 0AE14F63238315D2005417F8 /* TextField.swift */, D2755D7A23689C7500485468 /* TableViewCell.swift */, + D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */, + D264FAA92440F97600D98315 /* CollectionView.swift */, 0A5D59C323AD488600EFD9E9 /* Protocols */, ); path = BaseClasses; sourceTree = ""; }; + D2C78CD324252F4E00B69FDE /* Atomic */ = { + isa = PBXGroup; + children = ( + 017BEB7E23676E870024EF95 /* MoleculeObjectMapping.swift */, + D202AFE2242A5F1400E5BEDF /* Extensions */, + D2C78CD424252F5D00B69FDE /* Protocols */, + D29DF10D21E67A70003B2FB9 /* Atoms */, + D29DF10E21E67A77003B2FB9 /* Molecules */, + D22479902316A9CB003FCCF9 /* Organisms */, + D29DF0DF21E418B2003B2FB9 /* Templates */, + ); + path = Atomic; + sourceTree = ""; + }; + D2C78CD424252F5D00B69FDE /* Protocols */ = { + isa = PBXGroup; + children = ( + 012A88C7238DB02000FE3DA1 /* MoleculeDelegateProtocol.swift */, + 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, + 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */, + 011B58EE23A2AA850085F53C /* ModelProtocols */, + ); + path = Protocols; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1697,12 +1900,11 @@ D29DF2A921E7B2F9003B2FB9 /* MVMCoreUIConstants.h in Headers */, 0198F7A62256A80B0066C936 /* MFRadioButton.h in Headers */, D22D1F1A220341F60077CEC0 /* MVMCoreUICheckBox.h in Headers */, - D29DF29921E7ADB8003B2FB9 /* ProgrammaticScrollViewController.h in Headers */, + D29DF29921E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.h in Headers */, D29DF11C21E684A9003B2FB9 /* MVMCoreUISplitViewController.h in Headers */, D29DF29B21E7ADB9003B2FB9 /* StackableViewController.h in Headers */, D29770F421F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsViewController.h in Headers */, D29DF15421E69760003B2FB9 /* MVMCoreUIPanelButtonProtocol.h in Headers */, - D2A514582211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h in Headers */, 9458C3172406C8FD00930963 /* UIFont+FontWrapping.h in Headers */, D29DF0D121E404D4003B2FB9 /* MVMCoreUI.h in Headers */, D29DF29A21E7ADB8003B2FB9 /* MFProgrammaticTableViewController.h in Headers */, @@ -1736,7 +1938,6 @@ 0A21DB88235E06EF00C160A2 /* MFMdnTextField.h in Headers */, D29DF13121E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.h in Headers */, D29DF2C421E7BF57003B2FB9 /* MFTabBarSwipeAnimator.h in Headers */, - D2A5145D2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h in Headers */, D29DF2CA21E7BFC8003B2FB9 /* MFSizeThreshold.h in Headers */, D29DF28021E7AA51003B2FB9 /* MVMCoreUIDetailViewProtocol.h in Headers */, D29DF2BD21E7BEA4003B2FB9 /* MVMCoreUITabBarPageControlViewController.h in Headers */, @@ -1850,6 +2051,7 @@ 8D070BB0241B56530099AC56 /* ListRightVariableTotalDataModel.swift in Sources */, 943784F5236B77BB006A1E82 /* GraphView.swift in Sources */, 31BE15CC23D8924D00452370 /* CheckboxModel.swift in Sources */, + 8D3BA9BF2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift in Sources */, 94C661DA23CCF4FB00D9FE5B /* UIColor+Extension.swift in Sources */, D29DF32121ED0CBA003B2FB9 /* LabelView.m in Sources */, D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */, @@ -1859,8 +2061,10 @@ D29770F221F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.m in Sources */, D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */, D28A838923CCCFCB00DFE4FC /* LinkModel.swift in Sources */, + AA56A20F243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift in Sources */, 94C2D9A923872E5E0006CF46 /* LabelAttributeImageModel.swift in Sources */, DBC4391922442197001AB423 /* DashLine.swift in Sources */, + D264FAAA2440F97600D98315 /* CollectionView.swift in Sources */, 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */, AA11A41F23F15D3100D7962F /* ListRightVariablePayments.swift in Sources */, @@ -1868,53 +2072,62 @@ 0116A4E5228B19640094F3ED /* RadioButtonSelectionHelper.swift in Sources */, 017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, + 94382086243238D100B43AF3 /* WebViewModel.swift in Sources */, D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */, D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */, + 0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */, D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */, D2E2A99423D8CCBC000B42E6 /* HeadlineBodyLinkModel.swift in Sources */, 01004F3022721C3800991ECC /* RadioButton.swift in Sources */, D268C70E238C22D7007F2C1C /* DropDownFilterTableViewCell.swift in Sources */, - 017BEB3C2361EA1D0024EF95 /* MFViewController+Model.swift in Sources */, + D236E5B7242007C500C38625 /* MVMControllerModelProtocol.swift in Sources */, D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */, D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, D2B18B7F2360913400A9AEDC /* Control.swift in Sources */, - 011D95A924057AC7000E3791 /* FormActionFieldProtocol.swift in Sources */, + 011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */, D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */, 0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */, D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */, 942C378C2412F4FA0066E45E /* ModalMoleculeListTemplate.swift in Sources */, BB47A588241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift in Sources */, - 012A88C8238DB02000FE3DA1 /* ModelMoleculeDelegateProtocol.swift in Sources */, + 012A88C8238DB02000FE3DA1 /* MoleculeDelegateProtocol.swift in Sources */, 0A7EF86123D8AC2500B2AAD1 /* DigitEntryFieldModel.swift in Sources */, DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */, D224798C231450C8003FCCF9 /* HeadlineBodyToggle.swift in Sources */, - 017BEB442362192F0024EF95 /* MVMCoreUIMoleculeMappingObject+ModelExtension.swift in Sources */, 9458C3182406C8FD00930963 /* UIFont+FontWrapping.m in Sources */, 522679C123FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift in Sources */, 9445890C2385BCE300DE9FD4 /* ProgressBarModel.swift in Sources */, + D202AFE4242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift in Sources */, 9445891F2385D2E900DE9FD4 /* CaretViewModel.swift in Sources */, 01C851D323CF9E740021F976 /* LabelToggleModel.swift in Sources */, D29DF2C521E7BF57003B2FB9 /* MFTabBarSwipeAnimator.m in Sources */, 011D95A3240453F8000E3791 /* RuleRegexModel.swift in Sources */, D2E2A98323D8B32D000B42E6 /* EyebrowHeadlineBodyLinkModel.swift in Sources */, 012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */, + BB6C6AC1242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift in Sources */, + D21B7F77243BB70700051ABF /* MoleculeCollectionItemModel.swift in Sources */, D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */, - 011D9602240DA20A000E3791 /* ValidProtocol.swift in Sources */, + 011D9602240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift in Sources */, + D264FAA1243CF66B00D98315 /* ContainerCollectionReusableView.swift in Sources */, D260106323D0C05000764D80 /* StackItemModel.swift in Sources */, D2E2A99823D8D63C000B42E6 /* ActionDetailWithImageModel.swift in Sources */, + BBBBC87D24374A4900B0F079 /* ListThreeColumnBillChangesDividerModel.swift in Sources */, D2E2A99D23DA3217000B42E6 /* UIStackViewAlignment+Extension.swift in Sources */, 01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */, + D2A92884241ACB25004E01C6 /* ProgrammaticScrollViewController.swift in Sources */, 0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */, D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */, D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */, D29DF12E21E6851E003B2FB9 /* MVMCoreUITopAlertView.m in Sources */, + AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */, D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */, D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */, 014AA72F23C5059B006F3E93 /* ThreeLayerPageTemplateModel.swift in Sources */, 0A21DB91235E0EDB00C160A2 /* DigitBox.swift in Sources */, + BBAA4F04243D8E3B005AAD5F /* RadioBoxModel.swift in Sources */, D22D1F572204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m in Sources */, D28A838B23CCDA6B00DFE4FC /* ButtonModel.swift in Sources */, - D28A838D23CCDCC200DFE4FC /* PrimaryButton+MoleculeProtocolExtension.swift in Sources */, + D21B7F71243BAC1600051ABF /* CollectionViewCell.swift in Sources */, D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */, C7F8012323E846C300396FBD /* ListRVWheelModel.swift in Sources */, D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */, @@ -1930,25 +2143,25 @@ 52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */, 525239C02407BCFF00454969 /* ListTwoColumnPriceDetailsModel.swift in Sources */, D2E2A99A23D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift in Sources */, + D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */, 8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */, + 94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */, 014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */, - 017BEB4023620A230024EF95 /* TextFieldModel.swift in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, 94C2D9A723872DA90006CF46 /* LabelAttributeColorModel.swift in Sources */, + 943820842432382400B43AF3 /* WebView.swift in Sources */, 0103B84E23D7E33A009C315C /* HeadlineBodyToggleModel.swift in Sources */, D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */, 0A21DB85235E06EF00C160A2 /* MFTextField.m in Sources */, 014AA72623C501E2006F3E93 /* ContainerModelProtocol.swift in Sources */, AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */, 01EB369223609801006832FA /* MoleculeStackModel.swift in Sources */, - 012CA99E2385A2D3003F810F /* MFView+ModelExtension.swift in Sources */, 011D9626240EBB16000E3791 /* RadioButtonLabelModel.swift in Sources */, AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */, D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */, 944589232385DA9600DE9FD4 /* ImageViewModel.swift in Sources */, D213347723843825008E41B3 /* Line.swift in Sources */, - D28A837723C79FC600DFE4FC /* MFCustomButton+ActionModel.swift in Sources */, D2E2A99C23D8D975000B42E6 /* ImageHeadlineBodyModel.swift in Sources */, 525019DE2406430800EED91C /* ListProgressBarData.swift in Sources */, D28A837F23CCA96400DFE4FC /* TabsModel.swift in Sources */, @@ -1957,19 +2170,24 @@ D29E28D823D21AB800ACEA85 /* StringAndMoleculeView.swift in Sources */, 01EB369023609801006832FA /* MoleculeListItemModel.swift in Sources */, D28A838323CCBD3F00DFE4FC /* CircleProgressModel.swift in Sources */, + EA5124FF2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift in Sources */, D268C70C2386DFFD007F2C1C /* MoleculeStackItemModel.swift in Sources */, DBEFFA04225A829700230692 /* Label.swift in Sources */, D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */, + 0A7ECC5D243CE85300C828E8 /* DoughnutChartItemModel.swift in Sources */, 0A7EF85F23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift in Sources */, 011D959B240451E3000E3791 /* RuleRequiredModel.swift in Sources */, 526A265C240D1FF700B0D828 /* ListTwoColumnCompareChangesModel.swift in Sources */, + D2A92886241ACD99004E01C6 /* ProgrammaticTableViewController.swift in Sources */, + BBAA4F05243D8E3B005AAD5F /* RadioBoxesModel.swift in Sources */, 01509D952327ED1900EF99AA /* HeadlineBodyLinkToggle.swift in Sources */, 31BE15CB23D8924D00452370 /* CheckboxLabelModel.swift in Sources */, - D260105523CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift in Sources */, D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */, 5248BFEC23F12E350059236A /* ListThreeColumnPlanDataDivider.swift in Sources */, 0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */, + D264FA8E243BCD9A00D98315 /* CollectionTemplate.swift in Sources */, 0A7EF85B23D8A52800B2AAD1 /* EntryFieldModel.swift in Sources */, + 8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */, 94F217B723E0BF6100A47C06 /* PrimaryButtonView.m in Sources */, 0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */, 0A21DB8B235E06EF00C160A2 /* MFDigitTextBox.m in Sources */, @@ -1981,6 +2199,7 @@ 525019E52406852100EED91C /* ListFourColumnDataUsageDividerModel.swift in Sources */, 0A7EF86723D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift in Sources */, 94FB966323D797DA003D482B /* MFTextButton.m in Sources */, + D29C94D5242901C9003813BA /* MVMCoreUICommonViewsUtility+Extension.swift in Sources */, D260105323CEA61600764D80 /* ToggleModel.swift in Sources */, 014AA72523C501E2006F3E93 /* ContainerModel.swift in Sources */, 0A7EF86523D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift in Sources */, @@ -1992,9 +2211,14 @@ D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, D260105D23D0BCD400764D80 /* Stack.swift in Sources */, 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */, + BB54C5212434D92F0038326C /* ListRightVariableButtonAllTextAndLinksModel.swift in Sources */, + 8DD1E370243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, 01EB368F23609801006832FA /* LabelModel.swift in Sources */, + 0A6682AC243531C300AD3CA1 /* Padding.swift in Sources */, + AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */, 942C378E2412F5B60066E45E /* ModalMoleculeStackTemplate.swift in Sources */, + 8D4687E4242E2DF300802879 /* ListFourColumnDataUsageListItem.swift in Sources */, 01F2A03223A4498200D954D8 /* CaretLinkModel.swift in Sources */, 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */, 011B58F023A2AA980085F53C /* ListItemModelProtocol.swift in Sources */, @@ -2024,11 +2248,14 @@ C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */, 011D958524042432000E3791 /* RulesProtocol.swift in Sources */, 94AF4A3F23E9D13900676048 /* MFCaretButton.m in Sources */, + D264FAA3243E632F00D98315 /* ProgrammaticCollectionViewController.swift in Sources */, D29DF27A21E7A533003B2FB9 /* MVMCoreUISession.m in Sources */, D2A5146B2214905000345BFB /* ThreeLayerViewController.swift in Sources */, 526A265E240D200500B0D828 /* ListTwoColumnCompareChanges.swift in Sources */, 8D24041523E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift in Sources */, D28A838F23CCDEDE00DFE4FC /* TwoButtonViewModel.swift in Sources */, + D264FAAC2441009400D98315 /* RadioBoxCollectionViewCell.swift in Sources */, + BB2C969224330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift in Sources */, D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */, 012A88B1238C880100FE3DA1 /* CarouselPagingModelProtocol.swift in Sources */, D29DF2C921E7BFC6003B2FB9 /* MFSizeObject.m in Sources */, @@ -2038,8 +2265,12 @@ D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */, D29DF2C721E7BF57003B2FB9 /* MFTabBarInteractor.m in Sources */, C7F8012123E8303200396FBD /* ListRVWheel.swift in Sources */, - D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */, + BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */, + D29DF29521E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.m in Sources */, D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */, + 279B1569242BBC2F00921D6C /* ActionModelAdapter.swift in Sources */, + BB6C6AC0242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTallModel.swift in Sources */, + 8DEFA95E243DAC2F000D27E5 /* ListThreeColumnDataUsageDivider.swift in Sources */, D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */, D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */, AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */, @@ -2047,31 +2278,39 @@ 8D084AD02410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift in Sources */, 0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */, 8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */, + BBAA4F03243D8E3B005AAD5F /* RadioBoxes.swift in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, 525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */, 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */, + D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */, DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */, 0A21DB89235E06EF00C160A2 /* MFMdnTextField.m in Sources */, D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */, + D2A92882241AAB67004E01C6 /* ScrollingViewController.swift in Sources */, D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */, C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */, 0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */, D2D90B442404789000DD6EC9 /* MoleculeContainerProtocol.swift in Sources */, + 0A7ECC5F243CEB1200C828E8 /* ColorViewWithLabel.swift in Sources */, + 94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */, 012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */, D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */, 0AE14F64238315D2005417F8 /* TextField.swift in Sources */, D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */, - 017BEB4A236235BA0024EF95 /* ModelMoleculeViewProtocol.swift in Sources */, + D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */, C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */, 01EB3684236097C0006832FA /* MoleculeModelProtocol.swift in Sources */, D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, 8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */, + D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */, D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, + 8DD1E36E243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift in Sources */, D243859923A16B1800332775 /* Container.swift in Sources */, D2C521A923EDE79E00CA2634 /* ViewController.swift in Sources */, D260105B23D0BB7100764D80 /* StackModelProtocol.swift in Sources */, D29DF29821E7ADB8003B2FB9 /* MFScrollingViewController.m in Sources */, D28A839323CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift in Sources */, + BBBBC87C24374A4900B0F079 /* ListThreeColumnBillChangesDivider.swift in Sources */, D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */, D260105F23D0BFFC00764D80 /* StackItem.swift in Sources */, 9432A79F23DB47BA00719041 /* EntryFieldContainer.swift in Sources */, @@ -2084,8 +2323,9 @@ 94C2D9A123872BCC0006CF46 /* LabelAttributeUnderlineModel.swift in Sources */, D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */, D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */, - 011D9628240EFA1E000E3791 /* MFViewController+Form.swift in Sources */, 0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */, + AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */, + D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */, 52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */, D26C5A6B23F4A40D007AEECE /* ListItemModel.swift in Sources */, 0A21DB8D235E06EF00C160A2 /* MFDigitTextField.m in Sources */, @@ -2093,7 +2333,8 @@ 943784F6236B77BB006A1E82 /* GraphViewAnimationHandler.swift in Sources */, 011D95A1240453D0000E3791 /* RuleEqualsModel.swift in Sources */, D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */, - 011D95892404249B000E3791 /* FormProtocol.swift in Sources */, + 011D95892404249B000E3791 /* FormHolderModelProtocol.swift in Sources */, + BB54C5202434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift in Sources */, 948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */, 013F801923FB4A8E00AD8013 /* UIContentMode+Extension.swift in Sources */, 525239C22407BD1000454969 /* ListTwoColumnPriceDetails.swift in Sources */, @@ -2105,14 +2346,18 @@ D29DF26C21E6AA0B003B2FB9 /* FLAnimatedImage.m in Sources */, D28A839123CD4FD400DFE4FC /* CornerLabelsModel.swift in Sources */, 012A88F123985E0100FE3DA1 /* Color.swift in Sources */, + D22D8393241C27B100D3DF69 /* TemplateModel.swift in Sources */, 012A889C23889E8400FE3DA1 /* TemplateModelProtocol.swift in Sources */, D29770FC21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m in Sources */, 52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */, C003506123AA94CD00B6AC29 /* Button.swift in Sources */, DBC4391B224421A0001AB423 /* CaretLink.swift in Sources */, + D264FA90243BCE6800D98315 /* ThreeLayerCollectionViewController.swift in Sources */, 0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */, 0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */, + BB6C6AC824225290005F7224 /* ListOneColumnTextWithWhitespaceDividerShort.swift in Sources */, 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */, + D21B7F73243BAC6800051ABF /* CollectionItemModelProtocol.swift in Sources */, C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */, D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */, D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */, @@ -2125,28 +2370,35 @@ D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */, 011D959D2404536F000E3791 /* RuleAnyValueChangedModel.swift in Sources */, D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */, + BB6C6AC924225290005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift in Sources */, C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */, D29DF32421ED0DA2003B2FB9 /* TextButtonView.m in Sources */, + 8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */, D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */, 012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */, D29DF29E21E7AE3B003B2FB9 /* MFStyler.m in Sources */, - D2A514592211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m in Sources */, 94C661D923CCF4B400D9FE5B /* LeftRightLabelModel.swift in Sources */, 011D95AB2405C553000E3791 /* FormItemProtocol.swift in Sources */, D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */, 525019DD2406430800EED91C /* ListProgressBarDataModel.swift in Sources */, C6FA7D5223C77A4A00A3614A /* UnOrderedList.swift in Sources */, 01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */, + 0A6682A22434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift in Sources */, + EA5124FD243601600051A3A4 /* BGImageHeadlineBodyButton.swift in Sources */, 0105618D224BBE7700E1557D /* FormValidator.swift in Sources */, 01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */, + D21B7F75243BAC8900051ABF /* CarouselItem.swift in Sources */, D22D1F1B220341F60077CEC0 /* MVMCoreUICheckBox.m in Sources */, C695A69823C990C200BFB94E /* DoughnutChartView.swift in Sources */, + 8D3BA9BD2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift in Sources */, D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */, 011D959F240453A1000E3791 /* RuleAllValueChangedModel.swift in Sources */, 011D95AD2406BB57000E3791 /* FormHolderProtocol.swift in Sources */, 01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */, D260106523D0CEA700764D80 /* StackModel.swift in Sources */, + 0A6682AA2435125F00AD3CA1 /* Styler.swift in Sources */, D29770F521F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsViewController.m in Sources */, + D264FAA7243FE13B00D98315 /* RadioBox.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MVMCoreUI/Actions/ActionCollapseNotificationModel.swift b/MVMCoreUI/Actions/ActionCollapseNotificationModel.swift new file mode 100644 index 00000000..9a6e6793 --- /dev/null +++ b/MVMCoreUI/Actions/ActionCollapseNotificationModel.swift @@ -0,0 +1,17 @@ +// +// ActionCollapseNotificationModel.swift +// MVMCore +// +// Created by Ryan on 3/17/20. +// Copyright © 2020 myverizon. All rights reserved. +// + +import UIKit + +@objcMembers public class ActionCollapseNotificationModel: ActionModelProtocol { + public static var identifier: String = "collapseNotification" + public var actionType: String + public var extraParameters: JSONValueDictionary? + public var analyticsData: JSONValueDictionary? + public var title: String? +} diff --git a/MVMCoreUI/Actions/ActionOpenPanelModel.swift b/MVMCoreUI/Actions/ActionOpenPanelModel.swift new file mode 100644 index 00000000..55fd272a --- /dev/null +++ b/MVMCoreUI/Actions/ActionOpenPanelModel.swift @@ -0,0 +1,31 @@ +// +// ActionOpenPanelModel.swift +// MVMCore +// +// Created by Khan, Arshad on 12/02/20. +// Copyright © 2020 myverizon. All rights reserved. +// + +import Foundation + +public class ActionOpenPanelModel: ActionModelProtocol { + + public enum Panel: String, Codable { + case left + case right + case support // Legacy, means left + case menu // Legacy, means right. + } + + public static var identifier: String = "openPanel" + public var actionType: String = ActionOpenPanelModel.identifier + public var panel: Panel + public var extraParameters: JSONValueDictionary? + public var analyticsData: JSONValueDictionary? + // Temporary fix till server changes + public var title: String? + + public init(panel: Panel) { + self.panel = panel + } +} diff --git a/MVMCoreUI/Actions/ActionTopAlertModel.swift b/MVMCoreUI/Actions/ActionTopAlertModel.swift new file mode 100644 index 00000000..628633c2 --- /dev/null +++ b/MVMCoreUI/Actions/ActionTopAlertModel.swift @@ -0,0 +1,23 @@ +// +// ActionTopAlertModel.swift +// MVMCore +// +// Created by Suresh, Kamlesh on 12/16/19. +// Copyright © 2019 myverizon. All rights reserved. +// + +import Foundation + +@objcMembers public class ActionTopAlertModel: ActionModelProtocol { + public static var identifier: String = "topAlert" + public var actionType: String = ActionTopAlertModel.identifier + public var pageType: String + public var extraParameters: JSONValueDictionary? + public var analyticsData: JSONValueDictionary? + // Temporary fix till server changes + public var title: String? + + public init(pageType: String) { + self.pageType = pageType + } +} diff --git a/MVMCoreUI/Atoms/Buttons/ButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift similarity index 89% rename from MVMCoreUI/Atoms/Buttons/ButtonModel.swift rename to MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift index ac9d1dc1..178c309e 100644 --- a/MVMCoreUI/Atoms/Buttons/ButtonModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift @@ -18,8 +18,7 @@ public enum ButtonSize: String, Codable { case tiny } -public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormActionFieldProtocol { - +public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol { public static var identifier: String = "button" public var backgroundColor: Color? public var title: String @@ -33,27 +32,28 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormAction public var disabledFillColor: Color? public var disabledTextColor: Color? public var disabledBorderColor: Color? - public var groupName: String? = FormValidator.defaultGroupName + public var groupName: String = FormValidator.defaultGroupName - public func updateEnable(_ enabled: Bool) { - self.enabled = enabled + public func setValidity(_ valid: Bool, group: FormGroupRule) { + enabled = valid updateUI?() } + /// Temporary binding mechanism for the view to update on enable changes. public var updateUI: (() -> Void)? - init(with title: String, action: ActionModelProtocol) { + public init(with title: String, action: ActionModelProtocol) { self.title = title self.action = action } - init(secondaryButtonWith title: String, action: ActionModelProtocol) { + public init(secondaryButtonWith title: String, action: ActionModelProtocol) { self.title = title self.action = action style = .secondary } - init(primaryButtonWith title: String, action: ActionModelProtocol) { + public init(primaryButtonWith title: String, action: ActionModelProtocol) { self.title = title self.action = action style = .primary diff --git a/MVMCoreUI/Atoms/Buttons/CaretLink.swift b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift similarity index 77% rename from MVMCoreUI/Atoms/Buttons/CaretLink.swift rename to MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift index 50eb7f83..b437a56f 100644 --- a/MVMCoreUI/Atoms/Buttons/CaretLink.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift @@ -25,16 +25,37 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { @objc public var rightViewHeight: NSNumber? @objc public var rightViewWidth: NSNumber? - @objc public var enabledColor: UIColor = .black { + @objc public var enabledColor: UIColor = .mvmBlack { didSet { changeCaretColor() } } - @objc public var disabledColor: UIColor = .mfSilver() { + @objc public var disabledColor: UIColor = .mvmCoolGray3 { didSet { changeCaretColor() } } private var caretSpacingConstraint: NSLayoutConstraint? + //------------------------------------------------------ + // MARK: - Inits + //------------------------------------------------------ + + // Default values for view. + public required init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.init(frame: .zero) + backgroundColor = .clear + translatesAutoresizingMaskIntoConstraints = false + setTitleColor(enabledColor, for: .normal) + setTitleColor(disabledColor, for: .disabled) + } + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + //------------------------------------------------------ // MARK: - Lifecycle //------------------------------------------------------ @@ -82,8 +103,7 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { let width = CGFloat(rightViewWidth?.floatValue ?? CARET_VIEW_WIDTH) let height = CGFloat(rightViewHeight?.floatValue ?? CARET_VIEW_HEIGHT) - let edgeInsets: UIEdgeInsets = contentEdgeInsets - contentEdgeInsets = UIEdgeInsets(top: edgeInsets.top, left: edgeInsets.left, bottom: edgeInsets.bottom, right: 4 + width) + contentEdgeInsets.right = 4 + width let caretView: UIView = rightView ?? createCaretView() rightView = caretView @@ -93,12 +113,12 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { caretView.widthAnchor.constraint(equalToConstant: width).isActive = true caretView.heightAnchor.constraint(equalToConstant: height).isActive = true - let caretLabelSpacing = NSLayoutConstraint(item: caretView, attribute: .left, relatedBy: .equal, toItem: titleLabel, attribute: .right, multiplier: 1.0, constant: 4) + let caretLabelSpacing = NSLayoutConstraint(item: caretView, attribute: .left, relatedBy: .equal, toItem: titleLabel, attribute: .right, multiplier: 1.0, constant: 7) caretLabelSpacing.isActive = true caretSpacingConstraint = caretLabelSpacing - NSLayoutConstraint(item: caretView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0).isActive = true - NSLayoutConstraint(item: self, attribute: .right, relatedBy: .greaterThanOrEqual, toItem: caretView, attribute: .right, multiplier: 1.0, constant: 0).isActive = true + caretView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + trailingAnchor.constraint(greaterThanOrEqualTo: caretView.trailingAnchor).isActive = true caretView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true bottomAnchor.constraint(greaterThanOrEqualTo: caretView.bottomAnchor).isActive = true contentHorizontalAlignment = .left @@ -115,28 +135,22 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { //------------------------------------------------------ // MARK: - Atomization //------------------------------------------------------ - - // Default values for view. - @objc open func setAsMolecule() { - - backgroundColor = .clear - translatesAutoresizingMaskIntoConstraints = false - setTitleColor(enabledColor, for: .normal) - setTitleColor(disabledColor, for: .disabled) - } - public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - guard let caretLinkModel = model as? CaretLinkModel else { return } - if let color = caretLinkModel.backgroundColor { + + guard let model = model as? CaretLinkModel else { return } + + if let color = model.backgroundColor { backgroundColor = color.uiColor } - enabledColor = caretLinkModel.enabledColor.uiColor - if let color = caretLinkModel.disabledColor { + + enabledColor = model.enabledColor.uiColor + if let color = model.disabledColor { disabledColor = color.uiColor } - isEnabled = caretLinkModel.enabled - set(with: caretLinkModel.action, delegateObject: delegateObject, additionalData: additionalData) - setTitle(caretLinkModel.title, for: .normal) + + isEnabled = model.enabled + set(with: model.action, delegateObject: delegateObject, additionalData: additionalData) + setTitle(model.title, for: .normal) } public func needsToBeConstrained() -> Bool { diff --git a/MVMCoreUI/Atoms/Buttons/CaretLinkModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLinkModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Buttons/CaretLinkModel.swift rename to MVMCoreUI/Atomic/Atoms/Buttons/CaretLinkModel.swift diff --git a/MVMCoreUI/Atoms/Buttons/Link/ExternalLink.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLink.swift similarity index 90% rename from MVMCoreUI/Atoms/Buttons/Link/ExternalLink.swift rename to MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLink.swift index 3cd601b6..ba793000 100644 --- a/MVMCoreUI/Atoms/Buttons/Link/ExternalLink.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLink.swift @@ -17,7 +17,7 @@ open class ExternalLink: Link { public var exportImageView: UIImageView? //-------------------------------------------------- - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //-------------------------------------------------- open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { @@ -47,11 +47,11 @@ open class ExternalLink: Link { trailingAnchor.constraint(greaterThanOrEqualTo: exportIcon.trailingAnchor).isActive = true if let titleLabel = titleLabel { - let dimension = round(0.6 * titleLabel.font.pointSize) + let dimension = titleLabel.font.pointSize exportIcon.heightAnchor.constraint(equalToConstant: dimension).isActive = true exportIcon.widthAnchor.constraint(equalToConstant: dimension).isActive = true - exportIcon.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: PaddingOne).isActive = true - exportIcon.bottomAnchor.constraint(equalTo: titleLabel.lastBaselineAnchor).isActive = true + exportIcon.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 4).isActive = true + exportIcon.bottomAnchor.constraint(equalTo: titleLabel.lastBaselineAnchor, constant: 3).isActive = true } } } diff --git a/MVMCoreUI/Atoms/Buttons/Link/ExternalLinkModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLinkModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Buttons/Link/ExternalLinkModel.swift rename to MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLinkModel.swift diff --git a/MVMCoreUI/Atoms/Buttons/Link/Link.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift similarity index 98% rename from MVMCoreUI/Atoms/Buttons/Link/Link.swift rename to MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift index c64bd13b..d65ce3ce 100644 --- a/MVMCoreUI/Atoms/Buttons/Link/Link.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift @@ -44,7 +44,7 @@ import UIKit } //-------------------------------------------------- - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //-------------------------------------------------- public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { diff --git a/MVMCoreUI/Atoms/Buttons/Link/LinkModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Buttons/Link/LinkModel.swift rename to MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift diff --git a/MVMCoreUI/Atoms/Buttons/PillButton.swift b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift similarity index 95% rename from MVMCoreUI/Atoms/Buttons/PillButton.swift rename to MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift index da8cf0c7..19f032fc 100644 --- a/MVMCoreUI/Atoms/Buttons/PillButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift @@ -118,7 +118,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { return CGSize(width: max(width, getMinimumWidth()), height: getHeight()) } - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { // The button will get styled in the enable check in super. super.set(with: model, delegateObject, additionalData) @@ -127,10 +127,12 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { setTitle(model.title, for: .normal) model.updateUI = { [weak self] in - self?.enableField(model.enabled) + MVMCoreDispatchUtility.performBlock(onMainThread: { + self?.enableField(model.enabled) + }) } - FormValidator.setupValidation(molecule: model, delegate: delegateObject?.formHolderDelegate) + FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift similarity index 97% rename from MVMCoreUI/Atoms/Views/Checkbox.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index f288c4cd..cb404f5a 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -129,7 +129,7 @@ import MVMCore (model as? CheckboxModel)?.isChecked = isSelected shapeLayer?.removeAllAnimations() updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) - FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) updateAccessibilityLabel() } } @@ -158,6 +158,7 @@ import MVMCore super.init(frame: frame) accessibilityTraits = .button + isAccessibilityElement = true accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") updateAccessibilityLabel() } @@ -198,8 +199,6 @@ import MVMCore open override func setupView() { super.setupView() - guard constraints.isEmpty else { return } - isUserInteractionEnabled = true translatesAutoresizingMaskIntoConstraints = false backgroundColor = .clear @@ -382,11 +381,7 @@ import MVMCore checkWidth = 2.0 checkAndBypassAnimations(selected: false) } - - open func setAsMolecule() { - setupView() - } - + public override func updateView(_ size: CGFloat) { super.updateView(size) @@ -394,15 +389,13 @@ import MVMCore widthConstraint?.constant = dimension heightConstraint?.constant = dimension } - - //layoutIfNeeded() } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? CheckboxModel else { return } self.delegateObject = delegateObject - FormValidator.setupValidation(molecule: model, delegate: delegateObject?.formHolderDelegate) + FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) if let fieldKey = model.fieldKey { self.fieldKey = fieldKey diff --git a/MVMCoreUI/Atoms/Views/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift similarity index 94% rename from MVMCoreUI/Atoms/Views/CheckboxModel.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index 13b730a6..423ed1ee 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -29,10 +29,8 @@ import Foundation public var disabledCheckColor: Color = Color(uiColor: .mvmCoolGray3) public var action: ActionModelProtocol? - public var fieldKey: String? - public var fieldValue: JSONValue? - public var groupName: String? = FormValidator.defaultGroupName + public var groupName: String = FormValidator.defaultGroupName public var baseValue: AnyHashable? //-------------------------------------------------- @@ -41,8 +39,6 @@ import Foundation private enum CodingKeys: String, CodingKey { case moleculeName - case fieldKey - case fieldValue case isChecked = "checked" case isEnabled = "enabled" case isAnimated = "animated" @@ -56,23 +52,25 @@ import Foundation case disabledCheckColor case disabledBorderColor case action + case fieldKey case groupName } - - init(isChecked: Bool = false) {} public func formFieldValue() -> AnyHashable? { return isChecked } + public init(isChecked: Bool = false) { + self.isChecked = isChecked + baseValue = isChecked + } + //-------------------------------------------------- // MARK: - Codec //-------------------------------------------------- required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - fieldValue = try typeContainer.decodeIfPresent(JSONValue.self, forKey: .fieldValue) - fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) ?? 1 borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) ?? Color(uiColor: .black) checkColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkColor) ?? Color(uiColor: .black) @@ -86,6 +84,9 @@ import Foundation isRound = try typeContainer.decodeIfPresent(Bool.self, forKey: .isRound) ?? false isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? true action = try typeContainer.decodeModelIfPresent(codingKey: .action) + + baseValue = isChecked + fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { self.groupName = groupName } @@ -95,7 +96,6 @@ import Foundation var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(groupName, forKey: .groupName) - try container.encodeIfPresent(fieldValue, forKey: .fieldValue) try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(borderColor, forKey: .borderColor) try container.encode(borderWidth, forKey: .borderWidth) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift new file mode 100644 index 00000000..da762dbb --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift @@ -0,0 +1,183 @@ +// +// RadioBox.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/9/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class RadioBox: Control { + public let label = Label.createLabelRegularBodySmall(true) + public let subTextLabel = Label.createLabelRegularMicro(true) + public var isOutOfStock = false + public var accentColor = UIColor.mvmRed + + public let innerPadding: CGFloat = 12.0 + + private var borderLayer: CALayer? + private var strikeLayer: CALayer? + private var maskLayer: CALayer? + + public var subTextLabelHeightConstraint: NSLayoutConstraint? + + public var radioBoxModel: RadioBoxModel? { + return model as? RadioBoxModel + } + + // MARK: - MVMCoreViewProtocol + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + label.updateView(size) + subTextLabel.updateView(size) + layer.setNeedsDisplay() + } + + open override func setupView() { + super.setupView() + + layer.delegate = self + layer.borderColor = UIColor.black.cgColor + layer.borderWidth = 1 + + label.numberOfLines = 1 + addSubview(label) + NSLayoutConstraint.constraintPinSubview(label, pinTop: true, topConstant: innerPadding, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: innerPadding, pinRight: true, rightConstant: innerPadding) + + subTextLabel.textColor = .mvmCoolGray6 + subTextLabel.numberOfLines = 1 + addSubview(subTextLabel) + NSLayoutConstraint.constraintPinSubview(subTextLabel, pinTop: false, topConstant:0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: innerPadding, pinRight: true, rightConstant: innerPadding) + bottomAnchor.constraint(greaterThanOrEqualTo: subTextLabel.bottomAnchor, constant: innerPadding).isActive = true + subTextLabel.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 2).isActive = true + subTextLabelHeightConstraint = subTextLabel.heightAnchor.constraint(equalToConstant: 0) + subTextLabelHeightConstraint?.isActive = true + + addTarget(self, action: #selector(selectBox), for: .touchUpInside) + } + + // MARK: - MoleculeViewProtocol + + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? RadioBoxModel else { return } + isSelected = model.selected + isEnabled = model.enabled + label.text = model.text + subTextLabel.text = model.subText + isOutOfStock = model.strikethrough + subTextLabelHeightConstraint?.isActive = (subTextLabel.text?.count ?? 0) == 0 + } + + // MARK: - State Handling + + open override func draw(_ layer: CALayer, in ctx: CGContext) { + // Draw the strikethrough + strikeLayer?.removeFromSuperlayer() + if isOutOfStock { + let line = getStrikeThrough(color: .black, thickness: 1) + layer.addSublayer(line) + strikeLayer = line + } + + // Draw the border + borderLayer?.removeFromSuperlayer() + if isSelected { + layer.borderWidth = 0 + let border = getSelectedBorder() + layer.addSublayer(border) + borderLayer = border + } else { + layer.borderWidth = 1 + } + + // Handle Mask + maskLayer?.removeFromSuperlayer() + if !isEnabled { + let mask = getMaskLayer() + layer.mask = mask + } + } + + open override func layoutSubviews() { + super.layoutSubviews() + // Accounts for any size changes + layer.setNeedsDisplay() + } + + @objc open func selectBox() { + isSelected = true + radioBoxModel?.selected = isSelected + layer.setNeedsDisplay() + } + + @objc open func deselectBox() { + isSelected = false + radioBoxModel?.selected = isSelected + layer.setNeedsDisplay() + } + + /// Gets the selected state border + func getSelectedBorder() -> CAShapeLayer { + let layer = CAShapeLayer() + + let topLineWidth: CGFloat = 4 + let topLinePath = UIBezierPath() + topLinePath.lineWidth = topLineWidth + topLinePath.move(to: CGPoint(x: 0, y: topLineWidth / 2.0)) + topLinePath.addLine(to: CGPoint(x: bounds.width, y: topLineWidth / 2.0)) + + let topLineLayer = CAShapeLayer() + topLineLayer.fillColor = nil + topLineLayer.strokeColor = UIColor.mvmRed.cgColor + topLineLayer.lineWidth = 4 + topLineLayer.path = topLinePath.cgPath + layer.addSublayer(topLineLayer) + + let lineWidth: CGFloat = 1 + let halfLineWidth: CGFloat = 0.5 + let linePath = UIBezierPath() + linePath.move(to: CGPoint(x: halfLineWidth, y: topLineWidth)) + linePath.addLine(to: CGPoint(x: halfLineWidth, y: bounds.height)) + linePath.move(to: CGPoint(x: 0, y: bounds.height - halfLineWidth)) + linePath.addLine(to: CGPoint(x: bounds.width, y: bounds.height - halfLineWidth)) + linePath.move(to: CGPoint(x: bounds.width - halfLineWidth, y: bounds.height)) + linePath.addLine(to: CGPoint(x: bounds.width - halfLineWidth, y: topLineWidth)) + + let borderLayer = CAShapeLayer() + borderLayer.fillColor = nil + borderLayer.strokeColor = UIColor.black.cgColor + borderLayer.lineWidth = lineWidth + borderLayer.path = linePath.cgPath + layer.addSublayer(borderLayer) + + return layer + } + + /// Adds a border to edge + func getStrikeThrough(color: UIColor, thickness: CGFloat) -> CAShapeLayer { + let border = CAShapeLayer() + border.name = "strikethrough" + border.fillColor = nil + border.opacity = 1.0 + border.lineWidth = thickness + border.strokeColor = color.cgColor + + let linePath = UIBezierPath() + linePath.move(to: CGPoint(x: 0, y: bounds.height)) + linePath.addLine(to: CGPoint(x: bounds.width, y: 0)) + border.path = linePath.cgPath + return border + } + + func getMaskLayer() -> CALayer { + let mask = CALayer() + mask.backgroundColor = UIColor.white.cgColor + mask.opacity = 0.3 + mask.frame = bounds + return mask + } +} + diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxCollectionViewCell.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxCollectionViewCell.swift new file mode 100644 index 00000000..c90b5e92 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxCollectionViewCell.swift @@ -0,0 +1,23 @@ +// +// RadioBoxCollectionViewCell.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 01/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +open class RadioBoxCollectionViewCell: CollectionViewCell { + let radioBox = RadioBox() + + open override func setupView() { + super.setupView() + addMolecule(radioBox) + MVMCoreUIUtility.setMarginsFor(contentView, leading: 0, top: 0, trailing: 0, bottom: 0) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let model = model as? RadioBoxModel else { return } + radioBox.set(with: model, delegateObject, additionalData) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift new file mode 100644 index 00000000..b2a23c6a --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxModel.swift @@ -0,0 +1,68 @@ +// +// RadioBoxModel.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 31/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +@objcMembers public class RadioBoxModel: MoleculeModelProtocol { + public static var identifier: String = "radioBox" + public var text: String + public var subText: String? + public var backgroundColor: Color? = Color(uiColor: .white) + public var selectedAccentColor = Color(uiColor: .mvmRed) + public var selected: Bool = false + public var enabled: Bool = true + public var strikethrough: Bool = false + public var fieldValue: String? + + private enum CodingKeys: String, CodingKey { + case moleculeName + case text + case subText + case selectedAccentColor + case backgroundColor + case selected + case enabled + case strikethrough + case fieldValue + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + text = try typeContainer.decode(String.self, forKey: .text) + subText = try typeContainer.decodeIfPresent(String.self, forKey: .subText) + if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .selectedAccentColor) { + selectedAccentColor = color + } + if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) { + backgroundColor = color + } + if let isSelected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) { + selected = isSelected + } + if let isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { + enabled = isEnabled + } + if let isStrikeTrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) { + strikethrough = isStrikeTrough + } + + fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(text, forKey: .text) + try container.encodeIfPresent(subText, forKey: .subText) + try container.encode(selectedAccentColor, forKey: .selectedAccentColor) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(selected, forKey: .selected) + try container.encode(enabled, forKey: .enabled) + try container.encode(strikethrough, forKey: .strikethrough) + try container.encodeIfPresent(fieldValue, forKey: .fieldValue) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift new file mode 100644 index 00000000..6fe66bc2 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxes.swift @@ -0,0 +1,143 @@ +// +// RadioBoxes.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 31/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class RadioBoxes: View { + + public var collectionView: CollectionView! + public var collectionViewHeight: NSLayoutConstraint! + private let boxWidth: CGFloat = 151.0 + private let boxHeight: CGFloat = 64.0 + private let itemSpacing: CGFloat = 8.0 + + private var delegateObject: MVMCoreUIDelegateObject? + + /// The models for the molecules. + public var boxes: [RadioBoxModel]? + + private var size: CGFloat? + + open override func layoutSubviews() { + super.layoutSubviews() + // Accounts for any collection size changes + DispatchQueue.main.async { + self.collectionView.collectionViewLayout.invalidateLayout() + } + } + + // MARK: - MVMCoreViewProtocol + open override func setupView() { + super.setupView() + collectionView = createCollectionView() + addSubview(collectionView) + NSLayoutConstraint.constraintPinSubview(toSuperview: collectionView) + collectionViewHeight = collectionView.heightAnchor.constraint(equalToConstant: 300) + collectionViewHeight?.isActive = true + } + + // MARK: - MoleculeViewProtocol + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + self.delegateObject = delegateObject + + guard let radioBoxesModel = model as? RadioBoxesModel else { return } + boxes = radioBoxesModel.boxes + FormValidator.setupValidation(for: radioBoxesModel, delegate: delegateObject?.formHolderDelegate) + + backgroundColor = radioBoxesModel.backgroundColor?.uiColor + registerCells() + setHeight() + collectionView.reloadData() + } + + @objc override open func updateView(_ size: CGFloat) { + super.updateView(size) + self.size = size + collectionView.updateView(size) + } + + // MARK: - Creation + + /// Creates the layout for the collection. + open func createCollectionViewLayout() -> UICollectionViewLayout { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + layout.minimumLineSpacing = itemSpacing + layout.minimumInteritemSpacing = itemSpacing + return layout + } + + /// Creates the collection view. + open func createCollectionView() -> CollectionView { + let collection = CollectionView(frame: .zero, collectionViewLayout: createCollectionViewLayout()) + collection.dataSource = self + collection.delegate = self + return collection + } + + /// Registers the cells with the collection view + open func registerCells() { + collectionView.register(RadioBoxCollectionViewCell.self, forCellWithReuseIdentifier: "RadioBoxCollectionViewCell") + } + + // MARK: - JSON Setters + open func setHeight() { + guard let boxes = boxes, boxes.count > 0 else { + collectionViewHeight.constant = 0 + return + } + + // Calculate the height + let rows = ceil(CGFloat(boxes.count) / 2.0) + let height = (rows * boxHeight) + ((rows - 1) * itemSpacing) + collectionViewHeight?.constant = height + } +} + +extension RadioBoxes: UICollectionViewDelegateFlowLayout { + open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let itemWidth: CGFloat = (collectionView.bounds.width - itemSpacing) / 2 + return CGSize(width: itemWidth, height: boxHeight) + } +} + +extension RadioBoxes: UICollectionViewDataSource { + open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return boxes?.count ?? 0 + } + + open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let molecule = boxes?[indexPath.row], + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RadioBoxCollectionViewCell", for: indexPath) as? RadioBoxCollectionViewCell else { + fatalError() + } + cell.radioBox.isUserInteractionEnabled = false + cell.set(with: molecule, delegateObject, nil) + cell.updateView(size ?? collectionView.bounds.width) + if molecule.selected { + collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically) + } + cell.layoutIfNeeded() + return cell + } +} + +extension RadioBoxes: UICollectionViewDelegate { + public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + guard let cell = collectionView.cellForItem(at: indexPath) as? RadioBoxCollectionViewCell else { return } + cell.radioBox.selectBox() + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + } + + public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { + guard let cell = collectionView.cellForItem(at: indexPath) as? RadioBoxCollectionViewCell else { return } + cell.radioBox.deselectBox() + } +} + diff --git a/MVMCoreUI/Atoms/Views/ToggleModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxesModel.swift similarity index 52% rename from MVMCoreUI/Atoms/Views/ToggleModel.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxesModel.swift index 5e67be8a..28c4fab5 100644 --- a/MVMCoreUI/Atoms/Views/ToggleModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBoxesModel.swift @@ -1,64 +1,57 @@ // -// ToggleModel.swift +// RadioBoxesModel.swift // MVMCoreUI // -// Created by Scott Pfeil on 1/14/20. +// Created by Dhamodaram Nandi on 31/03/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // -import UIKit - -public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol { - - public static var identifier: String = "toggle" +import Foundation +@objcMembers public class RadioBoxesModel: MoleculeModelProtocol, FormFieldProtocol { + public static var identifier: String = "radioBoxes" public var backgroundColor: Color? - public var state: Bool = true - public var action: ActionModelProtocol? - public var alternateAction: ActionModelProtocol? + public var selectedAccentColor: Color? + public var boxes: [RadioBoxModel] public var fieldKey: String? - public var groupName: String? = FormValidator.defaultGroupName + public var groupName: String = FormValidator.defaultGroupName public var baseValue: AnyHashable? + /// Returns the fieldValue of the selected box, otherwise the text of the selected box. + public func formFieldValue() -> AnyHashable? { + let selectedBox = boxes.first { (box) -> Bool in + return box.selected + } + return selectedBox?.fieldValue ?? selectedBox?.text + } + private enum CodingKeys: String, CodingKey { case moleculeName - case state - case action + case selectedAccentColor case backgroundColor + case boxes case fieldKey - case alternateAction case groupName } - public func formFieldValue() -> AnyHashable? { - return state - } - - public init(_ state: Bool) { - self.state = state - } - required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { - self.state = state - } - action = try typeContainer.decodeModelIfPresent(codingKey: .action) - alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction) + selectedAccentColor = try typeContainer.decodeIfPresent(Color.self, forKey: .selectedAccentColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + boxes = try typeContainer.decode([RadioBoxModel].self, forKey: .boxes) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { self.groupName = groupName } + baseValue = formFieldValue() } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) - try container.encodeModelIfPresent(action, forKey: .action) - try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) try container.encode(moleculeName, forKey: .moleculeName) - try container.encode(state, forKey: .state) + try container.encode(boxes, forKey: .boxes) + try container.encodeIfPresent(selectedAccentColor, forKey: .selectedAccentColor) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(fieldKey, forKey: .fieldKey) - try container.encodeIfPresent(groupName, forKey: .groupName) + try container.encode(groupName, forKey: .groupName) } } diff --git a/MVMCoreUI/Atoms/Views/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift similarity index 52% rename from MVMCoreUI/Atoms/Views/RadioButton.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index ce95c639..a5fedef0 100644 --- a/MVMCoreUI/Atoms/Views/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -8,31 +8,37 @@ import UIKit -@objcMembers public class RadioButton: Control { + +@objcMembers open class RadioButton: Control { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- - var diameter: CGFloat = 30 { + public var diameter: CGFloat = 30 { didSet { widthConstraint?.constant = diameter } } - var enabledColor = UIColor.black - var disabledColor = UIColor.mfSilver() - var delegateObject: MVMCoreUIDelegateObject? - - var widthConstraint: NSLayoutConstraint? - var heightConstraint: NSLayoutConstraint? - - var radioModel: RadioButtonModel? { - return model as? RadioButtonModel + public override var isSelected: Bool { + didSet { + radioModel?.state = isSelected + } } - lazy var radioGroupName: String? = { - [unowned self] in return radioModel?.fieldKey + public var enabledColor: UIColor = .mvmBlack + public var disabledColor: UIColor = .mvmCoolGray3 + public var delegateObject: MVMCoreUIDelegateObject? + + public var radioModel: RadioButtonModel? { + return model as? RadioButtonModel + } + + lazy public var radioGroupName: String? = { + return radioModel?.fieldKey }() - - lazy var radioButtonSelectionHelper: RadioButtonSelectionHelper? = { - [unowned self] in + + lazy public var radioButtonSelectionHelper: RadioButtonSelectionHelper? = { if let radioGroupName = radioGroupName, let radioButtonModel = delegateObject?.formHolderDelegate?.formValidator?.radioButtonsModelByGroup[radioGroupName] { return radioButtonModel @@ -41,20 +47,47 @@ import UIKit } }() + public override var isEnabled: Bool { + didSet { + isUserInteractionEnabled = isEnabled + setNeedsDisplay() + } + } + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + + public var widthConstraint: NSLayoutConstraint? + public var heightConstraint: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func draw(_ rect: CGRect) { guard let context = UIGraphicsGetCurrentContext() else { return } + let color = isEnabled ? enabledColor.cgColor : disabledColor.cgColor layer.cornerRadius = bounds.width * 0.5 layer.borderColor = color layer.borderWidth = bounds.width * 0.0333 + if isSelected { // Space around inner circle is 1/5 the size - context.addEllipse(in: CGRect(x: bounds.width*0.2, y: bounds.height*0.2, width: bounds.width*0.6, height: bounds.height*0.6)) + context.addEllipse(in: CGRect(x: bounds.width * 0.2, + y: bounds.height * 0.2, + width: bounds.width * 0.6, + height: bounds.height * 0.6)) context.setFillColor(color) context.fillPath() } } + //-------------------------------------------------- + // MARK: - Validation + //-------------------------------------------------- + /// The action performed when tapped. func tapAction() { if let radioButtonModel = radioButtonSelectionHelper { @@ -62,7 +95,7 @@ import UIKit } else { isSelected = !isSelected } - FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) setNeedsDisplay() } @@ -81,11 +114,15 @@ import UIKit public func formFieldValue() -> AnyHashable? { return radioModel?.fieldValue } - -// MARK: - MVMViewProtocol + + //-------------------------------------------------- + // MARK: - MVMViewProtocol + //-------------------------------------------------- + open override func setupView() { super.setupView() - backgroundColor = .white + + backgroundColor = .mvmWhite clipsToBounds = true widthConstraint = widthAnchor.constraint(equalToConstant: 30) widthConstraint?.isActive = true @@ -97,20 +134,20 @@ import UIKit accessibilityTraits = .button accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") } - - public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - guard let model = model as? RadioButtonModel else { - return - } + + guard let model = model as? RadioButtonModel else { return } + self.delegateObject = delegateObject - let radioButtonModel = RadioButtonSelectionHelper.setupForRadioButtonGroup(model, - formValidator: delegateObject?.formHolderDelegate?.formValidator) - FormValidator.setupValidation(molecule: radioButtonModel, delegate: delegateObject?.formHolderDelegate) + isSelected = model.state + isEnabled = model.enabled + RadioButtonSelectionHelper.setupForRadioButtonGroup(model, self, delegateObject: delegateObject) } - + public override func reset() { - super.reset() - backgroundColor = .white + super.reset() + backgroundColor = .mvmWhite } } diff --git a/MVMCoreUI/Atoms/Views/RadioButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift similarity index 63% rename from MVMCoreUI/Atoms/Views/RadioButtonModel.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift index 0aa56cc0..ea1a4627 100644 --- a/MVMCoreUI/Atoms/Views/RadioButtonModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonModel.swift @@ -9,47 +9,77 @@ import Foundation import MVMCore -public class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { +open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "radioButton" public var backgroundColor: Color? public var state: Bool = false public var enabled: Bool = true - - public var baseValue: AnyHashable? - public var groupName: String? - public var fieldKey: String? + + /// The specific value to send to server. TODO: update this to be more generic. public var fieldValue: String? + public var baseValue: AnyHashable? + public var groupName: String = FormValidator.defaultGroupName + public var fieldKey: String? + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor case state case enabled - case fieldKey case fieldValue + case fieldKey case groupName } + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(_ state: Bool) { self.state = state + baseValue = state } - + + //-------------------------------------------------- + // MARK: - Validation + //-------------------------------------------------- + public func formFieldValue() -> AnyHashable? { - return state + return fieldValue } - + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { self.state = state } + if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { self.enabled = enabled } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + + baseValue = state fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) - groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) + if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { + self.groupName = groupName + } fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonSelectionHelper.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonSelectionHelper.swift new file mode 100644 index 00000000..5196bb9c --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButtonSelectionHelper.swift @@ -0,0 +1,82 @@ +// +// RadioButtonModel.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 5/14/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + + +@objcMembers public class RadioButtonSelectionHelper: FormFieldProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public var fieldKey: String? + public var groupName: String = FormValidator.defaultGroupName + private var selectedRadioButton: RadioButton? + private var selectedRadioButtonModel: RadioButtonModel? + public var baseValue: AnyHashable? + +//-------------------------------------------------- +// MARK: - Initializer +//-------------------------------------------------- + + public func set(_ radioButtonModel: RadioButtonModel, _ radioButton: RadioButton) { + self.fieldKey = radioButtonModel.fieldKey + self.groupName = radioButtonModel.groupName + + if radioButtonModel.state { + if self.baseValue == nil, + let selected = radioButtonModel.baseValue as? Bool, selected { + self.baseValue = radioButtonModel.fieldValue + } + selectedRadioButtonModel = radioButtonModel + + // Below code is needed for cell resuse scenario. + radioButton.isSelected = true + selectedRadioButton = radioButton + } else { + radioButton.isSelected = false + } + } + + //-------------------------------------------------- + // MARK: - Functions + //-------------------------------------------------- + + public static func setupForRadioButtonGroup(_ radioButtonModel: RadioButtonModel, _ radioButton: RadioButton, delegateObject: MVMCoreUIDelegateObject?) { + + guard let groupName = radioButtonModel.fieldKey, + let formValidator = delegateObject?.formHolderDelegate?.formValidator else { + return + } + + let radioButtonSelectionHelper = formValidator.radioButtonsModelByGroup[groupName] ?? RadioButtonSelectionHelper() + radioButtonSelectionHelper.set(radioButtonModel, radioButton) + formValidator.radioButtonsModelByGroup[groupName] = radioButtonSelectionHelper + FormValidator.setupValidation(for: radioButtonSelectionHelper, delegate: delegateObject?.formHolderDelegate) + } + + public func selected(_ radioButton: RadioButton) { + // Checks because the view could be reused + if selectedRadioButton?.radioModel === selectedRadioButtonModel { + selectedRadioButton?.isSelected = false + } else { + selectedRadioButtonModel?.state = false + } + + selectedRadioButton = radioButton + selectedRadioButton?.isSelected = true + selectedRadioButtonModel = selectedRadioButton?.radioModel + } +} + +// MARK: - FormValidationFormFieldProtocol +extension RadioButtonSelectionHelper { + public func formFieldValue() -> AnyHashable? { + return selectedRadioButtonModel?.fieldValue + } +} diff --git a/MVMCoreUI/Atoms/TextFields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryField.swift similarity index 84% rename from MVMCoreUI/Atoms/TextFields/BaseDropdownEntryField.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryField.swift index b7c59fd5..f7083df5 100644 --- a/MVMCoreUI/Atoms/TextFields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryField.swift @@ -52,6 +52,10 @@ import UIKit fatalError("DropdownEntryField does not support xib.") } + required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.init(model: model, delegateObject, additionalData) + } + //-------------------------------------------------- // MARK: - Setup //-------------------------------------------------- @@ -77,15 +81,3 @@ import UIKit dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData) } } - -// MARK: - MVMCoreUIMoleculeViewProtocol -extension BaseDropdownEntryField { - - @objc override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - guard let dictionary = json, !dictionary.isEmpty else { return } - - dropDownCaretView.setWithJSON(dictionary, delegateObject: delegateObject, additionalData: additionalData) - } -} diff --git a/MVMCoreUI/Atoms/TextFields/BaseDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryFieldModel.swift similarity index 100% rename from MVMCoreUI/Atoms/TextFields/BaseDropdownEntryFieldModel.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/BaseDropdownEntryFieldModel.swift diff --git a/MVMCoreUI/Atoms/TextFields/DateDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryField.swift similarity index 87% rename from MVMCoreUI/Atoms/TextFields/DateDropdownEntryField.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryField.swift index 95174096..a7a483fc 100644 --- a/MVMCoreUI/Atoms/TextFields/DateDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryField.swift @@ -58,6 +58,10 @@ import UIKit fatalError("DateDropdownEntryField init(coder:) has not been implemented") } + required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.init(model: model, delegateObject, additionalData) + } + //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- @@ -119,17 +123,3 @@ import UIKit dateFormat = model.dateFormat } } - -// MARK: - MVMCoreUIMoleculeViewProtocol -extension DateDropdownEntryField { - - @objc override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - guard let dictionary = json, !dictionary.isEmpty else { return } - - if let dateFormat = dictionary["dateFormat"] as? String { - self.dateFormat = dateFormat - } - } -} diff --git a/MVMCoreUI/Atoms/TextFields/DateDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryFieldModel.swift similarity index 100% rename from MVMCoreUI/Atoms/TextFields/DateDropdownEntryFieldModel.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/DateDropdownEntryFieldModel.swift diff --git a/MVMCoreUI/Atoms/TextFields/DigitBox.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DigitBox.swift similarity index 100% rename from MVMCoreUI/Atoms/TextFields/DigitBox.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/DigitBox.swift diff --git a/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift similarity index 92% rename from MVMCoreUI/Atoms/TextFields/DigitEntryField.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift index 53181bff..38adee03 100644 --- a/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryField.swift @@ -116,7 +116,7 @@ import UIKit for (index, field) in digitBoxes.enumerated() { if index < newValue.count { let indexChar = newValue.index(newValue.startIndex, offsetBy: index) - field.digitField.attributedPlaceholder = NSAttributedString(string: String(newValue[indexChar]), attributes: [NSAttributedString.Key.foregroundColor: UIColor.mfBattleshipGrey()]) + field.digitField.attributedPlaceholder = NSAttributedString(string: String(newValue[indexChar]), attributes: [NSAttributedString.Key.foregroundColor: UIColor.mvmCoolGray6]) } } @@ -193,6 +193,10 @@ import UIKit fatalError("DigitEntryField xib has not been implemented") } + required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.init(model: model, delegateObject, additionalData) + } + //-------------------------------------------------- // MARK: - Setup //-------------------------------------------------- @@ -207,7 +211,7 @@ import UIKit let digitBox = DigitBox() digitBox.isAccessibilityElement = true - MVMCoreUICommonViewsUtility.addDismissToolbar(digitBox.digitField, delegate: self) + digitBox.digitField.inputAccessoryView = MVMCoreUICommonViewsUtility.getToolbarWithDoneButton(delegate: self) digitBox.digitField.delegate = self digitBox.digitBoxDelegate = self return digitBox @@ -329,20 +333,22 @@ import UIKit } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - - + guard let model = model as? DigitEntryFieldModel else { return } numberOfDigits = model.digits - setAsSecureTextEntry(model.secureEntry) for digitBox in digitBoxes { - MVMCoreUICommonViewsUtility.addDismissToolbar(digitBox.digitField, delegate: delegateObject as? UITextFieldDelegate) + digitBox.digitField.inputAccessoryView = MVMCoreUICommonViewsUtility.getToolbarWithDoneButton(delegate: delegateObject?.observingTextFieldDelegate ?? self) } super.set(with: model, delegateObject, additionalData) } + + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 115 + } } // MARK: - TextField Delegate @@ -440,29 +446,3 @@ extension DigitEntryField { return proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true } } - -// MARK: - MVMCoreUIMoleculeViewProtocol -extension DigitEntryField { - - @objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - guard let dictionary = json else { return } - - numberOfDigits = dictionary["digits"] as? Int ?? 4 - - if let _ = dictionary["secureEntry"] as? Bool { - setAsSecureTextEntry(true) - } - - if !dictionary.isEmpty{ - for digitBox in digitBoxes { - MVMCoreUICommonViewsUtility.addDismissToolbar(digitBox.digitField, delegate: delegateObject as? UITextFieldDelegate) - } - } - } - - @objc open override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return 115 - } -} diff --git a/MVMCoreUI/Atoms/TextFields/DigitEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryFieldModel.swift similarity index 100% rename from MVMCoreUI/Atoms/TextFields/DigitEntryFieldModel.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/DigitEntryFieldModel.swift diff --git a/MVMCoreUI/Atoms/TextFields/EntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift similarity index 81% rename from MVMCoreUI/Atoms/TextFields/EntryField.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift index 43a15a3b..658af357 100644 --- a/MVMCoreUI/Atoms/TextFields/EntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/EntryField.swift @@ -20,8 +20,8 @@ import UIKit public private(set) var titleLabel: Label = { let label = Label() - label.font = MFStyler.fontB3() - label.textColor = .mfBattleshipGrey() + label.font = MFStyler.fontRegularMicro() + label.textColor = .mvmBlack label.setContentCompressionResistancePriority(.required, for: .vertical) return label }() @@ -31,8 +31,8 @@ import UIKit /// Provides contextual information on the TextField. public private(set) var feedbackLabel: Label = { let label = Label() - label.font = MFStyler.fontForTextFieldUnderLabel() - label.textColor = .black + label.font = MFStyler.fontRegularMicro() + label.textColor = .mvmBlack label.setContentCompressionResistancePriority(.required, for: .vertical) return label }() @@ -65,7 +65,8 @@ import UIKit public var isEnabled: Bool { get { return entryFieldContainer.isEnabled } set (enabled) { - self.feedbackLabel.textColor = enabled ? .black : .mfSilver() + self.titleLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3 + self.feedbackLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3 self.entryFieldContainer.isEnabled = enabled } } @@ -116,8 +117,7 @@ import UIKit set (newFeedback) { feedbackLabel.text = newFeedback feedbackLabel.accessibilityElementsHidden = feedbackLabel.text?.isEmpty ?? true - entryFieldContainer.refreshUI() - delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + entryFieldContainer.refreshUI(updateMoleculeLayout: true) } } @@ -161,6 +161,11 @@ import UIKit fatalError("EntryField does not support xib.") } + required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.init(frame: .zero) + set(with: model, delegateObject, additionalData) + } + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -174,7 +179,7 @@ import UIKit isAccessibilityElement = false setContentCompressionResistancePriority(.required, for: .vertical) accessibilityElements = [titleLabel, feedbackLabel] - backgroundColor = .clear + backgroundColor = .mvmWhite addSubview(titleLabel) @@ -226,15 +231,19 @@ import UIKit entryFieldContainer.updateView(size) } + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + @objc open override func reset() { super.reset() backgroundColor = .clear isAccessibilityElement = false - titleLabel.font = MFStyler.fontB3() - titleLabel.textColor = .mfBattleshipGrey() - feedbackLabel.font = MFStyler.fontForTextFieldUnderLabel() - feedbackLabel.textColor = .black + titleLabel.font = MFStyler.fontRegularMicro() + titleLabel.textColor = .mvmBlack + feedbackLabel.font = MFStyler.fontRegularMicro() + feedbackLabel.textColor = .mvmBlack entryFieldContainer.reset() } @@ -258,45 +267,8 @@ import UIKit self.isSelected = isSelected } } -} -// MARK: - MVMCoreUIMoleculeViewProtocol -extension EntryField { - - @objc override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - self.delegateObject = delegateObject - - guard let dictionary = json, !dictionary.isEmpty else { return } - - entryFieldContainer.setWithJSON(dictionary, delegateObject: delegateObject, additionalData: additionalData) - - if let titleText = dictionary[KeyTitle] as? String { - title = titleText - } - - if let disable = dictionary[KeyDisable] as? String, disable.isEqual(StringY) || dictionary.boolForKey(KeyDisable) { - isEnabled = false - } - - if let feedback = dictionary["feedback"] as? String { - self.standardMessage = feedback - } - - if let errMessage = dictionary[KeyErrorMessage] as? String { - errorMessage = errMessage - } - - if let isLocked = dictionary["isLocked"] as? Bool { - self.isLocked = isLocked - } - - if let isSelected = dictionary["isSelected"] as? Bool { - self.isSelected = isSelected - } - } - - @objc open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 115 } } diff --git a/MVMCoreUI/Atoms/TextFields/EntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift similarity index 88% rename from MVMCoreUI/Atoms/TextFields/EntryFieldModel.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift index 54b441f7..729b3516 100644 --- a/MVMCoreUI/Atoms/TextFields/EntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/EntryFieldModel.swift @@ -9,7 +9,7 @@ import Foundation -@objcMembers public class EntryFieldModel: MoleculeModelProtocol, FormFieldProtocol, ValidProtocol { +@objcMembers public class EntryFieldModel: MoleculeModelProtocol, FormFieldProtocol, FormRuleWatcherFieldProtocol { //-------------------------------------------------- // MARK: - Properties @@ -26,9 +26,10 @@ import Foundation public var isEnabled: Bool = true public var isLocked: Bool? public var isSelected: Bool? - public var fieldKey: String? - public var groupName: String? = FormValidator.defaultGroupName public var text: String? + + public var fieldKey: String? + public var groupName: String = FormValidator.defaultGroupName public var baseValue: AnyHashable? public var isValid: Bool? { @@ -36,9 +37,12 @@ import Foundation updateUI?() } } + + /// Temporary binding mechanism for the view to update on enable changes. public var updateUI: (() -> Void)? - public func setValidity(_ isValid: Bool) { - self.isValid = isValid + + public func setValidity(_ valid: Bool, rule: RulesProtocol) { + self.isValid = valid } //-------------------------------------------------- @@ -54,10 +58,10 @@ import Foundation case errorMessage = "errorMsg" case isLocked case isSelected - case fieldKey case isValid case isRequired = "required" case text + case fieldKey case groupName } @@ -69,6 +73,11 @@ import Foundation // MARK: - Initializers //-------------------------------------------------- + public init(with text: String) { + self.text = text + baseValue = text + } + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) @@ -78,12 +87,13 @@ import Foundation isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? true isLocked = try typeContainer.decodeIfPresent(Bool.self, forKey: .isLocked) isSelected = try typeContainer.decodeIfPresent(Bool.self, forKey: .isSelected) - fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) isValid = try typeContainer.decodeIfPresent(Bool.self, forKey: .isValid) text = try typeContainer.decodeIfPresent(String.self, forKey: .text) - + + baseValue = text + fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { - self.groupName = groupName + self.groupName = groupName } } diff --git a/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryField.swift similarity index 90% rename from MVMCoreUI/Atoms/TextFields/ItemDropdownEntryField.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryField.swift index 0ecfc4ad..bd6d793c 100644 --- a/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryField.swift @@ -51,6 +51,10 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { fatalError("ItemDropdownEntryField init(coder:) has not been implemented") } + required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.init(model: model, delegateObject, additionalData) + } + //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- @@ -135,21 +139,6 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource { } } -// MARK: - MVMCoreUIMoleculeViewProtocol -extension ItemDropdownEntryField { - - @objc override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - guard let dictionary = json, !dictionary.isEmpty else { return } - - if let options = dictionary["options"] as? [String] { - pickerData = options - setPickerDelegates(delegate: self) - } - } -} - // MARK: - Accessibility extension ItemDropdownEntryField { diff --git a/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryFieldModel.swift similarity index 100% rename from MVMCoreUI/Atoms/TextFields/ItemDropdownEntryFieldModel.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/ItemDropdownEntryFieldModel.swift diff --git a/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryField.swift similarity index 97% rename from MVMCoreUI/Atoms/TextFields/MdnEntryField.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryField.swift index 92a1e073..77b5a4a4 100644 --- a/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryField.swift @@ -65,6 +65,10 @@ import MVMCore fatalError("MdnEntryField xib not supported.") } + required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.init(model: model, delegateObject, additionalData) + } + //-------------------------------------------------- // MARK: - Setup //-------------------------------------------------- diff --git a/MVMCoreUI/Atoms/TextFields/MdnEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryFieldModel.swift similarity index 100% rename from MVMCoreUI/Atoms/TextFields/MdnEntryFieldModel.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/MdnEntryFieldModel.swift diff --git a/MVMCoreUI/Atoms/TextFields/TextEntryField.swift b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift similarity index 80% rename from MVMCoreUI/Atoms/TextFields/TextEntryField.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift index 5df115d9..de0d1789 100644 --- a/MVMCoreUI/Atoms/TextFields/TextEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryField.swift @@ -19,7 +19,7 @@ import UIKit } -@objcMembers open class TextEntryField: EntryField, UITextFieldDelegate { +@objcMembers open class TextEntryField: EntryField, UITextFieldDelegate, ObservingTextFieldDelegate { //-------------------------------------------------- // MARK: - Outlets //-------------------------------------------------- @@ -28,7 +28,8 @@ import UIKit let textField = TextField() textField.isAccessibilityElement = true textField.setContentCompressionResistancePriority(.required, for: .vertical) - textField.font = MFStyler.fontForTextField() + textField.font = MFStyler.fontRegularBodyLarge() + textField.textColor = .mvmBlack textField.smartQuotesType = .no textField.smartDashesType = .no textField.smartInsertDeleteType = .no @@ -40,7 +41,7 @@ import UIKit //-------------------------------------------------- /// Set enabled and disabled colors to be utilized when setting this texfield's isEnabled property. - public var textColor: (enabled: UIColor?, disabled: UIColor?) = (.black, .mfSilver()) + public var textColor: (enabled: UIColor?, disabled: UIColor?) = (.mvmBlack, .mvmCoolGray3) private var observingForChange: Bool = false @@ -49,6 +50,10 @@ import UIKit /// Validate when user resigns editing. Default: true public var validateWhenDoneEditing: Bool = true + + public var textEntryFieldModel: TextEntryFieldModel? { + return model as? TextEntryFieldModel + } //-------------------------------------------------- // MARK: - Computed Properties @@ -87,7 +92,7 @@ import UIKit get { return textField.text } set { textField.text = newValue - (model as? TextEntryFieldModel)?.text = newValue + textEntryFieldModel?.text = newValue } } @@ -154,20 +159,25 @@ import UIKit fatalError("TextEntryField does not support xib.") } + required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.init(model: model, delegateObject, additionalData) + } + //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @objc open override func setupFieldContainerContent(_ container: UIView) { - MFStyler.styleTextField(textField) + textField.font = MFStyler.fontRegularBodyLarge() container.addSubview(textField) NSLayoutConstraint.activate([ textField.heightAnchor.constraint(equalToConstant: 24), textField.topAnchor.constraint(equalTo: container.topAnchor, constant: 12), textField.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16), - container.bottomAnchor.constraint(equalTo: textField.bottomAnchor, constant: 12)]) + container.bottomAnchor.constraint(equalTo: textField.bottomAnchor, constant: 12) + ]) textFieldTrailingConstraint = container.trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 16) textFieldTrailingConstraint?.isActive = true @@ -184,14 +194,14 @@ import UIKit @objc open override func updateView(_ size: CGFloat) { super.updateView(size) - MFStyler.styleTextField(textField) + textField.font = MFStyler.fontRegularBodyLarge() layoutIfNeeded() } open override func reset() { super.reset() - textField.font = MFStyler.fontForTextField() + textField.font = MFStyler.fontRegularBodyLarge() } @objc deinit { @@ -228,7 +238,7 @@ import UIKit /// Validates the text of the entry field. @objc public func validateTextField() { text = textField.text - FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) } @objc public func updateValidation(_ isValid: Bool) { @@ -243,6 +253,7 @@ import UIKit observingTextFieldDelegate?.isValid?(textfield: self) } } + /// Executes on UITextField.textDidBeginEditingNotification @objc func startEditing() { isSelected = true @@ -261,7 +272,7 @@ import UIKit resignFirstResponder() if isValid { showError = false - entryFieldContainer.bottomBar?.backgroundColor = UIColor.black.cgColor + entryFieldContainer.bottomBar?.backgroundColor = UIColor.mvmBlack.cgColor } } @@ -269,33 +280,36 @@ import UIKit resignFirstResponder() } + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? TextEntryFieldModel else { return } model.updateUI = { [weak self] in - if self?.isSelected ?? false { - self?.updateValidation(model.isValid ?? true) - } + MVMCoreDispatchUtility.performBlock(onMainThread: { + if self?.isSelected ?? false { + self?.updateValidation(model.isValid ?? true) + } + }) } self.delegateObject = delegateObject - FormValidator.setupValidation(molecule: model, delegate: delegateObject?.formHolderDelegate) + FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) textColor.enabled = model.enabledTextColor?.uiColor textColor.disabled = model.disabledTextColor?.uiColor text = model.text placeholder = model.placeholder switch model.type { - case "password": + case .password: textField.isSecureTextEntry = true - - case "number": + case .number: textField.keyboardType = .numberPad - - case "email": + case .email: textField.keyboardType = .emailAddress - default: break } @@ -311,65 +325,7 @@ import UIKit uiTextFieldDelegate = delegateObject?.uiTextFieldDelegate observingTextFieldDelegate = delegateObject?.observingTextFieldDelegate - MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: uiTextFieldDelegate) - } -} - -// MARK: - MVMCoreUIMoleculeViewProtocol -extension TextEntryField { - - @objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - guard let delegateObject = delegateObject, - let dictionary = json - else { return } - - if let enabledTextColorHex = dictionary["enabledTextColor"] as? String { - textColor.enabled = UIColor.mfGet(forHex: enabledTextColorHex) - } - - if let disabledTextColorHex = dictionary["disabledTextColor"] as? String { - textColor.disabled = UIColor.mfGet(forHex: disabledTextColorHex) - } - - if let text = dictionary[KeyText] as? String { - self.text = text - } - - if let placeholder = dictionary[placeholder] as? String { - self.placeholder = placeholder - } - - switch dictionary.stringForkey(KeyType) { - case "password": - textField.isSecureTextEntry = true - - case "number": - textField.keyboardType = .numberPad - - case "email": - textField.keyboardType = .emailAddress - - default: - break - } - - let regex = dictionary.stringForkey("regex") - - if !regex.isEmpty { - validationBlock = { enteredValue in - guard let value = enteredValue else { return false } - return MVMCoreUIUtility.validate(value, withRegularExpression: regex) - } - } else { - defaultValidationBlock() - } - - - uiTextFieldDelegate = delegateObject.uiTextFieldDelegate - observingTextFieldDelegate = delegateObject.observingTextFieldDelegate - MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: uiTextFieldDelegate) + textField.inputAccessoryView = MVMCoreUICommonViewsUtility.getToolbarWithDoneButton(delegate: observingTextFieldDelegate ?? self) } } diff --git a/MVMCoreUI/Atoms/TextFields/TextEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift similarity index 91% rename from MVMCoreUI/Atoms/TextFields/TextEntryFieldModel.swift rename to MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift index 8b8e582e..c35d812e 100644 --- a/MVMCoreUI/Atoms/TextFields/TextEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/TextFields/TextEntryFieldModel.swift @@ -8,6 +8,13 @@ @objcMembers public class TextEntryFieldModel: EntryFieldModel { + + public enum EntryType: String, Codable { + case password + case number + case email + } + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -19,7 +26,7 @@ public var placeholder: String? public var enabledTextColor: Color? public var disabledTextColor: Color? - public var type: String? + public var type: EntryType? public var regex: String? //-------------------------------------------------- @@ -47,7 +54,7 @@ placeholder = try typeContainer.decodeIfPresent(String.self, forKey: .placeholder) enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledTextColor) disabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTextColor) - type = try typeContainer.decodeIfPresent(String.self, forKey: .type) + type = try typeContainer.decodeIfPresent(EntryType.self, forKey: .type) regex = try typeContainer.decodeIfPresent(String.self, forKey: .regex) } diff --git a/MVMCoreUI/Atoms/Views/Arrow.swift b/MVMCoreUI/Atomic/Atoms/Views/Arrow.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/Arrow.swift rename to MVMCoreUI/Atomic/Atoms/Views/Arrow.swift diff --git a/MVMCoreUI/Atoms/Views/ArrowModel.swift b/MVMCoreUI/Atomic/Atoms/Views/ArrowModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/ArrowModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/ArrowModel.swift diff --git a/MVMCoreUI/Atoms/Views/CaretView.swift b/MVMCoreUI/Atomic/Atoms/Views/CaretView.swift similarity index 94% rename from MVMCoreUI/Atoms/Views/CaretView.swift rename to MVMCoreUI/Atomic/Atoms/Views/CaretView.swift index fff56f82..93874287 100644 --- a/MVMCoreUI/Atoms/Views/CaretView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CaretView.swift @@ -178,16 +178,12 @@ open class CaretView: View { //------------------------------------------------------ // Default values for view. - @objc open override func setAsMolecule() { + public required init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.init(frame: .zero) defaultState() - } - - public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: CaretViewModel.self) else { return } set(with: model, delegateObject, additionalData) } - //MARK: - MVMCoreMoleculeViewProtocol override public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let caretModel = model as? CaretViewModel else { diff --git a/MVMCoreUI/Atoms/Views/CaretViewModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CaretViewModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/CaretViewModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/CaretViewModel.swift diff --git a/MVMCoreUI/Atoms/Views/CheckboxLabel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift similarity index 84% rename from MVMCoreUI/Atoms/Views/CheckboxLabel.swift rename to MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift index 72429aa2..5d4f68be 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxLabel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabel.swift @@ -65,6 +65,14 @@ alignCheckbox(.center) } + @objc override open func updateView(_ size: CGFloat) { + super.updateView(size) + + label.updateView(size) + checkbox.updateView(size) + layoutIfNeeded() + } + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -115,6 +123,9 @@ } } + //-------------------------------------------------- + // MARK: - Atomic + //-------------------------------------------------- open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let checkBoxWithLabelModel = model as? CheckboxLabelModel else { return } @@ -125,23 +136,11 @@ checkbox.set(with: checkBoxWithLabelModel.checkbox, delegateObject, additionalData) label.set(with: checkBoxWithLabelModel.label, delegateObject, additionalData) } -} - -// MARK: - Molecular -extension CheckboxLabel { - open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 200 } - @objc override open func updateView(_ size: CGFloat) { - super.updateView(size) - - label.updateView(size) - checkbox.updateView(size) - layoutIfNeeded() - } - open override func reset() { super.reset() @@ -149,17 +148,4 @@ extension CheckboxLabel { checkbox.reset() alignCheckbox(.center) } - - override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - guard let dictionary = json else { return } - - if let checkboxAlignment = dictionary["checkboxAlignment"] as? String, let position = CheckboxPosition(rawValue: checkboxAlignment) { - alignCheckbox(position) - } - - checkbox.setWithJSON(dictionary.dictionaryForKey("checkbox"), delegateObject: delegateObject, additionalData: additionalData) - label.setWithJSON(dictionary.dictionaryForKey("label"), delegateObject: delegateObject, additionalData: additionalData) - } } diff --git a/MVMCoreUI/Atoms/Views/CheckboxLabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift similarity index 94% rename from MVMCoreUI/Atoms/Views/CheckboxLabelModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift index 5a9d09f0..f3665eb0 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxLabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift @@ -16,8 +16,8 @@ public enum CheckboxPosition: String, Codable { @objcMembers public class CheckboxLabelModel: MoleculeModelProtocol { public static var identifier: String = "checkboxLabel" + public var moleculeName: String public var backgroundColor: Color? - public var checkboxAlignment: CheckboxPosition? public var checkbox: CheckboxModel public var label: LabelModel diff --git a/MVMCoreUI/Atoms/Views/CircleProgressModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CircleProgressModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/CircleProgressModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/CircleProgressModel.swift diff --git a/MVMCoreUI/Atoms/Views/DashLine.swift b/MVMCoreUI/Atomic/Atoms/Views/DashLine.swift similarity index 89% rename from MVMCoreUI/Atoms/Views/DashLine.swift rename to MVMCoreUI/Atomic/Atoms/Views/DashLine.swift index 63b28dbd..86d2fd61 100644 --- a/MVMCoreUI/Atoms/Views/DashLine.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/DashLine.swift @@ -87,12 +87,6 @@ open class DashLine: View { isHidden = false } - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: DashLineModel.self) else { return } - set(with: model, delegateObject, additionalData) - } - - //MARK: - MVMCoreMoleculeViewProtocol public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let dashLineModel = dashModel else { diff --git a/MVMCoreUI/Atoms/Views/DashLineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/DashLineModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/DashLineModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/DashLineModel.swift diff --git a/MVMCoreUI/Atoms/Views/GraphView.swift b/MVMCoreUI/Atomic/Atoms/Views/GraphView.swift similarity index 95% rename from MVMCoreUI/Atoms/Views/GraphView.swift rename to MVMCoreUI/Atomic/Atoms/Views/GraphView.swift index 997c58d0..3cf22d79 100644 --- a/MVMCoreUI/Atoms/Views/GraphView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/GraphView.swift @@ -32,11 +32,6 @@ import UIKit createGraphCircle(model) rotationAnimation(model) } - - override open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: CircleProgressModel.self) else { return } - set(with: model, delegateObject, additionalData) - } class func getAngle(_ piValue: Double) -> Double { return piValue / (2.0 * Double.pi) * 360.0 diff --git a/MVMCoreUI/Atoms/Views/GraphViewAnimationHandler.swift b/MVMCoreUI/Atomic/Atoms/Views/GraphViewAnimationHandler.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/GraphViewAnimationHandler.swift rename to MVMCoreUI/Atomic/Atoms/Views/GraphViewAnimationHandler.swift diff --git a/MVMCoreUI/Atoms/Views/ImageViewModel.swift b/MVMCoreUI/Atomic/Atoms/Views/ImageViewModel.swift similarity index 93% rename from MVMCoreUI/Atoms/Views/ImageViewModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/ImageViewModel.swift index 0f585119..d5ba0a14 100644 --- a/MVMCoreUI/Atoms/Views/ImageViewModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/ImageViewModel.swift @@ -11,7 +11,7 @@ import Foundation @objcMembers public class ImageViewModel: MoleculeModelProtocol { public static var identifier: String = "image" public var backgroundColor: Color? - public var moleculeName: String? = ImageViewModel.identifier + public var moleculeName: String = ImageViewModel.identifier public var image: String public var accessibilityText: String? public var fallbackImage: String? diff --git a/MVMCoreUI/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift similarity index 96% rename from MVMCoreUI/Atoms/Views/Label/Label.swift rename to MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index c7e72204..a630f63a 100644 --- a/MVMCoreUI/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -12,7 +12,7 @@ import MVMCore public typealias ActionBlock = () -> () -@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, ModelMoleculeViewProtocol { +@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol { //------------------------------------------------------ // MARK: - Properties @@ -128,6 +128,13 @@ public typealias ActionBlock = () -> () } } + required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.init(frame: .zero) + setupView() + styleB2(true) + set(with: model, delegateObject, additionalData) + } + //------------------------------------------------------ // MARK: - Factory Functions //------------------------------------------------------ @@ -316,15 +323,13 @@ public typealias ActionBlock = () -> () text = labelModel.text hero = labelModel.hero Label.setLabel(self, withHTML: labelModel.html) + isAccessibilityElement = hasText - let alignment = LabelAlignment(rawValue: labelModel.textAlignment ?? "") - switch alignment { + switch labelModel.textAlignment { case .center: textAlignment = .center - case .right: textAlignment = .right - default: textAlignment = .left } @@ -338,8 +343,7 @@ public typealias ActionBlock = () -> () accessibilityLabel = accessibilityText } - if let fontStyle = labelModel.fontStyle { - MFStyler.styleLabel(self, withStyle: fontStyle) + if let fontStyle = labelModel.fontStyle?.rawValue { MFStyler.styleLabel(self, withStyle: fontStyle, genericScaling: false) standardFontSize = font.pointSize } else { @@ -372,9 +376,9 @@ public typealias ActionBlock = () -> () attributedString.addAttribute(.baselineOffset, value: 0, range: range) case let colorAtt as LabelAttributeColorModel: - if let colorHex = colorAtt.textColor, !colorHex.isEmpty { + if let colorHex = colorAtt.textColor { attributedString.removeAttribute(.foregroundColor, range: range) - attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range) + attributedString.addAttribute(.foregroundColor, value: colorHex.uiColor, range: range) } case let imageAtt as LabelAttributeImageModel: @@ -395,7 +399,7 @@ public typealias ActionBlock = () -> () attributedString.insert(mutableString, at: imageAtt.location) case let fontAtt as LabelAttributeFontModel: - if let fontStyle = fontAtt.style { + if let fontStyle = fontAtt.style?.rawValue { let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle) attributedString.removeAttribute(.font, range: range) attributedString.removeAttribute(.foregroundColor, range: range) @@ -426,6 +430,7 @@ public typealias ActionBlock = () -> () continue } } + attributedText = attributedString originalAttributedString = attributedText } @@ -755,8 +760,6 @@ public typealias ActionBlock = () -> () */ static func getTextAttachmentImage(name: String = "externalLink", dimension: CGFloat) -> NSTextAttachment { - let dimension = round(dimension * 0.8) - let imageAttachment = NSTextAttachment() imageAttachment.image = MVMCoreUIUtility.imageNamed(name) imageAttachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension) @@ -766,23 +769,18 @@ public typealias ActionBlock = () -> () static func getTextAttachmentFrom(url: String, dimension: CGFloat, label: Label) -> NSTextAttachment { - let dimension = round(dimension * 0.8) - let imageAttachment = NSTextAttachment() imageAttachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension) DispatchQueue.global(qos: .default).async { - - guard let url = URL(string: url), - let data = try? Data(contentsOf: url) - else { return } - - DispatchQueue.main.sync { - imageAttachment.image = UIImage(data: data) - label.setNeedsDisplay() + MVMCoreCache.shared()?.getImage(url, useWidth: false, widthForS7: 0, useHeight: false, heightForS7: 0, localFallbackImageName: nil) { image, data, _ in + + DispatchQueue.main.sync { + imageAttachment.image = image + label.setNeedsDisplay() + } } } - return imageAttachment } @@ -870,16 +868,6 @@ extension Label { accessibilityTraits = .staticText } - @objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - clauses = [] - Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData) - hero = json?["hero"] as? Int - } - - public func setAsMolecule() { - styleB2(true) - } - public func needsToBeConstrained() -> Bool { return true } diff --git a/MVMCoreUI/Atoms/Views/Label/LabelAttributeActionModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeActionModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/Label/LabelAttributeActionModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeActionModel.swift diff --git a/MVMCoreUI/Atoms/Views/Label/LabelAttributeColorModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift similarity index 91% rename from MVMCoreUI/Atoms/Views/Label/LabelAttributeColorModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift index 01183614..86275d6d 100644 --- a/MVMCoreUI/Atoms/Views/Label/LabelAttributeColorModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeColorModel.swift @@ -17,7 +17,7 @@ import UIKit return "color" } - var textColor: String? + var textColor: Color? //-------------------------------------------------- // MARK: - Keys @@ -33,7 +33,7 @@ import UIKit required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - textColor = try typeContainer.decodeIfPresent(String.self, forKey: .textColor) + textColor = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor) try super.init(from: decoder) } diff --git a/MVMCoreUI/Atoms/Views/Label/LabelAttributeFontModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift similarity index 93% rename from MVMCoreUI/Atoms/Views/Label/LabelAttributeFontModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift index 2e9e6e1c..862f8f9b 100644 --- a/MVMCoreUI/Atoms/Views/Label/LabelAttributeFontModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeFontModel.swift @@ -18,7 +18,7 @@ import UIKit return "font" } - var style: String? + var style: Styler.Font? var name: String? var size: CGFloat? @@ -38,7 +38,7 @@ import UIKit required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - style = try typeContainer.decodeIfPresent(String.self, forKey: .style) + style = try typeContainer.decodeIfPresent(Styler.Font.self, forKey: .style) name = try typeContainer.decodeIfPresent(String.self, forKey: .name) size = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .size) try super.init(from: decoder) diff --git a/MVMCoreUI/Atoms/Views/Label/LabelAttributeImageModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/Label/LabelAttributeImageModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift diff --git a/MVMCoreUI/Atoms/Views/Label/LabelAttributeModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/Label/LabelAttributeModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeModel.swift diff --git a/MVMCoreUI/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeStrikeThroughModel.swift diff --git a/MVMCoreUI/Atoms/Views/Label/LabelAttributeUnderlineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/Label/LabelAttributeUnderlineModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift diff --git a/MVMCoreUI/Atoms/Views/Label/LabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift similarity index 93% rename from MVMCoreUI/Atoms/Views/Label/LabelModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift index 7a04c577..4ec23ad6 100644 --- a/MVMCoreUI/Atoms/Views/Label/LabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift @@ -19,10 +19,10 @@ import Foundation public var text: String public var accessibilityText: String? public var textColor: Color? - public var fontStyle: String? + public var fontStyle: Styler.Font? public var fontName: String? public var fontSize: CGFloat? - public var textAlignment: String? + public var textAlignment: NSTextAlignment? public var attributes: [LabelAttributeModel]? public var html: String? public var hero: Int? @@ -70,10 +70,10 @@ import Foundation accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) textColor = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - fontStyle = try typeContainer.decodeIfPresent(String.self, forKey: .fontStyle) + fontStyle = try typeContainer.decodeIfPresent(Styler.Font.self, forKey: .fontStyle) fontName = try typeContainer.decodeIfPresent(String.self, forKey: .fontName) fontSize = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .fontSize) - textAlignment = try typeContainer.decodeIfPresent(String.self, forKey: .textAlignment) + textAlignment = try typeContainer.decodeIfPresent(NSTextAlignment.self, forKey: .textAlignment) attributes = try typeContainer.decodeModelsIfPresent(codingKey: .attributes) html = try typeContainer.decodeIfPresent(String.self, forKey: .html) hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/LeftRightLabelModel.swift similarity index 91% rename from MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/LeftRightLabelModel.swift index 44564c93..07c534f8 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/LeftRightLabelModel.swift @@ -14,7 +14,7 @@ import UIKit //-------------------------------------------------- public static var identifier: String = "leftRightLabelView" - public var moleculeName: String? = LeftRightLabelModel.identifier + public var moleculeName: String = LeftRightLabelModel.identifier public var backgroundColor: Color? public var leftText: LabelModel public var rightText: LabelModel? diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atomic/Atoms/Views/LeftRightLabelView.swift similarity index 85% rename from MVMCoreUI/Atoms/Views/LeftRightLabelView.swift rename to MVMCoreUI/Atomic/Atoms/Views/LeftRightLabelView.swift index d8cebb51..a7c59cd3 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/LeftRightLabelView.swift @@ -40,11 +40,6 @@ import Foundation super.init(coder: aDecoder) } - public convenience init(json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { - self.init() - setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - } - override open func setupView() { super.setupView() @@ -162,23 +157,6 @@ import Foundation // MARK: - Atomization //------------------------------------------------------ - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) - - guard let dictionary = json else { return } - - leftTextLabel.setWithJSON(dictionary.optionalDictionaryForKey("leftText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) - rightTextLabel.setWithJSON(dictionary.optionalDictionaryForKey("rightText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) - - if !leftTextLabel.hasText { - constrainRightLabel() - } else if !rightTextLabel.hasText { - constrainLeftLabel() - } - } - - //MARK: - MVMCoreMoleculeViewProtocol - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let leftRightLabelModel = model as? LeftRightLabelModel else { diff --git a/MVMCoreUI/Atoms/Views/Line.swift b/MVMCoreUI/Atomic/Atoms/Views/Line.swift similarity index 73% rename from MVMCoreUI/Atoms/Views/Line.swift rename to MVMCoreUI/Atomic/Atoms/Views/Line.swift index 37bf5688..0429c1c6 100644 --- a/MVMCoreUI/Atoms/Views/Line.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Line.swift @@ -54,22 +54,10 @@ import UIKit setStyle(.standard) } - // MARK: - MVMCoreUIMoleculeViewProtocol - open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - - // If no type, default to standard. - if let typeString = json?.optionalStringForKey(KeyType), let type = LineModel.Style.init(rawValue: typeString) { - setStyle(type) - } else { - setStyle(.standard) - } - - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - } - + // MARK: - MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { if let lineModel = model as? LineModel { - setStyle(lineModel.type ?? .standard) + setStyle(lineModel.type) } super.set(with: model, delegateObject, additionalData) } @@ -78,13 +66,9 @@ import UIKit setStyle(.standard) } - open func copyBackgroundColor() -> Bool { - return false - } - - public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - guard let type = json?.optionalStringForKey(KeyType), let style = LineModel.Style(rawValue: type) else { return 1 } - switch style { + public override static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + guard let type = (model as? LineModel)?.type else { return 1 } + switch type { case .none: return 0 case .medium: @@ -101,4 +85,8 @@ extension Line: MVMCoreUIViewConstrainingProtocol { open func needsToBeConstrained() -> Bool { return true } + + open func copyBackgroundColor() -> Bool { + return false + } } diff --git a/MVMCoreUI/Atoms/Views/LineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/LineModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/LineModel.swift diff --git a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift b/MVMCoreUI/Atomic/Atoms/Views/MFLoadImageView.swift similarity index 90% rename from MVMCoreUI/Atoms/Views/MFLoadImageView.swift rename to MVMCoreUI/Atomic/Atoms/Views/MFLoadImageView.swift index d8415d2d..826759a7 100644 --- a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/MFLoadImageView.swift @@ -8,7 +8,8 @@ import UIKit -@objcMembers open class MFLoadImageView: ViewConstrainingView, ModelMoleculeViewProtocol { +// TODO: Change to container instead of ViewConstraining without breaking legacy stack. +@objcMembers open class MFLoadImageView: ViewConstrainingView, MoleculeViewProtocol { public let loadingSpinner = MFLoadingSpinner(frame: .zero) public let imageView = MFTransparentGIFView(frame: .zero) public var addSizeConstraintsForAspectRatio = false @@ -46,6 +47,13 @@ import UIKit super.init(coder: aDecoder) } + required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.init(frame: .zero) + addSizeConstraintsForAspectRatio = true + pinEdges(.all) + set(with: model, delegateObject, additionalData) + } + public func pinEdges(_ edge: UIRectEdge) { edges = edge if edge == UIRectEdge.all { @@ -211,11 +219,7 @@ import UIKit public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { self.delegateObject = delegateObject - // TODO: Temporary, should be moved to init once we have type erasure ready. - setAsMolecule() - guard let imageModel = model as? ImageViewModel else { - return - } + guard let imageModel = model as? ImageViewModel else { return } if let accessibilityString = imageModel.accessibilityText { imageView.accessibilityLabel = accessibilityString imageView.accessibilityTraits = .staticText @@ -241,40 +245,6 @@ import UIKit } } - // MARK: - MVMCoreUIMoleculeViewProtocol functions - open override func setAsMolecule() { - addSizeConstraintsForAspectRatio = true - pinEdges(.all) - } - - public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return json?.optionalCGFloatForKey("height") ?? 0 - } - - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - self.delegateObject = delegateObject - if let accessibilityString = json?.optionalStringForKey("accessibilityText") { - imageView.accessibilityLabel = accessibilityString - imageView.accessibilityTraits = .staticText - imageView.isAccessibilityElement = true - } - let width = json?.optionalCGFloatForKey("width") ?? imageWidth - let height = json?.optionalCGFloatForKey("height") ?? imageHeight - // For smoother transitions, set constraints that we know immediately. - if let width = width, addSizeConstraintsForAspectRatio { - setWidth(width) - } - if let height = height, addSizeConstraintsForAspectRatio { - setHeight(height) - } - if let imageName = json?.optionalStringForKey("image"), shouldLoadImage(withName: imageName, width: width, height: height) { - imageView.image = nil - imageView.animatedImage = nil - loadImage(withName: imageName, format: json?.optionalStringForKey("imageFormat"), width: width as NSNumber?, height: height as NSNumber?, customFallbackImage: json?.optionalStringForKey("fallbackImage")) - } - } - // MARK: - load functions public func loadImage(withName imageName: String?, format: String? = nil, width: NSNumber? = nil, height: NSNumber? = nil, customFallbackImage: String? = nil, allowServerParameters: Bool = false, localBundle: Bundle? = nil, completionHandler: MVMCoreGetImageBlock? = nil) { @@ -306,7 +276,7 @@ import UIKit let fallbackImageName = customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback") if let format = format, format.lowercased().contains("gif") { // Gifs aren't supported by default and need special handling - MVMCoreCache.shared()?.getGif(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, completionHandler: finishedLoadingBlock) + MVMCoreCache.shared()?.getGif(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) } else { MVMCoreCache.shared()?.getImage(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) } diff --git a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift b/MVMCoreUI/Atomic/Atoms/Views/MFTransparentGIFView.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift rename to MVMCoreUI/Atomic/Atoms/Views/MFTransparentGIFView.swift diff --git a/MVMCoreUI/Atoms/Views/MultiProgress.swift b/MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift similarity index 88% rename from MVMCoreUI/Atoms/Views/MultiProgress.swift rename to MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift index 02be560c..72f84f5e 100644 --- a/MVMCoreUI/Atoms/Views/MultiProgress.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/MultiProgress.swift @@ -63,7 +63,7 @@ import UIKit } } - //MARK: - MVMCoreMoleculeViewProtocol + //MARK: - MoleculeViewProtocol public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let multiProgressModel = multiProgressModel else { @@ -81,8 +81,7 @@ import UIKit progressList = nil } - public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: MultiProgressBarModel.self) else { return } - set(with: model, delegateObject, additionalData) + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return (model as? MultiProgressBarModel)?.thickness ?? 8 } } diff --git a/MVMCoreUI/Atoms/Views/MultiProgressModel.swift b/MVMCoreUI/Atomic/Atoms/Views/MultiProgressModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/MultiProgressModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/MultiProgressModel.swift diff --git a/MVMCoreUI/Atoms/Views/ProgressBar.swift b/MVMCoreUI/Atomic/Atoms/Views/ProgressBar.swift similarity index 76% rename from MVMCoreUI/Atoms/Views/ProgressBar.swift rename to MVMCoreUI/Atomic/Atoms/Views/ProgressBar.swift index 3f302135..1b9f1e74 100644 --- a/MVMCoreUI/Atoms/Views/ProgressBar.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/ProgressBar.swift @@ -8,7 +8,7 @@ import Foundation -@objcMembers open class ProgressBar: UIProgressView, MVMCoreViewProtocol, ModelMoleculeViewProtocol, MVMCoreUIMoleculeViewProtocol { +@objcMembers open class ProgressBar: UIProgressView, MVMCoreViewProtocol, MoleculeViewProtocol { var progressBarModel: ProgressBarModel? var thickness: CGFloat = 8.0 { @@ -32,7 +32,7 @@ import Foundation setupView() } - init() { + public init() { super.init(frame: .zero) setupView() } @@ -50,7 +50,7 @@ import Foundation public func updateView(_ size: CGFloat) { } - //MARK: - MVMCoreMoleculeViewProtocol + //MARK: - MoleculeViewProtocol public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let progressBarModel = model as? ProgressBarModel else { return @@ -63,12 +63,6 @@ import Foundation } } - // MARK: - MVMCoreUIMoleculeViewProtocol - public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: ProgressBarModel.self) else { return } - set(with: model, delegateObject, additionalData) - } - public func reset() { thickness = 8 progress = 0 @@ -76,7 +70,7 @@ import Foundation trackTintColor = UIColor.mfLightSilver() } - public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 8 } } diff --git a/MVMCoreUI/Atoms/Views/ProgressBarModel.swift b/MVMCoreUI/Atomic/Atoms/Views/ProgressBarModel.swift similarity index 100% rename from MVMCoreUI/Atoms/Views/ProgressBarModel.swift rename to MVMCoreUI/Atomic/Atoms/Views/ProgressBarModel.swift diff --git a/MVMCoreUI/Atoms/Views/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift similarity index 86% rename from MVMCoreUI/Atoms/Views/Toggle.swift rename to MVMCoreUI/Atomic/Atoms/Views/Toggle.swift index 7e45c567..956c2afc 100644 --- a/MVMCoreUI/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift @@ -25,13 +25,13 @@ public typealias ActionBlockConfirmation = () -> (Bool) //-------------------------------------------------- /// Holds the on and off colors for the container. - public var containerTintColor: (on: UIColor?, off: UIColor?)? = (on: .mfShamrock(), off: .black) + public var containerTintColor: (on: UIColor?, off: UIColor?)? = (on: .mvmGreen, off: .mvmBlack) /// Holds the on and off colors for the knob. - public var knobTintColor: (on: UIColor?, off: UIColor?)? = (on: .white, off: .white) + public var knobTintColor: (on: UIColor?, off: UIColor?)? = (on: .mvmWhite, off: .mvmWhite) /// Holds the on and off colors for the disabled state.. - public var disabledTintColor: (container: UIColor?, knob: UIColor?)? = (container: .mfSilver(), knob: .white) + public var disabledTintColor: (container: UIColor?, knob: UIColor?)? = (container: .mvmCoolGray3, knob: .mvmWhite) /// Set this flag to false if you do not want to animate state changes. public var isAnimated = true @@ -102,7 +102,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) } (model as? ToggleModel)?.state = isOn - FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") setNeedsLayout() layoutIfNeeded() @@ -189,8 +189,11 @@ public typealias ActionBlockConfirmation = () -> (Bool) public override func setupView() { super.setupView() - guard subviews.isEmpty else { return } + isAccessibilityElement = true + accessibilityTraits = .button + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint") + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") heightConstraint = heightAnchor.constraint(equalToConstant: Self.containerSize.height) heightConstraint?.isActive = true @@ -214,8 +217,6 @@ public typealias ActionBlockConfirmation = () -> (Bool) knobTrailingConstraint = trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 1) knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) knobLeadingConstraint?.isActive = true - - accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") } public override func reset() { @@ -334,57 +335,38 @@ public typealias ActionBlockConfirmation = () -> (Bool) } } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - guard let toggleModel = model as? ToggleModel else { - return - } - - self.model = model + super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject - let toggleModelJSON = toggleModel.toJSON() - FormValidator.setupValidation(molecule: toggleModel, delegate: delegateObject?.formHolderDelegate) - setWithJSON(toggleModelJSON, delegateObject: delegateObject, additionalData: additionalData) - } - - //TODO: change to setWith Model - public override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - guard let dictionary = json else { return } - - if let color = dictionary["onTintColor"] as? String { - containerTintColor?.on = UIColor.mfGet(forHex: color) + guard let model = model as? ToggleModel else { return } + + FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) + + if let color = model.onTintColor?.uiColor { + containerTintColor?.on = color } - if let color = dictionary["offTintColor"] as? String { - containerTintColor?.off = UIColor.mfGet(forHex: color) + if let color = model.offTintColor?.uiColor { + containerTintColor?.off = color } - if let color = dictionary["onKnobTintColor"] as? String { - knobTintColor?.on = UIColor.mfGet(forHex: color) + if let color = model.onKnobTintColor?.uiColor { + knobTintColor?.on = color } - if let color = dictionary["offKnobTintColor"] as? String { - knobTintColor?.off = UIColor.mfGet(forHex: color) + if let color = model.offKnobTintColor?.uiColor { + knobTintColor?.off = color } - if let state = dictionary["state"] as? Bool { - changeStateNoAnimation(state) - } + changeStateNoAnimation(model.state) + isAnimated = model.animated + isEnabled = model.enabled - if let actionMap = dictionary.optionalDictionaryForKey("action") { + if let actionMap = model.action?.toJSON() { didToggleAction = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } - - if let isAnimated = dictionary["isAnimated"] as? Bool { - self.isAnimated = isAnimated - } - - if let isEnabled = dictionary["isEnabled"] as? Bool{ - self.isEnabled = isEnabled - } } public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { @@ -392,13 +374,8 @@ public typealias ActionBlockConfirmation = () -> (Bool) } } -// MARK: - MVMCoreUIMoleculeViewProtocol +// MARK: - MVMCoreUIViewConstrainingProtocol extension Toggle { - - - public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return Self.getContainerHeight() - } public func needsToBeConstrained() -> Bool { return true diff --git a/MVMCoreUI/Atomic/Atoms/Views/ToggleModel.swift b/MVMCoreUI/Atomic/Atoms/Views/ToggleModel.swift new file mode 100644 index 00000000..9eb2f4b5 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/ToggleModel.swift @@ -0,0 +1,117 @@ +// +// ToggleModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/14/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol { + + public static var identifier: String = "toggle" + public var backgroundColor: Color? + public var state: Bool = false + public var animated: Bool = true + public var enabled: Bool = true + public var action: ActionModelProtocol? + public var alternateAction: ActionModelProtocol? + public var accessibilityText: String? + public var onTintColor: Color? + public var offTintColor: Color? + public var onKnobTintColor: Color? + public var offKnobTintColor: Color? + + public var fieldKey: String? + public var groupName: String = FormValidator.defaultGroupName + public var baseValue: AnyHashable? + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case state + case animated + case enabled + case action + case backgroundColor + case alternateAction + case accessibilityText + case onTintColor + case offTintColor + case onKnobTintColor + case offKnobTintColor + case fieldKey + case groupName + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + public func formFieldValue() -> AnyHashable? { + return state + } + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(_ state: Bool) { + self.state = state + baseValue = state + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { + self.state = state + } + if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { + self.enabled = enabled + } + if let animated = try typeContainer.decodeIfPresent(Bool.self, forKey: .animated) { + self.animated = animated + } + action = try typeContainer.decodeModelIfPresent(codingKey: .action) + alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + onTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .onTintColor) + offTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .offTintColor) + onKnobTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .onKnobTintColor) + offKnobTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .offKnobTintColor) + accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) + + baseValue = state + fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) + if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { + self.groupName = groupName + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeModelIfPresent(action, forKey: .action) + try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(state, forKey: .state) + try container.encode(animated, forKey: .animated) + try container.encode(enabled, forKey: .enabled) + try container.encodeIfPresent(onTintColor, forKey: .onTintColor) + try container.encodeIfPresent(onKnobTintColor, forKey: .onKnobTintColor) + try container.encodeIfPresent(onKnobTintColor, forKey: .onKnobTintColor) + try container.encodeIfPresent(offKnobTintColor, forKey: .offKnobTintColor) + try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) + try container.encodeIfPresent(fieldKey, forKey: .fieldKey) + try container.encodeIfPresent(groupName, forKey: .groupName) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift new file mode 100644 index 00000000..1ce2b123 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -0,0 +1,196 @@ +// +// MVMCoreUIWebView.swift +// MVMCoreUI +// +// Created by Ryan on 8/29/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit +import WebKit + +@objcMembers open class WebView: View, MVMCoreUIViewConstrainingProtocol { + + let mvmWebViewMessageHandler = "mvmWebViewMessageHandler" + + var webviewModel: WebViewModel? { + return model as? WebViewModel + } + var webView: WKWebView? + var overLayer = MVMCoreUICommonViewsUtility.commonView() + public let loadingSpinner = MFLoadingSpinner(frame: .zero) + var delegateObject: MVMCoreUIDelegateObject? + var webViewHeight: NSLayoutConstraint? + var dynamicHeight: Bool = true + + + override open func setupView() { + super.setupView() + createWebView(messageHandler: mvmWebViewMessageHandler) + setupOverLayer() + pinSpinnerView() + + //init height for loading spinner + webViewHeight = webView?.heightAnchor.constraint(equalToConstant: 44) + webViewHeight?.isActive = true + } + + func createWebView(messageHandler: String?) { + let wkUserController = WKUserContentController() + if let messageHandlerName = messageHandler { + wkUserController.add(self, name: messageHandlerName) + } + let wkConfig = WKWebViewConfiguration() + wkConfig.userContentController = wkUserController + let webView = WKWebView(frame: .zero, configuration: wkConfig) + webView.translatesAutoresizingMaskIntoConstraints = false + webView.uiDelegate = self + webView.navigationDelegate = self + self.webView = webView + addSubview(webView) + NSLayoutConstraint.constraintPinSubview(toSuperview: webView) + } + + // MARK: - MVMCoreUIMoleculeViewProtocol + override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + + //store the previous webview properties + let previousHtmlString = webviewModel?.htmlString + let previousURL = webView?.url + + super.set(with: model, delegateObject, additionalData) + self.delegateObject = delegateObject + var webViewHeightConstant: CGFloat = 44 + if let height = webviewModel?.height { + webViewHeightConstant = height + webViewHeight?.constant = height + dynamicHeight = false + } + if let url = webviewModel?.url { + if let previousUrl = previousURL, url == previousUrl { + //dont load the new + } else { + webView?.stopLoading() + webView?.load(URLRequest(url: url)) + webViewHeight?.constant = webViewHeightConstant + overLayer.isHidden = false + loadingSpinner.resumeSpinner() + } + } else if let htmlString = webviewModel?.htmlString { + if let previousHTML = previousHtmlString, previousHTML == htmlString { + //dont load the new html since they are the same html string as preivous + } else { + webView?.stopLoading() + webViewHeight?.constant = webViewHeightConstant + webView?.loadHTMLString(htmlString, baseURL: nil) + overLayer.isHidden = false + loadingSpinner.resumeSpinner() + } + } + + if let borderColor = webviewModel?.borderColor?.cgColor { + webView?.layer.borderWidth = 1.0 + webView?.layer.borderColor = borderColor + } else { + webView?.layer.borderWidth = 0.0 + webView?.layer.borderColor = UIColor.clear.cgColor + } + } + + func pinSpinnerView() { + addSubview(loadingSpinner) + // Setup spinner. + loadingSpinner.clipsToBounds = true + loadingSpinner.translatesAutoresizingMaskIntoConstraints = false + + loadingSpinner.heightAnchor.constraint(equalToConstant: 44.0).isActive = true + loadingSpinner.widthAnchor.constraint(equalToConstant: 44.0).isActive = true + loadingSpinner.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true + loadingSpinner.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + } + + func setupOverLayer() { + addSubview(overLayer) + overLayer.backgroundColor = .white + NSLayoutConstraint.constraintPinSubview(toSuperview: overLayer) + } +} + + +// MARK: - WKUIDelegate +extension WebView : WKUIDelegate { + public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + // hide loading + overLayer.isHidden = true + loadingSpinner.pause() + + //update webview's heigth when webview is ready + if !dynamicHeight { + return + } + /* was using "document.readyState" to check the state, while evaluateJavaScript "document.readyState",only works when webview contains userscrpt.otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred". + so webView.isLoading to check load finished state + */ + if !webView.isLoading { + webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in + guard let self = self else { + return + } + if let height = result as? CGFloat { + self.webViewHeight?.constant = height + } else { + //if failed to get height from javascript, using scrollview.contensize's height + let scrollHeight = self.webView?.scrollView.contentSize.height + self.webViewHeight?.constant = scrollHeight ?? 44 + } + self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + }) + } + } + + + public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { + //actually no error handle page show in webview. We can handle the error display view by our self. + //or stop loading by default + overLayer.isHidden = true + loadingSpinner.pause() + } + +} + + +// MARK: - WKNavigationDelegate +extension WebView : WKNavigationDelegate { + public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + //validate request url + //all validated link should be open in safari + if (navigationAction.navigationType == .linkActivated), let urlString = navigationAction.request.url?.absoluteString.removingPercentEncoding, !urlString.contains("#") { + MVMCoreActionHandler.shared()?.openURL(inWebView: navigationAction.request.url, actionInformation: nil, additionalData: nil, delegateObject: nil) + decisionHandler(.cancel) + } else { + decisionHandler(.allow) + } + } +} + + +// MARK: - WKScriptMessageHandler +extension WebView: WKScriptMessageHandler { + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + if message.name == mvmWebViewMessageHandler, let text = message.body as? String { + /* + receiving JavaScript func webkit.messageHandlers.{callHandler}.postMessage(body); + if body is string, will decode to actionmodel. + use legacy MVMCoreActionHanlder handleAction method for now + */ + if let data = text.data(using: .utf8) { + do { + let actionMap = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: nil, delegateObject: self.delegateObject) + } catch { + //actionModel should report error when actionhandler is finshed with actionmodel + } + } + } + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift b/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift new file mode 100644 index 00000000..25142fa5 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift @@ -0,0 +1,50 @@ +// +// WebviewModel.swift +// MVMCoreUI +// +// Created by Kruthika KP on 09/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class WebViewModel: MoleculeModelProtocol { + public static var identifier: String = "webview" + public var moleculeName: String = WebViewModel.identifier + public var backgroundColor: Color? + public var url: URL? + public var htmlString: String? + public var height: CGFloat? + public var borderColor: Color? + + private enum CodingKeys: String, CodingKey{ + case moleculeName + case backgroundColor + case url + case htmlString + case height + case borderColor + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + url = try typeContainer.decodeIfPresent(URL.self, forKey: .url) + htmlString = try typeContainer.decodeIfPresent(String.self, forKey: .htmlString) + if url == nil, htmlString == nil { + throw ModelRegistry.Error.decoderErrorModelNotMapped + } + height = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .height) + borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(url, forKey: .url) + try container.encodeIfPresent(htmlString, forKey: .htmlString) + try container.encodeIfPresent(height, forKey: .height) + try container.encodeIfPresent(borderColor, forKey: .borderColor) + } +} diff --git a/MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift b/MVMCoreUI/Atomic/Extensions/NSLayoutConstraintAxis+Extension.swift similarity index 100% rename from MVMCoreUI/Utility/NSLayoutConstraintAxis+Extension.swift rename to MVMCoreUI/Atomic/Extensions/NSLayoutConstraintAxis+Extension.swift diff --git a/MVMCoreUI/Atomic/Extensions/NSTextAlignment+Extension.swift b/MVMCoreUI/Atomic/Extensions/NSTextAlignment+Extension.swift new file mode 100644 index 00000000..d7fe2e0e --- /dev/null +++ b/MVMCoreUI/Atomic/Extensions/NSTextAlignment+Extension.swift @@ -0,0 +1,89 @@ +// +// NSTextAlignment+Extension.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/24/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +/** + When using this class in codable for a String value from server. + + Example use case.... + + var alignment: NSTextAlignment + + enum CodingKeys: String, CodingKey { + case alignment + } + + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let word = try container.decode(String.self, forKey: .alignment) + alignment = NSTextAlignment(rawValue: word)! + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(alignment.rawValueString, forKey: .alignment) + } + */ + +enum TextAlignmentError: Error { + case notAnAlignment +} + +extension NSTextAlignment: RawRepresentable { + + init?(rawValue: String) { + switch rawValue { + case "left": + self = .left + case "center": + self = .center + case "right": + self = .right + case "justified": + self = .justified + case "natural": + self = .natural + default: + return nil + } + } + + var rawValueString: String { + switch self { + case .left: + return "left" + case .center: + return "center" + case .right: + return "right" + case .justified: + return "justified" + case .natural: + return "natural" + @unknown default: + return "" + } + } +} + +extension NSTextAlignment: Codable { + public init(from decoder: Decoder) throws { + let typeContainer = try decoder.singleValueContainer() + let string = try typeContainer.decode(String.self) + guard let alignment = NSTextAlignment(rawValue: string) else { + throw TextAlignmentError.notAnAlignment + } + self = alignment + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(rawValueString) + } +} diff --git a/MVMCoreUI/Atomic/Extensions/UICollectionViewScrollPosition+Extension.swift b/MVMCoreUI/Atomic/Extensions/UICollectionViewScrollPosition+Extension.swift new file mode 100644 index 00000000..d96c9933 --- /dev/null +++ b/MVMCoreUI/Atomic/Extensions/UICollectionViewScrollPosition+Extension.swift @@ -0,0 +1,71 @@ +// +// UICollectionViewScrollPosition+Extension.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/24/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +enum ScrollPositionError: Error { + case notAPosition +} + +extension UICollectionView.ScrollPosition: RawRepresentable { + + init?(rawValue: String) { + switch rawValue { + case "left", "leading": + self = .left + case "centeredHorizontally", "center": + self = .centeredHorizontally + case "right", "trailing": + self = .right + case "top": + self = .top + case "bottom": + self = .bottom + case "centeredVertically": + self = .centeredVertically + default: + return nil + } + } + + + var rawValueString: String { + switch self { + case .left: + return "left" + case .centeredHorizontally: + return "centeredHorizontally" + case .right: + return "right" + case .top: + return "top" + case .bottom: + return "bottom" + case .centeredVertically: + return "centeredVertically" + default: + return "" + } + } +} + +extension UICollectionView.ScrollPosition: Codable { + public init(from decoder: Decoder) throws { + let typeContainer = try decoder.singleValueContainer() + let string = try typeContainer.decode(String.self) + guard let position = UICollectionView.ScrollPosition(rawValue: string) else { + throw ScrollPositionError.notAPosition + } + self = position + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(rawValueString) + } +} diff --git a/MVMCoreUI/Categories/UIContentMode+Extension.swift b/MVMCoreUI/Atomic/Extensions/UIContentMode+Extension.swift similarity index 100% rename from MVMCoreUI/Categories/UIContentMode+Extension.swift rename to MVMCoreUI/Atomic/Extensions/UIContentMode+Extension.swift diff --git a/MVMCoreUI/Utility/UIStackViewAlignment+Extension.swift b/MVMCoreUI/Atomic/Extensions/UIStackViewAlignment+Extension.swift similarity index 100% rename from MVMCoreUI/Utility/UIStackViewAlignment+Extension.swift rename to MVMCoreUI/Atomic/Extensions/UIStackViewAlignment+Extension.swift diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift new file mode 100644 index 00000000..ab0d5b43 --- /dev/null +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -0,0 +1,201 @@ +// +// MoleculeObjectMapping.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 10/28/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class MoleculeObjectMapping: NSObject { + + public var moleculeMapping: [String: MoleculeViewProtocol.Type] = [:] + + /// Returns the mapping object stored in the singleton + public static func shared() -> Self? { + return MVMCoreActionUtility.initializerClassCheck(CoreUIObject.sharedInstance()?.moleculeMap, classToVerify: self) as? Self + } + + /// Registers the model with the model registry and the view with the mapper. + public func register(viewClass: V.Type, viewModelClass: M.Type) { + try? ModelRegistry.register(viewModelClass) + moleculeMapping.updateValue(viewClass, forKey: viewModelClass.identifier) + } + + /// Returns the type of molecule view for the given model + public func getMoleculeClass(_ model: MoleculeModelProtocol) -> MoleculeViewProtocol.Type? { + return moleculeMapping[model.moleculeName] + } + + /// Creates a molecule with the given model. + public func createMolecule(_ model: MoleculeModelProtocol, delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> MoleculeViewProtocol? { + guard let type = moleculeMapping[model.moleculeName] else { return nil } + return type.init(model: model, delegateObject, additionalData) + } + + /// Call to register all of the CoreUI molecules. + public static func registerObjects() { + // Stacks + MoleculeObjectMapping.shared()?.register(viewClass: MoleculeStackView.self, viewModelClass: MoleculeStackModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: Stack.self, viewModelClass: StackModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: UnOrderedList.self, viewModelClass: UnOrderedListModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: NumberedList.self, viewModelClass: NumberedListModel.self) + + // Label + MoleculeObjectMapping.shared()?.register(viewClass: Label.self, viewModelClass: LabelModel.self) + // need to move labelattributemodel to different method + try? ModelRegistry.register(LabelAttributeFontModel.self) + try? ModelRegistry.register(LabelAttributeColorModel.self) + try? ModelRegistry.register(LabelAttributeImageModel.self) + try? ModelRegistry.register(LabelAttributeUnderlineModel.self) + try? ModelRegistry.register(LabelAttributeStrikeThroughModel.self) + try? ModelRegistry.register(LabelAttributeActionModel.self) + + // Buttons + MoleculeObjectMapping.shared()?.register(viewClass: PillButton.self, viewModelClass: ButtonModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: TwoButtonView.self, viewModelClass: TwoButtonViewModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ExternalLink.self, viewModelClass: ExternalLinkModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: Link.self, viewModelClass: LinkModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: CaretLink.self, viewModelClass: CaretLinkModel.self) + + // Entry Field + MoleculeObjectMapping.shared()?.register(viewClass: TextEntryField.self, viewModelClass: TextEntryFieldModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: MdnEntryField.self, viewModelClass: MdnEntryFieldModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: DigitEntryField.self, viewModelClass: DigitEntryFieldModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ItemDropdownEntryField.self, viewModelClass: ItemDropdownEntryFieldModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: DateDropdownEntryField.self, viewModelClass: DateDropdownEntryFieldModel.self) + + // Selectors + MoleculeObjectMapping.shared()?.register(viewClass: RadioButton.self, viewModelClass: RadioButtonModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: RadioBoxes.self, viewModelClass: RadioBoxesModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: Checkbox.self, viewModelClass: CheckboxModel.self) + + // Other Atoms + MoleculeObjectMapping.shared()?.register(viewClass: ProgressBar.self, viewModelClass: ProgressBarModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: MultiProgress.self, viewModelClass: MultiProgressBarModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: CaretView.self, viewModelClass: CaretViewModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: DashLine.self, viewModelClass: DashLineModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: MFLoadImageView.self, viewModelClass: ImageViewModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: Line.self, viewModelClass: LineModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: GraphView.self, viewModelClass: CircleProgressModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: Toggle.self, viewModelClass: ToggleModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: CheckboxLabel.self, viewModelClass: CheckboxLabelModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: Arrow.self, viewModelClass: ArrowModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: RadioButtonLabel.self, viewModelClass: RadioButtonLabelModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: WebView.self, viewModelClass: WebViewModel.self) + + // Horizontal Combination Molecules + MoleculeObjectMapping.shared()?.register(viewClass: StringAndMoleculeView.self, viewModelClass: StringAndMoleculeModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ImageHeadlineBody.self, viewModelClass: ImageHeadlineBodyModel.self) + + // Vertical Combination Molecules + MoleculeObjectMapping.shared()?.register(viewClass: HeadlineBody.self, viewModelClass: HeadlineBodyModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: HeadLineBodyCaretLinkImage.self, viewModelClass: HeadlineBodyCaretLinkImageModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: EyebrowHeadlineBodyLink.self, viewModelClass: EyebrowHeadlineBodyLinkModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: HeadlineBodyLink.self, viewModelClass: HeadlineBodyLinkModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: HeadlineBodyButton.self, viewModelClass: HeadlineBodyButtonModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: BGImageHeadlineBodyButton.self, viewModelClass: BGImageHeadlineBodyButtonModel.self) + + // Left Right Molecules + MoleculeObjectMapping.shared()?.register(viewClass: CornerLabels.self, viewModelClass: CornerLabelsModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: LeftRightLabelView.self, viewModelClass: LeftRightLabelModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: LabelToggle.self, viewModelClass: LabelToggleModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: HeadlineBodyToggle.self, viewModelClass: HeadlineBodyToggleModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: HeadlineBodyLinkToggle.self, viewModelClass: HeadlineBodyLinkToggleModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ActionDetailWithImage.self, viewModelClass: ActionDetailWithImageModel.self) + + // List items + MoleculeObjectMapping.shared()?.register(viewClass: MoleculeTableViewCell.self, viewModelClass: MoleculeListItemModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: DropDownFilterTableViewCell.self, viewModelClass: DropDownListItemModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: AccordionMoleculeTableViewCell.self, viewModelClass: AccordionListItemModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: TabsTableViewCell.self, viewModelClass: TabsListItemModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListProgressBarData.self, viewModelClass: ListProgressBarDataModel.self) + + // Other Items + MoleculeObjectMapping.shared()?.register(viewClass: MoleculeStackItem.self, viewModelClass: MoleculeStackItemModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: StackItem.self, viewModelClass: StackItemModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: MoleculeCollectionViewCell.self, viewModelClass: MoleculeCollectionItemModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: CarouselItem.self, viewModelClass: CarouselItemModel.self) + + + // Other Container Molecules + MoleculeObjectMapping.shared()?.register(viewClass: MoleculeHeaderView.self, viewModelClass: MoleculeHeaderModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: FooterView.self, viewModelClass: FooterModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: Scroller.self, viewModelClass: ScrollerModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ModuleMolecule.self, viewModelClass: ModuleMoleculeModel.self) + + // Other Molecules + MoleculeObjectMapping.shared()?.register(viewClass: DoughnutChartView.self, viewModelClass: DoughnutChartModel.self) + + // Other Organisms + MoleculeObjectMapping.shared()?.register(viewClass: Carousel.self, viewModelClass: CarouselModel.self) + + // Designed List Items + MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableIconWithRightCaret.self, viewModelClass: ListLeftVariableIconWithRightCaretModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableCheckboxAllTextAndLinks.self, viewModelClass: ListLeftVariableCheckboxAllTextAndLinksModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableRadioButtonAndPaymentMethod.self, viewModelClass: ListLeftVariableRadioButtonAndPaymentMethodModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableRadioButtonBodyText.self, viewModelClass: ListLeftVariableRadioButtonBodyTextModel.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) + MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariableTextLinkAllTextAndLinks.self, viewModelClass: ListRightVariableTextLinkAllTextAndLinksModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariableButtonAllTextAndLinks.self, viewModelClass: ListRightVariableButtonAllTextAndLinksModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListOneColumnFullWidthTextAllTextAndLinks.self, viewModelClass: ListOneColumnFullWidthTextAllTextAndLinksModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListOneColumnFullWidthTextBodyText.self, viewModelClass: ListOneColumnFullWidthTextBodyTextModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListTwoColumnCompareChanges.self, viewModelClass: ListTwoColumnCompareChangesModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListTwoColumnPriceDetails.self, viewModelClass: ListTwoColumnPriceDetailsModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListTwoColumnPriceDescription.self, viewModelClass: ListTwoColumnPriceDescriptionModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnInternationalData.self, viewModelClass: ListThreeColumnInternationalDataModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListFourColumnDataUsageListItem.self, viewModelClass: ListFourColumnDataUsageListItemModel.self) + + // Designed Section Dividers + MoleculeObjectMapping.shared()?.register(viewClass: ListFourColumnDataUsageDivider.self, viewModelClass: ListFourColumnDataUsageDividerModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnPlanDataDivider.self, viewModelClass: ListThreeColumnPlanDataDividerModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListOneColumnTextWithWhitespaceDividerShort.self, viewModelClass: ListOneColumnTextWithWhitespaceDividerShortModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListOneColumnTextWithWhitespaceDividerTall.self, viewModelClass: ListOneColumnTextWithWhitespaceDividerTallModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListOneColumnFullWidthTextDividerSubsection.self, viewModelClass: ListOneColumnFullWidthTextDividerSubsectionModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListTwoColumnSubsectionDivider.self, viewModelClass: ListTwoColumnSubsectionDividerModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnInternationalDataDivider.self, viewModelClass: ListThreeColumnInternationalDataDividerModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnSpeedTestDivider.self, viewModelClass: ListThreeColumnSpeedTestDividerModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnBillChangesDivider.self, viewModelClass: ListThreeColumnBillChangesDividerModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnDataUsageDivider.self, viewModelClass: ListThreeColumnDataUsageDividerModel.self) + + // Designed Headers + MoleculeObjectMapping.shared()?.register(viewClass: HeadersH2NoButtonsBodyText.self, viewModelClass: HeadersH2NoButtonsBodyTextModel.self) + + // TODO: Need View + try? ModelRegistry.register(TabsModel.self) + + // Helper models + try? ModelRegistry.register(RuleRequiredModel.self) + try? ModelRegistry.register(RuleAnyRequiredModel.self) + try? ModelRegistry.register(RuleAnyValueChangedModel.self) + try? ModelRegistry.register(RuleAllValueChangedModel.self) + try? ModelRegistry.register(RuleEqualsModel.self) + try? ModelRegistry.register(RuleRegexModel.self) + + // Actions + try? ModelRegistry.register(ActionTopAlertModel.self) + try? ModelRegistry.register(ActionCollapseNotificationModel.self) + try? ModelRegistry.register(ActionOpenPanelModel.self) + } + + /// Convenience function to get required modules for a give model + public static func getRequiredModules(for model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + guard let model = model else { return nil } + return MoleculeObjectMapping.shared()?.getMoleculeClass(model)?.requiredModules(with: model, delegateObject, error: error) + } + + /// Convenience function to add require modules for the given model to the passed in array. + public static func addRequiredModules(for model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, moduleList: inout [String]?, errorList: inout [MVMCoreErrorObject]?) { + guard let model = model else { return } + let error: AutoreleasingUnsafeMutablePointer? = nil + if let modules = getRequiredModules(for: model, delegateObject, error: error) { + moduleList?.append(contentsOf: modules) + } + if let error = error?.pointee { + errorList?.append(error) + } + } +} diff --git a/MVMCoreUI/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyText.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyText.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyText.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyTextModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyTextModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyTextModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyTextModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/FourColumn/ListFourColumnDataUsageListItem.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/FourColumn/ListFourColumnDataUsageListItem.swift new file mode 100644 index 00000000..2377a1f2 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/FourColumn/ListFourColumnDataUsageListItem.swift @@ -0,0 +1,77 @@ +// +// ListFourColumnDataUsageListItem.swift +// MVMCoreUI +// +// Created by Kruthika KP on 27/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class ListFourColumnDataUsageListItem: TableViewCell { + + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + var stack: Stack + let label1 = Label.commonLabelB2(true) + let label2 = Label.commonLabelB2(true) + let label3 = Label.commonLabelB2(true) + let label4 = Label.commonLabelB2(true) + let arrow = Arrow(frame: .zero) + let arrowAndLabel2Stack: Stack + + //----------------------------------------------------- + // MARK: - Initializers + //----------------------------------------------------- + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + arrowAndLabel2Stack = Stack.createStack(with: [(view: arrow, model: StackItemModel(horizontalAlignment: .fill)), + (view: label2, model: StackItemModel(horizontalAlignment: .leading))], + axis: .horizontal, spacing: 4) + label2.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) + label2.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) + stack = Stack.createStack(with: [(view: label1, model: StackItemModel(percent: 19, horizontalAlignment: .leading)), + (view: arrowAndLabel2Stack, model: StackItemModel(percent: 44, horizontalAlignment: .fill)), + (view: label3, model: StackItemModel(percent:17,horizontalAlignment: .leading)), + (view: label4, model: StackItemModel(percent:20,horizontalAlignment: .leading))], + axis: .horizontal,spacing: 8) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //----------------------------------------------------- + // MARK: - View Lifecycle + //----------------------------------------------------- + override open func setupView() { + super.setupView() + addMolecule(stack) + arrow.pinHeightAndWidth() + arrowAndLabel2Stack.restack() + stack.restack() + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListFourColumnDataUsageListItemModel else { return } + label1.set(with: model.label1, delegateObject, additionalData) + label2.set(with: model.label2, delegateObject, additionalData) + label3.set(with: model.label3, delegateObject, additionalData) + label4.set(with: model.label4, delegateObject, additionalData) + arrow.set(with: model.arrow, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 121 + } + + open override func reset() { + super.reset() + label1.styleB2(true) + label2.styleB2(true) + label3.styleB2(true) + label4.styleB2(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/FourColumn/ListFourColumnDataUsageListItemModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/FourColumn/ListFourColumnDataUsageListItemModel.swift new file mode 100644 index 00000000..bf82bb5f --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/FourColumn/ListFourColumnDataUsageListItemModel.swift @@ -0,0 +1,57 @@ +// +// ListFourColumnDataUsageListItemModel.swift +// MVMCoreUI +// +// Created by Kruthika KP on 27/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListFourColumnDataUsageListItemModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list4C" + public var label1: LabelModel + public var arrow: ArrowModel + public var label2: LabelModel + public var label3: LabelModel + public var label4: LabelModel + + public init(label1:LabelModel, label2:LabelModel, label3:LabelModel,label4:LabelModel, arrow:ArrowModel) { + self.label1 = label1 + self.label2 = label2 + self.label3 = label3 + self.label4 = label4 + self.arrow = arrow + super.init() + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case label1 + case label2 + case label3 + case label4 + case arrow + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + label1 = try typeContainer.decode(LabelModel.self, forKey: .label1) + label2 = try typeContainer.decode(LabelModel.self, forKey: .label2) + label3 = try typeContainer.decode(LabelModel.self, forKey: .label3) + label4 = try typeContainer.decode(LabelModel.self, forKey: .label4) + arrow = try typeContainer.decode(ArrowModel.self, forKey: .arrow) + 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(label1, forKey: .label1) + try container.encode(label2, forKey: .label2) + try container.encode(label3, forKey: .label3) + try container.encode(label4, forKey: .label4) + try container.encode(arrow, forKey: .arrow) + } +} diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift similarity index 72% rename from MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift index 8d7fb539..448d76e1 100644 --- a/MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift @@ -9,11 +9,18 @@ import Foundation @objcMembers open class ListLeftVariableCheckboxAllTextAndLinks: TableViewCell { - public let checkbox = Checkbox(frame: .zero) + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public let checkbox = Checkbox() 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: checkbox, model: StackItemModel(horizontalAlignment: .fill)), (view: eyebrowHeadlineBodyLink, model: StackItemModel(horizontalAlignment: .leading))], @@ -25,17 +32,25 @@ import Foundation fatalError("init(coder:) has not been implemented") } - // MARK: - View Lifecycle + //-------------------------------------------------- + // MARK: - Life Cycle + //-------------------------------------------------- + override open func setupView() { super.setupView() addMolecule(stack) stack.restack() } - // MARK:- MVMCoreUIMoleculeViewProtocol - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + //-------------------------------------------------- + // MARK: - MVMCoreUIMoleculeViewProtocol + //-------------------------------------------------- + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - guard let model = model as? ListLeftVariableCheckboxAllTextAndLinksModel else { return} + + guard let model = model as? ListLeftVariableCheckboxAllTextAndLinksModel else { return } + checkbox.set(with: model.checkbox, delegateObject, additionalData) eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData) } diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinksModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinksModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinksModel.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaret.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaret.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaret.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaret.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretModel.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethod.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethod.swift similarity index 88% rename from MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethod.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethod.swift index b4bb2fc3..4597a0b9 100644 --- a/MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethod.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethod.swift @@ -8,12 +8,13 @@ import UIKit + @objcMembers open class ListLeftVariableRadioButtonAndPaymentMethod: TableViewCell { - //----------------------------------------------------- // MARK: - Outlets //----------------------------------------------------- - let radioButton = RadioButton(frame: .zero) + + let radioButton = RadioButton() let leftImage = MFLoadImageView(pinnedEdges: .all) let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink() var stack: Stack @@ -21,6 +22,7 @@ import UIKit //----------------------------------------------------- // MARK: - Initializers //----------------------------------------------------- + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { stack = Stack.createStack(with: [(view: radioButton, model: StackItemModel(horizontalAlignment: .fill)), (view: leftImage, model: StackItemModel(horizontalAlignment: .fill)), @@ -36,8 +38,10 @@ import UIKit //----------------------------------------------------- // MARK: - View Lifecycle //----------------------------------------------------- + override open func setupView() { super.setupView() + leftImage.addSizeConstraintsForAspectRatio = true addMolecule(stack) stack.restack() eyebrowHeadlineBodyLink.body.textColor = .mvmOrangeAA @@ -53,7 +57,8 @@ import UIKit //---------------------------------------------------- // MARK: - Molecule //---------------------------------------------------- - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? ListLeftVariableRadioButtonAndPaymentMethodModel else { return} radioButton.set(with: model.radioButton, delegateObject, additionalData) @@ -64,4 +69,11 @@ import UIKit open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 90 } + + public override func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + + if radioButton.isEnabled { + radioButton.tapAction() + } + } } diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethodModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethodModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethodModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethodModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonBodyText.swift new file mode 100644 index 00000000..9b45eec7 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonBodyText.swift @@ -0,0 +1,67 @@ +// +// ListLeftVariableRadioButtonBodyText.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/1/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + + +open class ListLeftVariableRadioButtonBodyText: TableViewCell { + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + + let radioButton = RadioButton(frame: .zero) + let headlineBody = HeadlineBody() + var stack: Stack + + //----------------------------------------------------- + // MARK: - Initializers + //----------------------------------------------------- + + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: radioButton, model: StackItemModel(horizontalAlignment: .fill)), + (view: headlineBody, 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: - View Lifecycle + //----------------------------------------------------- + + 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? ListLeftVariableRadioButtonBodyTextModel else { return } + + radioButton.set(with: model.radioButton, delegateObject, additionalData) + headlineBody.set(with: model.headlineBody, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 90 + } + + public override func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + radioButton.tapAction() + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonBodyTextModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonBodyTextModel.swift new file mode 100644 index 00000000..bb8e7ded --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonBodyTextModel.swift @@ -0,0 +1,68 @@ +// +// ListLeftVariableRadioButtonBodyTextModel.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/1/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + + +open class ListLeftVariableRadioButtonBodyTextModel: ListItemModel, MoleculeModelProtocol { + //----------------------------------------------------- + // MARK: - Properties + //----------------------------------------------------- + + public static var identifier: String = "listLVRBBdy" + public var radioButton: RadioButtonModel + public var headlineBody: HeadlineBodyModel + + //----------------------------------------------------- + // MARK: - Initializer + //----------------------------------------------------- + + public init(radioButton: RadioButtonModel, headlineBody: HeadlineBodyModel) { + self.radioButton = radioButton + self.headlineBody = headlineBody + super.init() + } + + //----------------------------------------------------- + // MARK: - Methods + //----------------------------------------------------- + + open override func setDefaults() { + super.setDefaults() + headlineBody.style = .item + } + + //----------------------------------------------------- + // MARK: - Keys + //----------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case radioButton + case headlineBody + } + + //----------------------------------------------------- + // MARK: - Codec + //----------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + radioButton = try typeContainer.decode(RadioButtonModel.self, forKey: .radioButton) + headlineBody = try typeContainer.decode(HeadlineBodyModel.self, forKey: .headlineBody) + 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(radioButton, forKey: .radioButton) + try container.encode(headlineBody, forKey: .headlineBody) + } +} diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinksModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinksModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinksModel.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift similarity index 97% rename from MVMCoreUI/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift index 138cc69a..f25870aa 100644 --- a/MVMCoreUI/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift @@ -20,7 +20,7 @@ public class ListOneColumnFullWidthTextBodyTextModel: ListItemModel, MoleculeMod // Defaults to set override public func setDefaults() { super.setDefaults() - headlineBody.style = "item" + headlineBody.style = .item } private enum CodingKeys: String, CodingKey { diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRVWheel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRVWheel.swift similarity index 96% rename from MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRVWheel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRVWheel.swift index 5ad5d182..8ca28609 100644 --- a/MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRVWheel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRVWheel.swift @@ -39,7 +39,7 @@ import Foundation } //------------------------------------------------- - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //------------------------------------------------- open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.set(with: model, delegateObject, additionalData) @@ -50,7 +50,7 @@ import Foundation } //------------------------------------------------- - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //------------------------------------------------- open override func reset() { super.reset() diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRVWheelModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRVWheelModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRVWheelModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRVWheelModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinks.swift new file mode 100644 index 00000000..25885044 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinks.swift @@ -0,0 +1,51 @@ +// +// ListRightVariableButtonAllTextAndLinks.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 17/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +@objcMembers open class ListRightVariableButtonAllTextAndLinks: TableViewCell { + + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + public var stack: Stack + public let button = PillButton(frame: .zero) + public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink(frame: .zero) + + // MARK: - Initializers + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: eyebrowHeadlineBodyLink, model: StackItemModel(horizontalAlignment: .leading)), + (view: button, model: StackItemModel(horizontalAlignment:.fill))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //----------------------------------------------------- + // MARK: - View Lifecycle + //----------------------------------------------------- + override open func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListRightVariableButtonAllTextAndLinksModel else { return } + button.set(with: model.button, delegateObject, additionalData) + eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 90 + } +} + diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift new file mode 100644 index 00000000..41d4ec72 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift @@ -0,0 +1,49 @@ +// +// ListRightVariableButtonAllTextAndLinksModel.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 17/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +public class ListRightVariableButtonAllTextAndLinksModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "listRVBtn" + public var button: ButtonModel + public var eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel + + public init(button: ButtonModel, eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel) { + self.button = button + self.eyebrowHeadlineBodyLink = eyebrowHeadlineBodyLink + super.init() + } + + /// Defaults to set + override public func setDefaults() { + super.setDefaults() + self.button.size = .tiny + self.button.style = ButtonStyle.secondary + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case button + case eyebrowHeadlineBodyLink + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + button = try typeContainer.decode(ButtonModel.self, forKey: .button) + 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(button, forKey: .button) + try container.encode(eyebrowHeadlineBodyLink, forKey: .eyebrowHeadlineBodyLink) + } +} + diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePayments.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePayments.swift similarity index 97% rename from MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePayments.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePayments.swift index 9cfccd04..2eac0789 100644 --- a/MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePayments.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePayments.swift @@ -36,6 +36,7 @@ import Foundation //------------------------------------------------------- override open func setupView() { super.setupView() + rightImage.addSizeConstraintsForAspectRatio = true addMolecule(stack) stack.restack() } diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePaymentsModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePaymentsModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePaymentsModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePaymentsModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTextLinkAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTextLinkAllTextAndLinks.swift new file mode 100644 index 00000000..88d9448b --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTextLinkAllTextAndLinks.swift @@ -0,0 +1,50 @@ +// +// ListRightVariableTextLinkAllTextAndLinks.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 19/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +@objcMembers open class ListRightVariableTextLinkAllTextAndLinks: TableViewCell { + + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + public var stack: Stack + public let link = Link(frame: .zero) + public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink(frame: .zero) + + // MARK: - Initializers + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: eyebrowHeadlineBodyLink, model: StackItemModel(horizontalAlignment: .leading, verticalAlignment: .top)), + (view: link, model: StackItemModel(horizontalAlignment:.fill, verticalAlignment: .top))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //----------------------------------------------------- + // MARK: - View Lifecycle + //----------------------------------------------------- + override open func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListRightVariableTextLinkAllTextAndLinksModel else { return } + link.set(with: model.link, delegateObject, additionalData) + eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 90 + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTextLinkAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTextLinkAllTextAndLinksModel.swift new file mode 100644 index 00000000..9f6392fc --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTextLinkAllTextAndLinksModel.swift @@ -0,0 +1,41 @@ +// +// ListRightVariableTextLinkAllTextAndLinksModel.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 19/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +public class ListRightVariableTextLinkAllTextAndLinksModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "listRVLink" + public var link: LinkModel + public var eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel + + public init(link: LinkModel, eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel) { + self.link = link + self.eyebrowHeadlineBodyLink = eyebrowHeadlineBodyLink + super.init() + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case link + case eyebrowHeadlineBodyLink + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + link = try typeContainer.decode(LinkModel.self, forKey: .link) + 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(link, forKey: .link) + try container.encode(eyebrowHeadlineBodyLink, forKey: .eyebrowHeadlineBodyLink) + } +} diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalData.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalData.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalData.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalData.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalDataModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalDataModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalDataModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalDataModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnInternationalData.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnInternationalData.swift new file mode 100644 index 00000000..5fc836d1 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnInternationalData.swift @@ -0,0 +1,73 @@ +// +// ListThreeColumnInternationalData.swift +// MVMCoreUI +// +// Created by Kruthika KP on 06/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class ListThreeColumnInternationalData: TableViewCell { + + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + open var stack: Stack + public let leftLabel = Label.createLabelRegularBodySmall(true) + public let centerLabel = Label.createLabelRegularBodySmall(true) + public let rightLabel = Label.createLabelRegularBodySmall(true) + public let arrow = Arrow(frame: .zero) + public let arrowAndLabel2Stack: Stack + + //----------------------------------------------------- + // MARK: - Initializers + //----------------------------------------------------- + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + arrowAndLabel2Stack = Stack.createStack(with: [(view: arrow, model: StackItemModel(horizontalAlignment: .fill)), + (view: centerLabel, model: StackItemModel(horizontalAlignment: .leading))], + axis: .horizontal, spacing: 4) + centerLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) + centerLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 30, horizontalAlignment: .leading)), + (view: arrowAndLabel2Stack, model: StackItemModel(percent: 50, horizontalAlignment: .fill)), + (view: rightLabel, model: StackItemModel(percent:20,horizontalAlignment: .leading))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //----------------------------------------------------- + // MARK: - View Lifecycle + //----------------------------------------------------- + override open func setupView() { + super.setupView() + addMolecule(stack) + arrow.pinHeightAndWidth() + arrowAndLabel2Stack.restack() + stack.restack() + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListThreeColumnInternationalDataModel else { return } + leftLabel.set(with: model.leftLabel, delegateObject, additionalData) + centerLabel.set(with: model.centerLabel, delegateObject, additionalData) + rightLabel.set(with: model.rightLabel, delegateObject, additionalData) + arrow.set(with: model.arrow, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 90 + } + + open override func reset() { + super.reset() + leftLabel.styleRegularBodySmall(true) + centerLabel.styleRegularBodySmall(true) + rightLabel.styleRegularBodySmall(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnInternationalDataModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnInternationalDataModel.swift new file mode 100644 index 00000000..d0cd3791 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnInternationalDataModel.swift @@ -0,0 +1,52 @@ +// +// ListThreeColumnInternationalDataModel.swift +// MVMCoreUI +// +// Created by Kruthika KP on 06/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListThreeColumnInternationalDataModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list3CIntData" + public var leftLabel: LabelModel + public var centerLabel: LabelModel + public var rightLabel: LabelModel + public var arrow: ArrowModel + + public init(leftLabel:LabelModel, centerLabel:LabelModel, rightLabel:LabelModel, arrow:ArrowModel) { + self.leftLabel = leftLabel + self.centerLabel = centerLabel + self.rightLabel = rightLabel + self.arrow = arrow + super.init() + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case leftLabel + case centerLabel + case rightLabel + case arrow + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + leftLabel = try typeContainer.decode(LabelModel.self, forKey: .leftLabel) + centerLabel = try typeContainer.decode(LabelModel.self, forKey: .centerLabel) + rightLabel = try typeContainer.decode(LabelModel.self, forKey: .rightLabel) + arrow = try typeContainer.decode(ArrowModel.self, forKey: .arrow) + 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(leftLabel, forKey: .leftLabel) + try container.encode(centerLabel, forKey: .centerLabel) + try container.encode(rightLabel, forKey: .rightLabel) + try container.encode(arrow, forKey: .arrow) + } +} diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChangesModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChangesModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChangesModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChangesModel.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescriptionModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescriptionModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescriptionModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescriptionModel.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift similarity index 97% rename from MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift index 610821b3..43836cf0 100644 --- a/MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift @@ -32,7 +32,7 @@ import UIKit rightLabel.updateView(size) } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? ListTwoColumnPriceDetailsModel else { return } diff --git a/MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetailsModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetailsModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetailsModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetailsModel.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/LockUps/ListProgressBarData.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/ListProgressBarData.swift similarity index 97% rename from MVMCoreUI/Molecules/DesignedComponents/LockUps/ListProgressBarData.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/ListProgressBarData.swift index 48f734ad..16b1e93e 100644 --- a/MVMCoreUI/Molecules/DesignedComponents/LockUps/ListProgressBarData.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/ListProgressBarData.swift @@ -44,7 +44,7 @@ import UIKit } //----------------------------------------------------- - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //----------------------------------------------------- open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.set(with: model, delegateObject, additionalData) @@ -59,7 +59,7 @@ import UIKit } //----------------------------------------------------- - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //----------------------------------------------------- override open func reset() { super.reset() diff --git a/MVMCoreUI/Molecules/DesignedComponents/LockUps/ListProgressBarDataModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/ListProgressBarDataModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/LockUps/ListProgressBarDataModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/ListProgressBarDataModel.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift similarity index 96% rename from MVMCoreUI/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift index 8e37c16a..f857b167 100644 --- a/MVMCoreUI/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift @@ -38,7 +38,7 @@ import Foundation stack.restack() } - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? ListFourColumnDataUsageDividerModel else { return } @@ -52,7 +52,7 @@ import Foundation return 121 } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func reset() { super.reset() label1.styleBoldBodySmall(true) diff --git a/MVMCoreUI/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDividerModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDividerModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDividerModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDividerModel.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift new file mode 100644 index 00000000..ec65967d --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift @@ -0,0 +1,56 @@ +// +// ListOneColumnTextWithWhitespaceDividerShort.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 04/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +@objcMembers open class ListOneColumnTextWithWhitespaceDividerShort: TableViewCell { + + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + public var stack: Stack + public let headline = Label.commonLabelH3(true) + public let body = Label.commonLabelB2(true) + + // MARK: - Initializers + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: headline, model: StackItemModel(horizontalAlignment: .leading)), + (view: body, model: StackItemModel(spacing: 0, horizontalAlignment: .leading))], + axis: .vertical) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //----------------------------------------------------- + // MARK: - View Lifecycle + //----------------------------------------------------- + override open func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListOneColumnTextWithWhitespaceDividerShortModel else { return } + headline.set(with: model.headline, delegateObject, additionalData) + body.set(with: model.body, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 90 + } + + open override func reset() { + super.reset() + headline.styleH3(true) + body.styleB2(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift new file mode 100644 index 00000000..340ccddc --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift @@ -0,0 +1,48 @@ +// +// ListOneColumnTextWithWhitespaceDividerShortModel.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 04/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListOneColumnTextWithWhitespaceDividerShortModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list1CTxtDiv1" + public var headline: LabelModel + public var body: LabelModel + + public init(headline: LabelModel, body: LabelModel) { + self.headline = headline + self.body = body + super.init() + } + + /// Defaults to set + override public func setDefaults() { + super.setDefaults() + style = "shortDivider" + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case headline + case body + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + headline = try typeContainer.decode(LabelModel.self, forKey: .headline) + body = try typeContainer.decode(LabelModel.self, forKey: .body) + 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(headline, forKey: .headline) + try container.encode(body, forKey: .body) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift new file mode 100644 index 00000000..1f2bcd6f --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift @@ -0,0 +1,56 @@ +// +// ListOneColumnTextWithWhitespaceDividerTall.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 09/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +@objcMembers open class ListOneColumnTextWithWhitespaceDividerTall: TableViewCell { + + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + public var stack: Stack + public let headline = Label.commonLabelH3(true) + public let body = Label.commonLabelB2(true) + + // MARK: - Initializers + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: headline, model: StackItemModel(horizontalAlignment: .leading)), + (view: body, model: StackItemModel(spacing: 0, horizontalAlignment: .leading))], + axis: .vertical) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //----------------------------------------------------- + // MARK: - View Lifecycle + //----------------------------------------------------- + override open func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListOneColumnTextWithWhitespaceDividerTallModel else { return } + headline.set(with: model.headline, delegateObject, additionalData) + body.set(with: model.body, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 90 + } + + open override func reset() { + super.reset() + headline.styleH3(true) + body.styleB2(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift new file mode 100644 index 00000000..7c95b7e8 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift @@ -0,0 +1,48 @@ +// +// ListOneColumnTextWithWhitespaceDividerTallModel.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 09/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListOneColumnTextWithWhitespaceDividerTallModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list1CTxtDiv2" + public var headline: LabelModel + public var body: LabelModel + + public init(headline: LabelModel, body: LabelModel) { + self.headline = headline + self.body = body + super.init() + } + + /// Defaults to set + override public func setDefaults() { + super.setDefaults() + style = "tallDivider" + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case headline + case body + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + headline = try typeContainer.decode(LabelModel.self, forKey: .headline) + body = try typeContainer.decode(LabelModel.self, forKey: .body) + 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(headline, forKey: .headline) + try container.encode(body, forKey: .body) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift new file mode 100644 index 00000000..a8d80219 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift @@ -0,0 +1,56 @@ +// +// ListThreeColumnBillChangesDivider.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 20/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +@objcMembers open class ListThreeColumnBillChangesDivider: TableViewCell { + + public let leftLabel = Label.createLabelBoldBodySmall(true) + public let centerLabel = Label.createLabelBoldBodySmall(true) + public let rightLabel = Label.createLabelBoldBodySmall(true) + var stack: Stack + + // MARK: - Initializers + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 44, horizontalAlignment: .leading)), + (view: centerLabel, model: StackItemModel(percent: 33, horizontalAlignment: .leading)), + (view: rightLabel, model: StackItemModel(percent: 23, horizontalAlignment: .leading))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - MFViewProtocol + open override func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + // MARK: - ModelMoleculeViewProtocol + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListThreeColumnBillChangesDividerModel else { return } + leftLabel.set(with: model.leftLabel, delegateObject, additionalData) + centerLabel.set(with: model.centerLabel, 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() + leftLabel.styleBoldBodySmall(true) + centerLabel.styleBoldBodySmall(true) + rightLabel.styleBoldBodySmall(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDividerModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDividerModel.swift new file mode 100644 index 00000000..f70a696c --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDividerModel.swift @@ -0,0 +1,52 @@ +// +// ListThreeColumnBillChangesDividerModel.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 20/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +public class ListThreeColumnBillChangesDividerModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list3CBillChgDiv" + public var leftLabel: LabelModel + public var centerLabel: LabelModel + public var rightLabel: LabelModel + + public init(leftLabel: LabelModel, centerLabel:LabelModel, rightLabel: LabelModel) { + self.leftLabel = leftLabel + self.centerLabel = centerLabel + self.rightLabel = rightLabel + super.init() + } + + /// Defaults to set + override public func setDefaults() { + super.setDefaults() + style = "tallDivider" + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case leftLabel + case centerLabel + case rightLabel + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + leftLabel = try typeContainer.decode(LabelModel.self, forKey: .leftLabel) + centerLabel = try typeContainer.decode(LabelModel.self, forKey: .centerLabel) + 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(leftLabel, forKey: .leftLabel) + try container.encode(centerLabel, forKey: .centerLabel) + try container.encode(rightLabel, forKey: .rightLabel) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift new file mode 100644 index 00000000..eab5e8be --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift @@ -0,0 +1,61 @@ +// +// ListThreeColumnDataUsageDivider.swift +// MVMCoreUI +// +// Created by Kruthika KP on 20/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ListThreeColumnDataUsageDivider: TableViewCell { + + //----------------------------------------------------- + // MARK: - Outlets + //------------------------------------------------------- + public let leftLabel = Label.createLabelBoldBodySmall(true) + public let centerLabel = Label.createLabelBoldBodySmall(true) + public let rightLabel = Label.createLabelBoldBodySmall(true) + var stack: Stack + + //------------------------------------------------------ + // MARK: - Initializers + //------------------------------------------------------ + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 40, horizontalAlignment: .leading)), + (view: centerLabel, model: StackItemModel(percent: 37, horizontalAlignment: .leading)), + (view: rightLabel, model: StackItemModel(percent: 23, horizontalAlignment: .leading))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + open override func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + // MARK: - ModelMoleculeViewProtocol + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListThreeColumnDataUsageDividerModel else { return } + leftLabel.set(with: model.leftLabel, delegateObject, additionalData) + centerLabel.set(with: model.centerLabel, delegateObject, additionalData) + rightLabel.set(with: model.rightLabel, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 121 + } + + open override func reset() { + super.reset() + leftLabel.styleBoldBodySmall(true) + centerLabel.styleBoldBodySmall(true) + rightLabel.styleBoldBodySmall(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDividerModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDividerModel.swift new file mode 100644 index 00000000..f57bdf01 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDividerModel.swift @@ -0,0 +1,51 @@ +// +// ListThreeColumnDataUsageDividerModel.swift +// MVMCoreUI +// +// Created by Kruthika KP on 20/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListThreeColumnDataUsageDividerModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list3CDataUsgDiv" + public let leftLabel: LabelModel + public let centerLabel: LabelModel + public let rightLabel: LabelModel + + public init(leftLabel: LabelModel, centerLabel: LabelModel, rightLabel: LabelModel) { + self.leftLabel = leftLabel + self.centerLabel = centerLabel + self.rightLabel = rightLabel + super.init() + } + + override public func setDefaults() { + super.setDefaults() + style = "tallDivider" + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case leftLabel + case centerLabel + case rightLabel + } + + required init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + leftLabel = try typeContainer.decode(LabelModel.self, forKey: .leftLabel) + centerLabel = try typeContainer.decode(LabelModel.self, forKey: .centerLabel) + rightLabel = try typeContainer.decode(LabelModel.self, forKey: .rightLabel) + try super.init(from:decoder) + } + + public override func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(leftLabel, forKey: .leftLabel) + try container.encode(centerLabel, forKey: .centerLabel) + try container.encode(rightLabel, forKey: .rightLabel) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift new file mode 100644 index 00000000..7b6af23d --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift @@ -0,0 +1,66 @@ +// +// ListThreeColumnInternationalDataDivider.swift +// MVMCoreUI +// +// Created by Kruthika KP on 31/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ListThreeColumnInternationalDataDivider: TableViewCell { + + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + let leftLabel = Label.createLabelBoldBodySmall(true) + let centerLabel = Label.createLabelBoldBodySmall(true) + let rightLabel = Label.createLabelBoldBodySmall(true) + var stack: Stack + + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 30, horizontalAlignment: .leading)), + (view: centerLabel, model: StackItemModel(percent: 50, horizontalAlignment: .leading)), + (view: rightLabel, model: StackItemModel(percent: 20, horizontalAlignment: .leading))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //----------------------------------------------------- + // MARK: - MFViewProtocol + //----------------------------------------------------- + open override func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + //----------------------------------------------------- + // MARK: - ModelMoleculeViewProtocol + //----------------------------------------------------- + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListThreeColumnInternationalDataDividerModel else { return } + leftLabel.set(with: model.leftLabel, delegateObject, additionalData) + centerLabel.set(with: model.centerLabel, delegateObject, additionalData) + rightLabel.set(with: model.rightLabel, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 121 + } + + //----------------------------------------------------- + // MARK: - MVMCoreUIMoleculeViewProtocol + //----------------------------------------------------- + override open func reset() { + super.reset() + leftLabel.styleBoldBodySmall(true) + centerLabel.styleBoldBodySmall(true) + rightLabel.styleBoldBodySmall(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift new file mode 100644 index 00000000..a5f3bcf6 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift @@ -0,0 +1,52 @@ +// +// ListThreeColumnInternationalDataDividerModel.swift +// MVMCoreUI +// +// Created by Kruthika KP on 31/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListThreeColumnInternationalDataDividerModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list3CIntDataDiv" + public var leftLabel: LabelModel + public var centerLabel: LabelModel + public var rightLabel: LabelModel + + public init (leftLabel: LabelModel, centerLabel: LabelModel, rightLabel: LabelModel) { + self.leftLabel = leftLabel + self.centerLabel = centerLabel + self.rightLabel = rightLabel + super.init() + } + + override public func setDefaults() { + super.setDefaults() + style = "tallDivider" + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case leftLabel + case centerLabel + case rightLabel + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + leftLabel = try typeContainer.decode(LabelModel.self, forKey: .leftLabel) + centerLabel = try typeContainer.decode(LabelModel.self, forKey: .centerLabel) + 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(leftLabel, forKey: .leftLabel) + try container.encode(centerLabel, forKey: .centerLabel) + try container.encode(rightLabel, forKey: .rightLabel) + } +} diff --git a/MVMCoreUI/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift similarity index 98% rename from MVMCoreUI/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift index cc904a77..a641f60f 100644 --- a/MVMCoreUI/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift @@ -35,7 +35,7 @@ import Foundation stack.restack() } - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? ListThreeColumnPlanDataDividerModel else { return } diff --git a/MVMCoreUI/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDividerModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDividerModel.swift similarity index 92% rename from MVMCoreUI/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDividerModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDividerModel.swift index 3ddfe64b..e727a1c6 100644 --- a/MVMCoreUI/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDividerModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDividerModel.swift @@ -19,16 +19,15 @@ public class ListThreeColumnPlanDataDividerModel: ListItemModel, MoleculeModelPr self.centerHeadlineBody = centerHeadlineBody self.rightHeadlineBody = rightHeadlineBody super.init() - setDefaults() } /// Defaults to set override public func setDefaults() { super.setDefaults() style = "tallDivider" - leftHeadlineBody.style = "itemHeader" - centerHeadlineBody.style = "itemHeader" - rightHeadlineBody.style = "itemHeader" + leftHeadlineBody.style = .itemHeader + centerHeadlineBody.style = .itemHeader + rightHeadlineBody.style = .itemHeader } private enum CodingKeys: String, CodingKey { diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift new file mode 100644 index 00000000..aa9fbc2d --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift @@ -0,0 +1,63 @@ +// +// ListThreeColumnSpeedTestDivider.swift +// MVMCoreUI +// +// Created by Lekshmi S on 03/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ListThreeColumnSpeedTestDivider: TableViewCell { + + //------------------------------------------------------- + // MARK: - Outlets + //------------------------------------------------------- + let leftLabel = Label.createLabelBoldBodySmall(true) + let centerLabel = Label.createLabelBoldBodySmall(true) + let rightLabel = Label.createLabelBoldBodySmall(true) + var stack: Stack + + //------------------------------------------------------- + // MARK: - Initializers + //------------------------------------------------------- + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 37, horizontalAlignment: .leading)), (view: centerLabel, model: StackItemModel(percent: 33, horizontalAlignment: .leading)), (view: rightLabel, model: StackItemModel(percent: 30, horizontalAlignment: .leading))], axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //------------------------------------------------------- + // MARK: - View Lifecycle + //------------------------------------------------------- + open override 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? ListThreeColumnSpeedTestDividerModel else { return } + leftLabel.set(with: model.leftLabel, delegateObject, additionalData) + centerLabel.set(with: model.centerLabel, delegateObject, additionalData) + rightLabel.set(with: model.rightLabel, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 121 + } + + open override func reset() { + super.reset() + leftLabel.styleBoldBodySmall(true) + centerLabel.styleBoldBodySmall(true) + rightLabel.styleBoldBodySmall(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDividerModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDividerModel.swift new file mode 100644 index 00000000..6e590529 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDividerModel.swift @@ -0,0 +1,53 @@ +// +// ListThreeColumnSpeedTestDividerModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 03/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListThreeColumnSpeedTestDividerModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list3CSpdTstDiv" + public var leftLabel: LabelModel + public var centerLabel: LabelModel + public var rightLabel: LabelModel + + public init(leftLabel: LabelModel, centerLabel: LabelModel, rightLabel: LabelModel) { + self.leftLabel = leftLabel + self.centerLabel = centerLabel + self.rightLabel = rightLabel + super.init() + } + + /// Defaults to set + override public func setDefaults() { + super.setDefaults() + style = "tallDivider" + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case leftLabel + case centerLabel + case rightLabel + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + leftLabel = try typeContainer.decode(LabelModel.self, forKey: .leftLabel) + centerLabel = try typeContainer.decode(LabelModel.self, forKey: .centerLabel) + 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(leftLabel, forKey: .leftLabel) + try container.encode(centerLabel, forKey: .centerLabel) + try container.encode(rightLabel, forKey: .rightLabel) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift new file mode 100644 index 00000000..d26da0f4 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift @@ -0,0 +1,62 @@ +// +// ListTwoColumnSubsectionDivider.swift +// MVMCoreUI +// +// Created by Lekshmi S on 07/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ListTwoColumnSubsectionDivider: TableViewCell { + + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + let leftLabel = Label.createLabelBoldBodySmall(true) + let rightLabel = Label.createLabelBoldBodySmall(true) + var stack: Stack + + //------------------------------------------------------- + // MARK: - Initializers + //------------------------------------------------------- + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 75, horizontalAlignment: .leading)), + (view: rightLabel, model: StackItemModel(percent: 25, horizontalAlignment: .leading))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //------------------------------------------------------- + // MARK: - View Lifecycle + //------------------------------------------------------- + open override 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? ListTwoColumnSubsectionDividerModel else { return } + leftLabel.set(with: model.leftLabel, delegateObject, additionalData) + rightLabel.set(with: model.rightLabel, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 121 + } + + open override func reset() { + super.reset() + leftLabel.styleBoldBodySmall(true) + rightLabel.styleBoldBodySmall(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDividerModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDividerModel.swift new file mode 100644 index 00000000..c296b851 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDividerModel.swift @@ -0,0 +1,48 @@ +// +// ListTwoColumnSubsectionDividerModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 07/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListTwoColumnSubsectionDividerModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list2CSbscDiv" + public var leftLabel: LabelModel + public var rightLabel: LabelModel + + public init(leftLabel: LabelModel, rightLabel: LabelModel) { + self.leftLabel = leftLabel + self.rightLabel = rightLabel + super.init() + } + + /// Defaults to set + override public func setDefaults() { + super.setDefaults() + style = "tallDivider" + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case leftLabel + case rightLabel + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + leftLabel = try typeContainer.decode(LabelModel.self, forKey: .leftLabel) + 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(leftLabel, forKey: .leftLabel) + try container.encode(rightLabel, forKey: .rightLabel) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/Doughnut/ColorViewWithLabel.swift b/MVMCoreUI/Atomic/Molecules/Doughnut/ColorViewWithLabel.swift new file mode 100644 index 00000000..f8dd4b5c --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/Doughnut/ColorViewWithLabel.swift @@ -0,0 +1,73 @@ +// +// ColorViewWithLabel.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/7/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + + +open class ColorViewWithLabel: View { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public var label = Label.createLabelRegularBodySmall(true) + public var colorView = View() + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 8)! + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + public var heightConstraint: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + open override func setupView() { + super.setupView() + + addSubview(colorView) + addSubview(label) + + heightConstraint = colorView.heightAnchor.constraint(equalToConstant: sizeObject.getValueBasedOnApplicationWidth()) + heightConstraint?.isActive = true + colorView.widthAnchor.constraint(equalTo: colorView.heightAnchor).isActive = true + colorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + colorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + label.leadingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: PaddingOne).isActive = true + label.topAnchor.constraint(equalTo: topAnchor).isActive = true + trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true + bottomAnchor.constraint(equalTo: label.bottomAnchor).isActive = true + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + label.updateView(size) + heightConstraint?.constant = sizeObject.getValueBased(onSize: size) + setNeedsDisplay() + } + + open override func reset() { + super.reset() + label.reset() + } + + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + + guard let chartItemModel = model as? DoughnutChartItemModel else { return } + + label.set(with: chartItemModel.label, delegateObject, additionalData) + colorView.backgroundColor = chartItemModel.color.uiColor + } +} diff --git a/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChart.swift b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChart.swift new file mode 100644 index 00000000..bad0492d --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChart.swift @@ -0,0 +1,221 @@ +// +// DoughnutChart.swift +// MVMCoreUI +// +// Created by Murugan, Vimal on 07/01/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + + +open class DoughnutChart: View { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public var doughnutLayer = CALayer() + public var titleLabel = Label.createLabelBoldTitleLarge(true) + public var subTitleLabel = Label.createLabelRegularMicro(true) + public var labelContainer = MVMCoreUICommonViewsUtility.commonView() + public static let heightConstant: CGFloat = 136 + private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: heightConstant)! + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + + public var labelContainerLeftConstraint: NSLayoutConstraint? + public var labelContainerTopConstraint: NSLayoutConstraint? + public var labelContainerBottomConstraint: NSLayoutConstraint? + public var labelContainerRightConstraint: NSLayoutConstraint? + public var heightConstraint: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Computed Properties + //-------------------------------------------------- + + public var doughnutChartModel: DoughnutChartModel? { + get { return model as? DoughnutChartModel } + } + + public var height: CGFloat = heightConstant { + didSet { + if height != oldValue { + sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)! + updateContainer() + drawGraph() + } + } + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public convenience init() { + self.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + titleLabel.updateView(size) + subTitleLabel.updateView(size) + updateContainer() + drawGraph() + } + + open override func reset() { + super.reset() + titleLabel.reset() + subTitleLabel.reset() + clearLayers() + } + + open override func setupView() { + super.setupView() + + addSubview(labelContainer) + + labelContainer.addSubview(titleLabel) + labelContainer.addSubview(subTitleLabel) + titleLabel.textAlignment = .center + subTitleLabel.textAlignment = .center + + //Make label font size to adjust width if label content is high + titleLabel.numberOfLines = 1 + titleLabel.adjustsFontSizeToFitWidth = true + + layer.addSublayer(doughnutLayer) + heightConstraint = heightAnchor.constraint(equalToConstant: sizeObject.getValueBasedOnApplicationWidth()) + heightConstraint?.isActive = true + widthAnchor.constraint(equalTo: heightAnchor).isActive = true + + labelContainerLeftConstraint = labelContainer.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: lineWidth()) + labelContainerLeftConstraint?.isActive = true + labelContainerTopConstraint = labelContainer.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: lineWidth()) + labelContainerTopConstraint?.isActive = true + labelContainerRightConstraint = trailingAnchor.constraint(greaterThanOrEqualTo: labelContainer.trailingAnchor, constant: lineWidth()) + labelContainerRightConstraint?.isActive = true + labelContainerBottomConstraint = bottomAnchor.constraint(greaterThanOrEqualTo: labelContainer.bottomAnchor, constant: lineWidth()) + labelContainerBottomConstraint?.isActive = true + labelContainer.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + labelContainer.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true + + NSLayoutConstraint.constraintPinSubview(titleLabel, pinTop: true, pinBottom: false, pinLeft: true, pinRight: true) + NSLayoutConstraint.constraintPinSubview(subTitleLabel, pinTop: false, pinBottom: true, pinLeft: true, pinRight: true) + _ = NSLayoutConstraint(pinFirstView: titleLabel, toSecondView: subTitleLabel, withConstant: 0, directionVertical: true) + //Rotate view for initial draw + doughnutLayer.transform = CATransform3DMakeRotation(1 * .pi, 0, 0, 1) + } + + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + clearLayers() + guard let doughnutChartModel = doughnutChartModel else { return } + + titleLabel.setOptional(with: doughnutChartModel.title, delegateObject, additionalData) + subTitleLabel.setOptional(with: doughnutChartModel.subtitle, delegateObject, additionalData) + titleLabel.textAlignment = .center + subTitleLabel.textAlignment = .center + updateLabelContainer() + drawGraph() + } + + //-------------------------------------------------- + // MARK: - Draw Graph + //-------------------------------------------------- + + private func drawGraph() { + clearLayers() + let widthHeight = sizeObject.getValueBasedOnApplicationWidth() + doughnutLayer.frame = CGRect(x: 0, y: 0, width: widthHeight, height: widthHeight) + + if let doughnutChart = doughnutChartModel { + var prevPercent: CGFloat = 0.0 + for model in doughnutChart.sections { + prevPercent += drawBar(model, prevPercent) + } + } + } + + private func drawBar(_ chartModel: DoughnutChartItemModel, _ prevPercent: CGFloat) -> CGFloat { + + let shapeLayer = CAShapeLayer() + shapeLayer.frame = doughnutLayer.frame + shapeLayer.lineWidth = lineWidth() + shapeLayer.fillColor = nil + shapeLayer.strokeColor = chartModel.color.uiColor.cgColor + + let arcCenter = shapeLayer.position + let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth() / 2.0 + + let value: CGFloat = chartModel.percent + let gap: CGFloat = spaceRequired() ? lineGap() : 0.0 + let startAngle = ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi) + let endAngle = ((value / 100.0) * 2 * .pi) + startAngle - gap + let circlePath = UIBezierPath(arcCenter: arcCenter, + radius: radius, + startAngle: startAngle, + endAngle: endAngle, + clockwise: true) + + shapeLayer.path = circlePath.cgPath + doughnutLayer.addSublayer(shapeLayer) + return value + } + + private func clearLayers() { + doughnutLayer.sublayers?.forEach{ $0.removeFromSuperlayer() } + } + + public func updateContainer() { + + heightConstraint?.constant = sizeObject.getValueBasedOnApplicationWidth() + updateLabelContainer() + setNeedsDisplay() + } + + public func lineWidth() -> CGFloat { + return 12 + } + + public func lineGap() -> CGFloat { + //If array is having the single item then no space required + return doughnutChartModel?.sections.count == 1 ? 0.0 : 0.05 + } + + public func spaceRequired() -> Bool { + return doughnutChartModel?.spaceRequired ?? true + } + + public func updateLabelContainer() { + + labelContainer.setNeedsDisplay() + labelContainer.layoutIfNeeded() + let radius = sizeObject.getValueBasedOnApplicationWidth() / 2 - lineWidth() + let labelheight = labelContainer.frame.height / 2 + let padding = sizeObject.getValueBasedOnApplicationWidth() / 2 - sqrt(max(0, pow(radius, 2) - pow(labelheight, 2))) + + labelContainerLeftConstraint?.constant = round(padding) + labelContainerRightConstraint?.constant = round(padding) + labelContainerTopConstraint?.constant = max(radius - labelheight, labelheight) + labelContainerBottomConstraint?.constant = max(radius - labelheight, labelheight) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartItemModel.swift b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartItemModel.swift new file mode 100644 index 00000000..ee4e664a --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartItemModel.swift @@ -0,0 +1,44 @@ +// +// DoughnutChartItemModel.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/7/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + + +@objcMembers public class DoughnutChartItemModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public var backgroundColor: Color? + public static var identifier: String = "doughnutChartItem" + public var moleculeName: String = DoughnutChartItemModel.identifier + public var label: LabelModel + @Percent public var percent: CGFloat + public var color: Color + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case backgroundColor + case label + case percent + case color + } + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(percent: CGFloat, color: Color, label: LabelModel) { + self.percent = percent + self.color = color + self.label = label + } +} diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChartModel.swift b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartModel.swift similarity index 51% rename from MVMCoreUI/Molecules/Doughnut/DoughnutChartModel.swift rename to MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartModel.swift index 1e5c7dce..fbb034c6 100644 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChartModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartModel.swift @@ -8,31 +8,25 @@ import Foundation + @objcMembers public class DoughnutChartModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public var backgroundColor: Color? public static var identifier: String = "doughnutChart" - public var moleculeName: String? = DoughnutChartModel.identifier + public var moleculeName: String = DoughnutChartModel.identifier public var title: LabelModel? public var subtitle: LabelModel? public var sections: [DoughnutChartItemModel] public var spaceRequired: Bool? + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(sections: [DoughnutChartItemModel]) { self.sections = sections } } - -@objcMembers public class DoughnutChartItemModel: MoleculeModelProtocol { - public var backgroundColor: Color? - public static var identifier: String = "doughnutChartItem" - public var moleculeName: String? = DoughnutChartItemModel.identifier - public var label: LabelModel - @Percent public var percent: CGFloat - public var color: Color - - public init(percent: CGFloat, color: Color, label: LabelModel) { - self.percent = percent - self.color = color - self.label = label - } -} diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartView.swift similarity index 51% rename from MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift rename to MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartView.swift index 7d279759..1c0ea963 100644 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChartView.swift +++ b/MVMCoreUI/Atomic/Molecules/Doughnut/DoughnutChartView.swift @@ -8,47 +8,53 @@ import Foundation + @objcMembers open class DoughnutChartView: View { - var doughnutChart = DoughnutChart(frame: CGRect.zero) + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public var doughnutChart = DoughnutChart() var colorLablesStack = ColorViewLabelsStack() - var doughnutChartModel: DoughnutChartModel? { - get { return model as? DoughnutChartModel } + + public var doughnutChartModel: DoughnutChartModel? { + get { return model as? DoughnutChartModel } } + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func setupView() { super.setupView() - guard doughnutChart.superview == nil else { - return - } - doughnutChart.translatesAutoresizingMaskIntoConstraints = false + addSubview(doughnutChart) - colorLablesStack.translatesAutoresizingMaskIntoConstraints = false addSubview(colorLablesStack) doughnutChart.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - doughnutChart.topAnchor.constraint(equalTo: topAnchor).isActive = true + doughnutChart.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: doughnutChart.bottomAnchor).isActive = true let doughnutBottomAnchor = bottomAnchor.constraint(equalTo: doughnutChart.bottomAnchor) doughnutBottomAnchor.priority = UILayoutPriority(rawValue: 200) doughnutBottomAnchor.isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.bottomAnchor).isActive = true + let colorLablesBottomAnchor = bottomAnchor.constraint(equalTo: colorLablesStack.bottomAnchor) colorLablesBottomAnchor.priority = UILayoutPriority(rawValue: 204) colorLablesBottomAnchor.isActive = true - let colorLablesTopAnchor = colorLablesStack.topAnchor.constraint(equalTo: doughnutChart.topAnchor) + colorLablesStack.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true + + let colorLablesTopAnchor = colorLablesStack.topAnchor.constraint(equalTo: topAnchor) colorLablesTopAnchor.priority = .defaultLow colorLablesTopAnchor.isActive = true - colorLablesStack.topAnchor.constraint(greaterThanOrEqualTo: doughnutChart.topAnchor).isActive = true - bottomAnchor.constraint(greaterThanOrEqualTo: colorLablesStack.bottomAnchor).isActive = true trailingAnchor.constraint(equalTo: colorLablesStack.trailingAnchor).isActive = true - - let centerY = colorLablesStack.centerYAnchor.constraint(equalTo: doughnutChart.centerYAnchor) - centerY.priority = UILayoutPriority(rawValue: 500) - centerY.isActive = true - + doughnutChart.centerYAnchor.constraint(equalTo: colorLablesStack.centerYAnchor).isActive = true + colorLablesStack.leadingAnchor.constraint(equalTo: doughnutChart.trailingAnchor, constant: PaddingThree).isActive = true colorLablesStack.backgroundColor = .clear } @@ -65,12 +71,16 @@ import Foundation colorLablesStack.reset() } - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = doughnutChartModel else { return } doughnutChart.set(with: model, delegateObject, additionalData) - + // Create the stack model var stackItems: [MoleculeStackItemModel] = [] for item in model.sections { @@ -80,21 +90,19 @@ import Foundation stack.verticalAlignment = .fill colorLablesStack.set(with: stack, delegateObject, additionalData) } - - open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: DoughnutChartModel.self) else { return } - set(with: model, delegateObject, additionalData) - } } +// MARK: - MVMCoreUIViewConstrainingProtocol extension DoughnutChartView: MVMCoreUIViewConstrainingProtocol { + open func horizontalAlignment() -> UIStackView.Alignment { return .leading } } class ColorViewLabelsStack: MoleculeStackView { - override func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + + override func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let stackItemModels = stackModel?.molecules else { return } for model in stackItemModels { let view = ColorViewWithLabel() @@ -104,57 +112,3 @@ class ColorViewLabelsStack: MoleculeStackView { } } } - -class ColorViewWithLabel: View { - - var label = Label.commonLabelB2(true) - var colorView = MVMCoreUICommonViewsUtility.commonView() - private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: 8)! - var heightConstraint: NSLayoutConstraint? - - override func setupView() { - super.setupView() - guard colorView.superview == nil else { - return - } - translatesAutoresizingMaskIntoConstraints = false - addSubview(colorView) - addSubview(label) - - heightConstraint = colorView.heightAnchor.constraint(equalToConstant: sizeObject.getValueBasedOnApplicationWidth()) - heightConstraint?.isActive = true - colorView.widthAnchor.constraint(equalTo: colorView.heightAnchor).isActive = true - colorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - colorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - - label.leadingAnchor.constraint(equalTo: colorView.trailingAnchor, constant: PaddingOne).isActive = true - label.topAnchor.constraint(equalTo: topAnchor).isActive = true - trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true - bottomAnchor.constraint(equalTo: label.bottomAnchor).isActive = true - } - - override func updateView(_ size: CGFloat) { - super.updateView(size) - label.updateView(size) - heightConstraint?.constant = sizeObject.getValueBased(onSize: size) - setNeedsDisplay() - } - - override func reset() { - super.reset() - label.reset() - } - - override func setAsMolecule() { - label.setAsMolecule() - } - - override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - super.set(with: model, delegateObject, additionalData) - guard let chartItemModel = model as? DoughnutChartItemModel else { - return - } - label.set(with: chartItemModel.label, delegateObject, additionalData) - colorView.backgroundColor = chartItemModel.color.uiColor - } -} diff --git a/MVMCoreUI/Molecules/FooterModel.swift b/MVMCoreUI/Atomic/Molecules/FooterModel.swift similarity index 100% rename from MVMCoreUI/Molecules/FooterModel.swift rename to MVMCoreUI/Atomic/Molecules/FooterModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/FooterView.swift b/MVMCoreUI/Atomic/Molecules/FooterView.swift new file mode 100644 index 00000000..f54a84e5 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/FooterView.swift @@ -0,0 +1,12 @@ +// +// FooterView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/11/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class FooterView: MoleculeContainer { +} diff --git a/MVMCoreUI/Molecules/Header.swift b/MVMCoreUI/Atomic/Molecules/Header.swift similarity index 86% rename from MVMCoreUI/Molecules/Header.swift rename to MVMCoreUI/Atomic/Molecules/Header.swift index c878c5ff..63a13fbc 100644 --- a/MVMCoreUI/Molecules/Header.swift +++ b/MVMCoreUI/Atomic/Molecules/Header.swift @@ -12,14 +12,14 @@ open class HeaderView: Container { public let line = Line() /// Convenience for doing some default setting - open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? + open var molecule: MoleculeViewProtocol? var headerModel: HeaderModel? { get { return model as? HeaderModel } } /// Convenience function to add a molecule to the view. - open func addMolecule(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) { + open func addMolecule(_ molecule: MoleculeViewProtocol) { addSubview(molecule) containerHelper.constrainView(molecule) self.molecule = molecule @@ -29,7 +29,7 @@ open class HeaderView: Container { open override func updateView(_ size: CGFloat) { super.updateView(size) line.updateView(size) - molecule?.updateView(size) + (molecule as? MVMCoreViewProtocol)?.updateView(size) } public override func setupView() { @@ -41,14 +41,14 @@ open class HeaderView: Container { NSLayoutConstraint.pinViewRight(toSuperview: line, useMargins: true, constant: 0).isActive = true } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func reset() { super.reset() line.setStyle(.heavy) - molecule?.reset?() + molecule?.reset() } - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let headerModel = headerModel else { return } diff --git a/MVMCoreUI/Molecules/HeaderModel.swift b/MVMCoreUI/Atomic/Molecules/HeaderModel.swift similarity index 100% rename from MVMCoreUI/Molecules/HeaderModel.swift rename to MVMCoreUI/Atomic/Molecules/HeaderModel.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/Headers/HeadersH1Bill.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/DesignedComponents/Headers/HeadersH1Bill.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/Headers/HeadersH1Bill.swift rename to MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/DesignedComponents/Headers/HeadersH1Bill.swift diff --git a/MVMCoreUI/Molecules/DesignedComponents/Headers/HeadersH1BillModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/DesignedComponents/Headers/HeadersH1BillModel.swift similarity index 100% rename from MVMCoreUI/Molecules/DesignedComponents/Headers/HeadersH1BillModel.swift rename to MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/DesignedComponents/Headers/HeadersH1BillModel.swift diff --git a/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift similarity index 93% rename from MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift rename to MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift index 469b1929..1afe7489 100644 --- a/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift @@ -18,12 +18,10 @@ import UIKit open override func setupView() { super.setupView() - guard subviews.count == 0 else { - return - } headlineBody.headlineLabel.styleB1(true) headlineBody.spaceBetweenLabelsConstant = 0 - + imageView.addSizeConstraintsForAspectRatio = true + addSubview(headlineBody) addSubview(imageView) @@ -41,7 +39,7 @@ import UIKit imageView.updateView(size) } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func reset() { super.reset() headlineBody.reset() @@ -50,7 +48,7 @@ import UIKit imageView.reset() } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 95 } diff --git a/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBodyModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/ImageHeadlineBodyModel.swift similarity index 88% rename from MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBodyModel.swift rename to MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/ImageHeadlineBodyModel.swift index f34e7b94..4ac675c5 100644 --- a/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBodyModel.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/ImageHeadlineBodyModel.swift @@ -10,7 +10,7 @@ import Foundation public struct ImageHeadlineBodyModel: MoleculeModelProtocol { public static var identifier: String = "imageHeadlineBody" - public var moleculeName: String? = ImageHeadlineBodyModel.identifier + public var moleculeName: String = ImageHeadlineBodyModel.identifier public var backgroundColor: Color? public var image: ImageViewModel public var headlineBody: HeadlineBodyModel diff --git a/MVMCoreUI/Molecules/RadioButtonLabel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabel.swift similarity index 100% rename from MVMCoreUI/Molecules/RadioButtonLabel.swift rename to MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabel.swift diff --git a/MVMCoreUI/Molecules/RadioButtonLabelModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabelModel.swift similarity index 92% rename from MVMCoreUI/Molecules/RadioButtonLabelModel.swift rename to MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabelModel.swift index 28a0f558..62df9794 100644 --- a/MVMCoreUI/Molecules/RadioButtonLabelModel.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabelModel.swift @@ -11,7 +11,7 @@ import Foundation @objcMembers public class RadioButtonLabelModel: MoleculeModelProtocol { public static var identifier: String = "radioButtonLabel" public var backgroundColor: Color? - + public var moleculeName: String public var radioButton: RadioButtonModel public var label: LabelModel } diff --git a/MVMCoreUI/Molecules/HorizontalCombinationViews/TabsModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabsModel.swift similarity index 100% rename from MVMCoreUI/Molecules/HorizontalCombinationViews/TabsModel.swift rename to MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabsModel.swift diff --git a/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift similarity index 97% rename from MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift rename to MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift index 98160441..285c429c 100644 --- a/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonView.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift @@ -89,7 +89,7 @@ import UIKit equalWidthConstraint?.isActive = false } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func reset() { super.reset() setDefault() @@ -100,7 +100,7 @@ import UIKit return .center } - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { guard let model = model as? TwoButtonViewModel, let buttonModel = model.primaryButton ?? model.secondaryButton else { return 0 } diff --git a/MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift similarity index 100% rename from MVMCoreUI/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift rename to MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift diff --git a/MVMCoreUI/Molecules/Items/AccordionListItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/AccordionListItemModel.swift similarity index 100% rename from MVMCoreUI/Molecules/Items/AccordionListItemModel.swift rename to MVMCoreUI/Atomic/Molecules/Items/AccordionListItemModel.swift diff --git a/MVMCoreUI/Molecules/Items/AccordionMoleculeTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift similarity index 80% rename from MVMCoreUI/Molecules/Items/AccordionMoleculeTableViewCell.swift rename to MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift index 714c7dd9..a7970861 100644 --- a/MVMCoreUI/Molecules/Items/AccordionMoleculeTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift @@ -32,18 +32,14 @@ import UIKit override public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { accordionButton.isSelected = !accordionButton.isSelected accordionButton.setTitle(accordionButton.isSelected ? "-" : "+", for: .normal) - guard let model = accordionListItemModel else { - return - } - - guard let json = model.toJSON(), - let molecules = json.optionalArrayForKey("molecules") as? [[AnyHashable: Any]] - else { return } + guard let model = accordionListItemModel else { return } if accordionButton.isSelected { - delegateObject?.moleculeDelegate?.addMolecules(molecules, sender: self, animation: .automatic) + if let indexPath = delegateObject?.moleculeDelegate?.getIndexPath(for: model) { + delegateObject?.moleculeDelegate?.addMolecules(model.molecules, indexPath: indexPath, animation: .automatic) + } } else { - delegateObject?.moleculeDelegate?.removeMolecules(molecules, sender: self, animation: .automatic) + delegateObject?.moleculeDelegate?.removeMolecules(model.molecules, animation: .automatic) } if (accordionListItemModel?.hideLineWhenExpanded ?? false) && (self.bottomSeparatorView?.shouldBeVisible() ?? false) { diff --git a/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift b/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift new file mode 100644 index 00000000..3089596a --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift @@ -0,0 +1,78 @@ +// +// CarouselItem.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/6/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class CarouselItem: MoleculeCollectionViewCell { + + open var allowsPeaking = false + var peakingLeftArrow = UIImageView(image: MVMCoreUIUtility.imageNamed("peakingRightArrow")?.withRenderingMode(.alwaysTemplate)) + var peakingRightArrow = UIImageView(image: MVMCoreUIUtility.imageNamed("peakingRightArrow")?.withRenderingMode(.alwaysTemplate)) + var peakingCover = MVMCoreUICommonViewsUtility.commonView() + + open override func addMolecule(_ molecule: MoleculeViewProtocol) { + super.addMolecule(molecule) + contentView.sendSubviewToBack(molecule) + } + + open override func setupView() { + super.setupView() + + // Covers the card when peaking. + peakingCover.backgroundColor = .white + peakingCover.alpha = 0 + contentView.addSubview(peakingCover) + NSLayoutConstraint.constraintPinSubview(toSuperview: peakingCover) + + // A small arrow on the next card for when peaking. + let ratio: CGFloat = 0.015 + peakingLeftArrow.translatesAutoresizingMaskIntoConstraints = false + peakingLeftArrow.alpha = 0 + peakingLeftArrow.tintColor = .black + contentView.addSubview(peakingLeftArrow) + peakingLeftArrow.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true + NSLayoutConstraint.scalingPinViewLeft(toSuper: peakingLeftArrow, ratio: ratio, anchor: contentView.widthAnchor) + + peakingRightArrow.translatesAutoresizingMaskIntoConstraints = false + peakingRightArrow.transform = CGAffineTransform(scaleX: -1, y: 1) // Flip + peakingRightArrow.alpha = 0 + peakingRightArrow.tintColor = .black + contentView.addSubview(peakingRightArrow) + peakingRightArrow.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true + NSLayoutConstraint.scalingPinViewRight(toSuper: peakingRightArrow, ratio: ratio, anchor: contentView.widthAnchor) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let collectionModel = model as? CarouselItemModel else { return } + + // Handles peaking. + allowsPeaking = collectionModel.peakingUI ?? false + if let peakingArrowColor = collectionModel.peakingArrowColor { + let color = peakingArrowColor.uiColor + peakingLeftArrow.tintColor = color + peakingRightArrow.tintColor = color + } + } + + public func setPeaking(_ peaking: Bool, animated: Bool) { + guard allowsPeaking else { + return + } + let animation = {() in + self.peakingRightArrow.alpha = peaking ? 1 : 0 + self.peakingLeftArrow.alpha = peaking ? 1 : 0 + self.peakingCover.alpha = peaking ? 0.5 : 0 + } + if animated { + UIView.animate(withDuration: 0.4, animations: animation) + } else { + animation() + } + } +} diff --git a/MVMCoreUI/Molecules/Items/CarouselItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/CarouselItemModel.swift similarity index 68% rename from MVMCoreUI/Molecules/Items/CarouselItemModel.swift rename to MVMCoreUI/Atomic/Molecules/Items/CarouselItemModel.swift index 9fc218a7..ab6a1cbd 100644 --- a/MVMCoreUI/Molecules/Items/CarouselItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/CarouselItemModel.swift @@ -9,22 +9,20 @@ import Foundation -@objcMembers public class CarouselItemModel: MoleculeContainerModel, CarouselItemModelProtocol { - public static var identifier: String = "carouselItem" - public var backgroundColor: Color? +@objcMembers public class CarouselItemModel: MoleculeCollectionItemModel, CarouselItemModelProtocol { + public override class var identifier: String { + return "carouselItem" + } public var peakingUI: Bool? public var peakingArrowColor: Color? private enum CodingKeys: String, CodingKey { - case moleculeName - case backgroundColor case peakingUI case peakingArrowColor } required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) peakingUI = try typeContainer.decodeIfPresent(Bool.self, forKey: .peakingUI) peakingArrowColor = try typeContainer.decodeIfPresent(Color.self, forKey: .peakingArrowColor) try super.init(from: decoder) @@ -33,8 +31,6 @@ import Foundation public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(moleculeName, forKey: .moleculeName) - try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(peakingUI, forKey: .peakingUI) try container.encodeIfPresent(peakingArrowColor, forKey: .peakingArrowColor) } diff --git a/MVMCoreUI/Molecules/Items/DropDownFilterTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift similarity index 82% rename from MVMCoreUI/Molecules/Items/DropDownFilterTableViewCell.swift rename to MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift index 6b6f5a32..aa581ba1 100644 --- a/MVMCoreUI/Molecules/Items/DropDownFilterTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift @@ -29,16 +29,16 @@ import UIKit guard newValue != oldValue, let self = self, let index = self.dropDown.pickerData.firstIndex(of: newValue), - let dropListItemJSON = (self.listItemModel as? DropDownListItemModel).toJSON(), - let json2d = dropListItemJSON.optionalArrayForKey("molecules") as? [[[AnyHashable: Any]]], - !json2d.isEmpty && !(json2d.first?.isEmpty ?? false) + let model = self.listItemModel as? DropDownListItemModel else { return } if self.previousIndex != NSNotFound { - self.delegateObject?.moleculeDelegate?.removeMolecules(json2d[self.previousIndex], sender: self, animation: .fade) + self.delegateObject?.moleculeDelegate?.removeMolecules(model.molecules[self.previousIndex], animation: .fade) } - self.delegateObject?.moleculeDelegate?.addMolecules(json2d[index], sender: self, animation: .fade) + if let indexPath = self.delegateObject?.moleculeDelegate?.getIndexPath(for: model) { + self.delegateObject?.moleculeDelegate?.addMolecules(model.molecules[index], indexPath: indexPath, animation: .fade) + } self.previousIndex = index } } diff --git a/MVMCoreUI/Molecules/Items/DropDownListItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/DropDownListItemModel.swift similarity index 73% rename from MVMCoreUI/Molecules/Items/DropDownListItemModel.swift rename to MVMCoreUI/Atomic/Molecules/Items/DropDownListItemModel.swift index 0cd2f5ef..deb2374b 100644 --- a/MVMCoreUI/Molecules/Items/DropDownListItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/DropDownListItemModel.swift @@ -8,6 +8,7 @@ import Foundation + @objcMembers public class DropDownListItemModel: ListItemModel, MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties @@ -17,6 +18,10 @@ import Foundation public var molecules: [[ListItemModelProtocol & MoleculeModelProtocol]] public var dropDown: ItemDropdownEntryFieldModel + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + /// Defaults to set public override func setDefaults() { super.setDefaults() @@ -24,14 +29,25 @@ import Foundation line = LineModel(type: .none) style = "sectionFooter" } + + //-------------------------------------------------- + // MARK: - Functions + //-------------------------------------------------- + + public class func verify(dropdown: ItemDropdownEntryFieldModel, molecules: [[ListItemModelProtocol & MoleculeModelProtocol]]) throws { + guard dropdown.options.count == molecules.count else { + throw MolecularError.countImbalance("dropdown.options.count is not equal to molecules.count") + } + } //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- - public init(molecules: [[ListItemModelProtocol & MoleculeModelProtocol]], dropDown: ItemDropdownEntryFieldModel) { + public init(molecules: [[ListItemModelProtocol & MoleculeModelProtocol]], dropDown: ItemDropdownEntryFieldModel) throws { self.molecules = molecules self.dropDown = dropDown + try Self.verify(dropdown: dropDown, molecules: molecules) super.init() } @@ -53,6 +69,7 @@ import Foundation let typeContainer = try decoder.container(keyedBy: CodingKeys.self) molecules = try typeContainer.decodeModels2DIfPresent(codingKey: .molecules) ?? [[]] dropDown = try typeContainer.decode(ItemDropdownEntryFieldModel.self, forKey: .dropDown) + try Self.verify(dropdown: dropDown, molecules: molecules) try super.init(from: decoder) } diff --git a/MVMCoreUI/Molecules/Items/ListItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/ListItemModel.swift similarity index 99% rename from MVMCoreUI/Molecules/Items/ListItemModel.swift rename to MVMCoreUI/Atomic/Molecules/Items/ListItemModel.swift index 9365125a..9a00a94d 100644 --- a/MVMCoreUI/Molecules/Items/ListItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/ListItemModel.swift @@ -9,6 +9,7 @@ import Foundation + @objcMembers open class ListItemModel: ContainerModel, ListItemModelProtocol { //-------------------------------------------------- // MARK: - Properties diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionItemModel.swift new file mode 100644 index 00000000..ed9467fd --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionItemModel.swift @@ -0,0 +1,57 @@ +// +// MoleculeCollectionItemModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/6/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +/// A model for a collection item that is a container for any molecule. +@objcMembers public class MoleculeCollectionItemModel: MoleculeContainerModel, CollectionItemModelProtocol, MoleculeModelProtocol { + open class var identifier: String { + return "collectionItem" + } + public var backgroundColor: Color? + + /// Defaults to set + public func setDefaults() { + if useHorizontalMargins == nil { + useHorizontalMargins = true + } + if useVerticalMargins == nil { + useVerticalMargins = true + } + if topMarginPadding == nil { + topMarginPadding = PaddingDefault + } + if bottomMarginPadding == nil { + bottomMarginPadding = PaddingDefault + } + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + } + + public override init(with moleculeModel: MoleculeModelProtocol) { + super.init(with: moleculeModel) + setDefaults() + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + try super.init(from: decoder) + setDefaults() + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift new file mode 100644 index 00000000..42af1a53 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift @@ -0,0 +1,59 @@ +// +// MoleculeCollectionViewCell.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/2/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +/// A collection item that is a container for any molecule. +open class MoleculeCollectionViewCell: CollectionViewCell { + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let collectionModel = model as? MoleculeCollectionItemModel else { return } + if molecule == nil { + if let moleculeView = MoleculeObjectMapping.shared()?.createMolecule(collectionModel.molecule, delegateObject: delegateObject, additionalData: additionalData) { + addMolecule(moleculeView) + } + } else { + molecule?.set(with: collectionModel.molecule, delegateObject, additionalData) + } + + guard let molecule = molecule as? (UIView & MVMCoreUIViewConstrainingProtocol) else { return } + containerHelper.set(with: collectionModel, for: molecule) + accessibilityElements = molecule.subviews + } + + open override func reset() { + super.reset() + molecule?.reset() + backgroundColor = .white + } + + open class func nameForReuse(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + guard let moleculeModel = (model as? MoleculeCollectionItemModel)?.molecule else { return "\(self)<>" } + let className = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) + let moleculeName = className?.nameForReuse(with: moleculeModel, delegateObject) ?? moleculeModel.moleculeName + return "\(self)<\(moleculeName)>" + } + + open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + guard let moleculeModel = (model as? MoleculeCollectionItemModel)?.molecule, + let theClass = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) + else { return nil } + + return theClass.requiredModules(with: moleculeModel, delegateObject, error: error) + } + + open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + guard let model = model as? MoleculeCollectionItemModel, + let classType = MoleculeObjectMapping.shared()?.getMoleculeClass(model.molecule), + let height = classType.estimatedHeight(with: model.molecule, delegateObject) + else { return 100 } + + return height + (model.topMarginPadding ?? 0) + (model.bottomMarginPadding ?? 0) + } +} diff --git a/MVMCoreUI/Molecules/Items/MoleculeListItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeListItemModel.swift similarity index 100% rename from MVMCoreUI/Molecules/Items/MoleculeListItemModel.swift rename to MVMCoreUI/Atomic/Molecules/Items/MoleculeListItemModel.swift diff --git a/MVMCoreUI/Molecules/Items/MoleculeStackItem.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItem.swift similarity index 100% rename from MVMCoreUI/Molecules/Items/MoleculeStackItem.swift rename to MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItem.swift diff --git a/MVMCoreUI/Molecules/Items/MoleculeStackItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift similarity index 100% rename from MVMCoreUI/Molecules/Items/MoleculeStackItemModel.swift rename to MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift similarity index 55% rename from MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift rename to MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift index 33229092..0c9dec3b 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift @@ -11,7 +11,7 @@ import UIKit @objcMembers open class MoleculeTableViewCell: TableViewCell { - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { @@ -21,8 +21,8 @@ import UIKit } if molecule != nil { - (molecule as? ModelMoleculeViewProtocol)?.set(with: castModel.molecule, delegateObject, additionalData) - } else if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(castModel.molecule, delegateObject, false) { + molecule?.set(with: castModel.molecule, delegateObject, additionalData) + } else if let moleculeView = MoleculeObjectMapping.shared()?.createMolecule(castModel.molecule, delegateObject: delegateObject, additionalData: additionalData) { addMolecule(moleculeView) } super.set(with: model, delegateObject, additionalData) @@ -31,23 +31,23 @@ import UIKit public override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { guard let moleculeModel = (model as? MoleculeListItemModel)?.molecule else { return "\(self)<>" } - let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type - let moleculeName = className?.nameForReuse(with: moleculeModel, delegateObject) ?? moleculeModel.moleculeName ?? "" + let className = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) + let moleculeName = className?.nameForReuse(with: moleculeModel, delegateObject) ?? moleculeModel.moleculeName return "\(self)<\(moleculeName)>" } - public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), - let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON) + public static func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + guard let moleculeModel = (model as? MoleculeListItemModel)?.molecule, + let theClass = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) else { return nil } - return theClass.requiredModules?(moleculeJSON, delegateObject: delegateObject, error: error) + return theClass.requiredModules(with: moleculeModel, delegateObject, error: error) } public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - guard let moleculeModel = (model as? MoleculeContainerModel)?.molecule, - let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type, + guard let moleculeModel = (model as? MoleculeListItemModel)?.molecule, + let classType = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel), let height = classType.estimatedHeight(with: moleculeModel, delegateObject) else { return 80 } diff --git a/MVMCoreUI/Molecules/Items/StackItem.swift b/MVMCoreUI/Atomic/Molecules/Items/StackItem.swift similarity index 100% rename from MVMCoreUI/Molecules/Items/StackItem.swift rename to MVMCoreUI/Atomic/Molecules/Items/StackItem.swift diff --git a/MVMCoreUI/Molecules/Items/StackItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/StackItemModel.swift similarity index 71% rename from MVMCoreUI/Molecules/Items/StackItemModel.swift rename to MVMCoreUI/Atomic/Molecules/Items/StackItemModel.swift index 795b2983..436ed9cd 100644 --- a/MVMCoreUI/Molecules/Items/StackItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/StackItemModel.swift @@ -9,19 +9,29 @@ import Foundation @objcMembers public class StackItemModel: ContainerModel, StackItemModelProtocol, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "simpleStackItem" - public var moleculeName: String? = StackItemModel.identifier + public var moleculeName: String = StackItemModel.identifier public var backgroundColor: Color? public var spacing: CGFloat? public var percent: Int? public var gone: Bool = false + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public convenience init(spacing: CGFloat? = nil, percent: Int? = nil, horizontalAlignment: UIStackView.Alignment? = nil, verticalAlignment: UIStackView.Alignment? = nil, gone: Bool? = nil) { self.init() + self.horizontalAlignment = horizontalAlignment self.verticalAlignment = verticalAlignment self.spacing = spacing self.percent = percent + if let gone = gone { self.gone = gone } diff --git a/MVMCoreUI/Molecules/Items/StackItemModelProtocol.swift b/MVMCoreUI/Atomic/Molecules/Items/StackItemModelProtocol.swift similarity index 100% rename from MVMCoreUI/Molecules/Items/StackItemModelProtocol.swift rename to MVMCoreUI/Atomic/Molecules/Items/StackItemModelProtocol.swift diff --git a/MVMCoreUI/Molecules/Items/TabsListItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/TabsListItemModel.swift similarity index 100% rename from MVMCoreUI/Molecules/Items/TabsListItemModel.swift rename to MVMCoreUI/Atomic/Molecules/Items/TabsListItemModel.swift diff --git a/MVMCoreUI/Molecules/Items/TabsTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/TabsTableViewCell.swift similarity index 77% rename from MVMCoreUI/Molecules/Items/TabsTableViewCell.swift rename to MVMCoreUI/Atomic/Molecules/Items/TabsTableViewCell.swift index da700a28..16794eb1 100644 --- a/MVMCoreUI/Molecules/Items/TabsTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/TabsTableViewCell.swift @@ -44,7 +44,8 @@ import UIKit public override func reset() { super.reset() - tabs.reset() + // TODO: Uncomment when finished with Ryan Tab pr. + //tabs.reset() } public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { @@ -54,23 +55,19 @@ import UIKit extension TabsTableViewCell: TopTabbarDelegate { public func shouldSelectItem(at index: Int, topTabbar: TopTabbar) -> Bool { - if let model = tabsListItemModel, - let json = model.toJSON(), - let json2d = json.optionalArrayForKey("molecules") as? [[[AnyHashable: Any]]] { - let molecules = json2d[topTabbar.selectedIndex] - delegateObject?.moleculeDelegate?.removeMolecules(molecules, sender: self, animation: index < tabs.selectedIndex ? .right : .left) + if let model = tabsListItemModel { + let molecules = model.molecules[topTabbar.selectedIndex] + delegateObject?.moleculeDelegate?.removeMolecules(molecules, animation: index < tabs.selectedIndex ? .right : .left) } previousTabIndex = tabs.selectedIndex return true } public func topTabbar(_ topTabbar: TopTabbar, didSelectItemAt index: Int) { - if let model = tabsListItemModel, - let json = model.toJSON(), - let json2d = json.optionalArrayForKey("molecules") as? [[[AnyHashable: Any]]] { - let molecules = json2d[index] - delegateObject?.moleculeDelegate?.addMolecules(molecules, sender: self, animation: index < previousTabIndex ? .left : .right) - } + guard let model = tabsListItemModel, + let indexPath = delegateObject?.moleculeDelegate?.getIndexPath(for: model) else { return } + let molecules = model.molecules[index] + delegateObject?.moleculeDelegate?.addMolecules(molecules, indexPath: indexPath, animation: index < previousTabIndex ? .left : .right) } } diff --git a/MVMCoreUI/Molecules/LeftRightViews/ActionDetailWithImage.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ActionDetailWithImage.swift similarity index 94% rename from MVMCoreUI/Molecules/LeftRightViews/ActionDetailWithImage.swift rename to MVMCoreUI/Atomic/Molecules/LeftRightViews/ActionDetailWithImage.swift index b550ad0d..d693f2b5 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/ActionDetailWithImage.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ActionDetailWithImage.swift @@ -45,11 +45,6 @@ import UIKit super.init(coder: aDecoder) } - public convenience init(json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - self.init() - setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - } - //------------------------------------------------------ // MARK: - View Lifecycle //------------------------------------------------------ @@ -113,7 +108,7 @@ import UIKit setDefaultState() } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 197 } diff --git a/MVMCoreUI/Molecules/LeftRightViews/ActionDetailWithImageModel.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ActionDetailWithImageModel.swift similarity index 85% rename from MVMCoreUI/Molecules/LeftRightViews/ActionDetailWithImageModel.swift rename to MVMCoreUI/Atomic/Molecules/LeftRightViews/ActionDetailWithImageModel.swift index 9dbf53c0..d617f053 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/ActionDetailWithImageModel.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ActionDetailWithImageModel.swift @@ -10,7 +10,7 @@ import Foundation public struct ActionDetailWithImageModel: MoleculeModelProtocol { public static var identifier: String = "actionDetailWithImage" - public var moleculeName: String? = ActionDetailWithImageModel.identifier + public var moleculeName: String = ActionDetailWithImageModel.identifier public var backgroundColor: Color? public var headlineBodyButton: HeadlineBodyButtonModel public var image: ImageViewModel diff --git a/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift similarity index 95% rename from MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift rename to MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift index bbdda6af..c0018e9b 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift @@ -140,19 +140,14 @@ import UIKit width.isActive = true } - // MARK: - MVMCoreUIMoleculeViewProtocol - public override func setAsMolecule() { - super.setAsMolecule() - styleDefault() - } - + // MARK: - MoleculeViewProtocol public override func reset() { super.reset() styleDefault() spaceAboveMolecule = 6.0 spaceBelowMolecule = 6.0 - (middleView as? MVMCoreUIMoleculeViewProtocol)?.reset?() + (middleView as? MoleculeViewProtocol)?.reset() } func styleDefault() { @@ -170,8 +165,8 @@ import UIKit super.set(with: model, delegateObject, additionalData) guard let model = model as? CornerLabelsModel else { return } if middleView != nil { - (middleView as? ModelMoleculeViewProtocol)?.set(with: model, delegateObject, additionalData) - } else if let moleculeModel = model.molecule, let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(moleculeModel, delegateObject) { + (middleView as? MoleculeViewProtocol)?.set(with: model, delegateObject, additionalData) + } else if let moleculeModel = model.molecule, let molecule = MoleculeObjectMapping.shared()?.createMolecule(moleculeModel, delegateObject: delegateObject, additionalData: additionalData) { addMiddleView(molecule) } diff --git a/MVMCoreUI/Molecules/LeftRightViews/CornerLabelsModel.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabelsModel.swift similarity index 100% rename from MVMCoreUI/Molecules/LeftRightViews/CornerLabelsModel.swift rename to MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabelsModel.swift diff --git a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggle.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggle.swift similarity index 97% rename from MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggle.swift rename to MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggle.swift index bfed52e4..0d1f0089 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggle.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggle.swift @@ -37,7 +37,7 @@ import UIKit toggle.reset() } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? HeadlineBodyLinkToggleModel else { return } diff --git a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggleModel.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggleModel.swift similarity index 88% rename from MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggleModel.swift rename to MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggleModel.swift index aad8ec06..917bf5dc 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggleModel.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyLinkToggleModel.swift @@ -9,7 +9,7 @@ import Foundation public struct HeadlineBodyLinkToggleModel: MoleculeModelProtocol { public static var identifier: String = "headlineBodyLinkToggle" - public var moleculeName: String? = HeadlineBodyLinkToggleModel.identifier + public var moleculeName: String = HeadlineBodyLinkToggleModel.identifier public var backgroundColor: Color? public var headlineBodyLink: HeadlineBodyLinkModel public var toggle: ToggleModel diff --git a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggle.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggle.swift similarity index 86% rename from MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggle.swift rename to MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggle.swift index bbfe0ffc..138d58ed 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggle.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggle.swift @@ -34,13 +34,12 @@ import UIKit NSLayoutConstraint.pinViews(leftView: headlineBody, rightView: toggle, alignTop: false) } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - guard let headlineBodyToggleModel = model as? HeadlineBodyToggleModel else { - return - } - setWithJSON(headlineBodyToggleModel.toJSON(), delegateObject: delegateObject, additionalData: additionalData) + guard let headlineBodyToggleModel = model as? HeadlineBodyToggleModel else { return } + headlineBody.set(with: headlineBodyToggleModel.headlineBody, delegateObject, additionalData) + toggle.set(with: headlineBodyToggleModel.toggle, delegateObject, additionalData) } open class override func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { @@ -50,7 +49,7 @@ import UIKit return max(toggleHeight, headlineBody) } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func reset() { super.reset() headlineBody.reset() diff --git a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggleModel.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggleModel.swift similarity index 88% rename from MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggleModel.swift rename to MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggleModel.swift index bb3391a6..800c976b 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggleModel.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/HeadlineBodyToggleModel.swift @@ -11,7 +11,7 @@ import Foundation open class HeadlineBodyToggleModel: MoleculeModelProtocol { public static var identifier: String = "headlineBodyToggle" - public var moleculeName: String? = HeadlineBodyToggleModel.identifier + public var moleculeName: String = HeadlineBodyToggleModel.identifier open var backgroundColor: Color? open var headlineBody: HeadlineBodyModel open var toggle: ToggleModel diff --git a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift similarity index 95% rename from MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift rename to MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift index ccbdf9c3..9ce24d3b 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift @@ -31,7 +31,7 @@ import UIKit NSLayoutConstraint.pinViews(leftView: label, rightView: toggle, alignTop: false) } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { guard let model = model as? LabelToggleModel, let toggleHeight = Toggle.estimatedHeight(with: model.toggle, delegateObject), @@ -47,7 +47,7 @@ import UIKit toggle.set(with: labelToggleModel.toggle, delegateObject, additionalData) } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func reset() { super.reset() label.reset() diff --git a/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggleModel.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggleModel.swift new file mode 100644 index 00000000..29e7a609 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggleModel.swift @@ -0,0 +1,44 @@ +// +// LabelToggle.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 1/15/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class LabelToggleModel: MoleculeModelProtocol { + public static var identifier: String = "labelToggle" + public var moleculeName: String = LabelToggleModel.identifier + public var backgroundColor: Color? + public var label: LabelModel + public var toggle: ToggleModel + + init(_ label: LabelModel, _ toggle: ToggleModel) { + self.label = label + self.toggle = toggle + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case label + case toggle + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey:.backgroundColor) + label = try typeContainer.decode(LabelModel.self, forKey:.label) + toggle = try typeContainer.decode(ToggleModel.self, forKey:.toggle) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(label, forKey: .label) + try container.encode(toggle, forKey: .toggle) + } +} diff --git a/MVMCoreUI/Molecules/ModuleMolecule.swift b/MVMCoreUI/Atomic/Molecules/ModuleMolecule.swift similarity index 84% rename from MVMCoreUI/Molecules/ModuleMolecule.swift rename to MVMCoreUI/Atomic/Molecules/ModuleMolecule.swift index e92f41da..7374b067 100644 --- a/MVMCoreUI/Molecules/ModuleMolecule.swift +++ b/MVMCoreUI/Atomic/Molecules/ModuleMolecule.swift @@ -10,7 +10,7 @@ import UIKit open class ModuleMolecule: Container { - open var moduleMolecule: (UIView & MVMCoreUIMoleculeViewProtocol & ModelMoleculeViewProtocol)? + open var moduleMolecule: MoleculeViewProtocol? var moduleMoleculeModel: ModuleMoleculeModel? { get { return model as? ModuleMoleculeModel } } @@ -29,10 +29,10 @@ open class ModuleMolecule: Container { } if moduleMolecule == nil { - if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(moduleModel, delegateObject, false) { + if let moleculeView = MoleculeObjectMapping.shared()?.createMolecule(moduleModel, delegateObject: delegateObject, additionalData: additionalData) { addSubview(moleculeView) NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: false).values)) - moduleMolecule = moleculeView as? (UIView & MVMCoreUIMoleculeViewProtocol & ModelMoleculeViewProtocol) + moduleMolecule = moleculeView isAccessibilityElement = false if moleculeView.accessibilityElements != nil { @@ -50,7 +50,7 @@ open class ModuleMolecule: Container { guard let moduleMolecule = model as? ModuleMoleculeModel, let moduleModel = delegateObject?.moleculeDelegate?.getModuleWithName(moduleMolecule.moduleName), - let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moduleModel) as? ModelMoleculeViewProtocol.Type, + let classType = MoleculeObjectMapping.shared()?.getMoleculeClass(moduleModel), let height = classType.estimatedHeight(with: moduleModel, delegateObject) else { // Critical error return 0 @@ -61,7 +61,7 @@ open class ModuleMolecule: Container { public override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { guard let moduleMolecule = model as? ModuleMoleculeModel, let moduleModel = delegateObject?.moleculeDelegate?.getModuleWithName(moduleMolecule.moduleName), - let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moduleModel) as? ModelMoleculeViewProtocol.Type, + let classType = MoleculeObjectMapping.shared()?.getMoleculeClass(moduleModel), let name = classType.nameForReuse(with: moduleModel, delegateObject) else { // Critical error return "moduleMolecule<>" diff --git a/MVMCoreUI/Molecules/ModuleMoleculeModel.swift b/MVMCoreUI/Atomic/Molecules/ModuleMoleculeModel.swift similarity index 100% rename from MVMCoreUI/Molecules/ModuleMoleculeModel.swift rename to MVMCoreUI/Atomic/Molecules/ModuleMoleculeModel.swift diff --git a/MVMCoreUI/Molecules/MoleculeHeaderModel.swift b/MVMCoreUI/Atomic/Molecules/MoleculeHeaderModel.swift similarity index 100% rename from MVMCoreUI/Molecules/MoleculeHeaderModel.swift rename to MVMCoreUI/Atomic/Molecules/MoleculeHeaderModel.swift diff --git a/MVMCoreUI/Molecules/MoleculeHeaderView.swift b/MVMCoreUI/Atomic/Molecules/MoleculeHeaderView.swift similarity index 71% rename from MVMCoreUI/Molecules/MoleculeHeaderView.swift rename to MVMCoreUI/Atomic/Molecules/MoleculeHeaderView.swift index 3404afc3..bff5a14f 100644 --- a/MVMCoreUI/Molecules/MoleculeHeaderView.swift +++ b/MVMCoreUI/Atomic/Molecules/MoleculeHeaderView.swift @@ -30,13 +30,13 @@ public class MoleculeHeaderView: MoleculeContainer { NSLayoutConstraint.pinViewRight(toSuperview: line, useMargins: true, constant: 0).isActive = true } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func reset() { super.reset() line.setStyle(.heavy) } - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let headerModel = headerModel else { return } @@ -44,11 +44,4 @@ public class MoleculeHeaderView: MoleculeContainer { line.set(with: lineModel, delegateObject, additionalData) } } - - public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - if let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) { - return height + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing - } - return 121 - } } diff --git a/MVMCoreUI/Atomic/Molecules/NavigationItemModelProtocol.swift b/MVMCoreUI/Atomic/Molecules/NavigationItemModelProtocol.swift new file mode 100644 index 00000000..d7df7cc9 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/NavigationItemModelProtocol.swift @@ -0,0 +1,125 @@ +// +// NavigationItemModelProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/12/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol NavigationItemModelProtocol { + var title: String? { get set } + var titleView: MoleculeModelProtocol? { get set } + var hidden: Bool { get set } + var backgroundColor: Color? { get set } + var transparent: Bool { get set } + var tintColor: Color { get set } + var line: LineModel? { get set } + var systemBackButton: Bool { get set } + var showLeftPanelButton: Bool? { get set } + var showRightPanelButton: Bool? { get set } + var additionalLeftItems: [NavigationItemButtonModel]? { get set } + var additionalRightItems: [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 var identifier: String { + return "navigationItem" + } + + public var title: String? + public var titleView: MoleculeModelProtocol? + public var hidden: Bool + public var backgroundColor: Color? + public var transparent: Bool + public var tintColor: Color + public var line: LineModel? + public var systemBackButton = false + public var showLeftPanelButton: Bool? + public var showRightPanelButton: Bool? + public var additionalLeftItems: [NavigationItemButtonModel]? + public var additionalRightItems: [NavigationItemButtonModel]? + + init() { + hidden = false + transparent = false + backgroundColor = Color(uiColor: .white) + tintColor = Color(uiColor: .black) + line = LineModel(type: .standard) + } + + private enum CodingKeys: String, CodingKey { + case title + case titleView + case hidden + case backgroundColor + case transparent + case tintColor + case line + case systemBackButton + case showLeftPanelButton + case showRightPanelButton + case additionalLeftItems + case additionalRightItems + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + title = try typeContainer.decodeIfPresent(String.self, forKey: .title) + titleView = try typeContainer.decodeModelIfPresent(codingKey: .titleView) + hidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .hidden) ?? false + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .white) + transparent = try typeContainer.decodeIfPresent(Bool.self, forKey: .transparent) ?? false + tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black) + line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) + systemBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .systemBackButton) ?? false + showLeftPanelButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .showLeftPanelButton) + showRightPanelButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .showRightPanelButton) + additionalLeftItems = try typeContainer.decodeIfPresent([NavigationItemButtonModel].self, forKey: .additionalLeftItems) + additionalRightItems = try typeContainer.decodeIfPresent([NavigationItemButtonModel].self, forKey: .additionalRightItems) + } + + open func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(title, forKey: .title) + try container.encodeModelIfPresent(titleView, forKey: .titleView) + try container.encode(hidden, forKey: .hidden) + try container.encode(backgroundColor, forKey: .backgroundColor) + try container.encode(transparent, forKey: .transparent) + try container.encode(tintColor, forKey: .tintColor) + try container.encodeIfPresent(line, forKey: .line) + try container.encode(systemBackButton, forKey: .systemBackButton) + try container.encode(showLeftPanelButton, forKey: .showLeftPanelButton) + try container.encode(showRightPanelButton, forKey: .showRightPanelButton) + try container.encodeIfPresent(additionalLeftItems, forKey: .additionalLeftItems) + try container.encodeIfPresent(additionalRightItems, forKey: .additionalRightItems) + } +} diff --git a/MVMCoreUI/Molecules/Scroller.swift b/MVMCoreUI/Atomic/Molecules/Scroller.swift similarity index 85% rename from MVMCoreUI/Molecules/Scroller.swift rename to MVMCoreUI/Atomic/Molecules/Scroller.swift index 6184f3d0..56c33df7 100644 --- a/MVMCoreUI/Molecules/Scroller.swift +++ b/MVMCoreUI/Atomic/Molecules/Scroller.swift @@ -32,9 +32,9 @@ import UIKit public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { if let casteModel = model as? ScrollerModel { if view != nil { - (view as? ModelMoleculeViewProtocol)?.set(with: casteModel.molecule, delegateObject, additionalData) + (view as? MoleculeViewProtocol)?.set(with: casteModel.molecule, delegateObject, additionalData) } else { - if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(casteModel.molecule, delegateObject) { + if let molecule = MoleculeObjectMapping.shared()?.createMolecule(casteModel.molecule, delegateObject: delegateObject, additionalData: additionalData) { contentView.addSubview(molecule) molecule.translatesAutoresizingMaskIntoConstraints = false containerHelper.constrainView(molecule) diff --git a/MVMCoreUI/Molecules/ScrollerModel.swift b/MVMCoreUI/Atomic/Molecules/ScrollerModel.swift similarity index 83% rename from MVMCoreUI/Molecules/ScrollerModel.swift rename to MVMCoreUI/Atomic/Molecules/ScrollerModel.swift index 91dc2e1c..74256e99 100644 --- a/MVMCoreUI/Molecules/ScrollerModel.swift +++ b/MVMCoreUI/Atomic/Molecules/ScrollerModel.swift @@ -10,6 +10,6 @@ import UIKit public class ScrollerModel: MoleculeContainerModel, MoleculeModelProtocol { public static var identifier: String = "scroller" - public var moleculeName: String? = ScrollerModel.identifier + public var moleculeName: String = ScrollerModel.identifier public var backgroundColor: Color? } diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/BGImageHeadlineBodyButton.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/BGImageHeadlineBodyButton.swift new file mode 100644 index 00000000..f1ec076c --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/BGImageHeadlineBodyButton.swift @@ -0,0 +1,90 @@ +// +// BGImageHeadlineBodyButton.swift +// MVMCoreUI +// +// Created by Khan, Arshad on 02/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class BGImageHeadlineBodyButton: Container { + let headlineBody = HeadlineBody(frame: .zero) + let button = PillButton(frame: .zero) + let backgroundImageView = MFLoadImageView(pinnedEdges: .all) + let maxWidth: CGFloat = 350.0 + static let heightConstant: CGFloat = 320.0 + var heightConstraint: NSLayoutConstraint? + + // MARK: - MVMCoreViewProtocol + open override func updateView(_ size: CGFloat) { + super.updateView(size) + headlineBody.updateView(size) + button.updateView(size) + backgroundImageView.updateView(size) + backgroundImageView.alignFillHorizontal() + backgroundImageView.alignFillVertical() + } + + open override func setupView() { + super.setupView() + heightConstraint = heightAnchor.constraint(equalToConstant: Self.heightConstant) + heightConstraint?.isActive = true + + backgroundImageView.addSizeConstraintsForAspectRatio = true + + let container = MVMCoreUICommonViewsUtility.commonView() + addAndContain(container) + + container.addSubview(headlineBody) + container.addSubview(button) + + //Headline view + headlineBody.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 0).isActive = true + headlineBody.topAnchor.constraint(equalTo: container.topAnchor, constant: 0).isActive = true + + let headLineBodyWidth = headlineBody.widthAnchor.constraint(equalTo: container.widthAnchor, multiplier: 0.67) + headLineBodyWidth.priority = UILayoutPriority(rawValue: 999) + headLineBodyWidth.isActive = true + headlineBody.widthAnchor.constraint(lessThanOrEqualToConstant: maxWidth).isActive = true + + //Caret view + button.translatesAutoresizingMaskIntoConstraints = false + button.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 0).isActive = true + container.bottomAnchor.constraint(greaterThanOrEqualTo: button.bottomAnchor, constant: 0).isActive = true + + button.topAnchor.constraint(equalTo: headlineBody.bottomAnchor, constant: PaddingDefault).isActive = true + + //Background image view + backgroundImageView.translatesAutoresizingMaskIntoConstraints = false + backgroundImageView.imageView.contentMode = .scaleAspectFill + backgroundImageView.alignFillHorizontal() + backgroundImageView.alignFillVertical() + addSubview(backgroundImageView) + NSLayoutConstraint.constraintPinSubview(toSuperview: backgroundImageView) + sendSubviewToBack(backgroundImageView) + } + + // MARK: - MoleculeViewProtocol + open override func reset() { + super.reset() + headlineBody.reset() + backgroundImageView.reset() + } + + // MARK:- MoleculeViewProtocol + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 320 + } + + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? BGImageHeadlineBodyButtonModel else { return } + headlineBody.set(with: model.headlineBody, delegateObject, additionalData) + button.setOptional(with: model.button, delegateObject, additionalData) + button.isHidden = model.button == nil + backgroundImageView.set(with: model.image, delegateObject, additionalData) + backgroundImageView.alignFillHorizontal() + backgroundImageView.alignFillVertical() + } +} diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/BGImageHeadlineBodyButtonModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/BGImageHeadlineBodyButtonModel.swift new file mode 100644 index 00000000..650e4875 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/BGImageHeadlineBodyButtonModel.swift @@ -0,0 +1,75 @@ +// +// BGImageHeadlineBodyButtonModel.swift +// MVMCoreUI +// +// Created by Khan, Arshad on 02/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +import UIKit + +public class BGImageHeadlineBodyButtonModel: ContainerModel, MoleculeModelProtocol { + public static var identifier: String = "bgImageHeadlineBodyButton" + public var backgroundColor: Color? + public var button: ButtonModel? + public var headlineBody: HeadlineBodyModel + public var image: ImageViewModel + + init(headlineBody: HeadlineBodyModel, image: ImageViewModel) { + self.headlineBody = headlineBody + self.image = image + super.init() + setDefaults() + } + + /// Defaults to set + func setDefaults() { + if useHorizontalMargins == nil { + useHorizontalMargins = true + } + if useVerticalMargins == nil { + useVerticalMargins = true + } + if topMarginPadding == nil { + topMarginPadding = PaddingDefault + } + if bottomMarginPadding == nil { + bottomMarginPadding = PaddingDefault + } + if image.height == nil { + image.height = BGImageHeadlineBodyButton.heightConstant + } + button?.size = .tiny + button?.style = .secondary + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case headlineBody + case image + case button + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + headlineBody = try typeContainer.decode(HeadlineBodyModel.self, forKey: .headlineBody) + image = try typeContainer.decode(ImageViewModel.self, forKey: .image) + button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button) + try super.init(from: decoder) + setDefaults() + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(headlineBody, forKey: .headlineBody) + try container.encode(image, forKey: .image) + try container.encodeIfPresent(button, forKey: .button) + } +} + diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift similarity index 83% rename from MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift index 49b08b44..82f83530 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift @@ -19,7 +19,7 @@ import UIKit public let body = Label.commonLabelB2(true) public let link = Link() - var casteModel: EyebrowHeadlineBodyLinkModel? { + var castModel: EyebrowHeadlineBodyLinkModel? { get { return model as? EyebrowHeadlineBodyLinkModel } } @@ -41,7 +41,7 @@ import UIKit } //-------------------------------------------------- - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //-------------------------------------------------- open override func reset() { @@ -53,16 +53,16 @@ import UIKit } //-------------------------------------------------- - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //-------------------------------------------------- open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - eyebrow.setOptional(with: casteModel?.eyebrow, delegateObject, additionalData) - headline.setOptional(with: casteModel?.headline, delegateObject, additionalData) - body.setOptional(with: casteModel?.body, delegateObject, additionalData) - link.setOptional(with: casteModel?.link, delegateObject, additionalData) + eyebrow.setOptional(with: castModel?.eyebrow, delegateObject, additionalData) + headline.setOptional(with: castModel?.headline, delegateObject, additionalData) + body.setOptional(with: castModel?.body, delegateObject, additionalData) + link.setOptional(with: castModel?.link, delegateObject, additionalData) // Hide labels if neeeded. stack.stackModel?.molecules[0].gone = !eyebrow.hasText diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLinkModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLinkModel.swift similarity index 97% rename from MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLinkModel.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLinkModel.swift index e277f5de..0f158c2c 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLinkModel.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLinkModel.swift @@ -14,7 +14,7 @@ public class EyebrowHeadlineBodyLinkModel: MoleculeModelProtocol { //-------------------------------------------------- public static var identifier: String = "eyebrowHeadlineBodyLink" - public var moleculeName: String? = EyebrowHeadlineBodyLinkModel.identifier + public var moleculeName: String = EyebrowHeadlineBodyLinkModel.identifier public var backgroundColor: Color? public var eyebrow: LabelModel? public var headline: LabelModel? diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadLineBodyCaretLinkImage.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadLineBodyCaretLinkImage.swift similarity index 96% rename from MVMCoreUI/Molecules/VerticalCombinationViews/HeadLineBodyCaretLinkImage.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadLineBodyCaretLinkImage.swift index 00ccaeeb..8d4dba6d 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadLineBodyCaretLinkImage.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadLineBodyCaretLinkImage.swift @@ -27,12 +27,11 @@ import Foundation open override func setupView() { super.setupView() - guard subviews.count == 0 else { - return - } heightConstraint = heightAnchor.constraint(equalToConstant: Self.heightConstant) heightConstraint?.isActive = true + backgroundImageView.addSizeConstraintsForAspectRatio = true + let container = MVMCoreUICommonViewsUtility.commonView() addAndContain(container) @@ -65,14 +64,14 @@ import Foundation sendSubviewToBack(backgroundImageView) } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func reset() { super.reset() headlineBody.reset() backgroundImageView.reset() } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 320 } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift similarity index 77% rename from MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift index a120807d..b36525f9 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBody.swift @@ -9,9 +9,10 @@ import UIKit open class HeadlineBody: View { + let headlineLabel = Label.commonLabelH2(true) let messageLabel = Label.commonLabelB2(true) - var spaceBetweenLabelsConstant = PaddingTwo + var spaceBetweenLabelsConstant = PaddingOne var spaceBetweenLabels: NSLayoutConstraint? var leftConstraintTitle: NSLayoutConstraint? var rightConstraintTitle: NSLayoutConstraint? @@ -23,15 +24,15 @@ open class HeadlineBody: View { } // MARK: - Styling - func style(with styleString: String?) { - guard let styleString = styleString else { return } - - switch styleString { - case "header": + func style(with style: HeadlineBodyModel.Style?) { + switch style { + case .landingHeader: + styleLandingPageHeader() + case .header: stylePageHeader() - case "item": + case .item: styleListItem() - case "itemHeader": + case .itemHeader: styleListItemDivider() default: break } @@ -71,8 +72,6 @@ open class HeadlineBody: View { open override func setupView() { super.setupView() - - guard subviews.isEmpty else { return } backgroundColor = .clear clipsToBounds = true @@ -81,6 +80,10 @@ open class HeadlineBody: View { addSubview(view) NSLayoutConstraint.constraintPinSubview(toSuperview: view) + view.isAccessibilityElement = false + view.shouldGroupAccessibilityChildren = true + view.accessibilityElements = [headlineLabel, messageLabel] + view.addSubview(headlineLabel) view.addSubview(messageLabel) @@ -121,7 +124,7 @@ open class HeadlineBody: View { } //-------------------------------------------------- - // MARK: - ModelMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //-------------------------------------------------- public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { @@ -139,28 +142,8 @@ open class HeadlineBody: View { messageLabel.setOptional(with: headlineBodyModel.body, delegateObject, additionalData) } - //-------------------------------------------------- - // MARK: - MVMCoreUIMoleculeViewProtocol - //-------------------------------------------------- - - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - style(with: json?.optionalStringForKey("style")) - - let headlineJSON = json?.optionalDictionaryForKey("headline") - headlineLabel.setWithJSON(headlineJSON, delegateObject: delegateObject, additionalData: additionalData) - - let bodyJSON = json?.optionalDictionaryForKey("body") - messageLabel.setWithJSON(bodyJSON, delegateObject: delegateObject, additionalData: additionalData) - } - open override func reset() { super.reset() stylePageHeader() } - - public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return 58 - } } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyButton.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyButton.swift similarity index 90% rename from MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyButton.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyButton.swift index 90183e6e..35522146 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyButton.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyButton.swift @@ -15,7 +15,7 @@ import UIKit //------------------------------------------------------ let headlineBody = HeadlineBody(frame: .zero) - let button = PrimaryButton.primaryTinyButton(false)! + let button = PillButton(frame: .zero) //------------------------------------------------------ // MARK: - Properties @@ -45,11 +45,6 @@ import UIKit super.init(coder: aDecoder) } - public convenience init(json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - self.init() - setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - } - //------------------------------------------------------ // MARK: - View Lifecycle //------------------------------------------------------ @@ -89,8 +84,7 @@ import UIKit headlineBody.headlineLabel.font = MFStyler.fontH3() headlineBody.messageLabel.font = MFStyler.fontB3() - button.setAsTiny(true) - button.setAsSecondaryCustom() + button.styleSecondary() button.isHidden = false buttonHeadlinePadding = PaddingTwo } @@ -107,7 +101,7 @@ import UIKit defaultState() } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 320 } diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyButtonModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyButtonModel.swift new file mode 100644 index 00000000..51d011e8 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyButtonModel.swift @@ -0,0 +1,63 @@ +// +// HeadlineBodyButtonModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/22/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class HeadlineBodyButtonModel: MoleculeModelProtocol { + public static var identifier: String = "headlineBodyButton" + public var moleculeName: String = HeadlineBodyButtonModel.identifier + public var backgroundColor: Color? + + public var headlineBody: HeadlineBodyModel + public var button: ButtonModel + public var buttonHeadlinePadding: CGFloat + + //-------------------------------------------------- + // MARK: - Method + //-------------------------------------------------- + + /// Defaults to set + public func setDefaults() { + button.size = .tiny + button.style = .secondary + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case headlineBody + case button + case buttonHeadlinePadding + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + headlineBody = try typeContainer.decode(HeadlineBodyModel.self, forKey: .headlineBody) + button = try typeContainer.decode(ButtonModel.self, forKey: .button) + buttonHeadlinePadding = try typeContainer.decode(CGFloat.self, forKey: .buttonHeadlinePadding) + setDefaults() + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(headlineBody, forKey: .headlineBody) + try container.encode(button, forKey: .button) + try container.encode(buttonHeadlinePadding, forKey: .buttonHeadlinePadding) + } +} diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift similarity index 96% rename from MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift index eb8d7c60..f30b89ff 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyCaretLinkImageModel.swift @@ -62,7 +62,7 @@ public class HeadlineBodyCaretLinkImageModel: ContainerModel, MoleculeModelProto public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(HeadlineBodyCaretLinkImageModel.identifier, forKey: .moleculeName) + try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encode(headlineBody, forKey: .headlineBody) try container.encode(image, forKey: .image) diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift similarity index 97% rename from MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift index e9bfabaa..da5014a9 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift @@ -58,7 +58,7 @@ import UIKit } } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol open override func reset() { super.reset() headlineBody.reset() @@ -66,7 +66,7 @@ import UIKit link.reset() } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? HeadlineBodyLinkModel else { return } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyLinkModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLinkModel.swift similarity index 88% rename from MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyLinkModel.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLinkModel.swift index 27f0d670..4ef4018b 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyLinkModel.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLinkModel.swift @@ -10,7 +10,7 @@ import Foundation public struct HeadlineBodyLinkModel: MoleculeModelProtocol { public static var identifier: String = "headlineBodyLink" - public var moleculeName: String? = HeadlineBodyLinkModel.identifier + public var moleculeName: String = HeadlineBodyLinkModel.identifier public var headlineBody: HeadlineBodyModel public var link: LinkModel public var backgroundColor: Color? diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift new file mode 100644 index 00000000..ebc5614e --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift @@ -0,0 +1,47 @@ +// +// HeadlineBody.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 10/3/19. +// Copyright © 2019 Suresh, Kamlesh. All rights reserved. +// + +import Foundation + + +@objcMembers open class HeadlineBodyModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "headlineBody" + public var moleculeName: String = HeadlineBodyModel.identifier + public var headline: LabelModel? + public var body: LabelModel? + public var style: Style? + public var backgroundColor: Color? + + //-------------------------------------------------- + // MARK: - Enum + //-------------------------------------------------- + + /// Convenience styles for common situations. + public enum Style: String, Codable { + case landingHeader + case header + case itemHeader + case item + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + public init(headline: LabelModel) { + self.headline = headline + } + + public init(body: LabelModel) { + self.body = body + } +} diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/Lists/NumberedList.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/NumberedList.swift similarity index 100% rename from MVMCoreUI/Molecules/VerticalCombinationViews/Lists/NumberedList.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/NumberedList.swift diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/Lists/NumberedListModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/NumberedListModel.swift similarity index 100% rename from MVMCoreUI/Molecules/VerticalCombinationViews/Lists/NumberedListModel.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/NumberedListModel.swift diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeModel.swift similarity index 100% rename from MVMCoreUI/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeModel.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeModel.swift diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeStack.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeStack.swift similarity index 82% rename from MVMCoreUI/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeStack.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeStack.swift index 48f39fa0..4ff045ce 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeStack.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeStack.swift @@ -14,8 +14,8 @@ open class StringAndMoleculeStack: MoleculeStackView { guard let model = stackModel else { return } for stackItemModel in model.molecules { guard let stringAndMoleculeModel = stackItemModel.molecule as? StringAndMoleculeModel, - let moleculeName = stringAndMoleculeModel.molecule.moleculeName, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forName: moleculeName) as? (UIView & ModelMoleculeViewProtocol) else { + let molecule = MoleculeObjectMapping.shared()?.createMolecule(stringAndMoleculeModel.molecule, delegateObject: delegateObject + , additionalData: additionalData) else { // Throw error return } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeView.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeView.swift similarity index 94% rename from MVMCoreUI/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeView.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeView.swift index 09ed8a7b..6db91703 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeView.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/StringAndMoleculeStack/StringAndMoleculeView.swift @@ -11,7 +11,7 @@ import Foundation open class StringAndMoleculeView: View { var label = Label.commonLabelB2(true) var string: String - var molecule: UIView & ModelMoleculeViewProtocol + var molecule: MoleculeViewProtocol var leftWidthConstraint: NSLayoutConstraint? @Percent var percentage: CGFloat = 5 @@ -27,7 +27,7 @@ open class StringAndMoleculeView: View { } // MARK: - Inits - public init(string: String, molecule: UIView & ModelMoleculeViewProtocol) { + public init(string: String, molecule: MoleculeViewProtocol) { self.string = string self.molecule = molecule super.init(frame: .zero) @@ -73,7 +73,7 @@ open class StringAndMoleculeView: View { override open func reset() { super.reset() label.reset() - (molecule as? MVMCoreUIMoleculeViewProtocol)?.reset?() + molecule.reset() } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/Lists/UnOrderedList.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/UnOrderedList.swift similarity index 100% rename from MVMCoreUI/Molecules/VerticalCombinationViews/Lists/UnOrderedList.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/UnOrderedList.swift diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/Lists/UnOrderedListModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/UnOrderedListModel.swift similarity index 100% rename from MVMCoreUI/Molecules/VerticalCombinationViews/Lists/UnOrderedListModel.swift rename to MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/Lists/UnOrderedListModel.swift diff --git a/MVMCoreUI/Organisms/Carousel.swift b/MVMCoreUI/Atomic/Organisms/Carousel.swift similarity index 81% rename from MVMCoreUI/Organisms/Carousel.swift rename to MVMCoreUI/Atomic/Organisms/Carousel.swift index ba675adf..820c5a5d 100644 --- a/MVMCoreUI/Organisms/Carousel.swift +++ b/MVMCoreUI/Atomic/Organisms/Carousel.swift @@ -10,7 +10,7 @@ import UIKit open class Carousel: View { - public let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + public let collectionView = CollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) /// The current index of the collection view. Includes dummy cells when looping. public var currentIndex = 0 @@ -28,11 +28,11 @@ open class Carousel: View { /// The number of pages that there are. Used for the page control and for calculations. Should not include the looping dummy cells. Be sure to set this if subclassing and not using the molecules. open var numberOfPages = 0 - /// The json for the molecules. + /// The models for the molecules. var molecules: [MoleculeModelProtocol]? /// The horizontal alignment of the cell in the collection view. Only noticeable if the itemWidthPercent is less than 100%. - var itemAlignment = UICollectionView.ScrollPosition.left + public var itemAlignment = UICollectionView.ScrollPosition.left /// From 0-1. The item width as a percent of the carousel width. public var itemWidthPercent: Float = 1 @@ -50,27 +50,25 @@ open class Carousel: View { // For adding pager public var bottomPin: NSLayoutConstraint? - // MARK: - MVMCoreViewProtocol - open override func setupView() { - super.setupView() - guard collectionView.superview == nil else { - return - } - collectionView.translatesAutoresizingMaskIntoConstraints = false - collectionView.dataSource = self - collectionView.delegate = self - collectionView.showsHorizontalScrollIndicator = false - collectionView.backgroundColor = .clear - collectionView.isAccessibilityElement = false - addSubview(collectionView) - bottomPin = NSLayoutConstraint.constraintPinSubview(toSuperview: collectionView)?[ConstraintBot] as? NSLayoutConstraint - - collectionViewHeight = collectionView.heightAnchor.constraint(equalToConstant: 300) - collectionViewHeight?.isActive = false + public var delegateObject: MVMCoreUIDelegateObject? + + private var size: CGFloat? + + // Updates the model and index. + public func updateModelIndex() { + (model as? CarouselModel)?.index = pageIndex } - open override func updateView(_ size: CGFloat) { - super.updateView(size) + open override func layoutSubviews() { + super.layoutSubviews() + // Accounts for any collection size changes + DispatchQueue.main.async { + self.layoutCollection() + } + } + + /// Invalidates the layout and ensures we are paged to the correct cell. + open func layoutCollection() { collectionView.collectionViewLayout.invalidateLayout() showPeaking(false) @@ -82,8 +80,32 @@ open class Carousel: View { } } - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MVMCoreViewProtocol + open override func setupView() { + super.setupView() + collectionView.dataSource = self + collectionView.delegate = self + addSubview(collectionView) + bottomPin = NSLayoutConstraint.constraintPinSubview(toSuperview: collectionView)?[ConstraintBot] as? NSLayoutConstraint + + collectionViewHeight = collectionView.heightAnchor.constraint(equalToConstant: 300) + collectionViewHeight?.isActive = false + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + self.size = size + + // Update cells and re-layout. + for cell in collectionView.visibleCells { + (cell as? MVMCoreViewProtocol)?.updateView(size) + } + layoutCollection() + } + + // MARK: - MoleculeViewProtocol public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + self.delegateObject = delegateObject super.set(with: model, delegateObject, additionalData) guard let carouselModel = model as? CarouselModel else { return } collectionView.backgroundColor = backgroundColor @@ -95,7 +117,9 @@ open class Carousel: View { setupLayout(with: carouselModel) prepareMolecules(with: carouselModel) itemWidthPercent = (carouselModel.itemWidthPercent ?? 100) / 100 - setAlignment(with: carouselModel.itemAlignment) + if let alignment = carouselModel.itemAlignment { + itemAlignment = alignment + } if let height = carouselModel.height { collectionViewHeight?.constant = CGFloat(height) @@ -103,6 +127,9 @@ open class Carousel: View { } setupPagingMolecule(carouselModel.pagingMolecule, delegateObject: delegateObject) + + pageIndex = carouselModel.index + pagingView?.setPage(carouselModel.index) collectionView.reloadData() } @@ -138,10 +165,10 @@ open class Carousel: View { } /// Sets up the paging molecule - open func setupPagingMolecule(_ molecule: CarouselPagingModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) { + open func setupPagingMolecule(_ molecule: (CarouselPagingModelProtocol & MoleculeModelProtocol)?, delegateObject: MVMCoreUIDelegateObject?) { var pagingView: (UIView & MVMCoreUIPagingProtocol)? = nil if let molecule = molecule { - pagingView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(molecule, delegateObject, false) as? (UIView & MVMCoreUIPagingProtocol) + pagingView = MoleculeObjectMapping.shared()?.createMolecule(molecule, delegateObject: delegateObject) as? (UIView & MVMCoreUIPagingProtocol) } addPaging(view: pagingView, position: (CGFloat(molecule?.position ?? 20))) } @@ -158,24 +185,10 @@ open class Carousel: View { // MARK: - Convenience /// Returns the (identifier, class) of the molecule for the given map. func getMoleculeInfo(with molecule: MoleculeModelProtocol, delegateObject: MVMCoreUIDelegateObject?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { - guard let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule) , - let moleculeName = (className as? ModelMoleculeViewProtocol.Type)?.nameForReuse(with: molecule, delegateObject) ?? molecule.moleculeName else { + guard let className = MoleculeObjectMapping.shared()?.getMoleculeClass(molecule) else { return nil } - return (moleculeName, className, molecule) - } - - /// Sets the alignment from the string. - open func setAlignment(with string: String?) { - switch string { - case "leading": - itemAlignment = .left - case "trailing": - itemAlignment = .right - case "center": - itemAlignment = .centeredHorizontally - default: break - } + return (className.nameForReuse(with: molecule, delegateObject) ?? molecule.moleculeName, className, molecule) } /// Adds a paging view. Centers it horizontally with the collection view. The position is the vertical distance from the center of the page view to the bottom of the collection view. @@ -206,6 +219,7 @@ open class Carousel: View { } let currentPage = pager.currentPage() localSelf.pageIndex = currentPage + localSelf.updateModelIndex() localSelf.goTo(localSelf.currentIndex, animated: !UIAccessibility.isVoiceOverRunning) }) } @@ -217,15 +231,15 @@ open class Carousel: View { // Show overlay and arrow in peaking Cell let visibleItemsPaths = collectionView.indexPathsForVisibleItems.sorted { $0.row < $1.row } if let firstItem = visibleItemsPaths.first, firstItem.row != currentIndex { - (collectionView.cellForItem(at: firstItem) as? MoleculeCollectionViewCell)?.setPeaking(true, animated: true) + (collectionView.cellForItem(at: firstItem) as? CarouselItem)?.setPeaking(true, animated: true) } if let lastItem = visibleItemsPaths.last, lastItem.row != currentIndex { - (collectionView.cellForItem(at: lastItem) as? MoleculeCollectionViewCell)?.setPeaking(true, animated: true) + (collectionView.cellForItem(at: lastItem) as? CarouselItem)?.setPeaking(true, animated: true) } } else { // Hide peaking. for item in collectionView.visibleCells { - (item as? MoleculeCollectionViewCell)?.setPeaking(false, animated: true) + (item as? CarouselItem)?.setPeaking(false, animated: true) } } } @@ -238,10 +252,12 @@ open class Carousel: View { cell.accessibilityElementsHidden = false var array = cell.accessibilityElements - if let acc = pagingView?.accessibilityElements { - array?.append(contentsOf: acc) - } else { - array?.append(pagingView!) + if let pagingView = pagingView { + if let acc = pagingView.accessibilityElements { + array?.append(contentsOf: acc) + } else { + array?.append(pagingView) + } } self.accessibilityElements = array @@ -258,7 +274,7 @@ extension Carousel: UICollectionViewDelegateFlowLayout { } open func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - (cell as? MoleculeCollectionViewCell)?.setPeaking(false, animated: false) + (cell as? CarouselItem)?.setPeaking(false, animated: false) } } @@ -273,11 +289,11 @@ extension Carousel: UICollectionViewDataSource { return UICollectionViewCell() } let cell = collectionView.dequeueReusableCell(withReuseIdentifier: moleculeInfo.identifier, for: indexPath) - if let protocolCell = cell as? MVMCoreUIMoleculeViewProtocol & ModelMoleculeViewProtocol { - protocolCell.reset?() - protocolCell.set(with: moleculeInfo.molecule, nil, nil) - protocolCell.updateView(collectionView.bounds.width) + if let protocolCell = cell as? MoleculeViewProtocol { + protocolCell.reset() + protocolCell.set(with: moleculeInfo.molecule, delegateObject, nil) } + (cell as? MVMCoreViewProtocol)?.updateView(size ?? collectionView.bounds.width) setAccessiblity(cell, index: indexPath.row) return cell } @@ -287,19 +303,18 @@ extension Carousel: UIScrollViewDelegate { func goTo(_ index: Int, animated: Bool) { showPeaking(false) - setAccessiblity(collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)), index: index) - self.currentIndex = index - self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: animated) - if let cell = collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)) { - setAccessiblity(collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)), index: index) + setAccessiblity(collectionView.cellForItem(at: IndexPath(row: currentIndex, section: 0)), index: index) + currentIndex = index + updateModelIndex() + collectionView.scrollToItem(at: IndexPath(row: currentIndex, section: 0), at: itemAlignment, animated: animated) + if let cell = collectionView.cellForItem(at: IndexPath(row: currentIndex, section: 0)) { + setAccessiblity(collectionView.cellForItem(at: IndexPath(row: currentIndex, section: 0)), index: index) UIAccessibility.post(notification: .layoutChanged, argument: cell) } } func handleUserOnBufferCell() { - guard loop else { - return - } + guard loop else { return } let lastPageIndex = numberOfPages + 1 let goToIndex = {(index: Int) in @@ -327,9 +342,11 @@ extension Carousel: UIScrollViewDelegate { let index = scrollView.contentOffset.x / (itemWidth + separatorWidth) let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1 if index < 1 { - self.currentIndex = 0 + currentIndex = 0 + updateModelIndex() } else if index > CGFloat(lastCellIndex - 1) { - self.currentIndex = lastCellIndex + currentIndex = lastCellIndex + updateModelIndex() } } @@ -339,7 +356,7 @@ extension Carousel: UIScrollViewDelegate { open func scrollViewDidScroll(_ scrollView: UIScrollView) { // Check if the user is dragging the card even further past the next card. - checkForDraggingOutOfBounds(scrollView) + //checkForDraggingOutOfBounds(scrollView) // Let the pager know our progress if needed. pagingView?.scrollViewDidScroll?(collectionView) @@ -355,9 +372,7 @@ extension Carousel: UIScrollViewDelegate { targetContentOffset.pointee = scrollView.contentOffset // This is for setting up smooth custom paging. (Since UICollectionView only handles paging based on collection view size and not cell size). - guard let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing else { - return - } + guard let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing else { return } // We switch cards if we pass the velocity threshold or position threshold (currently 50%). let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent) diff --git a/MVMCoreUI/Organisms/CarouselModel.swift b/MVMCoreUI/Atomic/Organisms/CarouselModel.swift similarity index 62% rename from MVMCoreUI/Organisms/CarouselModel.swift rename to MVMCoreUI/Atomic/Organisms/CarouselModel.swift index da12b7b8..c5f73a01 100644 --- a/MVMCoreUI/Organisms/CarouselModel.swift +++ b/MVMCoreUI/Atomic/Organisms/CarouselModel.swift @@ -12,14 +12,14 @@ import UIKit public static var identifier: String = "carousel" public var backgroundColor: Color? public var molecules: [CarouselItemModel] - + public var index: Int = 0 public var spacing: Float? public var border: Bool? public var loop: Bool? public var height: Float? public var itemWidthPercent: Float? - public var itemAlignment: String? - public var pagingMolecule: CarouselPagingModelProtocol? + public var itemAlignment: UICollectionView.ScrollPosition? + public var pagingMolecule: (CarouselPagingModelProtocol & MoleculeModelProtocol)? public init(molecules: [CarouselItemModel]){ self.molecules = molecules @@ -29,6 +29,7 @@ import UIKit case moleculeName case backgroundColor case molecules + case index case spacing case border case loop @@ -40,15 +41,16 @@ import UIKit required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - self.molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules) - self.backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - self.spacing = try typeContainer.decode(Float.self, forKey: .spacing) - self.border = try typeContainer.decode(Bool.self, forKey: .border) - self.loop = try typeContainer.decode(Bool.self, forKey: .loop) - self.height = try typeContainer.decode(Float.self, forKey: .height) - self.itemWidthPercent = try typeContainer.decode(Float.self, forKey: .itemWidthPercent) - self.itemAlignment = try typeContainer.decode(String.self, forKey: .itemAlignment) - self.pagingMolecule = try typeContainer.decodeModelIfPresent(codingKey: .pagingMolecule) + molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .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) + border = try typeContainer.decodeIfPresent(Bool.self, forKey: .border) + loop = try typeContainer.decodeIfPresent(Bool.self, forKey: .loop) + height = try typeContainer.decodeIfPresent(Float.self, forKey: .height) + itemWidthPercent = try typeContainer.decodeIfPresent(Float.self, forKey: .itemWidthPercent) + itemAlignment = try typeContainer.decodeIfPresent(UICollectionView.ScrollPosition.self, forKey: .itemAlignment) + pagingMolecule = try typeContainer.decodeModelIfPresent(codingKey: .pagingMolecule) } public func encode(to encoder: Encoder) throws { diff --git a/MVMCoreUI/Organisms/MoleculeStackModel.swift b/MVMCoreUI/Atomic/Organisms/MoleculeStackModel.swift similarity index 100% rename from MVMCoreUI/Organisms/MoleculeStackModel.swift rename to MVMCoreUI/Atomic/Organisms/MoleculeStackModel.swift diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Atomic/Organisms/MoleculeStackView.swift similarity index 89% rename from MVMCoreUI/Organisms/MoleculeStackView.swift rename to MVMCoreUI/Atomic/Organisms/MoleculeStackView.swift index 12d79dcd..0f13cde5 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Atomic/Organisms/MoleculeStackView.swift @@ -36,7 +36,7 @@ open class MoleculeStackView: Stack { override open func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { guard let stackItemModels = stackModel?.molecules else { return } for model in stackItemModels { - if let stackItem = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model, delegateObject) as? MoleculeStackItem { + if let stackItem = MoleculeObjectMapping.shared()?.createMolecule(model, delegateObject: delegateObject, additionalData: additionalData) as? MoleculeStackItem { stackItems.append(stackItem) } } diff --git a/MVMCoreUI/Organisms/Stack.swift b/MVMCoreUI/Atomic/Organisms/Stack.swift similarity index 92% rename from MVMCoreUI/Organisms/Stack.swift rename to MVMCoreUI/Atomic/Organisms/Stack.swift index 8fa220ec..e957a2a6 100644 --- a/MVMCoreUI/Organisms/Stack.swift +++ b/MVMCoreUI/Atomic/Organisms/Stack.swift @@ -15,10 +15,11 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto //-------------------------------------------------- open var contentView: UIView = MVMCoreUICommonViewsUtility.commonView() + open var stackItems: [UIView] = [] + open var stackModel: T? { get { return model as? T } } - open var stackItems: [UIView] = [] //-------------------------------------------------- // MARK: - Helpers @@ -37,15 +38,21 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto guard let stackModel = stackModel else { return } let stackItems = self.stackItems self.stackItems = [] - let lastItemIndex = stackModel.molecules.lastIndex(where: { (item) -> Bool in - return !item.gone - }) + let lastItemIndex = stackModel.molecules.lastIndex { !$0.gone } // Adds the views let totalSpace = getTotalSpace() for (index, view) in stackItems.enumerated() { addView(view, stackModel.molecules[index], totalSpacing: totalSpace, lastItem: lastItemIndex == index) } + + isAccessibilityElement = false + var accessibleViews: [Any] = [] + for (index, view) in stackItems.enumerated() where !stackModel.molecules[index].gone { + accessibleViews.append(view) + } + + accessibilityElements = accessibleViews } /// Removes all stack items views from the view. @@ -128,14 +135,14 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto } //-------------------------------------------------- - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //-------------------------------------------------- open override func reset() { super.reset() backgroundColor = .clear for item in stackItems { - (item as? MVMCoreUIMoleculeViewProtocol)?.reset?() + (item as? MoleculeViewProtocol)?.reset() } } @@ -162,13 +169,11 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto } var name = "stack<" for case let item in model.molecules { - if let moleculeName = item.moleculeName { - if let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? ModelMoleculeViewProtocol.Type, - let nameForReuse = moleculeClass.nameForReuse(with: item, delegateObject) { - name.append(nameForReuse + ",") - } else { - name.append(moleculeName + ",") - } + if let moleculeClass = MoleculeObjectMapping.shared()?.moleculeMapping[item.moleculeName], + let nameForReuse = moleculeClass.nameForReuse(with: item, delegateObject) { + name.append(nameForReuse + ",") + } else { + name.append(item.moleculeName + ",") } } name.append(">") @@ -183,7 +188,7 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto for case let item in model.molecules { if item.gone { continue } - let height = (MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(item) as? ModelMoleculeViewProtocol.Type)?.estimatedHeight(with: item, delegateObject) ?? 0 + let height = (MoleculeObjectMapping.shared()?.getMoleculeClass(item))?.estimatedHeight(with: item, delegateObject) ?? 0 if !horizontal { // Vertical stack aggregates the items let spacing = item.spacing ?? model.spacing @@ -200,7 +205,7 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto guard let model = model as? T else { return nil } var modules: [String] = [] for case let item in model.molecules { - if let modulesForMolecule = (MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(item) as? ModelMoleculeViewProtocol.Type)?.requiredModules(with: item, delegateObject, error: error) { + if let modulesForMolecule = (MoleculeObjectMapping.shared()?.getMoleculeClass(item))?.requiredModules(with: item, delegateObject, error: error) { modules += modulesForMolecule } } @@ -218,7 +223,7 @@ open class Stack: Container where T: (StackModelProtocol & MoleculeModelProto open func setStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let models = stackModel?.molecules else { return } for (index, element) in models.enumerated() { - (stackItems[index] as? ModelMoleculeViewProtocol)?.set(with: element, delegateObject, additionalData) + (stackItems[index] as? MoleculeViewProtocol)?.set(with: element, delegateObject, additionalData) } } diff --git a/MVMCoreUI/Organisms/StackModel.swift b/MVMCoreUI/Atomic/Organisms/StackModel.swift similarity index 100% rename from MVMCoreUI/Organisms/StackModel.swift rename to MVMCoreUI/Atomic/Organisms/StackModel.swift diff --git a/MVMCoreUI/Organisms/StackModelProtocol.swift b/MVMCoreUI/Atomic/Organisms/StackModelProtocol.swift similarity index 100% rename from MVMCoreUI/Organisms/StackModelProtocol.swift rename to MVMCoreUI/Atomic/Organisms/StackModelProtocol.swift diff --git a/MVMCoreUI/BaseClasses/ButtonModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/ButtonModelProtocol.swift similarity index 89% rename from MVMCoreUI/BaseClasses/ButtonModelProtocol.swift rename to MVMCoreUI/Atomic/Protocols/ModelProtocols/ButtonModelProtocol.swift index d16c2464..1288f4e3 100644 --- a/MVMCoreUI/BaseClasses/ButtonModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/ButtonModelProtocol.swift @@ -9,6 +9,5 @@ import Foundation public protocol ButtonModelProtocol: EnableableModelProtocol { - var enabled: Bool { get set } var action: ActionModelProtocol { get set } } diff --git a/MVMCoreUI/Models/ModelProtocols/CarouselItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift similarity index 100% rename from MVMCoreUI/Models/ModelProtocols/CarouselItemModelProtocol.swift rename to MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift diff --git a/MVMCoreUI/Models/ModelProtocols/CarouselPagingModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselPagingModelProtocol.swift similarity index 76% rename from MVMCoreUI/Models/ModelProtocols/CarouselPagingModelProtocol.swift rename to MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselPagingModelProtocol.swift index 94b1277a..72138290 100644 --- a/MVMCoreUI/Models/ModelProtocols/CarouselPagingModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselPagingModelProtocol.swift @@ -9,6 +9,6 @@ import Foundation -public protocol CarouselPagingModelProtocol: MoleculeModelProtocol { +public protocol CarouselPagingModelProtocol { var position: Float? { get } } diff --git a/MVMCoreUI/Models/ModelProtocols/ContainerModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/ContainerModelProtocol.swift similarity index 100% rename from MVMCoreUI/Models/ModelProtocols/ContainerModelProtocol.swift rename to MVMCoreUI/Atomic/Protocols/ModelProtocols/ContainerModelProtocol.swift diff --git a/MVMCoreUI/Models/ModelProtocols/DisableableModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/DisableableModelProtocol.swift similarity index 100% rename from MVMCoreUI/Models/ModelProtocols/DisableableModelProtocol.swift rename to MVMCoreUI/Atomic/Protocols/ModelProtocols/DisableableModelProtocol.swift diff --git a/MVMCoreUI/Models/ModelProtocols/ListItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/ListItemModelProtocol.swift similarity index 100% rename from MVMCoreUI/Models/ModelProtocols/ListItemModelProtocol.swift rename to MVMCoreUI/Atomic/Protocols/ModelProtocols/ListItemModelProtocol.swift diff --git a/MVMCoreUI/Models/ModelProtocols/MoleculeModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift similarity index 70% rename from MVMCoreUI/Models/ModelProtocols/MoleculeModelProtocol.swift rename to MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift index 2928cf2a..dda19b35 100644 --- a/MVMCoreUI/Models/ModelProtocols/MoleculeModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/MoleculeModelProtocol.swift @@ -1,14 +1,20 @@ import Foundation +public enum MolecularError: Swift.Error { + case error(String) + case countImbalance(String) +} + + public protocol MoleculeModelProtocol: ModelProtocol { - var moleculeName: String? { get } + var moleculeName: String { get } var backgroundColor: Color? { get set } } public extension MoleculeModelProtocol { - var moleculeName: String? { + var moleculeName: String { get { return Self.identifier } } diff --git a/MVMCoreUI/Models/ModelProtocols/PageModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/PageModelProtocol.swift similarity index 68% rename from MVMCoreUI/Models/ModelProtocols/PageModelProtocol.swift rename to MVMCoreUI/Atomic/Protocols/ModelProtocols/PageModelProtocol.swift index 34b58818..be4e4e65 100644 --- a/MVMCoreUI/Models/ModelProtocols/PageModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/PageModelProtocol.swift @@ -8,9 +8,9 @@ import Foundation - public protocol PageModelProtocol { var pageType: String { get set } + /// Temporary: for legacy response var screenHeading: String? { get set } - var isAtomicTabs: Bool? { get set } + var navigationItem: (NavigationItemModelProtocol & MoleculeModelProtocol)? { get set } } diff --git a/MVMCoreUI/Models/ModelProtocols/TemplateModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/TemplateModelProtocol.swift similarity index 96% rename from MVMCoreUI/Models/ModelProtocols/TemplateModelProtocol.swift rename to MVMCoreUI/Atomic/Protocols/ModelProtocols/TemplateModelProtocol.swift index a981ec64..3ed37d6b 100644 --- a/MVMCoreUI/Models/ModelProtocols/TemplateModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/TemplateModelProtocol.swift @@ -9,7 +9,7 @@ import Foundation -public protocol TemplateModelProtocol: PageModelProtocol, ModelProtocol, FormProtocol { +public protocol TemplateModelProtocol: PageModelProtocol, ModelProtocol { var template: String { get } } diff --git a/MVMCoreUI/Atomic/Protocols/MoleculeDelegateProtocol.swift b/MVMCoreUI/Atomic/Protocols/MoleculeDelegateProtocol.swift new file mode 100644 index 00000000..4f152216 --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/MoleculeDelegateProtocol.swift @@ -0,0 +1,33 @@ +// +// MoleculeDelegateProtocol.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 11/26/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol MoleculeDelegateProtocol: AnyObject { + + /// returns a module for the corresponding module name. + func getModuleWithName(_ name: String?) -> [AnyHashable : Any]? + func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? + + /// Notifies the delegate that the molecule layout update. Should be called when the layout may change due to an async method. Mainly used for list or collections. + func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) //optional + + /// Asks the delegate to add or remove molecules. Mainly used for list or collections. + func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? + func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) + func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) +} + +extension MoleculeDelegateProtocol { + + public func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {} + + public func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { return nil } + public func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) {} + public func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) {} +} diff --git a/MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift b/MVMCoreUI/Atomic/Protocols/MoleculeViewProtocol.swift similarity index 54% rename from MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift rename to MVMCoreUI/Atomic/Protocols/MoleculeViewProtocol.swift index fbeadd96..1654a34b 100644 --- a/MVMCoreUI/Molecules/ModelMoleculeViewProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/MoleculeViewProtocol.swift @@ -1,5 +1,5 @@ // -// ModelMoleculeViewProtocol.swift +// MoleculeViewProtocol.swift // MVMCoreUI // // Created by Suresh, Kamlesh on 10/24/19. @@ -8,30 +8,54 @@ import Foundation -public protocol ModelMoleculeViewProtocol { +import UIKit +import MVMCore.MVMCoreViewProtocol + +public protocol MoleculeViewProtocol: UIView { + + /// Initializes the view with the model + init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) + + /// Sets the view with the model. func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) + + /// Resets to default state before set with json is called again. + func reset() + + /// Allows the molecule to set its name for reuse. Default could be moleculeName. Mainly used for list or collections. static func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? + + /// For the molecule list to load more efficiently. Mainly used for list or collections. static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? + + /// Can return the required modules static func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? } -extension ModelMoleculeViewProtocol { +extension MoleculeViewProtocol { + /// Calls set with model + public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + self.init(frame: .zero) + set(with: model, delegateObject, additionalData) + } + + /// Uses moleculeName public static func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { return model.moleculeName } + + // Do nothing, optionals. + public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {} + public func reset() {} + public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return nil } public static func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { return nil } - // Temporary - public static func decodeJSONToModel(json: [AnyHashable: Any], type: T.Type) throws -> T where T : Decodable { - let data = try JSONSerialization.data(withJSONObject: json) - let decoder = JSONDecoder() - return try decoder.decode(type, from: data) - } + /// Temporary convenience function for helping bridge the gap for legacy code. public func setOptional(with model: T?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { if let model = model { set(with: model, delegateObject, additionalData) diff --git a/MVMCoreUI/Templates/TemplateProtocol.swift b/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift similarity index 58% rename from MVMCoreUI/Templates/TemplateProtocol.swift rename to MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift index fcb6bde2..56b1a86c 100644 --- a/MVMCoreUI/Templates/TemplateProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift @@ -9,22 +9,18 @@ import Foundation -public protocol TemplateProtocol: FormHolderProtocol { +public protocol TemplateProtocol: AnyObject { associatedtype TemplateModel: TemplateModelProtocol var templateModel: TemplateModel? { get set } } -public extension TemplateProtocol where Self: MFViewController { - func parseTemplateJSON() throws { - guard let pageJSON = self.loadObject?.pageJSON else { return } +public extension TemplateProtocol where Self: ViewController { + func parseTemplate(json: [AnyHashable: Any]?) throws { + guard let pageJSON = json else { return } let data = try JSONSerialization.data(withJSONObject: pageJSON) let decoder = JSONDecoder() let templateModel = try decoder.decode(TemplateModel.self, from: data) - - if self.formValidator == nil { - let rules = templateModel.formRules - self.formValidator = FormValidator(rules) - } self.templateModel = templateModel + self.pageModel = templateModel as? MVMControllerModelProtocol } } diff --git a/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift b/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift new file mode 100644 index 00000000..1d2f07a6 --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift @@ -0,0 +1,195 @@ +// +// CollectionTemplate.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/6/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objc open class CollectionTemplate: ThreeLayerCollectionViewController, TemplateProtocol { + public typealias TemplateModel = CollectionTemplateModel + public var templateModel: CollectionTemplateModel? + + public var moleculesInfo: [(identifier: String, class: AnyClass, molecule: (CollectionItemModelProtocol & MoleculeModelProtocol))]? + + var observer: NSKeyValueObservation? + + //-------------------------------------------------- + // MARK: - Computed Properties + //-------------------------------------------------- + open override func parsePageJSON() throws { + try parseTemplate(json: loadObject?.pageJSON) + try super.parsePageJSON() + } + + open override var loadObject: MVMCoreLoadObject? { + didSet { + guard loadObject != oldValue else { return } + + updateRequiredModules() + observer?.invalidate() + if let newObject = loadObject { + observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] object, change in + self?.updateRequiredModules() + } + } + } + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + open override func viewForTop() -> UIView? { + guard let headerModel = templateModel?.header, + let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) + else { return super.viewForTop() } + + // Temporary, Default the horizontal padding + if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil { + container.useHorizontalMargins = true + } + + return molecule + } + + override open func viewForBottom() -> UIView? { + guard let footerModel = templateModel?.footer, + let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) + else { return super.viewForBottom() } + + return molecule + } + + open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { + guard super.shouldFinishProcessingLoad(loadObject, error: error) else { return false } + + // This template requires atleast one of the three layers. + if templateModel?.header == nil, + templateModel?.molecules?.count ?? 0 == 0, + templateModel?.footer == nil, + let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), messageToLog: "Collection template requires atleast one of the following: header, footer, molecules", code: CoreUIErrorCode.ErrorCodeListMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) { + error.pointee = errorObject + return false + } + return true + } + + + open override func handleNewData() { + setup() + registerCells() + super.handleNewData() + } + + //-------------------------------------------------- + // MARK: - Collection + //-------------------------------------------------- + + open override func update(cell: UICollectionViewCell, size: CGFloat) { + super.update(cell: cell, size: size) + + // Update the width for columns. + if let collectionView = collectionView, + let columns = templateModel?.columns, columns > 0, + let cell = cell as? CollectionTemplateItemProtocol { + let width = (size - collectionView.adjustedContentInset.left - collectionView.adjustedContentInset.right) / CGFloat(columns) + cell.set(width: width) + } + } + + open override func registerCells() { + super.registerCells() + guard let moleculesInfo = moleculesInfo else { return } + + for moleculeInfo in moleculesInfo { + collectionView?.register(moleculeInfo.class, forCellWithReuseIdentifier: moleculeInfo.identifier) + } + } + + open override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return moleculesInfo?.count ?? 0 + } + + open override func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } + + open override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let moleculeInfo = moleculesInfo?[indexPath.row] + else { return UICollectionViewCell() } + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: moleculeInfo.identifier, for: indexPath) + (cell as? MoleculeViewProtocol)?.reset() + (cell as? MoleculeViewProtocol)?.set(with: moleculeInfo.molecule, delegateObjectIVar, nil) + update(cell: cell, size: view.frame.width) + // Neded to fix an apple defect where the cell is not the correct size on certain devices for certain cells + cell.layoutIfNeeded() + return cell + } + + public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + (collectionView.cellForItem(at: indexPath) as? CollectionTemplateItemProtocol)?.didSelectCell(at: indexPath, delegateObject: delegateObjectIVar, additionalData: nil) + } + + public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + (cell as? CollectionTemplateItemProtocol)?.willDisplay() + } + + //-------------------------------------------------- + // MARK: - Convenience + //-------------------------------------------------- + + /// Returns the (identifier, class) of the molecule for the given map. + open func getMoleculeInfo(with item: (CollectionItemModelProtocol & MoleculeModelProtocol)?) -> (identifier: String, class: AnyClass, molecule: CollectionItemModelProtocol & MoleculeModelProtocol)? { + guard let item = item, + let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(item) else { return nil } + let moleculeName = moleculeClass.nameForReuse(with: item, delegateObjectIVar) ?? item.moleculeName + return (moleculeName, moleculeClass, item) + } + + /// Sets up the molecule list and ensures no errors loading all content. + open func getMoleculeInfoList() -> [(identifier: String, class: AnyClass, molecule: (CollectionItemModelProtocol & MoleculeModelProtocol))]? { + + var moleculeList: [(identifier: String, class: AnyClass, molecule: CollectionItemModelProtocol & MoleculeModelProtocol)] = [] + + if let molecules = templateModel?.molecules { + for molecule in molecules { + if let info = getMoleculeInfo(with: molecule) { + moleculeList.append(info) + } + } + } + + return moleculeList.count > 0 ? moleculeList : nil + } + + /// Sets up the header, footer, molecule list and ensures no errors loading all content. + open func setup() { + moleculesInfo = getMoleculeInfoList() + } + + /// Adds modules from requiredModules() to the MVMCoreViewControllerMapping.requiredModules map. + open func updateRequiredModules() { + if let requiredModules = requiredModules(), let pageType = pageType { + MVMCoreViewControllerMappingObject.shared()?.addRequiredModules(toMapping: requiredModules, forPageType: pageType) + } + } + + /// Gets modules required by the loadObject.pageJSON. + open func requiredModules() -> [Any]? { + var modules: [String]? = [] + var errors: [MVMCoreErrorObject]? = nil + MoleculeObjectMapping.addRequiredModules(for: templateModel?.header, delegateObjectIVar, moduleList: &modules, errorList: &errors) + MoleculeObjectMapping.addRequiredModules(for: templateModel?.footer, delegateObjectIVar, moduleList: &modules, errorList: &errors) + + if let molecules = templateModel?.molecules { + for molecule in molecules { + MoleculeObjectMapping.addRequiredModules(for: molecule, delegateObjectIVar, moduleList: &modules, errorList: &errors) + } + } + + return modules + } +} diff --git a/MVMCoreUI/Atomic/Templates/CollectionTemplateItemProtocol.swift b/MVMCoreUI/Atomic/Templates/CollectionTemplateItemProtocol.swift new file mode 100644 index 00000000..55db2c6d --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/CollectionTemplateItemProtocol.swift @@ -0,0 +1,29 @@ +// +// CollectionTemplateItemProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/9/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +/// A protocol that items of the CollectionTemplate must conform to. +public protocol CollectionTemplateItemProtocol: UICollectionViewCell { + + /// Set the width of the item. Used for the columns functionality + func set(width: CGFloat) + + /// Handle action when cell is pressed + func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) + + /// Called when the cell will display. + func willDisplay() +} + +// Default implementation does nothing +extension CollectionTemplateItemProtocol { + public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {} + + public func willDisplay() {} +} diff --git a/MVMCoreUI/Atomic/Templates/CollectionTemplateModel.swift b/MVMCoreUI/Atomic/Templates/CollectionTemplateModel.swift new file mode 100644 index 00000000..56c56456 --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/CollectionTemplateModel.swift @@ -0,0 +1,67 @@ +// +// CollectionTemplateModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/6/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class CollectionTemplateModel: TemplateModel { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public override class var identifier: String { + return "collection" + } + public var header: MoleculeModelProtocol? + public var molecules: [CollectionItemModelProtocol & MoleculeModelProtocol]? + public var footer: MoleculeModelProtocol? + public var columns: Int? + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(pageType: String, screenHeading: String?, molecules: [CollectionItemModelProtocol & MoleculeModelProtocol]) { + super.init(pageType: pageType) + self.screenHeading = screenHeading + self.molecules = molecules + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case molecules + case header + case footer + case columns + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + molecules = try typeContainer.decodeModelsIfPresent(codingKey: .molecules) + header = try typeContainer.decodeModelIfPresent(codingKey: .header) + footer = try typeContainer.decodeModelIfPresent(codingKey: .footer) + columns = try typeContainer.decodeIfPresent(Int.self, forKey: .columns) + 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.encodeModelsIfPresent(molecules, forKey: .molecules) + try container.encodeModelIfPresent(header, forKey: .header) + try container.encodeModelIfPresent(footer, forKey: .footer) + try container.encodeIfPresent(columns, forKey: .columns) + } +} + diff --git a/MVMCoreUI/Templates/ListPageTemplateModel.swift b/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift similarity index 64% rename from MVMCoreUI/Templates/ListPageTemplateModel.swift rename to MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift index 12c300f9..d2418cbc 100644 --- a/MVMCoreUI/Templates/ListPageTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift @@ -8,21 +8,14 @@ import Foundation -@objcMembers public class ListPageTemplateModel: TemplateModelProtocol { - - public var formRules: [FormGroupRule]? - public var formValidator: FormValidator? - +@objcMembers public class ListPageTemplateModel: TemplateModel { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - public static var identifier: String = "list" - - public var pageType: String - public var screenHeading: String? - public var isAtomicTabs: Bool? - + public override class var identifier: String { + return "list" + } public var header: MoleculeModelProtocol? public var molecules: [ListItemModelProtocol & MoleculeModelProtocol]? public var footer: MoleculeModelProtocol? @@ -33,7 +26,7 @@ import Foundation //-------------------------------------------------- public init(pageType: String, screenHeading: String?, molecules: [ListItemModelProtocol & MoleculeModelProtocol]) { - self.pageType = pageType + super.init(pageType: pageType) self.screenHeading = screenHeading self.molecules = molecules } @@ -43,15 +36,10 @@ import Foundation //-------------------------------------------------- private enum CodingKeys: String, CodingKey { - case moleculeName - case pageType - case screenHeading case molecules case header case footer case line - case isAtomicTabs - case formRules } //-------------------------------------------------- @@ -60,26 +48,20 @@ import Foundation required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - pageType = try typeContainer.decode(String.self, forKey: .pageType) - screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading) molecules = try typeContainer.decodeModelsIfPresent(codingKey: .molecules) - isAtomicTabs = try typeContainer.decodeIfPresent(Bool.self, forKey: .isAtomicTabs) header = try typeContainer.decodeModelIfPresent(codingKey: .header) footer = try typeContainer.decodeModelIfPresent(codingKey: .footer) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) - formRules = try typeContainer.decodeIfPresent([FormGroupRule].self, forKey: .formRules) + try super.init(from: decoder) } - public func encode(to encoder: Encoder) throws { + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(pageType, forKey: .pageType) - try container.encodeIfPresent(screenHeading, forKey: .screenHeading) try container.encodeModelsIfPresent(molecules, forKey: .molecules) - try container.encodeIfPresent(isAtomicTabs, forKey: .isAtomicTabs) try container.encodeModelIfPresent(header, forKey: .header) try container.encodeModelIfPresent(footer, forKey: .footer) try container.encode(line, forKey: .line) - try container.encodeIfPresent(formRules, forKey: .formRules) } } diff --git a/MVMCoreUI/Templates/ModalMoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift similarity index 69% rename from MVMCoreUI/Templates/ModalMoleculeListTemplate.swift rename to MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift index 3ad13f31..eda35d6b 100644 --- a/MVMCoreUI/Templates/ModalMoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift @@ -12,10 +12,10 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate { public var closeButton: MFCustomButton? - override open func newDataBuildScreen() { - super.newDataBuildScreen() - closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: { [weak self] _ in - self?.dismiss() + override open func handleNewData() { + super.handleNewData() + closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: { _ in + MVMCoreNavigationHandler.shared()?.removeCurrentViewController() }, verticalCentered: false) } } diff --git a/MVMCoreUI/Templates/ModalMoleculeStackTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift similarity index 65% rename from MVMCoreUI/Templates/ModalMoleculeStackTemplate.swift rename to MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift index 9f1ea9b9..b50400a7 100644 --- a/MVMCoreUI/Templates/ModalMoleculeStackTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift @@ -10,10 +10,12 @@ import UIKit open class ModalMoleculeStackTemplate: MoleculeStackTemplate { - override open func newDataBuildScreen() { - super.newDataBuildScreen() + override open func handleNewData() { + super.handleNewData() MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: {[weak self] _ in - self?.dismiss() + if let _ = self { + MVMCoreNavigationHandler.shared()?.removeCurrentViewController() + } }, verticalCentered: false) } diff --git a/MVMCoreUI/Templates/MoleculeListCellProtocol.swift b/MVMCoreUI/Atomic/Templates/MoleculeListCellProtocol.swift similarity index 94% rename from MVMCoreUI/Templates/MoleculeListCellProtocol.swift rename to MVMCoreUI/Atomic/Templates/MoleculeListCellProtocol.swift index 5d9b9525..b9cfc02f 100644 --- a/MVMCoreUI/Templates/MoleculeListCellProtocol.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeListCellProtocol.swift @@ -8,7 +8,7 @@ import Foundation -public protocol MoleculeListCellProtocol { +public protocol MoleculeListCellProtocol: UITableViewCell { /// Can set the separator according to what the moleculeList commands. func setLines(with model: LineModel?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, indexPath: IndexPath) diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift similarity index 55% rename from MVMCoreUI/Templates/MoleculeListTemplate.swift rename to MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift index 59e91b76..b236e332 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift @@ -12,12 +12,6 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol //-------------------------------------------------- // MARK: - Stored Properties //-------------------------------------------------- - - public var formValidator: FormValidator? - public func validate() { - // Can override - } - public var moleculesInfo: [(identifier: String, class: AnyClass, molecule: (ListItemModelProtocol & MoleculeModelProtocol))]? var observer: NSKeyValueObservation? @@ -27,13 +21,9 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol //-------------------------------------------------- // MARK: - Computed Properties //-------------------------------------------------- - - open override func parsePageJSON(_ error: NSErrorPointer) { - do { - try parseTemplateJSON() - } catch let parseError { - error?.pointee = parseError as NSError - } + open override func parsePageJSON() throws { + try parseTemplate(json: loadObject?.pageJSON) + try super.parsePageJSON() } open override var loadObject: MVMCoreLoadObject? { @@ -56,7 +46,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol open override func viewForTop() -> UIView { guard let headerModel = templateModel?.header, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false) + let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) else { return super.viewForTop() } // Temporary, Default the horizontal padding @@ -69,7 +59,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol override open func viewForBottom() -> UIView { guard let footerModel = templateModel?.footer, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, false) + let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) else { return super.viewForBottom() } return molecule @@ -90,10 +80,10 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol return true } - open override func newDataBuildScreen() { - super.newDataBuildScreen() + open override func handleNewData() { setup() registerWithTable() + super.handleNewData() } //-------------------------------------------------- @@ -109,9 +99,9 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } } - open override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { guard let moleculeInfo = moleculesInfo?[indexPath.row], - let estimatedHeight = (moleculeInfo.class as? ModelMoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) + let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) else { return 0 } return estimatedHeight @@ -127,52 +117,30 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) else { return UITableViewCell() } - let delegate = delegateObject() as? MVMCoreUIDelegateObject - let moleculeCell = cell as? MVMCoreUIMoleculeViewProtocol - moleculeCell?.reset?() - - if let protocolCell = cell as? MoleculeListCellProtocol { - protocolCell.setLines(with: templateModel?.line, delegateObject: delegate, additionalData: nil, indexPath: indexPath) - } - - (moleculeCell as? ModelMoleculeViewProtocol)?.set(with: moleculeInfo.molecule, delegate, nil) - moleculeCell?.updateView(tableView.bounds.width) + (cell as? MoleculeViewProtocol)?.reset() + (cell as? MoleculeListCellProtocol)?.setLines(with: templateModel?.line, delegateObject: delegateObjectIVar, additionalData: nil, indexPath: indexPath) + (cell as? MoleculeViewProtocol)?.set(with: moleculeInfo.molecule, delegateObjectIVar, nil) + (cell as? MVMCoreViewProtocol)?.updateView(tableView.bounds.width) // Neded to fix an apple defect where the cell is not the correct size on certain devices for certain cells cell.layoutIfNeeded() return cell } - open override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - if let protocolCell = cell as? MoleculeListCellProtocol { - protocolCell.willDisplay() - } + open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + (cell as? MoleculeListCellProtocol)?.willDisplay() } - open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - + open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath) as? MoleculeListCellProtocol { cell.didSelectCell(at: indexPath, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil) } } - //-------------------------------------------------- - // MARK: - Cache Handling - //-------------------------------------------------- - - open override func pageTypesToListenFor() -> [Any]? { - guard let pageType = self.pageType else { return super.pageTypesToListenFor() } - return [pageType] - } - - open override func modulesToListenFor() -> [Any]? { - return loadObject?.requestParameters?.modules - } - //-------------------------------------------------- // MARK: - MoleculeDelegateProtocol //-------------------------------------------------- - open override func moleculeLayoutUpdated(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) { + open override func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { if let tableView = tableView { let point = molecule.convert(molecule.bounds.origin, to: tableView) @@ -183,76 +151,18 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } } - open override func addMolecules(_ molecules: [[AnyHashable : Any]], indexPath: IndexPath, animation: UITableView.RowAnimation) { - var tmpMolecules = [ListItemModelProtocol & MoleculeModelProtocol]() - - molecules.forEach { molecule in - if let data = try? JSONSerialization.data(withJSONObject: molecule), let listItemModel = try? JSONDecoder().decode(MoleculeListItemModel.self, from: data) { - tmpMolecules.append(listItemModel) - } - } - - DispatchQueue.main.async { - var indexPaths: [IndexPath] = [] - - for molecule in tmpMolecules { - if let info = self.getMoleculeInfo(with: molecule) { - self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) - let index = indexPath.row + 1 + indexPaths.count - self.moleculesInfo?.insert(info, at: index) - indexPaths.append(IndexPath(row: index, section: 0)) - } - } - - self.tableView?.insertRows(at: indexPaths, with: animation) - self.updateViewConstraints() - self.view.layoutIfNeeded() - } + open override func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { + guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in + //TODO: check for molecule protocol eqaulity + let json = moleculeInfo.molecule.toJSON() + return json == molecule.toJSON() + }) else { return nil } + return IndexPath(row: index, section: 0) } - open override func addMolecules(_ molecules: [[AnyHashable: Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) { - - DispatchQueue.main.async { [weak self] in - guard let indexPath = self?.tableView?.indexPath(for: sender) else { return } - DispatchQueue.global().async { - self?.addMolecules(molecules, indexPath: indexPath, animation: animation) - } - } - } - - open override func removeMolecules(_ molecules: [[AnyHashable: Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) { - - var tmpMolecules = [ListItemModelProtocol & MoleculeModelProtocol]() - - molecules.forEach { molecule in - if let data = try? JSONSerialization.data(withJSONObject: molecule), - let listItemModel = try? JSONDecoder().decode(MoleculeListItemModel.self, from: data) { - tmpMolecules.append(listItemModel) - } - } - - var indexPaths: [IndexPath] = [] - - //TODO: cehck for molecule protocola eqality - - for molecule in tmpMolecules { - if let removeIndex = moleculesInfo?.firstIndex(where: { molecule.toJSON() == $0.molecule.toJSON() }) { - - moleculesInfo?.remove(at: removeIndex) - indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0)) - } - } - - tableView?.deleteRows(at: indexPaths, with: animation) - updateViewConstraints() - view.layoutIfNeeded() - } - - open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], sender: UITableViewCell, animation: UITableView.RowAnimation) { - + open override func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) { // This dispatch is needed to fix a race condition that can occur if this function is called during the table setup. DispatchQueue.main.async { - guard let indexPath = self.tableView?.indexPath(for: sender) else { return } var indexPaths: [IndexPath] = [] for molecule in molecules { @@ -264,16 +174,16 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } } + guard indexPaths.count > 0 else { return } self.tableView?.insertRows(at: indexPaths, with: animation) self.updateViewConstraints() self.view.layoutIfNeeded() } } - open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], sender: UITableViewCell, animation: UITableView.RowAnimation) { - + open override func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) { var indexPaths: [IndexPath] = [] - //TODO: cehck for molecule protocola eqality + //TODO: check for molecule protocol equality for molecule in molecules { if let removeIndex = moleculesInfo?.firstIndex(where: { molecule.toJSON() == $0.molecule.toJSON() }) { @@ -282,6 +192,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } } + guard indexPaths.count > 0 else { return } tableView?.deleteRows(at: indexPaths, with: animation) updateViewConstraints() view.layoutIfNeeded() @@ -295,10 +206,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol func getMoleculeInfo(with listItem: (ListItemModelProtocol & MoleculeModelProtocol)?) -> (identifier: String, class: AnyClass, molecule: ListItemModelProtocol & MoleculeModelProtocol)? { guard let listItem = listItem, - let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(listItem), - let moleculeName = (moleculeClass as? ModelMoleculeViewProtocol.Type)?.nameForReuse(with: listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName - else { return nil } - + let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem) else { return nil } + let moleculeName = moleculeClass.nameForReuse(with: listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName return (moleculeName, moleculeClass, listItem) } @@ -332,19 +241,17 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol /// Gets modules required by the loadObject.pageJSON. open func requiredModules() -> [Any]? { + var modules: [String]? = [] + var errors: [MVMCoreErrorObject]? = nil + MoleculeObjectMapping.addRequiredModules(for: templateModel?.header, delegateObjectIVar, moduleList: &modules, errorList: &errors) + MoleculeObjectMapping.addRequiredModules(for: templateModel?.footer, delegateObjectIVar, moduleList: &modules, errorList: &errors) - let modules: NSMutableArray = [] - let delegate = delegateObject() as? MVMCoreUIDelegateObject - - MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate, moduleList: modules, errorList: nil) - MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate, moduleList: modules, errorList: nil) - - if let molecules = loadObject?.pageJSON?.optionalArrayForKey(KeyMolecules) as? [[AnyHashable: Any]] { + if let molecules = templateModel?.molecules { for molecule in molecules { - MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: molecule, delegateObject: delegate, moduleList: modules, errorList: nil) + MoleculeObjectMapping.addRequiredModules(for: molecule, delegateObjectIVar, moduleList: &modules, errorList: &errors) } } - return modules as? [Any] + return modules } } diff --git a/MVMCoreUI/Templates/MoleculeStackCenteredTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeStackCenteredTemplate.swift similarity index 100% rename from MVMCoreUI/Templates/MoleculeStackCenteredTemplate.swift rename to MVMCoreUI/Atomic/Templates/MoleculeStackCenteredTemplate.swift diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeStackTemplate.swift similarity index 58% rename from MVMCoreUI/Templates/MoleculeStackTemplate.swift rename to MVMCoreUI/Atomic/Templates/MoleculeStackTemplate.swift index 6a895120..b26bbc8a 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeStackTemplate.swift @@ -9,20 +9,12 @@ import UIKit open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol { - public func validate() { - - } - - public var formValidator: FormValidator? - var observer: NSKeyValueObservation? public var templateModel: StackPageTemplateModel? - open override func parsePageJSON(_ error: NSErrorPointer) { - do { - try parseTemplateJSON() - } catch let parseError { - error?.pointee = parseError as NSError - } + + open override func parsePageJSON() throws { + try parseTemplate(json: loadObject?.pageJSON) + try super.parsePageJSON() } open override var loadObject: MVMCoreLoadObject? { @@ -45,7 +37,7 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol { open override func viewForTop() -> UIView? { guard let headerModel = templateModel?.header, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { + let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) else { return nil } return molecule @@ -66,24 +58,14 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol { override open func viewForBottom() -> UIView? { guard let footerModel = templateModel?.footer, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { + let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) else { return nil } return molecule } // MARK: - cache handling - open override func pageTypesToListenFor() -> [Any]? { - guard let pageType = self.pageType else { - return super.pageTypesToListenFor() - } - return [pageType] - } - - open override func modulesToListenFor() -> [Any]? { - return loadObject?.requestParameters?.modules - } - + /// Adds modules from requiredModules() to the MVMCoreViewControllerMapping.requiredModules map. open func updateRequiredModules() { if let requiredModules = requiredModules(), let pageType = pageType { @@ -93,11 +75,11 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol { /// Gets modules required by the loadObject.pageJSON. open func requiredModules() -> [Any]? { - let modules: NSMutableArray = [] - let delegate = delegateObject() as? MVMCoreUIDelegateObject - MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate, moduleList: modules, errorList: nil) - MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate, moduleList: modules, errorList: nil) - MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("stack"), delegateObject: delegate, moduleList: modules, errorList: nil) - return modules as? [Any] + var modules: [String]? = [] + var errors: [MVMCoreErrorObject]? = nil + MoleculeObjectMapping.addRequiredModules(for: templateModel?.header, delegateObjectIVar, moduleList: &modules, errorList: &errors) + MoleculeObjectMapping.addRequiredModules(for: templateModel?.footer, delegateObjectIVar, moduleList: &modules, errorList: &errors) + MoleculeObjectMapping.addRequiredModules(for: templateModel?.moleculeStack, delegateObjectIVar, moduleList: &modules, errorList: &errors) + return modules } } diff --git a/MVMCoreUI/Atomic/Templates/StackCenteredPageTemplateModel.swift b/MVMCoreUI/Atomic/Templates/StackCenteredPageTemplateModel.swift new file mode 100644 index 00000000..20d727e3 --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/StackCenteredPageTemplateModel.swift @@ -0,0 +1,15 @@ +// +// StackCenteredPageTemplate.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 11/22/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class StackCenteredPageTemplateModel: StackPageTemplateModel { + public override class var identifier: String { + return "stackCenterTemplate" + } +} diff --git a/MVMCoreUI/Templates/StackPageTemplateModel.swift b/MVMCoreUI/Atomic/Templates/StackPageTemplateModel.swift similarity index 51% rename from MVMCoreUI/Templates/StackPageTemplateModel.swift rename to MVMCoreUI/Atomic/Templates/StackPageTemplateModel.swift index ed94e4ef..2b683d98 100644 --- a/MVMCoreUI/Templates/StackPageTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/StackPageTemplateModel.swift @@ -9,54 +9,39 @@ import Foundation -@objcMembers public class StackPageTemplateModel: TemplateModelProtocol { - public var formRules: [FormGroupRule]? - public var formValidator: FormValidator? - - public static var identifier: String = "stack" - - public var pageType: String - public var screenHeading: String? - public var isAtomicTabs: Bool? +@objcMembers public class StackPageTemplateModel: TemplateModel { + public override class var identifier: String { + return "stack" + } public var header: MoleculeModelProtocol? public var moleculeStack: MoleculeStackModel public var footer: MoleculeModelProtocol? public init(pageType: String, moleculeStack: MoleculeStackModel) { - self.pageType = pageType self.moleculeStack = moleculeStack + super.init(pageType: pageType) } private enum CodingKeys: String, CodingKey { - case pageType - case screenHeading case header case footer case stack - case isAtomicTabs - case formRules } required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - pageType = try typeContainer.decode(String.self, forKey: .pageType) moleculeStack = try typeContainer.decode(MoleculeStackModel.self, forKey: .stack) - screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading) - isAtomicTabs = try typeContainer.decodeIfPresent(Bool.self, forKey: .isAtomicTabs) header = try typeContainer.decodeModelIfPresent(codingKey: .header) footer = try typeContainer.decodeModelIfPresent(codingKey: .footer) - formRules = try typeContainer.decodeIfPresent([FormGroupRule].self, forKey: .formRules) + try super.init(from: decoder) } - public func encode(to encoder: Encoder) throws { + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(pageType, forKey: .pageType) try container.encode(moleculeStack, forKey: .stack) - try container.encodeIfPresent(screenHeading, forKey: .screenHeading) - try container.encodeIfPresent(isAtomicTabs, forKey: .isAtomicTabs) try container.encodeModelIfPresent(header, forKey: .header) try container.encodeModelIfPresent(footer, forKey: .footer) - try container.encodeIfPresent(formRules, forKey: .formRules) } } diff --git a/MVMCoreUI/Atomic/Templates/TemplateModel.swift b/MVMCoreUI/Atomic/Templates/TemplateModel.swift new file mode 100644 index 00000000..dbbbc494 --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/TemplateModel.swift @@ -0,0 +1,52 @@ +// +// TemplateModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/13/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class TemplateModel: MVMControllerModelProtocol { + public class var identifier: String { + return "" + } + public var pageType: String + public var template: String { + // Although this is done in the extension, it is needed for the encoding. + return Self.identifier + } + public var screenHeading: String? + public var navigationItem: (NavigationItemModelProtocol & MoleculeModelProtocol)? + public var formRules: [FormGroupRule]? + + public init(pageType: String) { + self.pageType = pageType + } + + private enum CodingKeys: String, CodingKey { + case pageType + case template + case screenHeading + case formRules + case navigationItem + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + pageType = try typeContainer.decode(String.self, forKey: .pageType) + screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading) + formRules = try typeContainer.decodeIfPresent([FormGroupRule].self, forKey: .formRules) + navigationItem = try typeContainer.decodeModelIfPresent(codingKey: .navigationItem) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(pageType, forKey: .pageType) + try container.encode(template, forKey: .template) + try container.encodeIfPresent(screenHeading, forKey: .screenHeading) + try container.encodeIfPresent(formRules, forKey: .formRules) + try container.encodeModelIfPresent(navigationItem, forKey: .navigationItem) + } +} diff --git a/MVMCoreUI/Templates/ThreeLayerPageTemplateModel.swift b/MVMCoreUI/Atomic/Templates/ThreeLayerPageTemplateModel.swift similarity index 60% rename from MVMCoreUI/Templates/ThreeLayerPageTemplateModel.swift rename to MVMCoreUI/Atomic/Templates/ThreeLayerPageTemplateModel.swift index 5ad39c4b..61476707 100644 --- a/MVMCoreUI/Templates/ThreeLayerPageTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/ThreeLayerPageTemplateModel.swift @@ -8,52 +8,38 @@ import Foundation -@objcMembers public class ThreeLayerPageTemplateModel: TemplateModelProtocol { - - public var formRules: [FormGroupRule]? - public var formValidator: FormValidator? - - public static var identifier: String = "threeLayer" - - public var pageType: String - public var screenHeading: String? - public var isAtomicTabs: Bool? - +@objcMembers public class ThreeLayerPageTemplateModel: TemplateModel { + public override class var identifier: String { + return "threeLayer" + } public var header: MoleculeModelProtocol? public var middle: MoleculeModelProtocol? public var footer: MoleculeModelProtocol? public init(pageType: String, header: MoleculeModelProtocol?, middle: MoleculeModelProtocol?, footer: MoleculeModelProtocol?) { - self.pageType = pageType + super.init(pageType: pageType) self.header = header self.middle = middle self.footer = footer } private enum CodingKeys: String, CodingKey { - case pageType - case screenHeading case header case footer case middle - case isAtomicTabs } required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - pageType = try typeContainer.decode(String.self, forKey: .pageType) - screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading) - isAtomicTabs = try typeContainer.decodeIfPresent(Bool.self, forKey: .isAtomicTabs) header = try typeContainer.decodeModelIfPresent(codingKey: .header) middle = try typeContainer.decodeModelIfPresent(codingKey: .middle) footer = try typeContainer.decodeModelIfPresent(codingKey: .footer) + try super.init(from: decoder) } - public func encode(to encoder: Encoder) throws { + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(pageType, forKey: .pageType) - try container.encodeIfPresent(screenHeading, forKey: .screenHeading) - try container.encodeIfPresent(isAtomicTabs, forKey: .isAtomicTabs) try container.encodeModelIfPresent(header, forKey: .header) try container.encodeModelIfPresent(header, forKey: .middle) try container.encodeModelIfPresent(footer, forKey: .footer) diff --git a/MVMCoreUI/Templates/ThreeLayerTemplate.swift b/MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift similarity index 55% rename from MVMCoreUI/Templates/ThreeLayerTemplate.swift rename to MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift index 148d102d..96e85fad 100644 --- a/MVMCoreUI/Templates/ThreeLayerTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift @@ -9,20 +9,11 @@ import UIKit @objcMembers open class ThreeLayerTemplate: ThreeLayerViewController, TemplateProtocol { - public func validate() { - - } - - public var formValidator: FormValidator? - public var templateModel: ThreeLayerPageTemplateModel? - open override func parsePageJSON(_ error: NSErrorPointer) { - do { - try parseTemplateJSON() - } catch let parseError { - error?.pointee = parseError as NSError - } + open override func parsePageJSON() throws { + try parseTemplate(json: loadObject?.pageJSON) + try super.parsePageJSON() } override open func viewDidLoad() { @@ -31,14 +22,14 @@ import UIKit // Do any additional setup after loading the view. } - open override func newDataBuildScreen() { - super.newDataBuildScreen() + open override func handleNewData() { + super.handleNewData() heightConstraint?.isActive = true } open override func viewForTop() -> UIView? { guard let headerModel = templateModel?.header, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { + let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) else { return nil } return molecule @@ -46,7 +37,7 @@ import UIKit open override func viewForMiddle() -> UIView? { guard let middleModel = templateModel?.middle, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(middleModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { + let molecule = MoleculeObjectMapping.shared()?.createMolecule(middleModel, delegateObject: delegateObjectIVar) else { return nil } return molecule @@ -54,16 +45,12 @@ import UIKit override open func viewForBottom() -> UIView? { guard let footerModel = templateModel?.footer, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { + let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) else { return nil } return molecule } - open override func spaceBetweenMiddleAndBottom() -> CGFloat? { - return 0 - } - open override func spaceBetweenTopAndMiddle() -> CGFloat? { return 0 } diff --git a/MVMCoreUI/Atoms/Buttons/MFCustomButton+ActionModel.swift b/MVMCoreUI/Atoms/Buttons/MFCustomButton+ActionModel.swift deleted file mode 100644 index 84f4be33..00000000 --- a/MVMCoreUI/Atoms/Buttons/MFCustomButton+ActionModel.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// MFCustomButton+ActionModel.swift -// MVMCoreUI -// -// Created by Scott Pfeil on 1/9/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import Foundation - -public extension MFCustomButton { - - func set(with action: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - - buttonDelegate = delegateObject?.buttonDelegate - add({ [weak self] sender in - guard let self = self else { return } - if let data = try? action.encode(using: JSONEncoder()), - let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any], - delegateObject?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) - } - }, for: .touchUpInside) - } -} diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift b/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift deleted file mode 100644 index 6cf0df21..00000000 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton+MoleculeProtocolExtension.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// PrimaryButton+MoleculeProtocolExtension.swift -// MVMCoreUI -// -// Created by Scott Pfeil on 1/13/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import Foundation - -// temporary until link is finished -extension PrimaryButton: ModelMoleculeViewProtocol { - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - guard let model = model as? ButtonModel else { return } - - setTitle(model.title, for: .normal) - backgroundColor = model.backgroundColor?.uiColor - - if let style = model.style { - switch style { - case .primary: - setAsStandardCustom() - case .secondary: - setAsSecondaryCustom() - } - } - - if let size = model.size { - switch size { - case .standard: - setAsTiny(false) - case .tiny: - setAsTiny(true) - } - } - set(with: model.action, delegateObject: delegateObject, additionalData: additionalData) - } -} diff --git a/MVMCoreUI/Atoms/TextFields/TextFieldModel.swift b/MVMCoreUI/Atoms/TextFields/TextFieldModel.swift deleted file mode 100644 index d901d13a..00000000 --- a/MVMCoreUI/Atoms/TextFields/TextFieldModel.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// TextFieldModel.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 10/24/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import UIKit - -@objcMembers public class TextFieldModel: MoleculeModelProtocol { - - public static var identifier: String = "textField" - public var backgroundColor: Color? - public var moleculeName: String? = TextFieldModel.identifier - public var editable: Bool? - public var disabled: Bool? - public var errorMsg: String? - public var label: String? - public var type: String? - public var value: String? - public var regex: String? - - public var required: Bool? - public var fieldKey: String? - public var groupName: String? -} diff --git a/MVMCoreUI/Atoms/Views/MFView+ModelExtension.swift b/MVMCoreUI/Atoms/Views/MFView+ModelExtension.swift deleted file mode 100644 index 840a5d18..00000000 --- a/MVMCoreUI/Atoms/Views/MFView+ModelExtension.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// MFView+ModelExtension.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 11/20/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation - -extension MFView { - public func setUpDefaultWithModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - self.model = model - if let backgroundColor = model.backgroundColor { - self.backgroundColor = backgroundColor.uiColor - } - } -} - -extension ModelMoleculeViewProtocol where Self: MFView { - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - setUpDefaultWithModel(model, delegateObject, additionalData) - } -} diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift deleted file mode 100644 index 1b7ad307..00000000 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// MVMCoreUISwitch+Model.swift -// MVMCoreUI -// -// Created by Scott Pfeil on 1/14/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import Foundation - -// temporary until link is finished -extension MVMCoreUISwitch: ModelMoleculeViewProtocol { - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - guard let model = model as? ToggleModel else { return } - - FormValidator.setupValidation(molecule: model, delegate: delegateObject?.formHolderDelegate) - setState(model.state, animated: false) - - guard let action = model.action else { return } - actionBlock = { - if let data = try? action.encode(using: JSONEncoder()), - let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any] { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) - - } - } - } -} diff --git a/MVMCoreUI/BaseClasses/Button.swift b/MVMCoreUI/BaseClasses/Button.swift index 8722b800..f4ab51c6 100644 --- a/MVMCoreUI/BaseClasses/Button.swift +++ b/MVMCoreUI/BaseClasses/Button.swift @@ -8,7 +8,7 @@ public typealias ButtonAction = (Button) -> () -@objcMembers open class Button: UIButton, MFButtonProtocol, ModelMoleculeViewProtocol { +@objcMembers open class Button: UIButton, MFButtonProtocol, MoleculeViewProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -86,7 +86,7 @@ public typealias ButtonAction = (Button) -> () } } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { self.model = model @@ -111,6 +111,10 @@ public typealias ButtonAction = (Button) -> () open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { return nil } + + open func reset() { + backgroundColor = .clear + } } // MARK: - MVMCoreViewProtocol @@ -127,14 +131,6 @@ extension Button: MVMCoreViewProtocol { } } -// MARK: - MVMCoreUIMoleculeViewProtocol -extension Button: MVMCoreUIMoleculeViewProtocol { - - open func reset() { - backgroundColor = .clear - } -} - // MARK: AppleGuidelinesProtocol extension Button: AppleGuidelinesProtocol { diff --git a/MVMCoreUI/BaseClasses/CollectionView.swift b/MVMCoreUI/BaseClasses/CollectionView.swift new file mode 100644 index 00000000..a1a57376 --- /dev/null +++ b/MVMCoreUI/BaseClasses/CollectionView.swift @@ -0,0 +1,47 @@ +// +// CollectionView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/10/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class CollectionView: UICollectionView, MVMCoreViewProtocol { + + private var initialSetupPerformed = false + + private func initialSetup() { + if !initialSetupPerformed { + initialSetupPerformed = true + setupView() + } + } + + public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { + super.init(frame: frame, collectionViewLayout: layout) + initialSetup() + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + public func updateView(_ size: CGFloat) { + for cell in visibleCells { + (cell as? MVMCoreViewProtocol)?.updateView(size) + } + collectionViewLayout.invalidateLayout() + } + + public func setupView() { + translatesAutoresizingMaskIntoConstraints = false + showsHorizontalScrollIndicator = false + showsVerticalScrollIndicator = false + backgroundColor = .clear + isAccessibilityElement = false + contentInsetAdjustmentBehavior = .always + } +} diff --git a/MVMCoreUI/BaseClasses/CollectionViewCell.swift b/MVMCoreUI/BaseClasses/CollectionViewCell.swift new file mode 100644 index 00000000..e11b350c --- /dev/null +++ b/MVMCoreUI/BaseClasses/CollectionViewCell.swift @@ -0,0 +1,104 @@ +// +// CollectionViewCell.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/6/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +/// A base collection view cell with basic mvm functionality. +open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCoreViewProtocol, CollectionTemplateItemProtocol { + + // Convenience helpers + open var molecule: MoleculeViewProtocol? + public let containerHelper = ContainerHelper() + open var model: CollectionItemModelProtocol? + + /// The width, used for establishing columns + open var width: CGFloat? + + private var initialSetupPerformed = false + + // MARK: - Inits + public override init(frame: CGRect) { + super.init(frame: .zero) + initialSetup() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + initialSetup() + } + + private func initialSetup() { + if !initialSetupPerformed { + initialSetupPerformed = true + setupView() + } + } + + // MARK: - MVMCoreViewProtocol + open func setupView() { + isAccessibilityElement = false + contentView.isAccessibilityElement = false + insetsLayoutMarginsFromSafeArea = false + contentView.insetsLayoutMarginsFromSafeArea = false + contentView.preservesSuperviewLayoutMargins = false + MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0) + } + + open func updateView(_ size: CGFloat) { + if let model = model as? ContainerModelProtocol { + containerHelper.updateViewMargins(contentView, model: model, size: size) + } + (molecule as? MVMCoreViewProtocol)?.updateView(size) + } + + open func reset() { + molecule?.reset() + backgroundColor = .white + width = nil + } + + // MARK: - MoleculeViewProtocol + open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let model = model as? CollectionItemModelProtocol else { return } + self.model = model + + if let moleculeModel = model as? MoleculeModelProtocol, + let backgroundColor = moleculeModel.backgroundColor { + self.backgroundColor = backgroundColor.uiColor + } + + // align if needed. + if let model = model as? ContainerModelProtocol { + containerHelper.set(with: model, for: molecule as? MVMCoreUIViewConstrainingProtocol) + } + } + + /// Convenience function. Adds a molecule to the view. + open func addMolecule(_ molecule: MoleculeViewProtocol) { + contentView.addSubview(molecule) + containerHelper.constrainView(molecule) + self.molecule = molecule + } + + // MARK: - CollectionTemplateItemProtocol + public func set(width: CGFloat) { + self.width = width + } + + // Column logic, set width. + override open func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { + let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes) + guard let width = width else { return autoLayoutAttributes } + + let targetSize = CGSize(width: width, height: 0) + let newSize = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.defaultLow) + let newFrame = CGRect(origin: autoLayoutAttributes.frame.origin, size: newSize) + autoLayoutAttributes.frame = newFrame + return autoLayoutAttributes + } +} diff --git a/MVMCoreUI/BaseClasses/Control.swift b/MVMCoreUI/BaseClasses/Control.swift index 265e56b3..d8acb6f6 100644 --- a/MVMCoreUI/BaseClasses/Control.swift +++ b/MVMCoreUI/BaseClasses/Control.swift @@ -8,11 +8,12 @@ import UIKit -@objcMembers open class Control: UIControl, ModelMoleculeViewProtocol { + +@objcMembers open class Control: UIControl, MoleculeViewProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - open var json: [AnyHashable: Any]? + open var model: MoleculeModelProtocol? private var initialSetupPerformed = false @@ -47,7 +48,7 @@ import UIKit } } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { self.model = model if let backgroundColor = model.backgroundColor { @@ -66,6 +67,10 @@ import UIKit open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { return nil } + + open func reset() { + backgroundColor = .clear + } } // MARK: - AppleGuidelinesProtocol @@ -78,7 +83,7 @@ extension Control: AppleGuidelinesProtocol { // MARK: - MVMCoreViewProtocol extension Control: MVMCoreViewProtocol { - open func updateView(_ size: CGFloat) {} + open func updateView(_ size: CGFloat) { } /// Will be called only once. open func setupView() { @@ -86,18 +91,3 @@ extension Control: MVMCoreViewProtocol { insetsLayoutMarginsFromSafeArea = false } } - -// MARK: - MVMCoreUIMoleculeViewProtocol -extension Control: MVMCoreUIMoleculeViewProtocol { - open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - self.json = json - - if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { - backgroundColor = UIColor.mfGet(forHex: backgroundColorString) - } - } - - open func reset() { - backgroundColor = .clear - } -} diff --git a/MVMCoreUI/BaseClasses/Protocols/CollectionItemModelProtocol.swift b/MVMCoreUI/BaseClasses/Protocols/CollectionItemModelProtocol.swift new file mode 100644 index 00000000..9361ab1e --- /dev/null +++ b/MVMCoreUI/BaseClasses/Protocols/CollectionItemModelProtocol.swift @@ -0,0 +1,13 @@ +// +// CollectionItemModelProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/6/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol CollectionItemModelProtocol { + +} diff --git a/MVMCoreUI/BaseClasses/TableViewCell.swift b/MVMCoreUI/BaseClasses/TableViewCell.swift index ebf25a3c..dc4d270a 100644 --- a/MVMCoreUI/BaseClasses/TableViewCell.swift +++ b/MVMCoreUI/BaseClasses/TableViewCell.swift @@ -8,9 +8,9 @@ import UIKit -@objcMembers open class TableViewCell: UITableViewCell, MVMCoreUIMoleculeViewProtocol, MoleculeListCellProtocol, ModelMoleculeViewProtocol { +@objcMembers open class TableViewCell: UITableViewCell, MoleculeViewProtocol, MoleculeListCellProtocol, MVMCoreViewProtocol { - open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? + open var molecule: MoleculeViewProtocol? open var listItemModel: ListItemModelProtocol? public let containerHelper = ContainerHelper() @@ -86,7 +86,7 @@ import UIKit } /// Adds the molecule to the view. - open func addMolecule(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) { + open func addMolecule(_ molecule: MoleculeViewProtocol) { contentView.addSubview(molecule) containerHelper.constrainView(molecule) self.molecule = molecule @@ -139,7 +139,7 @@ import UIKit topSeparatorView?.updateView(size) bottomSeparatorView?.updateView(size) - molecule?.updateView(size) + (molecule as? MVMCoreViewProtocol)?.updateView(size) } open func setupView() { @@ -182,7 +182,7 @@ import UIKit } open func reset() { - molecule?.reset?() + molecule?.reset() backgroundColor = .white } @@ -197,10 +197,13 @@ import UIKit // MARK: - Caret View /// Adds the standard mvm style caret to the accessory view @objc public func addCaretViewAccessory() { + guard accessoryView == nil else { return } let caret = CaretView(lineWidth: 1) caret.translatesAutoresizingMaskIntoConstraints = true + caret.isAccessibilityElement = true + caret.accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "AccTabHint") caret.size = .small(.vertical) if let size = caret.size?.dimensions() { caret.frame = CGRect(origin: CGPoint.zero, size: size) @@ -246,7 +249,7 @@ import UIKit // MARK: - MoleculeListCellProtocol /// For when the separator between cells shows using json and frequency. Default is type: standard, frequency: allExceptTop. - public func setLines(with model: LineModel?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?, indexPath: IndexPath) { + public func setLines(with model: LineModel?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, indexPath: IndexPath) { addSeparatorsIfNeeded() if let model = model { topSeparatorView?.set(with: model, delegateObject, additionalData) @@ -258,7 +261,7 @@ import UIKit setSeparatorFrequency(model?.frequency ?? .allExceptTop, indexPath: indexPath) } - public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { //TODO: Use object when handleAction is rewrote to handle action model if let actionMap = self.listItemModel?.action?.toJSON() { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) diff --git a/MVMCoreUI/BaseClasses/TextField.swift b/MVMCoreUI/BaseClasses/TextField.swift index b1a13854..c571d52d 100644 --- a/MVMCoreUI/BaseClasses/TextField.swift +++ b/MVMCoreUI/BaseClasses/TextField.swift @@ -17,9 +17,6 @@ public protocol TextFieldDidDeleteProtocol: class { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - - open var json: [AnyHashable: Any]? - private var initialSetupPerformed = false /// Set to true to hide the blinking textField cursor. @@ -87,23 +84,14 @@ extension TextField: MVMCoreViewProtocol { } } -/// MARK:- MVMCoreUIMoleculeViewProtocol -extension TextField: MVMCoreUIMoleculeViewProtocol { - - open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - self.json = json - - guard let dictionary = json else { return } - - if let backgroundColorString = dictionary.optionalStringForKey(KeyBackgroundColor) { - backgroundColor = UIColor.mfGet(forHex: backgroundColorString) - } - - if let text = dictionary[KeyText] as? String { - self.text = text +/// MARK:- MoleculeViewProtocol +extension TextField: MoleculeViewProtocol { + open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + if let color = model.backgroundColor?.uiColor { + backgroundColor = color } } - + open func reset() { backgroundColor = .clear } diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index 860ce442..b5fdc5e7 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -8,8 +8,7 @@ import UIKit -@objcMembers open class View: UIView, ModelMoleculeViewProtocol { - open var json: [AnyHashable: Any]? +@objcMembers open class View: UIView, MoleculeViewProtocol { open var model: MoleculeModelProtocol? private var initialSetupPerformed = false @@ -39,7 +38,7 @@ import UIKit } } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { self.model = model if let backgroundColor = model.backgroundColor { @@ -58,6 +57,10 @@ import UIKit open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { return nil } + + open func reset() { + backgroundColor = .clear + } } // MARK:- MVMCoreViewProtocol @@ -72,23 +75,3 @@ extension View: MVMCoreViewProtocol { MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0) } } - -// MARK:- MVMCoreUIMoleculeViewProtocol -extension View: MVMCoreUIMoleculeViewProtocol { - - open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - self.json = json - - if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { - backgroundColor = UIColor.mfGet(forHex: backgroundColorString) - } - } - - open func reset() { - backgroundColor = .clear - } - - open func setAsMolecule() { - - } -} diff --git a/MVMCoreUI/BaseControllers/ContainerCollectionReusableView.swift b/MVMCoreUI/BaseControllers/ContainerCollectionReusableView.swift new file mode 100644 index 00000000..d7fb0ed4 --- /dev/null +++ b/MVMCoreUI/BaseControllers/ContainerCollectionReusableView.swift @@ -0,0 +1,30 @@ +// +// ContainerCollectionReusableView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/7/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +/// CollectionReusableView that can contains any other view. +public class ContainerCollectionReusableView: UICollectionReusableView { + public var view: UIView? + public var topConstraint: NSLayoutConstraint? + public var bottomConstraint: NSLayoutConstraint? + + public func addAndContain(view: UIView) { + guard self.view != view else { return } + self.view?.removeFromSuperview() + view.setContentCompressionResistancePriority(.required, for: .vertical) + addSubview(view) + self.view = view + topConstraint = view.topAnchor.constraint(equalTo: topAnchor) + topConstraint?.isActive = true + bottomConstraint = bottomAnchor.constraint(equalTo: view.bottomAnchor) + bottomConstraint?.isActive = true + rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true + view.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + } +} diff --git a/MVMCoreUI/BaseControllers/MFViewController+Form.swift b/MVMCoreUI/BaseControllers/MFViewController+Form.swift deleted file mode 100644 index 2c3b14bf..00000000 --- a/MVMCoreUI/BaseControllers/MFViewController+Form.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// MFViewController+Form.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 3/3/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import Foundation - -extension MFViewController: ObservingTextFieldDelegate { -} - -public extension MFViewController { - @objc func startValidation() { - (self as? FormHolderProtocol)?.formValidator?.validate() - } - - @objc func addFormParams(requestParameters: MVMCoreRequestParameters) { - (self as? FormHolderProtocol)?.formValidator?.addFormParams(requestParameters: requestParameters) - } -} diff --git a/MVMCoreUI/BaseControllers/MFViewController+Model.swift b/MVMCoreUI/BaseControllers/MFViewController+Model.swift deleted file mode 100644 index 05e506eb..00000000 --- a/MVMCoreUI/BaseControllers/MFViewController+Model.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// MFViewController+Model.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 10/24/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation - -extension MFViewController: MoleculeDelegateProtocol { - - public func getModuleWithName(_ name: String?) -> [AnyHashable: Any]? { - guard let name = name else { return nil } - - return loadObject?.modulesJSON?.optionalDictionaryForKey(name) - } - - public func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? { - guard let moduleJSON = loadObject?.modulesJSON?.optionalDictionaryForKey(moleculeName), - let moleculeName = moduleJSON.optionalStringForKey("moleculeName"), - let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) - else { return nil } - - do { - return try modelType.decode(jsonDict: moduleJSON) as? MoleculeModelProtocol - } catch { - MVMCoreUILoggingHandler.logDebugMessage(withDelegate: "error: \(error)") - } - - return nil - } - - // These are required because swift will call the extension function otherwise. - @objc public func moleculeLayoutUpdated(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) {} - - @objc public func addMolecules(_ molecules: [[AnyHashable: Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) {} - @objc public func addMolecules(_ molecules: [[AnyHashable : Any]], indexPath: IndexPath, animation: UITableView.RowAnimation) {} - @objc public func removeMolecules(_ molecules: [[AnyHashable: Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) {} -} diff --git a/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift b/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift new file mode 100644 index 00000000..579a612e --- /dev/null +++ b/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift @@ -0,0 +1,13 @@ +// +// MVMControllerModelProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/16/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol MVMControllerModelProtocol: TemplateModelProtocol, FormHolderModelProtocol { + +} diff --git a/MVMCoreUI/BaseControllers/ProgrammaticCollectionViewController.swift b/MVMCoreUI/BaseControllers/ProgrammaticCollectionViewController.swift new file mode 100644 index 00000000..5e7b87b0 --- /dev/null +++ b/MVMCoreUI/BaseControllers/ProgrammaticCollectionViewController.swift @@ -0,0 +1,76 @@ +// +// ProgrammaticCollectionViewController.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/8/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +/// A base view controller with a collection view. +@objc open class ProgrammaticCollectionViewController: ScrollingViewController { + + public var collectionView: UICollectionView? + + open override func loadView() { + let view = UIView() + view.backgroundColor = .white + + let collection = createCollectionView() + view.addSubview(collection) + NSLayoutConstraint.constraintPinSubview(toSuperview: collection) + + collectionView = collection + scrollView = collectionView + self.view = view + } + + /// A place to register cells with the collectionView + open func registerCells() {} + + open override func viewDidLoad() { + super.viewDidLoad() + registerCells() + } + + /// Creates the layout for the collection. + open func createCollectionViewLayout() -> UICollectionViewLayout { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + layout.minimumLineSpacing = 0 + layout.minimumInteritemSpacing = 0 + layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize + return layout + } + + /// Creates the collection view. + open func createCollectionView() -> UICollectionView { + let collection = CollectionView(frame: .zero, collectionViewLayout: createCollectionViewLayout()) + collection.dataSource = self + collection.delegate = self + return collection + } + + deinit { + collectionView?.delegate = nil + collectionView?.dataSource = nil + } +} + +extension ProgrammaticCollectionViewController: UICollectionViewDataSource { + open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 0 + } + + open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + return UICollectionViewCell() + } + + open func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } +} + +extension ProgrammaticCollectionViewController: UICollectionViewDelegate { +} diff --git a/MVMCoreUI/BaseControllers/ProgrammaticScrollViewController.swift b/MVMCoreUI/BaseControllers/ProgrammaticScrollViewController.swift new file mode 100644 index 00000000..b6ed6353 --- /dev/null +++ b/MVMCoreUI/BaseControllers/ProgrammaticScrollViewController.swift @@ -0,0 +1,56 @@ +// +// ProgrammaticScrollViewController.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/12/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class ProgrammaticScrollViewController: ScrollingViewController { + public var topConstraint: NSLayoutConstraint? + public var bottomConstraint: NSLayoutConstraint? + + public override init(with scrollView: UIScrollView) { + super.init(with: scrollView) + } + + public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + } + + open override func loadView() { + + let view = UIView() + view.backgroundColor = .white + + let scrollView = UIScrollView() + scrollView.backgroundColor = .clear + scrollView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(scrollView) + + // Sets the constraints for the scroll view + let constraints = NSLayoutConstraint.constraintPinSubview(toSuperview: scrollView) + topConstraint = constraints?[ConstraintTop] as? NSLayoutConstraint + bottomConstraint = constraints?[ConstraintBot] as? NSLayoutConstraint + + let contentView = MVMCoreUICommonViewsUtility.commonView() + scrollView.addSubview(contentView) + + // Sets the constraints for the content view + NSLayoutConstraint.constraintPinSubview(toSuperview: contentView) + + // Super will set later. + contentWidthConstraint = contentView.widthAnchor.constraint(equalToConstant: 320.0) + contentWidthConstraint?.isActive = true + + self.contentView = contentView + self.scrollView = scrollView + self.view = view + } +} diff --git a/MVMCoreUI/BaseControllers/ProgrammaticTableViewController.swift b/MVMCoreUI/BaseControllers/ProgrammaticTableViewController.swift new file mode 100644 index 00000000..b27845b3 --- /dev/null +++ b/MVMCoreUI/BaseControllers/ProgrammaticTableViewController.swift @@ -0,0 +1,86 @@ +// +// ProgrammaticTableViewController.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/12/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class ProgrammaticTableViewController: ProgrammaticScrollViewController, UITableViewDelegate, UITableViewDataSource { + @IBOutlet public var tableView: UITableView! + + public init(with tableView: UITableView) { + self.tableView = tableView + super.init(with: tableView) + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + } + + public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + } + + open override func loadView() { + let view = UIView() + view.backgroundColor = .white + + let tableView = createTableView() + tableView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(tableView) + + // Sets the constraints for the scroll view + let constraints = NSLayoutConstraint.constraintPinSubview(toSuperview: tableView) + topConstraint = constraints?[ConstraintTop] as? NSLayoutConstraint + bottomConstraint = constraints?[ConstraintBot] as? NSLayoutConstraint + + self.tableView = tableView + scrollView = tableView + self.view = view + } + + open override func viewDidLoad() { + super.viewDidLoad() + registerWithTable() + } + + /// This class should create the table view that will be used here. Subclass this for different table styles. + open func createTableView() -> UITableView { + let tableView = UITableView(frame: .zero, style: .grouped) + tableView.backgroundColor = .clear + tableView.separatorStyle = UITableViewCell.SeparatorStyle.none + tableView.delegate = self + tableView.dataSource = self + tableView.insetsContentViewsToSafeArea = false + return tableView + } + + // Registers classes and nibs. Can subclass for different nibs. Can call super and then add new ones after as well. + open func registerWithTable() {} + + /// Sets the table to have no section headers or footers. + open func setNoSectionHeadersFooters() { + tableView.sectionHeaderHeight = CGFloat.leastNormalMagnitude + tableView.sectionFooterHeight = CGFloat.leastNormalMagnitude + } + + /// For subclassing, returns the number of sections for table. This function calls numberOfSectionsForTableview aftre ensuring the table is setup properly. + open func getNumberOfSections() -> Int { + return 1 + } + + open func numberOfSections(in tableView: UITableView) -> Int { + return tableView.bounds.width > 1 ? getNumberOfSections() : 0 + } + + open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 0 + } + + open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + return UITableViewCell() + } +} diff --git a/MVMCoreUI/BaseControllers/ScrollingViewController.swift b/MVMCoreUI/BaseControllers/ScrollingViewController.swift new file mode 100644 index 00000000..2e4eceef --- /dev/null +++ b/MVMCoreUI/BaseControllers/ScrollingViewController.swift @@ -0,0 +1,109 @@ +// +// ScrollingViewController.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/12/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class ScrollingViewController: ViewController { + public var dismissKeyboardTapGesture: UITapGestureRecognizer? + @IBOutlet public var scrollView: UIScrollView! + public var contentView: UIView? + public var contentWidthConstraint: NSLayoutConstraint? + + private var keyboardNotificationsAdded = false + private var keyboardIsShowing = false + private var preKeyboardContentInset: UIEdgeInsets? + + public init(with scrollView: UIScrollView) { + self.scrollView = scrollView + super.init(nibName: nil, bundle: nil) + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + } + + public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + } + + // MARK: - View Life Cycle + open override func viewDidLoad() { + super.viewDidLoad() + + // Adds the tap gesture to dismiss the keyboard. + dismissKeyboardTapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissFieldInput(sender:))) + view.addGestureRecognizer(dismissKeyboardTapGesture!) + dismissKeyboardTapGesture?.isEnabled = false + scrollView.alwaysBounceVertical = false + scrollView.delegate = self + } + + open override func updateViewConstraints() { + super.updateViewConstraints() + // Sets the width of the content to the width of the screen. + contentWidthConstraint?.constant = view.bounds.width - scrollView.contentInset.left - scrollView.contentInset.right + } + + open override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + view.setNeedsUpdateConstraints() + view.layoutSubviews() + } + + open override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + registerForKeyboardNotifications() + } + + // MARK: - Keyboard Handling + open func registerForKeyboardNotifications() { + if !keyboardNotificationsAdded { + keyboardNotificationsAdded = true + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil) + } + } + + open func unregisterForKeyboardNotifications() { + if keyboardNotificationsAdded { + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) + keyboardNotificationsAdded = false + } + } + + @objc open func keyboardWillShow(notification: Notification) { + // Stores the current scroll insets if the keyboard was hidden. + if !keyboardIsShowing { + preKeyboardContentInset = scrollView.contentInset + } + keyboardIsShowing = true + + // Enables the tap gesture. + dismissKeyboardTapGesture?.isEnabled = true + + MVMCoreUIUtility.setScrollViewInsetForKeyboardShow(notification, scrollView: scrollView, viewController: self) { [weak self] () -> CGRect in + return self?.rectToScrollToWhenKeyboardPopsUp() ?? .zero + } + } + + @objc open func keyboardWillBeHidden(notification: Notification) { + keyboardIsShowing = false + + // Disables the tap gesture. + dismissKeyboardTapGesture?.isEnabled = false + + MVMCoreUIUtility.setScrollViewInsetForKeyboardHide(notification, scrollView: scrollView, viewController: self, contentInset: preKeyboardContentInset ?? scrollView.contentInset) + } + + open func rectToScrollToWhenKeyboardPopsUp() -> CGRect? { + guard let field = selectedField, + let parent = selectedField?.superview else { return nil } + return scrollView.convert(field.frame, from: parent) + } +} diff --git a/MVMCoreUI/BaseControllers/ThreeLayerCollectionViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerCollectionViewController.swift new file mode 100644 index 00000000..aa34f2f2 --- /dev/null +++ b/MVMCoreUI/BaseControllers/ThreeLayerCollectionViewController.swift @@ -0,0 +1,223 @@ +// +// ThreeLayerCollectionViewController.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/6/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +/// A view controller that has three main layers, a header, collection rows, and a footer. The header is added as a supplement header to the first section, and the footer is added as a supplement footer to the last section. This view controller allows for flexible space between the three layers to fit the screeen. +@objc open class ThreeLayerCollectionViewController: ProgrammaticCollectionViewController, UICollectionViewDelegateFlowLayout { + + private var topView: UIView? + private var bottomView: UIView? + private var headerView: ContainerCollectionReusableView? + private var footerView: ContainerCollectionReusableView? + private let headerID = "header" + private let footerID = "footer" + + /// Updates the padding for flexible space (header or footer) + private func updateFlexibleSpace() { + guard let tableView = collectionView else { return } + + let minimumSpace: CGFloat = minimumFillSpace() + var currentSpace: CGFloat = 0 + var totalMinimumSpace: CGFloat = 0 + + var fillTop = false + if spaceBelowTopView() == nil, headerView != nil { + fillTop = true + currentSpace += headerView?.bottomConstraint?.constant ?? 0 + totalMinimumSpace += minimumSpace + } + + var fillBottom = false + if spaceAboveBottomView() == nil, footerView != nil { + fillBottom = true + currentSpace += footerView?.topConstraint?.constant ?? 0 + totalMinimumSpace += minimumSpace + } + + guard fillTop || fillBottom else { return } + + let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: tableView, minimumHeight: totalMinimumSpace) + + // If the bottom view is outside of the scroll, then only the top view constraint is being used, so we have to double it to account for the bottom constraint not being there when we compare to the new value. + var currentSpaceForCompare: CGFloat = currentSpace + if fillTop { + currentSpaceForCompare = currentSpace * 2; + } + + if !MVMCoreGetterUtility.cgfequalwiththreshold(newSpace, currentSpaceForCompare, 1) { + if fillTop && fillBottom { + // space both + let half = newSpace / 2 + headerView?.bottomConstraint?.constant = half + footerView?.topConstraint?.constant = half + collectionView?.collectionViewLayout.invalidateLayout() + } else if fillTop { + // Only top is spaced (half the size if the bottom view is out of the scroll because it needs to be sized as if there are two spacers but there is only one. + headerView?.bottomConstraint?.constant = newSpace + collectionView?.collectionViewLayout.invalidateLayout() + } else if fillBottom { + // Only bottom is spaced. + footerView?.topConstraint?.constant = newSpace + collectionView?.collectionViewLayout.invalidateLayout() + } + } + } + + //MARK: - ViewController + open override func updateViews() { + super.updateViews() + // Needed due to dispatch in reloadCollectionData. + DispatchQueue.main.async { + let width = self.view.bounds.width + if let topView = self.topView as? MVMCoreViewProtocol { + topView.updateView(width) + } + if let bottomView = self.bottomView as? MVMCoreViewProtocol { + bottomView.updateView(width) + } + if let cells = self.collectionView?.visibleCells { + for cell in cells { + self.update(cell: cell, size: width) + } + } + self.invalidateCollectionLayout() + } + } + + open override func handleNewData() { + super.handleNewData() + createViewForHeader() + createViewForFooter() + reloadCollectionData() + } + + override open func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + + //MARK: - Spacing + // If both are subclassed to return a value, then the buttons will not be pinned towards the bottom because neither spacing would try to fill the screen. + /// Space between the top view and the collection rows, nil to fill. 0 default + open func spaceBelowTopView() -> CGFloat? { + return 0 + } + + /// Space between the bottom view and the collection rows, nil to fill. nil default + open func spaceAboveBottomView() -> CGFloat? { + return nil + } + + /// can override to return a minimum fill space. 0 default + open func minimumFillSpace() -> CGFloat { + return 0 + } + + //MARK: - Header Footer + /// Creates the top view. + open func createViewForHeader() { + guard let topView = viewForTop() else { + self.topView = nil + self.headerView = nil + return + } + self.topView = topView + } + + /// Creates the footer + open func createViewForFooter() { + guard let bottomView = viewForBottom() else { + self.bottomView = nil + self.footerView = nil + return + } + self.bottomView = bottomView + } + + //MARK: - Functions to subclass + /// Subclass for a top view. + open func viewForTop() -> UIView? { + return nil + } + + /// Subclass for a bottom view. + open func viewForBottom() -> UIView? { + return nil + } + + //MARK: - Collection + + /// Should be used to refresh the layout of the collection view. Updates flexible padding. + open func invalidateCollectionLayout() { + self.collectionView?.collectionViewLayout.invalidateLayout() + // TODO: Improve this workaround (autolayout for cells happens async so contentSize isn't accurate immediately) + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1, execute: { + self.updateFlexibleSpace() + }) + } + + /// Should be used to reload the data of the collection view. Updates flexible padding. + open func reloadCollectionData() { + collectionView?.reloadData() + // TODO: Improve this workaround (autolayout for cells happens async so contentSize isn't accurate immediately) + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1, execute: { + self.updateFlexibleSpace() + }) + } + + /// Called in updateView, updates the cell. + open func update(cell: UICollectionViewCell, size: CGFloat) { + (cell as? MVMCoreViewProtocol)?.updateView(size) + } + + open override func registerCells() { + super.registerCells() + collectionView?.register(ContainerCollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: headerID) + collectionView?.register(ContainerCollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: footerID) + } + + open override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 0 + } + + open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + guard let _ = topView, + section == 0 else { return .zero } + + // Calculate the height of the header since apple doesn't support autolayout. Width is fixed, height is tall as content. + let header = headerView ?? self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: IndexPath(row: 0, section: section)) + return header.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, height: UIView.layoutFittingExpandedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) + } + + open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { + guard let _ = bottomView, + section == numberOfSections(in: collectionView) - 1 else { return .zero } + + // Calculate the height of the footr since apple doesn't support autolayout. Width is fixed, height is tall as content. + let footer = footerView ?? self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionFooter, at: IndexPath(row: 0, section: section)) + return footer.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, height: UIView.layoutFittingExpandedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) + } + + open func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + if kind == UICollectionView.elementKindSectionFooter, + let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerID, for: indexPath) as? ContainerCollectionReusableView { + footerView.addAndContain(view: bottomView!) + footerView.topConstraint?.constant = spaceAboveBottomView() ?? 0 + self.footerView = footerView + return footerView + } else if kind == UICollectionView.elementKindSectionHeader, + let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerID, for: indexPath) as? ContainerCollectionReusableView { + headerView.addAndContain(view: topView!) + headerView.bottomConstraint?.constant = spaceBelowTopView() ?? 0 + self.headerView = headerView + return headerView + } + fatalError() + } +} diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift index 7342e761..000f2e7f 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -9,7 +9,7 @@ import UIKit import MVMAnimationFramework -open class ThreeLayerTableViewController: MFProgrammaticTableViewController { +open class ThreeLayerTableViewController: ProgrammaticTableViewController { // The three main views private var topView: UIView? private var bottomView: UIView? @@ -33,11 +33,11 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { bottomView.updateView(width) showFooter(width) } - self.tableView?.reloadData() + tableView?.reloadData() } - open override func newDataBuildScreen() { - super.newDataBuildScreen() + open override func handleNewData() { + super.handleNewData() createViewForTableHeader() createViewForTableFooter() tableView?.reloadData() @@ -45,8 +45,8 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { override open func viewDidLoad() { super.viewDidLoad() - setToHaveNoSectionHeadersFooters() // Do any additional setup after loading the view. + setNoSectionHeadersFooters() } //MARK: - Spacing @@ -227,26 +227,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { return view } - //MARK: - Scrollview - open override func scrollViewDidScroll(_ scrollView: UIScrollView) { - // To stop handscroll animation if animating after scroll - stopHandScrollAnimation(true) - } - deinit { tableView?.delegate = nil } - - //MARK: - Animation - open override func setupIntroAnimations() { - if let topView = topView, topView.subviews.count > 0 { - introAnimationManager?.addAnimation(animation: MVMAnimations.fadeUpAnimation(view: topView)) - } - if let tableView = tableView { - introAnimationManager?.addAnimation(animation: MVMAnimations.animateTableViewFadeInCells(tableView: tableView)) - } - if let bottomView = bottomView, bottomView.subviews.count > 0 { - introAnimationManager?.addAnimation(animation: MVMAnimations.fadeUpAnimation(view: bottomView)) - } - } } diff --git a/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift index ea6923c5..9eacd25c 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift @@ -61,15 +61,19 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController { } } - open override func newDataBuildScreen() { - super.newDataBuildScreen() - + open override func handleNewData() { + super.handleNewData() + // Removes the views topView?.removeFromSuperview() middleView?.removeFromSuperview() bottomView?.removeFromSuperview() safeAreaView?.removeFromSuperview() - MVMCoreUIStackableViewController.remove(contentView?.subviews) + if let subViews = contentView?.subviews { + for view in subViews { + view.removeFromSuperview() + } + } // Reset constraints bottomConstraint?.isActive = true @@ -241,18 +245,3 @@ extension ThreeLayerViewController { } } } - -//MARK:-Animation -extension ThreeLayerViewController { - open override func setupIntroAnimations() { - if let topView = topView, topView.subviews.count > 0 { - introAnimationManager?.addAnimation(animation: MVMAnimations.fadeUpAnimation(view: topView)) - } - if let middleView = middleView, middleView.subviews.count > 0 { - introAnimationManager?.addAnimation(animation: MVMAnimations.fadeUpAnimation(view: middleView)) - } - if let bottomView = bottomView, bottomView.subviews.count > 0 { - introAnimationManager?.addAnimation(animation: MVMAnimations.fadeUpAnimation(view: bottomView)) - } - } -} diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 1bb06324..eb4be908 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -8,19 +8,112 @@ import UIKit -@objcMembers open class ViewController: UIViewController, MVMCoreViewControllerProtocol { - public var pageType: String? - public var loadObject: MVMCoreLoadObject? - public var pageModel: PageModelProtocol? +@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate { + @objc public var pageType: String? + @objc public var loadObject: MVMCoreLoadObject? + public var pageModel: MVMControllerModelProtocol? + + /// Set if this page is containted in a manager. + public var manager: (UIViewController & MVMCoreViewManagerProtocol)? + + /// A temporary iVar backer for delegateObject() until we change the protocol + public var delegateObjectIVar: MVMCoreUIDelegateObject? + public func delegateObject() -> DelegateObject? { + return delegateObjectIVar + } + + public var formValidator: FormValidator? + + public var needsUpdateUI = false + private var observingForResponses = false + private var initialLoadFinished = false + private var previousScreenSize = CGSize.zero + + public var selectedField: UIView? - // MARK: Response handling - public func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { + /// Checks if the screen width has changed + open func screenSizeChanged() -> Bool { + return !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1) + } + + // MARK: - Response handling + open func observeForResponseJSONUpdates() { + guard !observingForResponses, + (pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0) else { return } + observingForResponses = true + NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil) + } + + open func stopObservingForResponseJSONUpdates() { + guard observingForResponses else { return } + NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil) + observingForResponses = false + } + + open func pagesToListenFor() -> [String]? { + guard let pageType = loadObject?.pageType else { return nil } + return [pageType] + } + + open func modulesToListenFor() -> [String]? { + return loadObject?.requestParameters?.modules as? [String] + } + + @objc open func responseJSONUpdated(notification: Notification) { + // Checks for a page we are listening for. + var newData = false + if let pagesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyPageMap), + let pageType = pagesToListenFor()?.first(where: { (pageTypeListened) -> Bool in + guard let page = pagesLoaded.optionalDictionaryForKey(pageTypeListened), + let pageType = page.optionalStringForKey(KeyPageType), + pageType == pageTypeListened else { return false } + return true + }) { + newData = true + loadObject?.pageJSON = pagesLoaded.optionalDictionaryForKey(pageType) + } + + // Checks for modules we are listening for. + if let modulesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyModuleMap), + let modulesListened = modulesToListenFor() { + for moduleName in modulesListened { + if let module = modulesLoaded.optionalDictionaryForKey(moduleName) { + newData = true + var currentModules = loadObject?.modulesJSON ?? [:] + currentModules.updateValue(module, forKey: moduleName) + loadObject?.modulesJSON = currentModules + } + } + } + + guard newData else { return } + do { + try parsePageJSON() + MVMCoreDispatchUtility.performBlock(onMainThread: { + self.handleNewDataAndUpdateUI() + // If the screen is showing, can update the navigation controller. + if MVMCoreUIUtility.getCurrentVisibleController() == self.manager ?? self { + self.setNavigationController() + } + }) + } catch { + if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: pageType))") { + MVMCoreLoggingHandler.shared()?.addError(toLog: coreError) + } + } + } + + open func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { pageType = loadObject.pageType self.loadObject = loadObject + // Verifies all modules needed are loaded. TODO: change to ViewController + guard MFViewController.verifyRequiredModulesLoaded(for: loadObject, error: error) else { return false } + // Parse the model for the page. do { try parsePageJSON() + return true } catch let parsingError { // Log all parsing errors and fail load. if let errorObject = MVMCoreErrorObject.createErrorObject(for: parsingError, location: MVMCoreLoadHandler.sharedGlobal()?.errorLocation(forRequest: loadObject)) { @@ -29,15 +122,12 @@ import UIKit } return false } - - // Verifies all modules needed are loaded. - return MFViewController.verifyRequiredModulesLoaded(for: loadObject, error: error) } - @objc func parsePageJSON() throws { + open func parsePageJSON() throws { } - public class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: inout MVMCoreErrorObject?) -> Bool { + open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: inout MVMCoreErrorObject?) -> Bool { guard let pageType = loadObject?.pageType, var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType) else { return true } guard let loadedModules = loadObject?.modulesJSON else { return false } @@ -58,25 +148,269 @@ import UIKit return true } - open func setNavigationItem() { - navigationItem.title = pageModel?.screenHeading - navigationItem.accessibilityLabel = pageModel?.screenHeading + /// Calls processNewData and then sets the ui to update with updateView + open func handleNewDataAndUpdateUI() { + handleNewData() + needsUpdateUI = true + view.setNeedsLayout() } - open func newDataBuildScreen() { - // TODO atomize the navigation item - setNavigationItem() + /// 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() + 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 + if formValidator == nil { + let rules = pageModel?.formRules + formValidator = FormValidator(rules) + } + } + + // MARK: - Navigation Item (Move to model base) + open func setNavigationController() { + guard let navigationItemModel = pageModel?.navigationItem, + let navigationController = manager?.navigationController ?? navigationController else { + MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() + return + } + let viewController = manager ?? self + if navigationController == MVMCoreUISplitViewController.main()?.navigationController, + navigationController.topViewController == viewController { + MVMCoreUISession.sharedGlobal()?.splitViewController?.setupPanels() + showBottomProgressBar() + } + NavigationController.set(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + } + + // Eventually will be moved to server + open func isMasterInitiallyAccessible() -> Bool { + if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { + return false + } + return MVMCoreUISession.sharedGlobal()?.launchAppLoadedSuccessfully ?? false + } + + // Eventually will be moved to server + open func isSupportInitiallyAccessible() -> Bool { + if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { + return false + } + return (MVMCoreUISession.sharedGlobal()?.launchAppLoadedSuccessfully ?? false) || showRightPanelForScreenBeforeLaunchApp() + } + + open func showRightPanelForScreenBeforeLaunchApp() -> Bool { + return loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false } + // Eventually will be moved to separate button in navigation item model + open func isOverridingRightButton() -> Bool { + guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink") else { + return false + } + MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject()) + return true + } + + // Eventually will be moved to separate button in navigation item model + open func isOverridingLeftButton() -> Bool { + guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink") else { + return false + } + MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject()) + return true + } + + // Eventually will be moved to Model + open func showBottomProgressBar() { + if MVMCoreUISplitViewController.main()?.getCurrentVisibleController() == self, + let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), + let progress = Float(progressString) { + MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(progress / Float(100)) + } + } + + // MARK: - View lifecycle open func initialLoad() { + observeForResponseJSONUpdates() } open func updateViews() { + _ = formValidator?.validate() } override open func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. + MVMCoreLoggingHandler.logDebugMessage(withDelegate: "View Controller Loaded : \(self)") + + // We use our own margins. + viewRespectsSystemMinimumLayoutMargins = false + + // Presents from the bottom. + modalPresentationStyle = MVMCoreGetterUtility.isOnIPad() ? UIModalPresentationStyle.formSheet : UIModalPresentationStyle.overCurrentContext + + // Create the default delegate object. + delegateObjectIVar = MVMCoreUIDelegateObject.create(withDelegateForAll: self) + + // Do some initial loading. + if !initialLoadFinished { + initialLoadFinished = true + initialLoad() + } + + // Handle data for first load. Dispatched to allow subclasses to finish their view did load implementations. + DispatchQueue.main.async { + self.handleNewDataAndUpdateUI() + } + } + + open override func viewDidLayoutSubviews() { + // Add to fix a constraint bug where the width is zero and things get messed up. + guard isViewLoaded, view.bounds.width > 1 else { + super.viewDidLayoutSubviews() + return + } + + // First update should be explicit (hence the zero check) + if needsUpdateUI || (previousScreenSize != .zero && screenSizeChanged()) { + updateViews() + needsUpdateUI = false + } + + previousScreenSize = view.bounds.size; + + super.viewDidLayoutSubviews() + } + + open override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + // Update the navigation bar ui when view is appearing + setNavigationController() + } + + open override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if manager == nil { + MVMCoreUISession.sharedGlobal()?.currentPageType = pageType + MVMCoreUILoggingHandler.shared()?.defaultLogPageState(forController: self) + } + } + + deinit { + stopObservingForResponseJSONUpdates() + MVMCoreLoggingHandler.logDebugMessage(withDelegate: "View Controller Deallocated : \(self)") + } + + open override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + return MVMCoreGetterUtility.isOnIPad() ? UIInterfaceOrientationMask.all : UIInterfaceOrientationMask.portrait + } + + open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + // Updates the detail view width + coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in + }) { (UIViewControllerTransitionCoordinatorContext) in + self.view.setNeedsLayout() + } + } + + // MARK: - MVMCoreViewManagerViewControllerProtocol + open func viewControllerReady(inManager manager: UIViewController & MVMCoreViewManagerProtocol) { + // Janky way to track current page. + MVMCoreUISession.sharedGlobal()?.currentPageType = pageType + MVMCoreUILoggingHandler.shared()?.defaultLogPageState(forController: self) + } + + // MARK: - MVMCoreActionDelegateProtocol + open func handleOpenPage(for requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) { + formValidator?.addFormParams(requestParameters: requestParameters) + requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType") + MVMCoreActionHandler.defaultHandleOpenPage(for: requestParameters, additionalData: additionalData, delegateObject: delegateObject()) + } + + open func logAction(withActionInformation actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) { + MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: self, actionInformation: actionInformation, additionalData: additionalData) + } + + // MARK: - MoleculeDelegateProtocol + open func getModuleWithName(_ name: String?) -> [AnyHashable : Any]? { + guard let name = name else { return nil } + return loadObject?.modulesJSON?.optionalDictionaryForKey(name) + } + + open func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? { + guard let moduleJSON = loadObject?.modulesJSON?.optionalDictionaryForKey(moleculeName), + let moleculeName = moduleJSON.optionalStringForKey("moleculeName"), + let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) + else { return nil } + do { + return try modelType.decode(jsonDict: moduleJSON) as? MoleculeModelProtocol + } catch { + MVMCoreUILoggingHandler.logDebugMessage(withDelegate: "error: \(error)") + } + + return nil + } + + // Needed otherwise when subclassed, the extension gets called. + open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {} + open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { return nil } + open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) {} + open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) {} + + // MARK: - UITextFieldDelegate (Check if this is still needed) + // To Remove TextFields Bug: Keyboard is not dismissing after reaching textfield max length limit + open func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } + + open func textFieldDidBeginEditing(_ textField: UITextField) { + selectedField = textField + + // TODO: Make this into a protocol + if UIAccessibility.isVoiceOverRunning { + if let toolBar = textField.inputAccessoryView as? UIToolbar, let _ = toolBar.items?.last, let pickerView = textField.inputView as? UIPickerView { + view.accessibilityElements = [pickerView, toolBar] + } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: textField.inputView) + } + } + } + + open func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) { + if textField === selectedField { + if UIAccessibility.isVoiceOverRunning { + view.accessibilityElements = nil + UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: textField) + } + selectedField = nil + } + } + + @objc open func dismissFieldInput(sender: Any?) { + selectedField?.resignFirstResponder() + } + + // MARK: - UITextViewDelegate (Check if this is still needed) + open func textViewDidBeginEditing(_ textView: UITextView) { + selectedField = textView + } + + open func textViewDidEndEditing(_ textView: UITextView) { + if textView === selectedField { + selectedField = nil + } } } diff --git a/MVMCoreUI/Categories/UIColor+Extension.swift b/MVMCoreUI/Categories/UIColor+Extension.swift index 94bf780d..1ef2e217 100644 --- a/MVMCoreUI/Categories/UIColor+Extension.swift +++ b/MVMCoreUI/Categories/UIColor+Extension.swift @@ -33,7 +33,6 @@ extension UIColor { "green": (.mvmGreen, "#008330"), "green33": (.mvmGreen33, "#ABE4BF"), "green66": (.mvmGreen66, "#57C880"), - "greenShade1": (.mvmGreenShade1, "#178437"), "greenShade2": (.mvmGreenShade2, "#0F5B25"), "orange": (.mvmOrange, "#ED7000"), "orange66": (.mvmOrange66, "#F3A157"), @@ -42,15 +41,11 @@ extension UIColor { "orangeShade2": (.mvmOrangeShade2, "#984700"), "orangeAA": (.mvmOrangeAA, "#CC4D0F"), "blue": (.mvmBlue, "#0077B4"), - "blue33": (.mvmBlue33, "#57B1DF"), + "blue33": (.mvmBlue33, "#ABD8EF"), "blue66": (.mvmBlue66, "#57B1DF"), "blueShade1": (.mvmBlueShade1, "#136598"), "blueShade2": (.mvmBlueShade2, "#0B4467"), "yellow": (.mvmYellow, "#FFBC3D"), - "yellow33": (.mvmYellow33, "#FFE9BF"), - "yellow66": (.mvmYellow66, "#FFD37F"), - "yellowShade1": (.mvmYellowShade1, "#CC9630"), - "yellowShade2": (.mvmYellowShade2, "#997126"), "coolGray1": (.mvmCoolGray1, "#F6F6F6"), "coolGray3": (.mvmCoolGray3, "#D8DADA"), "coolGray6": (.mvmCoolGray6, "#747676"), @@ -149,9 +144,6 @@ extension UIColor { /// HEX: #57C880 public static let mvmGreen66 = UIColor.color8Bits(red: 87, green: 200, blue: 128) - /// HEX: #178437 - public static let mvmGreenShade1 = UIColor.color8Bits(red: 23, green: 132, blue: 55) - /// HEX: #0F5B25 public static let mvmGreenShade2 = UIColor.color8Bits(red: 15, green: 91, blue: 37) @@ -162,8 +154,8 @@ extension UIColor { /// HEX: #0077B4 public static let mvmBlue = UIColor.color8Bits(red: 0, green: 119, blue: 180) - /// HEX: #57B1DF - public static let mvmBlue33 = UIColor.color8Bits(red: 87, green: 177, blue: 223) + /// HEX: #ABD8EF + public static let mvmBlue33 = UIColor.color8Bits(red: 171, green: 216, blue: 239) /// HEX: #57B1DF public static let mvmBlue66 = UIColor.color8Bits(red: 87, green: 177, blue: 223) @@ -181,18 +173,6 @@ extension UIColor { /// HEX: #FFBC3D public static let mvmYellow = UIColor.color8Bits(red: 255, green: 188, blue: 61) - /// HEX: #FFE9BF - public static let mvmYellow33 = UIColor.color8Bits(red: 255, green: 233, blue: 191) - - /// HEX: #FFD37F - public static let mvmYellow66 = UIColor.color8Bits(red: 255, green: 211, blue: 127) - - /// HEX: #CC9630 - public static let mvmYellowShade1 = UIColor.color8Bits(red: 204, green: 150, blue: 48) - - /// HEX: #997126 - public static let mvmYellowShade2 = UIColor.color8Bits(red: 153, green: 113, blue: 38) - //-------------------------------------------------- // MARK: - Gray //-------------------------------------------------- @@ -289,7 +269,7 @@ extension UIColor { return .white } - public class func setBackgroundColor(forNavigationBar color: UIColor, navigationBar: UINavigationBar, transparent: Bool) { + public class func setBackgroundColor(_ color: UIColor, for navigationBar: UINavigationBar, isTransparent: Bool) { DispatchQueue.main.async { @@ -305,7 +285,7 @@ extension UIColor { let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() - if transparent { + if isTransparent { navigationBar.setBackgroundImage(UIImage(), for: .default) navigationBar.isTranslucent = false } else { diff --git a/MVMCoreUI/Categories/UIColor+MFConvenience.h b/MVMCoreUI/Categories/UIColor+MFConvenience.h index c3c78c7a..92b28a36 100644 --- a/MVMCoreUI/Categories/UIColor+MFConvenience.h +++ b/MVMCoreUI/Categories/UIColor+MFConvenience.h @@ -24,6 +24,7 @@ + (nonnull UIColor *)mvmOrange; + (nonnull UIColor *)mfPumpkinColor; + (nonnull UIColor *)mfShamrock; ++ (nonnull UIColor *)mvmGreen; + (nonnull UIColor *)mfCerulean; + (nonnull UIColor *)mfWhiteTwo; diff --git a/MVMCoreUI/Categories/UIColor+MFConvenience.m b/MVMCoreUI/Categories/UIColor+MFConvenience.m index 6ff8f70f..33df3848 100644 --- a/MVMCoreUI/Categories/UIColor+MFConvenience.m +++ b/MVMCoreUI/Categories/UIColor+MFConvenience.m @@ -61,6 +61,10 @@ return [UIColor mfColor8bitsWithRed:0 green:134 blue:49 alpha:1.0]; } ++ (nonnull UIColor *)mvmGreen { + return [UIColor mfColor8bitsWithRed:0 green:134 blue:48 alpha:1.0]; +} + + (nonnull UIColor *)mfCerulean { return [UIColor mfColor8bitsWithRed:0 green:122 blue:184 alpha:1.0]; } diff --git a/MVMCoreUI/Categories/UIStackView+Extension.swift b/MVMCoreUI/Categories/UIStackView+Extension.swift index e968fd7c..b88dfb51 100644 --- a/MVMCoreUI/Categories/UIStackView+Extension.swift +++ b/MVMCoreUI/Categories/UIStackView+Extension.swift @@ -8,16 +8,18 @@ import Foundation -extension UIStackView: MVMCoreUIMoleculeViewProtocol { +extension UIStackView: MVMCoreViewProtocol { public func updateView(_ size: CGFloat) { for view in arrangedSubviews { (view as? MVMCoreViewProtocol)?.updateView(size) } } - +} + +extension UIStackView: MoleculeViewProtocol { public func reset() { for view in arrangedSubviews { - (view as? MVMCoreUIMoleculeViewProtocol)?.reset?() + (view as? MoleculeViewProtocol)?.reset() } } } diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 6e18e427..eb869e89 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -39,4 +39,34 @@ import UIKit MVMCoreUISession.sharedGlobal()?.setup(asStandardLoadViewDelegate: navigationController) return navigationController } + + public static func set(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { + viewController.navigationItem.title = navigationItemModel.title + viewController.navigationItem.accessibilityLabel = navigationItemModel.title + viewController.navigationItem.hidesBackButton = !navigationItemModel.systemBackButton + + navigationController.setNavigationBarHidden(navigationItemModel.hidden, animated: true) + UIColor.setBackgroundColor(navigationItemModel.backgroundColor?.uiColor ?? .white, for: navigationController.navigationBar, isTransparent: navigationItemModel.transparent) + + let tint = navigationItemModel.tintColor.uiColor + navigationController.navigationBar.tintColor = tint + + // 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) + } + + if navigationController == MVMCoreUISplitViewController.main()?.navigationController, + navigationController.topViewController == viewController { + // Update Panels + MVMCoreUISplitViewController.main()?.setLeftPanelIsAccessible(navigationItemModel.showLeftPanelButton ?? false, for: viewController) + MVMCoreUISplitViewController.main()?.setRightPanelIsAccessible(navigationItemModel.showRightPanelButton ?? false, for: viewController) + MVMCoreUISession.sharedGlobal()?.splitViewController?.setNavigationIconColor(tint) + } + } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index 44642f3a..342d4b06 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -100,7 +100,7 @@ CGFloat const PanelAnimationDuration = 0.2; - (MFNumberOfDrawers)numberOfDrawersShouldShow:(NSNumber *)forWidth { CGFloat width; - if (forWidth) { + if (forWidth != nil) { width = [forWidth floatValue]; } else { width = [MVMCoreUISplitViewController getApplicationViewWidth]; @@ -153,11 +153,11 @@ CGFloat const PanelAnimationDuration = 0.2; - (IBAction)leftPanelButtonPressed:(id)sender { [MVMCoreDispatchUtility performBlockOnMainThread:^{ UIViewController *viewController = self.navigationController.topViewController; - + UIViewController *detailViewController = [self getCurrentDetailViewController]; // If Viewcontroller confirms to protocol and implements the the method leftPanelButtonPressed - if ([viewController conformsToProtocol:@protocol(MVMCoreUIDetailViewProtocol)] - && [viewController respondsToSelector:@selector(isOverridingLeftButton)] - && [((UIViewController *)viewController) isOverridingLeftButton]) { + if ([detailViewController conformsToProtocol:@protocol(MVMCoreUIDetailViewProtocol)] + && [detailViewController respondsToSelector:@selector(isOverridingLeftButton)] + && [((UIViewController *)detailViewController) isOverridingLeftButton]) { return; } @@ -175,9 +175,9 @@ CGFloat const PanelAnimationDuration = 0.2; - (IBAction)backButtonPressed:(id)sender { [MVMCoreDispatchUtility performBlockOnMainThread:^{ - UIViewController *viewController = self.navigationController.topViewController; - if ([viewController conformsToProtocol:@protocol(MVMCoreUIDetailViewProtocol)] && [viewController respondsToSelector:@selector(backButtonPressed)]) { - [((UIViewController *)viewController) backButtonPressed]; + UIViewController *detailViewController = [self getCurrentDetailViewController]; + if ([detailViewController conformsToProtocol:@protocol(MVMCoreUIDetailViewProtocol)] && [detailViewController respondsToSelector:@selector(backButtonPressed)]) { + [((UIViewController *)detailViewController) backButtonPressed]; } else { [[MVMCoreNavigationHandler sharedNavigationHandler] popTopViewControllerAnimated:YES]; } @@ -187,11 +187,12 @@ CGFloat const PanelAnimationDuration = 0.2; - (IBAction)rightPanelButtonPressed:(id)sender { [MVMCoreDispatchUtility performBlockOnMainThread:^{ UIViewController *viewController = self.navigationController.topViewController; + UIViewController *detailViewController = [self getCurrentDetailViewController]; // If Viewcontroller confirms to protocol and implements the the method rightPanelButtonPressed - if ([viewController conformsToProtocol:@protocol(MVMCoreUIDetailViewProtocol)] - && [viewController respondsToSelector:@selector(isOverridingRightButton)] - && [((UIViewController *)viewController) isOverridingRightButton]) { + if ([detailViewController conformsToProtocol:@protocol(MVMCoreUIDetailViewProtocol)] + && [detailViewController respondsToSelector:@selector(isOverridingRightButton)] + && [((UIViewController *)detailViewController) isOverridingRightButton]) { return; } @@ -814,7 +815,6 @@ CGFloat const PanelAnimationDuration = 0.2; // The main view. NavigationController *navigationController = [NavigationController setupNavigationController]; self.navigationController = navigationController; - self.automaticallyAdjustsScrollViewInsets = NO; UIView *mainView = navigationController.view; mainView.translatesAutoresizingMaskIntoConstraints = NO; diff --git a/MVMCoreUI/Containers/TabBarController/MVMCoreUITabBarPageControlViewController.m b/MVMCoreUI/Containers/TabBarController/MVMCoreUITabBarPageControlViewController.m index cb48eb54..1df6bd67 100644 --- a/MVMCoreUI/Containers/TabBarController/MVMCoreUITabBarPageControlViewController.m +++ b/MVMCoreUI/Containers/TabBarController/MVMCoreUITabBarPageControlViewController.m @@ -181,7 +181,7 @@ __block NSInteger currentIndex = NSNotFound; if (!self.loadObject.pageDataFromCache) { NSNumber *currentIndexNumber = [self.loadObject.pageJSON optionalNumberForKey:@"currentTabIndex"]; - if (currentIndexNumber) { + if (currentIndexNumber != nil) { currentIndex = [currentIndexNumber integerValue]; } } @@ -227,7 +227,7 @@ self.selectedIndex = i; [tabbar selectIndex:self.selectedIndex animated:NO]; - if ([self.viewController respondsToSelector:@selector(shouldCacheInManager)] && [((UIViewController *)self.viewController) shouldCacheInManager]) { + if (![self.viewController respondsToSelector:@selector(shouldCacheInManager)] || [((UIViewController *)self.viewController) shouldCacheInManager]) { [viewControllers addObject:self.viewController]; } else { [viewControllers addObject:[NSNull null]]; diff --git a/MVMCoreUI/Containers/Views/Container/Container.swift b/MVMCoreUI/Containers/Views/Container/Container.swift index a08cd651..eb05cd21 100644 --- a/MVMCoreUI/Containers/Views/Container/Container.swift +++ b/MVMCoreUI/Containers/Views/Container/Container.swift @@ -15,13 +15,18 @@ open class Container: View, ContainerProtocol { get { return model as? ContainerModelProtocol } } - // MARK:- ModelMoleculeViewProtocol + // MARK:- MoleculeViewProtocol override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let containerModel = model as? ContainerModelProtocol else { return } containerHelper.set(with: containerModel, for: view as? MVMCoreUIViewConstrainingProtocol) } + override open func reset() { + super.reset() + (view as? MoleculeViewProtocol)?.reset() + } + // MARK:- ContainerProtocol open func alignHorizontal(_ alignment: UIStackView.Alignment) { containerHelper.alignHorizontal(alignment) @@ -62,21 +67,3 @@ public extension Container { addAndContain(view) } } - -// MARK: - MVMCoreUIMoleculeViewProtocol -public extension Container { - override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - guard let view = view else { return } - containerHelper.set(with: json, for: view) - } - - override func reset() { - super.reset() - (view as? MVMCoreUIMoleculeViewProtocol)?.reset?() - } - - override func setAsMolecule() { - (view as? MVMCoreUIMoleculeViewProtocol)?.setAsMolecule?() - } -} diff --git a/MVMCoreUI/Containers/Views/Container/ContainerModel.swift b/MVMCoreUI/Containers/Views/Container/ContainerModel.swift index a18e8589..ec6f9769 100644 --- a/MVMCoreUI/Containers/Views/Container/ContainerModel.swift +++ b/MVMCoreUI/Containers/Views/Container/ContainerModel.swift @@ -9,6 +9,10 @@ import Foundation open class ContainerModel: ContainerModelProtocol, Codable { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public var horizontalAlignment: UIStackView.Alignment? public var verticalAlignment: UIStackView.Alignment? public var useHorizontalMargins: Bool? @@ -17,6 +21,10 @@ open class ContainerModel: ContainerModelProtocol, Codable { public var topMarginPadding: CGFloat? public var bottomMarginPadding: CGFloat? + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case verticalAlignment case horizontalAlignment @@ -26,6 +34,10 @@ open class ContainerModel: ContainerModelProtocol, Codable { case bottomMarginPadding } + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public init() {} public convenience init(horizontalAlignment: UIStackView.Alignment? = nil, verticalAlignment: UIStackView.Alignment? = nil) { @@ -33,6 +45,10 @@ open class ContainerModel: ContainerModelProtocol, Codable { self.horizontalAlignment = horizontalAlignment self.verticalAlignment = verticalAlignment } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) diff --git a/MVMCoreUI/Containers/Views/EntryFieldContainer.swift b/MVMCoreUI/Containers/Views/EntryFieldContainer.swift index 773721f7..3a8fac64 100644 --- a/MVMCoreUI/Containers/Views/EntryFieldContainer.swift +++ b/MVMCoreUI/Containers/Views/EntryFieldContainer.swift @@ -17,7 +17,7 @@ import UIKit /// The bottom border line. Height is dynamic based on scenario. public var bottomBar: CAShapeLayer? = { let layer = CAShapeLayer() - layer.backgroundColor = UIColor.black.cgColor + layer.backgroundColor = UIColor.mvmBlack.cgColor layer.drawsAsynchronously = true layer.anchorPoint = CGPoint(x: 0.5, y: 1.0); return layer @@ -46,7 +46,7 @@ import UIKit /// Determines if the top, left, and right borders should be drawn. private var hideBorders = false - public var borderStrokeColor: UIColor = .mfSilver() + public var borderStrokeColor: UIColor = .mvmCoolGray3 private var borderPath: UIBezierPath = UIBezierPath() //-------------------------------------------------- @@ -210,8 +210,8 @@ import UIKit isUserInteractionEnabled = true hideBorders = false - borderStrokeColor = .mfSilver() - bottomBar?.backgroundColor = UIColor.black.cgColor + borderStrokeColor = .mvmCoolGray3 + bottomBar?.backgroundColor = UIColor.mvmBlack.cgColor refreshUI(bottomBarSize: 1) } @@ -219,8 +219,8 @@ import UIKit isUserInteractionEnabled = true hideBorders = false - borderStrokeColor = .mfPumpkin() - bottomBar?.backgroundColor = UIColor.mfPumpkin().cgColor + borderStrokeColor = .mvmOrange + bottomBar?.backgroundColor = UIColor.mvmOrange.cgColor refreshUI(bottomBarSize: 4) } @@ -228,8 +228,8 @@ import UIKit isUserInteractionEnabled = true hideBorders = false - borderStrokeColor = .black - bottomBar?.backgroundColor = UIColor.mfPumpkin().cgColor + borderStrokeColor = .mvmBlack + bottomBar?.backgroundColor = UIColor.mvmOrange.cgColor refreshUI(bottomBarSize: 4) } @@ -237,8 +237,8 @@ import UIKit isUserInteractionEnabled = true hideBorders = false - borderStrokeColor = .black - bottomBar?.backgroundColor = UIColor.black.cgColor + borderStrokeColor = .mvmBlack + bottomBar?.backgroundColor = UIColor.mvmBlack.cgColor refreshUI(bottomBarSize: 1) } @@ -255,25 +255,33 @@ import UIKit isUserInteractionEnabled = false hideBorders = false - borderStrokeColor = .mfSilver() - bottomBar?.backgroundColor = UIColor.mfSilver().cgColor + borderStrokeColor = .mvmCoolGray3 + bottomBar?.backgroundColor = UIColor.mvmCoolGray3.cgColor refreshUI(bottomBarSize: 1) } - open func refreshUI(bottomBarSize: CGFloat? = nil) { + open func refreshUI(bottomBarSize: CGFloat? = nil, updateMoleculeLayout: Bool = false) { if !disableAllBorders { let size: CGFloat = bottomBarSize ?? (showError ? 4 : 1) + var heightChanged = false + + if let bottomHeight = bottomBar?.bounds.height { + heightChanged = size != bottomHeight + } bottomBar?.frame = CGRect(x: 0, y: bounds.height - size, width: bounds.width, height: size) - delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + if updateMoleculeLayout || heightChanged { + delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + } + setNeedsDisplay() layoutIfNeeded() } } //-------------------------------------------------- - // MARK: - MVMCoreUIMoleculeViewProtocol + // MARK: - MoleculeViewProtocol //-------------------------------------------------- open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { @@ -281,13 +289,3 @@ import UIKit self.delegateObject = delegateObject } } - -extension EntryFieldContainer { - - override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - self.delegateObject = delegateObject - - guard let dictionary = json, !dictionary.isEmpty else { return } - } -} diff --git a/MVMCoreUI/Containers/Views/MoleculeContainer.swift b/MVMCoreUI/Containers/Views/MoleculeContainer.swift index f98451dd..98ecfa28 100644 --- a/MVMCoreUI/Containers/Views/MoleculeContainer.swift +++ b/MVMCoreUI/Containers/Views/MoleculeContainer.swift @@ -14,28 +14,13 @@ open class MoleculeContainer: Container { public func addMolecule(_ molecule: UIView) { addAndContain(molecule) } - - override public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule) else { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - return - } - if view == nil { - if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: false) { - addAndContain(molecule) - } - } else { - (view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON?(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) - } - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { if let casteModel = model as? MoleculeContainerModelProtocol { if view != nil { - (view as? ModelMoleculeViewProtocol)?.set(with: casteModel.molecule, delegateObject, additionalData) + (view as? MoleculeViewProtocol)?.set(with: casteModel.molecule, delegateObject, additionalData) } else { - if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(casteModel.molecule, delegateObject) { + if let molecule = MoleculeObjectMapping.shared()?.createMolecule(casteModel.molecule, delegateObject: delegateObject, additionalData: additionalData) { addMolecule(molecule) } } @@ -45,16 +30,16 @@ open class MoleculeContainer: Container { public override static func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { guard let containerModel = model as? MoleculeContainerModelProtocol, - let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(containerModel.molecule) as? ModelMoleculeViewProtocol.Type, + let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(containerModel.molecule), let moleculeName = moleculeClass.nameForReuse(with: containerModel.molecule, delegateObject) else { - return "\(model.moleculeName ?? "moleculeContainer")<>" + return "\(model.moleculeName)<>" } - return "\(model.moleculeName ?? "moleculeContainer")<\(moleculeName)>" + return "\(model.moleculeName)<\(moleculeName)>" } public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { guard let containerModel = model as? MoleculeContainerModelProtocol else { return 0 } - guard let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(containerModel.molecule) as? ModelMoleculeViewProtocol.Type, + guard let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(containerModel.molecule), let moleculeHeight = moleculeClass.estimatedHeight(with: containerModel.molecule, delegateObject) else { return (containerModel.topMarginPadding ?? 0) + (containerModel.bottomMarginPadding ?? 0) } @@ -63,7 +48,7 @@ open class MoleculeContainer: Container { public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { guard let containerModel = model as? MoleculeContainerModelProtocol, - let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(containerModel.molecule) as? ModelMoleculeViewProtocol.Type else { return nil } + let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(containerModel.molecule) else { return nil } return moleculeClass.requiredModules(with: containerModel.molecule, delegateObject, error: error) } } diff --git a/MVMCoreUI/FormUIHelpers/FormActionFieldProtocol.swift b/MVMCoreUI/FormUIHelpers/FormActionFieldProtocol.swift deleted file mode 100644 index 31208e95..00000000 --- a/MVMCoreUI/FormUIHelpers/FormActionFieldProtocol.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// FormActionFieldProtocol.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 1/31/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import Foundation - -public protocol FormActionFieldProtocol: EnableableModelProtocol, FormItemProtocol { - func updateEnable(_ enabled: Bool) - var updateUI: (() -> Void)? { get set } -} diff --git a/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift b/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift index c59ccd08..b9d2defe 100644 --- a/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift @@ -5,13 +5,19 @@ // Created by Suresh, Kamlesh on 1/31/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // +// Form fields are items can be interacted with. They have value, and may need to be validated. import Foundation public protocol FormFieldProtocol: FormItemProtocol { + /// How the validator identifies the field when validating rules. var fieldKey: String? { get set } + + /// A place to store the initial value of the field for checking if the value has changed. var baseValue: AnyHashable? { get set } + + /// Returns the value of the field. Used for validations and possibly for sending to server. func formFieldValue() -> AnyHashable? } diff --git a/MVMCoreUI/FormUIHelpers/FormGroupWatcherFieldProtocol.swift b/MVMCoreUI/FormUIHelpers/FormGroupWatcherFieldProtocol.swift new file mode 100644 index 00000000..624ea374 --- /dev/null +++ b/MVMCoreUI/FormUIHelpers/FormGroupWatcherFieldProtocol.swift @@ -0,0 +1,14 @@ +// +// FormGroupWatcherFieldProtocol.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 1/31/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// +// Fields that want to be informed of group validity. + +import Foundation + +public protocol FormGroupWatcherFieldProtocol: FormItemProtocol { + func setValidity(_ valid: Bool, group: FormGroupRule) +} diff --git a/MVMCoreUI/FormUIHelpers/FormHolderModelProtocol.swift b/MVMCoreUI/FormUIHelpers/FormHolderModelProtocol.swift new file mode 100644 index 00000000..b548024c --- /dev/null +++ b/MVMCoreUI/FormUIHelpers/FormHolderModelProtocol.swift @@ -0,0 +1,14 @@ +// +// FormHolderModelProtocol.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 2/5/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// +// A protocol for the model of the delegate object that holds the form validator. The rules that need to be validated are stored in the model. + +import Foundation + +public protocol FormHolderModelProtocol { + var formRules: [FormGroupRule]? { get set } +} diff --git a/MVMCoreUI/FormUIHelpers/FormHolderProtocol.swift b/MVMCoreUI/FormUIHelpers/FormHolderProtocol.swift index 57c41a21..26bceac4 100644 --- a/MVMCoreUI/FormUIHelpers/FormHolderProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/FormHolderProtocol.swift @@ -5,13 +5,10 @@ // Created by Suresh, Kamlesh on 2/26/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // +// A delegate object that holds the form validator. import Foundation - -//Protocol for Validation public protocol FormHolderProtocol: NSObjectProtocol { var formValidator: FormValidator? { get set } - /// Should call formValidator's validate method - func validate() } diff --git a/MVMCoreUI/FormUIHelpers/FormItemProtocol.swift b/MVMCoreUI/FormUIHelpers/FormItemProtocol.swift index 9b671238..0c7f71bd 100644 --- a/MVMCoreUI/FormUIHelpers/FormItemProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/FormItemProtocol.swift @@ -5,15 +5,11 @@ // Created by Suresh, Kamlesh on 2/25/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // +// The base form item protocol. Shared by different types of items. In our case, the models (not views) are form items which are registered with the validator. import Foundation public protocol FormItemProtocol { - static var defaultGroupName: String? { get } - var groupName: String? { get set } -} -extension FormItemProtocol{ - public static var defaultGroupName: String? { - return "default" - } + /// The group name to used to group this item for validation, enableability, and value getting. + var groupName: String { get set } } diff --git a/MVMCoreUI/FormUIHelpers/FormRuleWatcherFieldProtocol.swift b/MVMCoreUI/FormUIHelpers/FormRuleWatcherFieldProtocol.swift new file mode 100644 index 00000000..3fc47d25 --- /dev/null +++ b/MVMCoreUI/FormUIHelpers/FormRuleWatcherFieldProtocol.swift @@ -0,0 +1,14 @@ +// +// FormRuleWatcherFieldProtocol.swift +// MVMCoreUI +// +// Created by Suresh, Kamlesh on 3/2/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// +// Fields that want to be informed of rule validity. + +import Foundation + +public protocol FormRuleWatcherFieldProtocol { + func setValidity(_ valid: Bool, rule: RulesProtocol) +} diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index 8977fad5..744304ba 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -9,108 +9,132 @@ import UIKit import MVMCore -@objcMembers public class FormValidator: NSObject { +@objcMembers public class FormValidator: NSObject { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + static var defaultGroupName: String = "default" - var extraValidationBlock: (() -> Bool)? var formRules: [FormGroupRule]? - var delegate: FormHolderProtocol? - var fieldMolecules: [String: FormFieldProtocol] = [:] - var formActionMolecules: [FormActionFieldProtocol] = [] + weak var delegate: FormHolderProtocol? + var fields: [String: FormFieldProtocol] = [:] + var groupWatchers: [FormGroupWatcherFieldProtocol] = [] var radioButtonsModelByGroup: [String: RadioButtonSelectionHelper] = [:] + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(_ formRules: [FormGroupRule]?) { self.formRules = formRules } - public func insertMolecule(_ molecule: FormItemProtocol) { - if var molecule = molecule as? FormFieldProtocol, - let fieldKey = molecule.fieldKey { - if molecule.baseValue == nil { - molecule.baseValue = molecule.formFieldValue() - } - fieldMolecules[fieldKey] = molecule - } - if let molecule = molecule as? FormActionFieldProtocol { - formActionMolecules.append(molecule) + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + /// Adds the form field to the validator. + public func add(_ field: FormFieldProtocol) { + if let fieldKey = field.fieldKey { + fields[fieldKey] = field } } - public static func setupValidation(molecule: FormItemProtocol, delegate: FormHolderProtocol?) { + /// Adds the form action to the validator. + public func add(_ action: FormGroupWatcherFieldProtocol) { + groupWatchers.append(action) + } + + /// Determines the type of item and adds it. + private func insert(_ item: FormItemProtocol) { + if let item = item as? FormFieldProtocol { + add(item) + } else if let item = item as? FormGroupWatcherFieldProtocol { + add(item) + } + } + + /// Convenience function. Gets the form validator from the holder and sets up the item with it. + public static func setupValidation(for item: FormItemProtocol, delegate: FormHolderProtocol?) { if let validator = delegate?.formValidator { validator.delegate = delegate - validator.insertMolecule(molecule) - } - } - - public static func getFormValidatorFor(delegate: FormHolderProtocol?) -> FormValidator? { - return delegate?.formValidator - } - - public static func validate(delegate: FormHolderProtocol?) { - delegate?.formValidator?.validate() - } - - public func validate() { - guard let formRules = formRules else { - return - } - formActionMolecules.forEach { (actionModel) in - if let groupName = actionModel.groupName, - let formRule = formRules.first(where: { $0.groupName == groupName }) { - validate(groupName, actionModel, formRule.rules) - } + validator.insert(item) + + // TODO: Temporary hacks, rewrite architecture to support this. + _ = validator.validate() } } - public func validate(_ groupName: String, _ actionModel: FormActionFieldProtocol, _ rules: [RulesProtocol]) { + /// Convenience function. Gets the form validator from the holder and asks it to validate. + public static func validate(delegate: FormHolderProtocol?) -> Bool? { + return delegate?.formValidator?.validate() + } + + /// Validates all rule groups. Returns if valid + public func validate() -> Bool { var valid = true - for rule in rules { - valid = valid && rule.isValid(fieldMolecules) - if !valid { - break - } + guard let formRules = formRules else { return valid } + + for group in formRules { + let groupValid = validateGroup(group) + valid = valid && groupValid } - actionModel.updateEnable(valid) + + return valid } - public func formField(for fieldKey: String) -> FormFieldProtocol? { - return fieldMolecules[fieldKey] + /// Validates a given rule group. Returns if valid + public func validateGroup(_ group: FormGroupRule) -> Bool { + // Validate each rule. + var valid = true + for rule in group.rules { + valid = valid && rule.validate(fields) + } + + // Notify the group watchers of validity. + for watcher in groupWatchers { + if watcher.groupName == group.groupName { + watcher.setValidity(valid, group: group) + } + } + return valid } } -// mark Form params +// mark Form params +// TODO: Temporary hacks, rewrite architecture to support this. @objc public extension FormValidator { @objc func addFormParams(requestParameters: MVMCoreRequestParameters) { - let formButton = getFormButton(forPageType: requestParameters.pageType) - let groupName = formButton?.groupName ?? FormValidator.defaultGroupName + let groupName = getGroupName(forPageType: requestParameters.pageType) ?? FormValidator.defaultGroupName let formParams = self.getFormParams(forGroup: groupName) requestParameters.add(formParams) } @objc func getFormParams( forGroup groupName: String) -> [String: Any] { var extraParam: [String: Any] = [:] - MVMCoreDispatchUtility.performSyncBlock(onMainThread: { - for (fieldKey, molecule) in self.fieldMolecules { - if let formFieldValue = molecule.formFieldValue(), - groupName == molecule.groupName { - extraParam[fieldKey] = formFieldValue - } + for (fieldKey, field) in fields { + if let formFieldValue = field.formFieldValue(), + groupName == field.groupName { + extraParam[fieldKey] = formFieldValue } - }) + } return extraParam } } -// Temporary +// TODO: Temporary hacks, rewrite architecture to support this. public extension FormValidator { - func getFormButton(forPageType pageType: String?) -> ButtonModel? { - for actionItem in formActionMolecules { + + func getGroupName(forPageType pageType: String?) -> String? { + + for actionItem in groupWatchers { if let buttonModel = actionItem as? ButtonModel, pageType == (buttonModel.action as? ActionOpenPageModel)?.pageType { - return buttonModel + return buttonModel.groupName } } + return nil } } diff --git a/MVMCoreUI/FormUIHelpers/FormProtocol.swift b/MVMCoreUI/FormUIHelpers/Rules/FormHolderModelProtocol.swift similarity index 50% rename from MVMCoreUI/FormUIHelpers/FormProtocol.swift rename to MVMCoreUI/FormUIHelpers/Rules/FormHolderModelProtocol.swift index bef7f547..a9428a2b 100644 --- a/MVMCoreUI/FormUIHelpers/FormProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/FormHolderModelProtocol.swift @@ -1,15 +1,14 @@ // -// Validatable.swift +// FormHolderModelProtocol.swift // MVMCoreUI // // Created by Suresh, Kamlesh on 2/5/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // +// A protocol for the model of the delegate object that holds the form validator. The rules are stored in the model. import Foundation -//Protocol for Validation -public protocol FormProtocol: class { +public protocol FormHolderModelProtocol { var formRules: [FormGroupRule]? { get set } - var formValidator: FormValidator? { get set } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/FormGroupRule.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/FormGroupRule.swift index 34dfb7b8..56a5a2cf 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/FormGroupRule.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/FormGroupRule.swift @@ -5,24 +5,41 @@ // Created by Suresh, Kamlesh on 2/24/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // +// Used by the form validator for storing the rules for a particular group. import Foundation open class FormGroupRule: Codable { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + var groupName: String var rules: [RulesProtocol] + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + init(_ groupName: String, _ rules: [RulesProtocol]) { self.groupName = groupName self.rules = rules } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case groupName case rules } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) self.groupName = try typeContainer.decode(String.self, forKey: .groupName) diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAllValueChangedModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAllValueChangedModel.swift index 7535c6dd..1f50bcc7 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAllValueChangedModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAllValueChangedModel.swift @@ -9,11 +9,18 @@ import Foundation public class RuleAllValueChangedModel: RulesProtocol { - + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "allValueChanged" public var type: String = RuleAllValueChangedModel.identifier public var fields: [String] - + + //-------------------------------------------------- + // MARK: - Validation + //-------------------------------------------------- + public func isValid(_ formField: FormFieldProtocol) -> Bool { return formField.baseValue != formField.formFieldValue() } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyRequiredModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyRequiredModel.swift index d41c0b0c..6ca905bf 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyRequiredModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyRequiredModel.swift @@ -35,7 +35,7 @@ public class RuleAnyRequiredModel: RulesProtocol { return false } - public func isValid(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { + public func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { for formKey in fields { guard let formField = fieldMolecules[formKey] else { continue } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyValueChangedModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyValueChangedModel.swift index 042243db..450fdb2a 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyValueChangedModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyValueChangedModel.swift @@ -8,25 +8,31 @@ import Foundation -public class RuleAnyValueChangedModel: RulesProtocol { +public class RuleAnyValueChangedModel: RulesProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "anyValueChanged" public var type: String = RuleAnyValueChangedModel.identifier public var fields: [String] + //-------------------------------------------------- + // MARK: - Validation + //-------------------------------------------------- + public func isValid(_ formField: FormFieldProtocol) -> Bool { return formField.baseValue != formField.formFieldValue() } - public func isValid(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { + public func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { for formKey in fields { - guard let formField = fieldMolecules[formKey] else { - continue - } + guard let formField = fieldMolecules[formKey] else { continue } if isValid(formField) { return true } - } + } return false } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsModel.swift index e6b338a4..fb7585f2 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsModel.swift @@ -10,31 +10,40 @@ import Foundation public class RuleEqualsModel: RulesProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "equals" public var type: String = RuleEqualsModel.identifier public var fields: [String] + + //-------------------------------------------------- + // MARK: - Validation + //-------------------------------------------------- public func isValid(_ formField: FormFieldProtocol) -> Bool { return false } - public func isValid(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { + public func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { var valid = true var compareValue: AnyHashable? + for formKey in fields { - guard let formField = fieldMolecules[formKey] else { - continue - } + guard let formField = fieldMolecules[formKey] else { continue } if compareValue == nil { compareValue = formField.formFieldValue() continue } - if compareValue != formField.formFieldValue(){ + + if compareValue != formField.formFieldValue() { valid = false break } } + return valid } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRegexModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRegexModel.swift index db1c4859..68eea7e5 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRegexModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRegexModel.swift @@ -8,16 +8,27 @@ import Foundation + public class RuleRegexModel: RulesProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "regex" public var type: String = RuleRegexModel.identifier public var fields: [String] public var regex: String + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public func isValid(_ formField: FormFieldProtocol) -> Bool { + if let stringToValidate = formField.formFieldValue() as? String { return MVMCoreUIUtility.validate(stringToValidate, withRegularExpression: regex) } + return false } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift index 3900249a..b34a24df 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift @@ -10,21 +10,30 @@ import Foundation public class RuleRequiredModel: RulesProtocol { - - public static var identifier: String = "required" + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "allRequired" public var type: String = RuleRequiredModel.identifier public var fields: [String] + //-------------------------------------------------- + // MARK: - Validation + //-------------------------------------------------- + public func isValid(_ formField: FormFieldProtocol) -> Bool { - guard let value = formField.formFieldValue() else { - return false - } + guard let value = formField.formFieldValue() else { return false } + var valid = true + if let valueString = value as? String { valid = valueString.count > 0 + } else if let valueBool = value as? Bool { valid = valueBool } + return valid } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift index 25119e03..305b4c35 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift @@ -14,16 +14,22 @@ public enum RulesCodingKey: String, CodingKey { } public protocol RulesProtocol: ModelProtocol { - var type: String { get set } + // The type of rule + var type: String { get } + + // The fields that this rule applies to. var fields: [String] { get set } - func isValid(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool + + // Returns if a given field is valid according to the rule func isValid(_ formField: FormFieldProtocol) -> Bool - func setValid(_ formField: FormFieldProtocol, _ isValid: Bool) + + // Validates the rule and returns the result. + func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool } public extension RulesProtocol { - var ruleType: String? { + var type: String { get { return Self.identifier } } @@ -35,22 +41,15 @@ public extension RulesProtocol { return "\(RulesProtocol.self)" } - func isValid(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { - var valid = true - - for formKey in fields { - guard let formField = fieldMolecules[formKey] else { continue } - let fieldValidity = isValid(formField) - setValid(formField, fieldValidity) - valid = valid && fieldValidity - } - - return valid - } - - func setValid(_ formField: FormFieldProtocol, _ isValid: Bool) { - guard let formFieldValid = formField as? ValidProtocol else { return } - - formFieldValid.setValidity(isValid) + // Individual rule can override the function to validate based on the rule type. + func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool { + var valid = true + for formKey in fields { + guard let formField = fieldMolecules[formKey] else { continue } + let fieldValidity = isValid(formField) + (formField as? FormRuleWatcherFieldProtocol)?.setValidity(fieldValidity, rule: self) + valid = valid && fieldValidity + } + return valid } } diff --git a/MVMCoreUI/FormUIHelpers/ValidProtocol.swift b/MVMCoreUI/FormUIHelpers/ValidProtocol.swift deleted file mode 100644 index 20afd256..00000000 --- a/MVMCoreUI/FormUIHelpers/ValidProtocol.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// ValidProtocol.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 3/2/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import Foundation - -public protocol ValidProtocol { - var isValid: Bool? { get set } - func setValidity(_ isValid: Bool) - var updateUI: (() -> Void)? { get set } -} diff --git a/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift b/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift new file mode 100644 index 00000000..5e45b6ed --- /dev/null +++ b/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift @@ -0,0 +1,24 @@ +// +// ActionModelAdapter.swift +// MVMCoreUI +// +// Created by Kyle on 3/25/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +public extension Dictionary { + + func asActionModel() throws -> ActionModelProtocol { + guard let castedSelf = self as? [String: Any] else { + throw ModelRegistry.Error.decoderOther(message: "Dictionary is not of type [String: Any]") + } + guard let actionType = ModelRegistry.getType(for: castedSelf.stringForkey(KeyActionType), with: ActionModelProtocol.self) else { + throw ModelRegistry.Error.decoderErrorModelNotMapped + } + guard let actionModel = try actionType.decode(jsonDict: castedSelf) as? ActionModelProtocol else { + throw ModelRegistry.Error.decoderOther(message: "Could not decode to ActionModelProtocol") + } + return actionModel + } + +} diff --git a/MVMCoreUI/BaseControllers/ProgrammaticScrollViewController.h b/MVMCoreUI/Legacy/Controllers/MFProgrammaticScrollViewController.h similarity index 61% rename from MVMCoreUI/BaseControllers/ProgrammaticScrollViewController.h rename to MVMCoreUI/Legacy/Controllers/MFProgrammaticScrollViewController.h index 9e486066..f152b932 100644 --- a/MVMCoreUI/BaseControllers/ProgrammaticScrollViewController.h +++ b/MVMCoreUI/Legacy/Controllers/MFProgrammaticScrollViewController.h @@ -1,5 +1,5 @@ // -// ProgrammaticScrollViewController.h +// MFProgrammaticScrollViewController.h // myverizon // // Created by Scott Pfeil on 1/26/16. @@ -8,7 +8,7 @@ #import -@interface ProgrammaticScrollViewController : MFScrollingViewController +@interface MFProgrammaticScrollViewController : MFScrollingViewController @end diff --git a/MVMCoreUI/BaseControllers/ProgrammaticScrollViewController.m b/MVMCoreUI/Legacy/Controllers/MFProgrammaticScrollViewController.m similarity index 92% rename from MVMCoreUI/BaseControllers/ProgrammaticScrollViewController.m rename to MVMCoreUI/Legacy/Controllers/MFProgrammaticScrollViewController.m index 2bfeb35d..5152ec79 100644 --- a/MVMCoreUI/BaseControllers/ProgrammaticScrollViewController.m +++ b/MVMCoreUI/Legacy/Controllers/MFProgrammaticScrollViewController.m @@ -1,21 +1,21 @@ // -// ProgrammaticScrollViewController.m +// MFProgrammaticScrollViewController.m // myverizon // // Created by Scott Pfeil on 1/26/16. // Copyright © 2016 Verizon Wireless. All rights reserved. // -#import "ProgrammaticScrollViewController.h" +#import "MFProgrammaticScrollViewController.h" @import MVMCore.NSDictionary_MFConvenience; #import "NSLayoutConstraint+MFConvenience.h" #import "MVMCoreUICommonViewsUtility.h" -@interface ProgrammaticScrollViewController () +@interface MFProgrammaticScrollViewController () @end -@implementation ProgrammaticScrollViewController +@implementation MFProgrammaticScrollViewController - (void)loadView { diff --git a/MVMCoreUI/BaseControllers/MFProgrammaticTableViewController.h b/MVMCoreUI/Legacy/Controllers/MFProgrammaticTableViewController.h similarity index 100% rename from MVMCoreUI/BaseControllers/MFProgrammaticTableViewController.h rename to MVMCoreUI/Legacy/Controllers/MFProgrammaticTableViewController.h diff --git a/MVMCoreUI/BaseControllers/MFProgrammaticTableViewController.m b/MVMCoreUI/Legacy/Controllers/MFProgrammaticTableViewController.m similarity index 100% rename from MVMCoreUI/BaseControllers/MFProgrammaticTableViewController.m rename to MVMCoreUI/Legacy/Controllers/MFProgrammaticTableViewController.m diff --git a/MVMCoreUI/BaseControllers/MFScrollingViewController.h b/MVMCoreUI/Legacy/Controllers/MFScrollingViewController.h similarity index 100% rename from MVMCoreUI/BaseControllers/MFScrollingViewController.h rename to MVMCoreUI/Legacy/Controllers/MFScrollingViewController.h diff --git a/MVMCoreUI/BaseControllers/MFScrollingViewController.m b/MVMCoreUI/Legacy/Controllers/MFScrollingViewController.m similarity index 100% rename from MVMCoreUI/BaseControllers/MFScrollingViewController.m rename to MVMCoreUI/Legacy/Controllers/MFScrollingViewController.m diff --git a/MVMCoreUI/BaseControllers/MFViewController.h b/MVMCoreUI/Legacy/Controllers/MFViewController.h similarity index 98% rename from MVMCoreUI/BaseControllers/MFViewController.h rename to MVMCoreUI/Legacy/Controllers/MFViewController.h index 38ced354..cd96a07f 100644 --- a/MVMCoreUI/BaseControllers/MFViewController.h +++ b/MVMCoreUI/Legacy/Controllers/MFViewController.h @@ -96,9 +96,6 @@ // This view controller should subclass this function and check the load to make sure it has all the needed data. Fills the error object if there are any errors. Returns if we should finish the load or not. - (BOOL)shouldFinishProcessingLoad:(nonnull MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error; -/// Called in newDataBuildScreen. Can override to parse the json into a model object. -- (void)parsePageJSON:(NSError * _Nullable * _Nullable)error; - // Sets the screen to use the screen heading. // it is required in device flow, where we are showing greeting name as screen heading, // device details screen heading needs to be updated/refreshed again, if user has changed device nick name diff --git a/MVMCoreUI/BaseControllers/MFViewController.m b/MVMCoreUI/Legacy/Controllers/MFViewController.m similarity index 97% rename from MVMCoreUI/BaseControllers/MFViewController.m rename to MVMCoreUI/Legacy/Controllers/MFViewController.m index fef3b860..68dd75c4 100644 --- a/MVMCoreUI/BaseControllers/MFViewController.m +++ b/MVMCoreUI/Legacy/Controllers/MFViewController.m @@ -88,25 +88,10 @@ self.pageType = loadObject.pageType; self.loadObject = loadObject; - NSError *parseError = nil; - [self parsePageJSON:&parseError]; - if (parseError) { - if (error) { - MVMCoreErrorObject *errorObject = [MVMCoreErrorObject createErrorObjectForNSError:parseError location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]; - errorObject.messageToDisplay = [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess]; - *error = errorObject; - } - return false; - } - // Verifies all modules needed are loaded. return [MFViewController verifyRequiredModulesLoadedForLoadObject:loadObject error:error]; } -- (void)parsePageJSON:(NSError * _Nullable * _Nullable)error { - -} - // Sets the screen to use the screen heading. // it is required in device flow, where we are showing greeting name as screen heading, // device details screen heading needs to be updated/refreshed again, if user has changed device nick name @@ -255,8 +240,6 @@ - (BOOL)newPageLoaded:(nonnull NSDictionary *)page { self.loadObject.pageJSON = page; - NSError *parseError = nil; - [self parsePageJSON:&parseError]; return YES; } @@ -484,7 +467,6 @@ - (void)newDataBuildAndUpdate { [MVMCoreDispatchUtility performBlockOnMainThread:^{ [self newDataBuildScreen]; - [self startValidation]; self.needToUpdateUI = YES; [self.view setNeedsLayout]; }]; @@ -681,8 +663,6 @@ if (requestParameters.openSupportPanel) { [[MVMCoreUISession sharedGlobal].splitViewController.rightPanel willOpenWithActionInformation:actionInformation]; } - - [self addFormParamsWithRequestParameters:requestParameters]; requestParameters.parentPageType = [self.loadObject.pageJSON stringForKey:@"parentPageType"]; [[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegateObject:[self delegateObject]]; } diff --git a/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.h b/MVMCoreUI/Legacy/Controllers/MVMCoreUIStackableViewController.h similarity index 95% rename from MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.h rename to MVMCoreUI/Legacy/Controllers/MVMCoreUIStackableViewController.h index 0c702925..12d02f43 100644 --- a/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.h +++ b/MVMCoreUI/Legacy/Controllers/MVMCoreUIStackableViewController.h @@ -6,9 +6,9 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -#import +#import -@interface MVMCoreUIStackableViewController : ProgrammaticScrollViewController +@interface MVMCoreUIStackableViewController : MFProgrammaticScrollViewController // An array of ui elements that will be spaced out on the screen top to bottom from index 0 to formUIArray.count. @property (nullable, strong, nonatomic) NSArray *formUIArray; diff --git a/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.m b/MVMCoreUI/Legacy/Controllers/MVMCoreUIStackableViewController.m similarity index 100% rename from MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.m rename to MVMCoreUI/Legacy/Controllers/MVMCoreUIStackableViewController.m diff --git a/MVMCoreUI/Legacy/Controllers/TopLabelsAndBottomButtonsViewController.m b/MVMCoreUI/Legacy/Controllers/TopLabelsAndBottomButtonsViewController.m index 5998c5da..f99bce31 100644 --- a/MVMCoreUI/Legacy/Controllers/TopLabelsAndBottomButtonsViewController.m +++ b/MVMCoreUI/Legacy/Controllers/TopLabelsAndBottomButtonsViewController.m @@ -35,8 +35,8 @@ @property (strong, nonatomic) UIView *topAccessoryView; // Adds the button view to the screen. Out of the scroll or in. -- (void)addViewOutsideOfScrollView:(UIView *)bottomView; -- (void)addViewToContentView:(UIView *)bottomView; +- (void)addViewOutsideOfScrollView:(nonnull UIView *)bottomView; +- (void)addViewToContentView:(nonnull UIView *)bottomView; @end @@ -133,8 +133,7 @@ self.topConstraintForTopView.active = YES; // Checks if we are using a different object than the bottom buttons. UIView *bottomView = [self useCustomViewInsteadOfButtons]; - self.customBottemView = (bottomView != nil); - if (!self.customBottemView) { + if (bottomView == nil) { // Sets up the buttons/button. NSDictionary *primaryButtonDictionary = [self primaryButtonMap]; @@ -144,8 +143,10 @@ self.primaryButton = buttonView.primaryButton; bottomView = buttonView; self.bottomView = bottomView; + self.customBottemView = NO; } else { self.bottomView = bottomView; + self.customBottemView = YES; } [self updateTopLabelsBottomButtonsPadding]; @@ -220,7 +221,7 @@ // Needs the height constraint heightConstraint.active = YES; - if (!spaceAbove && !spaceBelow) { + if (spaceAbove == nil && spaceBelow == nil) { // No set space above or below, make the spacers the same height with a default minimum. UIView *topSpacer = [MVMCoreUICommonViewsUtility commonView]; @@ -237,7 +238,7 @@ minimumHeightSpacer.active = YES; [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topBetweenEdgeView]-0-[topSpacer]-0-[viewBetween]-0-[bottomSpacer]-0-[bottomBetweenEdgeView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(topBetweenEdgeView,topSpacer,viewBetween,bottomSpacer,bottomBetweenEdgeView)]]; - } else if (spaceAbove) { + } else if (spaceAbove != nil) { // Space above is set, space below is free. UIView *bottomSpacer = [MVMCoreUICommonViewsUtility commonView]; @@ -249,7 +250,7 @@ NSDictionary *verticalMetrics = @{@"top":spaceAbove}; [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topBetweenEdgeView]-top-[viewBetween]-0-[bottomSpacer]-0-[bottomBetweenEdgeView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:verticalMetrics views:NSDictionaryOfVariableBindings(topBetweenEdgeView,viewBetween,bottomSpacer,bottomBetweenEdgeView)]]; - } else if (spaceBelow) { + } else if (spaceBelow != nil) { // Space below is set, space above is free. UIView *topSpacer = [MVMCoreUICommonViewsUtility commonView]; diff --git a/MVMCoreUI/Legacy/Views/ButtonView.swift b/MVMCoreUI/Legacy/Views/ButtonView.swift index 088c4a00..3bdb909b 100644 --- a/MVMCoreUI/Legacy/Views/ButtonView.swift +++ b/MVMCoreUI/Legacy/Views/ButtonView.swift @@ -36,10 +36,6 @@ import UIKit } // MARK: - MVMCoreViewProtocol - open override func reset() { - primaryButton?.reset() - } - open override func updateView(_ size: CGFloat) { super.updateView(size) MVMCoreDispatchUtility.performBlock(onMainThread: { @@ -54,25 +50,15 @@ import UIKit } // MARK: - MVMCoreUIMoleculeViewProtocol - open override func setAsMolecule() { - super.setAsMolecule() - primaryButton?.setAsMolecule() - } - - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { primaryButton?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) } - public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return 42 - } - + // MARK: - Constraining open override func copyBackgroundColor() -> Bool { return true } - // MARK: - Constraining func setupButton() { if let primaryButton = primaryButton, !subviews.contains(primaryButton) { addSubview(primaryButton) diff --git a/MVMCoreUI/Legacy/Views/LabelView.m b/MVMCoreUI/Legacy/Views/LabelView.m index d13ff39a..0bb98bd6 100644 --- a/MVMCoreUI/Legacy/Views/LabelView.m +++ b/MVMCoreUI/Legacy/Views/LabelView.m @@ -9,6 +9,7 @@ #import #import "MFStyler.h" #import + @interface LabelView () // Sets up the view. @@ -36,11 +37,6 @@ } } -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - [super setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; - [self.label setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; -} - - (void)alignLeft { [super alignLeft]; self.label.textAlignment = NSTextAlignmentLeft; diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Legacy/Views/LabelWithInternalButton.swift similarity index 92% rename from MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift rename to MVMCoreUI/Legacy/Views/LabelWithInternalButton.swift index 251f5b45..13a16a33 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Legacy/Views/LabelWithInternalButton.swift @@ -15,7 +15,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @available(*, deprecated, message: "Use Label instead.") -@objcMembers open class LabelWithInternalButton: UIControl, MVMCoreViewProtocol, MFButtonProtocol, MVMCoreUIMoleculeViewProtocol { +@objcMembers open class LabelWithInternalButton: UIControl, MVMCoreViewProtocol, MFButtonProtocol, MoleculeViewProtocol { //------------------------------------------------------ // MARK: - Properties //------------------------------------------------------ @@ -585,59 +585,4 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } setLabelAttributes() } - - //------------------------------------------------------ - // MARK: - Atomization - //------------------------------------------------------ - - // Default values for view. - @objc open func setAsMolecule() { - - } - - @objc open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - // Configure class properties with JSON values - guard let dictionary = json else { return } - - if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String { - self.backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) - } - - if let normalTextFont = dictionary["normalTextFont"] as? String, let normalSize = dictionary["normalSize"] as? CGFloat { - self.normalTextFont = UIFont(name: normalTextFont, size: normalSize) - } - - if let actionTextFont = dictionary["actionTextFont"] as? String, let actionSize = dictionary["actionSize"] as? CGFloat { - self.actionTextFont = UIFont(name: actionTextFont, size: actionSize) - } - - if let normalTextColorHex = dictionary["normalTextColor"] as? String { - self.normalTextColor = UIColor.mfGet(forHex: normalTextColorHex) - } - - if let actionTextColorHex = dictionary["actionTextColor"] as? String { - self.actionTextColor = UIColor.mfGet(forHex: actionTextColorHex) - } - - if let makeWholeViewClickable = dictionary["makeWholeViewClickable"] as? Bool { - self.makeWholeViewClickable = makeWholeViewClickable - } - - if let frontText = dictionary["frontText"] as? String { - self.frontText = frontText - } - - if let backText = dictionary["backText"] as? String { - self.backText = backText - } - - if let actionText = dictionary["actionText"] as? String { - self.actionText = actionText - } - - // Want this to be last because it has a willSet feature. - if let text = dictionary["text"] as? String { - self.text = text - } - } } diff --git a/MVMCoreUI/Legacy/Views/MFCaretButton.m b/MVMCoreUI/Legacy/Views/MFCaretButton.m index bd8f058c..e5deff1c 100644 --- a/MVMCoreUI/Legacy/Views/MFCaretButton.m +++ b/MVMCoreUI/Legacy/Views/MFCaretButton.m @@ -49,7 +49,7 @@ CGFloat const CaretViewWidth = 6.6f; - (void)addCaretImageView { [self.rightView removeFromSuperview]; UIEdgeInsets edgeInsets = self.contentEdgeInsets; - CGFloat rightInset = self.rightViewWidth?self.rightViewWidth.floatValue:CaretViewWidth; + CGFloat rightInset = self.rightViewWidth != nil ? self.rightViewWidth.floatValue : CaretViewWidth; UIEdgeInsets newInsets = UIEdgeInsetsMake(edgeInsets.top, edgeInsets.left, edgeInsets.bottom, 4 + rightInset); self.contentEdgeInsets = newInsets; UIView *caretViewIs = self.rightView; @@ -60,11 +60,11 @@ CGFloat const CaretViewWidth = 6.6f; self.rightView.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:self.rightView]; - CGFloat width = self.rightViewWidth?self.rightViewWidth.floatValue:CaretViewWidth; + CGFloat width = self.rightViewWidth != nil ? self.rightViewWidth.floatValue : CaretViewWidth; NSLayoutConstraint *caretViewWidthConstraint = [NSLayoutConstraint constraintWithItem:caretViewIs attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:width]; caretViewWidthConstraint.active = YES; - CGFloat height = self.rightViewHeight?self.rightViewHeight.floatValue:CaretViewHeight; + CGFloat height = self.rightViewHeight != nil ? self.rightViewHeight.floatValue : CaretViewHeight; NSLayoutConstraint *caretViewHeightConstraint = [NSLayoutConstraint constraintWithItem:caretViewIs attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:height]; caretViewHeightConstraint.active = YES; diff --git a/MVMCoreUI/Legacy/Views/MFCaretView.m b/MVMCoreUI/Legacy/Views/MFCaretView.m index 182f7a74..c8036f57 100644 --- a/MVMCoreUI/Legacy/Views/MFCaretView.m +++ b/MVMCoreUI/Legacy/Views/MFCaretView.m @@ -65,7 +65,7 @@ CGContextClearRect(context, rect); CGFloat lineWidth; - if (self.lineWidth) { + if (self.lineWidth != nil) { lineWidth = self.lineWidth.floatValue; } else { lineWidth = self.frame.size.width/2.6; diff --git a/MVMCoreUI/Legacy/Views/MFCustomButton.h b/MVMCoreUI/Legacy/Views/MFCustomButton.h index 9d08883d..c43f87bc 100644 --- a/MVMCoreUI/Legacy/Views/MFCustomButton.h +++ b/MVMCoreUI/Legacy/Views/MFCustomButton.h @@ -10,7 +10,6 @@ @import MVMCore.MVMCoreActionDelegateProtocol; #import #import -#import @class DelegateObject; typedef void (^ButtonTapBlock)(id _Nonnull sender); diff --git a/MVMCoreUI/Legacy/Views/MFDigitTextField.m b/MVMCoreUI/Legacy/Views/MFDigitTextField.m index 5f76e88b..931707f3 100644 --- a/MVMCoreUI/Legacy/Views/MFDigitTextField.m +++ b/MVMCoreUI/Legacy/Views/MFDigitTextField.m @@ -19,6 +19,7 @@ #import "MVMCoreUIUtility.h" #import "MVMCoreUIConstants.h" #import + @import MVMCore.MVMCoreDispatchUtility; @interface MFDigitTextField () @@ -145,22 +146,6 @@ [self alignCenterHorizontal]; } -#pragma mark - Molecule - -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - NSNumber *digitsNumber = [json optionalNumberForKey:@"digits"]; - NSUInteger digits = digitsNumber ? digitsNumber.integerValue : 4; - if (digits != self.numberOfDigits) { - self.numberOfDigits = digits; - [self buildTextFieldsViewForSize:[MVMCoreUIUtility getWidth]]; - } - [super setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; -} - -+ (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject { - return 44; -} - #pragma mark - Getters - (NSString *)placeholder { diff --git a/MVMCoreUI/Legacy/Views/MFRadioButton.h b/MVMCoreUI/Legacy/Views/MFRadioButton.h index c5e053c0..740a6192 100644 --- a/MVMCoreUI/Legacy/Views/MFRadioButton.h +++ b/MVMCoreUI/Legacy/Views/MFRadioButton.h @@ -43,10 +43,10 @@ typedef void (^CircleSelectedClosure)(_Nonnull id sender); @property (nonatomic, strong, nullable) NSLayoutConstraint *widthConstraint; @property (nonatomic) BOOL respondsToTapGesture; @property (nonatomic, getter = isSelected) BOOL selected; -@property (strong, nonatomic) NSLayoutConstraint *innerHeightConstarint; -@property (strong, nonatomic) NSLayoutConstraint *innerWidthConstarint; -@property (strong, nonatomic) NSLayoutConstraint *outerHeightConstarint; -@property (strong, nonatomic) NSLayoutConstraint *outerWidthConstarint; +@property (strong, nonatomic, nullable) NSLayoutConstraint *innerHeightConstarint; +@property (strong, nonatomic, nullable) NSLayoutConstraint *innerWidthConstarint; +@property (strong, nonatomic, nullable) NSLayoutConstraint *outerHeightConstarint; +@property (strong, nonatomic, nullable) NSLayoutConstraint *outerWidthConstarint; // Set line width manually - (void)setCheckMarkLineWidth:(CGFloat)lineWidth; diff --git a/MVMCoreUI/Legacy/Views/MFTextButton.h b/MVMCoreUI/Legacy/Views/MFTextButton.h index 1e81e1f0..6240adb4 100644 --- a/MVMCoreUI/Legacy/Views/MFTextButton.h +++ b/MVMCoreUI/Legacy/Views/MFTextButton.h @@ -10,7 +10,7 @@ #import @class MFSizeObject; -@interface MFTextButton : MFCustomButton +@interface MFTextButton : MFCustomButton @property (nonnull, strong, nonatomic) MFSizeObject *sizeObject; diff --git a/MVMCoreUI/Legacy/Views/MFTextButton.m b/MVMCoreUI/Legacy/Views/MFTextButton.m index a0ee6ca7..31b3a4ee 100644 --- a/MVMCoreUI/Legacy/Views/MFTextButton.m +++ b/MVMCoreUI/Legacy/Views/MFTextButton.m @@ -127,29 +127,6 @@ return theButton; } -#pragma mark - MVMCoreUIMoleculeViewProtocol - -- (void)reset { - [self setTitleColor:[UIColor mfTextButtonColor] forState:UIControlStateNormal]; -} - -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - NSString *color = [json string:KeyTextColor]; - if (color) { - [self setTitleColor:[UIColor mfGetColorForHex:color] forState:UIControlStateNormal]; - } - [self setWithActionMap:json delegateObject:delegateObject additionalData:additionalData]; - if ([self titleForState:UIControlStateNormal].length == 0) { - self.heightConstraint.constant = 0; - } else { - self.heightConstraint.constant = self.sizeObject.standardSize; - } -} - -+ (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject { - return 31; -} - #pragma mark - MVMCoreUIViewConstrainingProtocol - (BOOL)needsToBeConstrained { diff --git a/MVMCoreUI/Legacy/Views/MFTextField.h b/MVMCoreUI/Legacy/Views/MFTextField.h index aa3ab573..260bc936 100644 --- a/MVMCoreUI/Legacy/Views/MFTextField.h +++ b/MVMCoreUI/Legacy/Views/MFTextField.h @@ -28,7 +28,7 @@ @end -@interface MFTextField : ViewConstrainingView +@interface MFTextField : ViewConstrainingView @property (nullable, weak, nonatomic) UIView *view; @@ -131,8 +131,4 @@ - (void)setAccessibilityString:(nullable NSString *)accessibilityString; -// For Validator Protocol -- (nullable NSString *)formFieldName; -- (nullable id)formFieldValue; - @end diff --git a/MVMCoreUI/Legacy/Views/MFTextField.m b/MVMCoreUI/Legacy/Views/MFTextField.m index 081f9fe8..da6dcaa3 100644 --- a/MVMCoreUI/Legacy/Views/MFTextField.m +++ b/MVMCoreUI/Legacy/Views/MFTextField.m @@ -555,19 +555,4 @@ } } -#pragma mark - MVMCoreUIMoleculeViewProtocol - -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - [super setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; - if ([delegateObject isKindOfClass:[MVMCoreUIDelegateObject class]]) { - [self setWithMap:json]; - self.uiTextFieldDelegate = delegateObject.uiTextFieldDelegate; - [MVMCoreUICommonViewsUtility addDismissToolbar:self.textField delegate:self.uiTextFieldDelegate]; - } -} - - -+ (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject { - return 76; -} @end diff --git a/MVMCoreUI/Legacy/Views/MFView.h b/MVMCoreUI/Legacy/Views/MFView.h index dd8e01a8..ab179d57 100644 --- a/MVMCoreUI/Legacy/Views/MFView.h +++ b/MVMCoreUI/Legacy/Views/MFView.h @@ -7,14 +7,10 @@ // #import -#import #import @import MVMCore.MVMCoreViewProtocol; -@interface MFView : UIView - -@property (nullable, nonatomic, strong) NSDictionary *json; -@property (nullable, nonatomic, strong) id model; +@interface MFView : UIView // Called in the initialization functions. Can setup ui here. - (void)setupView; diff --git a/MVMCoreUI/Legacy/Views/MFView.m b/MVMCoreUI/Legacy/Views/MFView.m index 6d939fc8..a01d1ca7 100644 --- a/MVMCoreUI/Legacy/Views/MFView.m +++ b/MVMCoreUI/Legacy/Views/MFView.m @@ -44,24 +44,4 @@ - (void)updateView:(CGFloat)size { } -#pragma mark - MVMCoreUIMoleculeViewProtocol - -- (void)setAsMolecule { - self.translatesAutoresizingMaskIntoConstraints = NO; - self.insetsLayoutMarginsFromSafeArea = NO; -} - -- (void)reset { - self.backgroundColor = [UIColor clearColor]; -} - -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - self.json = json; - - NSString *colorString = [json string:KeyBackgroundColor]; - if (colorString) { - self.backgroundColor = [UIColor mfGetColorForHex:colorString]; - } -} - @end diff --git a/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.h b/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.h index f1638f6a..ba7bf4cc 100644 --- a/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.h +++ b/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.h @@ -12,7 +12,7 @@ @class Label; @class MFSizeObject; -@interface MVMCoreUICheckBox : UIControl +@interface MVMCoreUICheckBox : UIControl @property (nullable, weak, nonatomic) MVMCoreUICheckMarkView *checkMark; @property (readonly, nonatomic) BOOL isSelected; diff --git a/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.m b/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.m index 4c6bf8a5..8e0afcd0 100644 --- a/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.m +++ b/MVMCoreUI/Legacy/Views/MVMCoreUICheckBox.m @@ -21,7 +21,7 @@ static const CGFloat FaultTolerance = 20.f; static const CGFloat CheckBoxHeightWidth = 18.0; -@interface MVMCoreUICheckBox () +@interface MVMCoreUICheckBox () @property (nonatomic, readwrite) BOOL isSelected; @property (weak, nonatomic) UIView *checkedSquare; @@ -42,13 +42,12 @@ static const CGFloat CheckBoxHeightWidth = 18.0; @property (nonatomic) BOOL isRequired; @property (nullable, strong, nonatomic) NSString *fieldKey; -@property (nullable, strong, nonatomic) MVMCoreUIDelegateObject *delegateObject; @end @implementation MVMCoreUICheckBox -#pragma mark - MVMCoreUIMoleculeViewProtocol +#pragma mark - Constraining protocol - (BOOL)needsToBeConstrained { return YES; @@ -58,29 +57,6 @@ static const CGFloat CheckBoxHeightWidth = 18.0; return UIStackViewAlignmentLeading; } -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - - self.delegateObject = delegateObject; - self.fieldKey = [json stringForKey:KeyFieldKey]; - self.isRequired = [json boolForKey:KeyRequired]; - - NSString *checkedColorHex = [json string:@"checkedColor"]; - NSString *unCheckedColorHex = [json string:@"unCheckedColor"]; - - UIColor *checkedColor = checkedColorHex ? [UIColor mfGetColorForHex:checkedColorHex]: [UIColor clearColor]; - UIColor *unCheckedColor = unCheckedColorHex ? [UIColor mfGetColorForHex:unCheckedColorHex]: [UIColor clearColor]; - - [self setupWithCheckedColor:checkedColor - unCheckColor:unCheckedColor - label:[json dict:KeyLabel] - delegateObject:delegateObject - additionalData: additionalData]; -} - -+ (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject { - return CheckBoxHeightWidth; -} - #pragma mark - convenient class methods + (instancetype)mfCheckBox { @@ -269,11 +245,6 @@ static const CGFloat CheckBoxHeightWidth = 18.0; [self setDescriptionText:text]; } -- (void)setupWithCheckedColor:(UIColor *)checkedColor unCheckColor:(UIColor *)unCheckedColor label:(NSDictionary *)labelJson delegateObject:(DelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - [self setupWithCheckedColor:checkedColor unCheckColor:unCheckedColor]; - [self.descriptionLabel setWithJSON:labelJson delegateObject:delegateObject additionalData:additionalData]; -} - - (void)updateView:(CGFloat)size { [MVMCoreDispatchUtility performBlockOnMainThread:^{ [self.descriptionLabel updateView:size]; @@ -330,8 +301,6 @@ static const CGFloat CheckBoxHeightWidth = 18.0; } completion:nil]; [self.checkMark updateCheckSelected:NO animated:animated]; } - -// [FormValidator enableByValidationWithDelegate:self.delegateObject.formValidationProtocol]; } - (void)setColor:(nullable UIColor *)color forState:(UIControlState)state { diff --git a/MVMCoreUI/Legacy/Views/MVMCoreUIMoleculeViewProtocol.h b/MVMCoreUI/Legacy/Views/MVMCoreUIMoleculeViewProtocol.h deleted file mode 100644 index f11a5002..00000000 --- a/MVMCoreUI/Legacy/Views/MVMCoreUIMoleculeViewProtocol.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// MVMCoreUIMoleculeViewProtocol.h -// MVMCoreUI -// -// Created by Scott Pfeil on 2/11/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -#import -@import MVMCore.MVMCoreViewProtocol; -@class MVMCoreUIDelegateObject; -@class MVMCoreErrorObject; -@class MoleculeModelProtocol; - -@protocol MVMCoreUIMoleculeViewProtocol - -@optional - -/// Sets up the ui based on the json -- (void)setWithJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData; - -/// Called after init to provide an early setter for any molecule specific logic -- (void)setAsMolecule; - -/// Resets to default state before set with json is called again. -- (void)reset; - -/// For the molecule list to load more efficiently. -+ (CGFloat)estimatedHeightForRow:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject; - -/// Allows the molecule to set its name for reuse. Default could be moleculeName. -+ (nullable NSString *)nameForReuse:(nullable NSDictionary *)molecule delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject; - -/// Can return the required modules -+ (nullable NSArray *)requiredModules:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error; - -@end - - diff --git a/MVMCoreUI/Legacy/Views/MVMCoreUIPageControl.h b/MVMCoreUI/Legacy/Views/MVMCoreUIPageControl.h index e86d3f9e..ba71bd70 100644 --- a/MVMCoreUI/Legacy/Views/MVMCoreUIPageControl.h +++ b/MVMCoreUI/Legacy/Views/MVMCoreUIPageControl.h @@ -8,10 +8,9 @@ #import #import -#import #import -@interface MVMCoreUIPageControl : UIControl +@interface MVMCoreUIPageControl : UIControl // These properties effectively do what their corresponding namesakes do in UIPageControl @property (nonatomic) NSInteger currentPage; diff --git a/MVMCoreUI/Legacy/Views/MVMCoreUIPageControl.m b/MVMCoreUI/Legacy/Views/MVMCoreUIPageControl.m index f2b9976e..feef75ab 100644 --- a/MVMCoreUI/Legacy/Views/MVMCoreUIPageControl.m +++ b/MVMCoreUI/Legacy/Views/MVMCoreUIPageControl.m @@ -311,25 +311,6 @@ static CGFloat const IndicatorRectangleHeight = 4; self.currentPage = page; } -#pragma mark - MVMCoreUIMoleculeViewProtocol - -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - NSString *colorString = [json string:KeyBackgroundColor]; - if (colorString) { - self.backgroundColor = [UIColor mfGetColorForHex:colorString]; - } - colorString = [json string:@"barsColor"]; - if (colorString) { - UIColor *color = [UIColor mfGetColorForHex:colorString]; - self.pageIndicatorTintColor = color; - self.currentPageIndicatorTintColor = color; - } - colorString = [json string:@"currentBarColor"]; - if (colorString) { - self.currentPageIndicatorTintColor = [UIColor mfGetColorForHex:colorString]; - } -} - #pragma mark - Accessibility - (UIAccessibilityTraits)accessibilityTraits { return UIAccessibilityTraitAdjustable; diff --git a/MVMCoreUI/Legacy/Views/MVMCoreUISwitch.h b/MVMCoreUI/Legacy/Views/MVMCoreUISwitch.h index 256aabfa..91444466 100644 --- a/MVMCoreUI/Legacy/Views/MVMCoreUISwitch.h +++ b/MVMCoreUI/Legacy/Views/MVMCoreUISwitch.h @@ -8,12 +8,11 @@ #import -#import #import @import MVMCore.MVMCoreViewProtocol; typedef void(^ValueChangeBlock)(void); -@interface MVMCoreUISwitch : UIControl +@interface MVMCoreUISwitch : UIControl @property (assign, nonatomic, getter=isOn) BOOL on; @property (nullable, strong, nonatomic) UIColor *onTintColor; diff --git a/MVMCoreUI/Legacy/Views/MVMCoreUISwitch.m b/MVMCoreUI/Legacy/Views/MVMCoreUISwitch.m index 8cbb434d..0799c224 100644 --- a/MVMCoreUI/Legacy/Views/MVMCoreUISwitch.m +++ b/MVMCoreUI/Legacy/Views/MVMCoreUISwitch.m @@ -38,9 +38,6 @@ const CGFloat SwitchShakeIntensity = 2; @property (nonatomic) BOOL canChangeValue; -@property (strong, nonatomic, nullable) NSDictionary *json; -@property (nullable, strong, nonatomic) DelegateObject *delegate; - @end @implementation MVMCoreUISwitch @@ -64,21 +61,21 @@ const CGFloat SwitchShakeIntensity = 2; } - (instancetype)initWithCoder:(NSCoder *)coder { - if ([super initWithCoder:coder]) { + if (self = [super initWithCoder:coder]) { [self setupView]; } return self; } - (instancetype)initWithFrame:(CGRect)frame { - if ([super initWithFrame:frame]) { + if (self = [super initWithFrame:frame]) { [self setupView]; } return self; } - (instancetype)init { - if ([super init]) { + if (self = [super init]) { [self setupView]; } return self; @@ -141,51 +138,13 @@ const CGFloat SwitchShakeIntensity = 2; } } -#pragma mark - MVMCoreUIMoleculeViewProtocol - -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - self.json = json; - self.delegate = delegateObject; - - NSString *color = [json string:@"onTintColor"]; - if (color) { - self.onTintColor = [UIColor mfGetColorForHex:color]; - } - - color = [json string:@"offTintColor"]; - if (color) { - self.offTintColor = [UIColor mfGetColorForHex:color]; - } - - color = [json string:@"onKnobTintColor"]; - if (color) { - self.onKnobTintColor = [UIColor mfGetColorForHex:color]; - } - - color = [json string:@"offKnobTintColor"]; - if (color) { - self.offKnobTintColor = [UIColor mfGetColorForHex:color]; - } - - [self setState:[json boolForKey:@"state"] animated:false]; - - NSDictionary *actionMap = [json dict:@"action"]; - if (actionMap) { - [self addTarget:self action:@selector(addCustomAction) forControlEvents:UIControlEventTouchUpInside]; - } -} - - (void)addCustomAction { if (self.actionBlock) { self.actionBlock(); } } -+ (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject { - return [self getSwitchHeight]; -} - -#pragma mark - MVMCoreUIMoleculeViewProtocol +#pragma mark - Constraining Protocol - (BOOL)needsToBeConstrained { return YES; @@ -338,10 +297,6 @@ const CGFloat SwitchShakeIntensity = 2; if (self.valueChangedBlock) { self.valueChangedBlock(); } -// if (self.delegate && [self.delegate respondsToSelector:@selector(formValidationProtocol)] && [[self.delegate performSelector:@selector(formValidationProtocol)] respondsToSelector:@selector(formValidatorModel)]) { -// FormValidator *formValidator = [[self.delegate performSelector:@selector(formValidationProtocol)] performSelector:@selector(formValidatorModel)]; -// [formValidator enableByValidation]; -// } } - (void)setState:(BOOL)state withoutBlockAnimated:(BOOL)animated { diff --git a/MVMCoreUI/Legacy/Views/PrimaryButton.h b/MVMCoreUI/Legacy/Views/PrimaryButton.h index e83484a0..a93e6fc5 100644 --- a/MVMCoreUI/Legacy/Views/PrimaryButton.h +++ b/MVMCoreUI/Legacy/Views/PrimaryButton.h @@ -10,7 +10,7 @@ #import #import #import -#import +@class MVMCoreUIDelegateObject; typedef enum : NSUInteger { PrimaryButtonTypeRed, @@ -26,7 +26,7 @@ typedef enum : NSUInteger { static CGFloat const PrimaryButtonHeight = 42.0; static CGFloat const PrimaryButtonSmallHeight = 30.0; -@interface PrimaryButton : MFCustomButton +@interface PrimaryButton : MFCustomButton @property (nonatomic, readonly, assign) PrimaryButtonType primaryButtonType; //use reset function to set @@ -118,6 +118,9 @@ static CGFloat const PrimaryButtonSmallHeight = 30.0; - (void)resetButtonType:(PrimaryButtonType)type small:(BOOL)isSmall bordered:(BOOL)bordered; - (void)resetButtonType:(PrimaryButtonType)type tiny:(BOOL)isTiny bordered:(BOOL)bordered; +// Convenience setter for common keys +- (void)setWithJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData; + #pragma mark - Handling Validations // Sets the enabled property depending on the validity checks diff --git a/MVMCoreUI/Legacy/Views/PrimaryButton.m b/MVMCoreUI/Legacy/Views/PrimaryButton.m index 0e16ce8e..ded6bac2 100644 --- a/MVMCoreUI/Legacy/Views/PrimaryButton.m +++ b/MVMCoreUI/Legacy/Views/PrimaryButton.m @@ -499,6 +499,25 @@ [self setTitleColor:[self titleColorForState:UIControlStateNormal] forState:UIControlStateHighlighted]; } +- (void)setAsStandardCustom { + // Default to standard look. + self.primaryButtonType = PrimaryButtonTypeCustom; + self.fillColor = [UIColor blackColor]; + self.textColor = [UIColor whiteColor]; + self.disabledTextColor = self.textColor; + self.borderColor = nil; + _bordered = false; +} + +- (void)setAsSecondaryCustom { + // Default to standard look. + self.primaryButtonType = PrimaryButtonTypeCustom; + self.fillColor = nil; + self.textColor = nil; + self.borderColor = [UIColor blackColor]; + _bordered = true; +} + - (void)setBordered:(BOOL)bordered { if (bordered != _bordered) { if (self.primaryButtonType == PrimaryButtonTypeCustom) { @@ -636,42 +655,7 @@ return button; } -#pragma mark - Molecule protocol - -- (void)reset { - [self setAsStandardCustom]; -} - -- (void)setAsStandardCustom { - // Default to standard look. - self.primaryButtonType = PrimaryButtonTypeCustom; - self.fillColor = [UIColor blackColor]; - self.textColor = [UIColor whiteColor]; - self.disabledTextColor = self.textColor; - self.borderColor = nil; - _bordered = false; -} - -- (void)setAsSecondaryCustom { - // Default to standard look. - self.primaryButtonType = PrimaryButtonTypeCustom; - self.fillColor = nil; - self.textColor = nil; - self.borderColor = [UIColor blackColor]; - _bordered = true; -} - -- (void)setAsMolecule { - self.translatesAutoresizingMaskIntoConstraints = false; - [self pinHeight]; - [self setAsStandardCustom]; -} - - (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - - self.validationRequired = [json boolForKey:@"required"]; - self.requiredGroupsList = [json array:@"requiredGroups"]; - self.primaryButtonType = PrimaryButtonTypeCustom; NSString *style = [json string:@"style"]; if ([style isEqualToString:@"primary"]) { @@ -712,6 +696,8 @@ [self setWithActionMap:json delegateObject:delegateObject additionalData:additionalData]; } +#pragma mark - Constraining Protocol + - (UIStackViewAlignment)horizontalAlignment { return UIStackViewAlignmentCenter; } diff --git a/MVMCoreUI/Legacy/Views/PrimaryButtonView.m b/MVMCoreUI/Legacy/Views/PrimaryButtonView.m index 8c76353c..2855359a 100644 --- a/MVMCoreUI/Legacy/Views/PrimaryButtonView.m +++ b/MVMCoreUI/Legacy/Views/PrimaryButtonView.m @@ -19,9 +19,6 @@ @interface PrimaryButtonView () @property (weak, nonatomic) UIView *twoButtonView; -@property (weak, nonatomic) NSLayoutConstraint *alignCenterPin; -@property (weak, nonatomic) NSLayoutConstraint *alignCenterLeftPin; -@property (weak, nonatomic) NSLayoutConstraint *alignCenterRightPin; @property (weak, nonatomic) NSLayoutConstraint *height; @property (nonatomic, strong) NSArray *horizontalConstraints; @@ -38,21 +35,6 @@ }]; } -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - [super setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; - NSString *backgroundColorString = [json string:@"backgroundColor"]; - if (backgroundColorString) { - self.backgroundColor = [UIColor mfGetColorForHex:backgroundColorString]; - } - NSDictionary *primaryButtonMap = [json dict:@"primaryButton"]; - NSDictionary *secondaryButtonMap = [json dict:@"secondaryButton"]; - [self setupUIWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap]; - [self.primaryButton setAsStandardCustom]; - [self.secondaryButton setAsSecondaryCustom]; - [self.primaryButton setWithJSON:primaryButtonMap delegateObject:delegateObject additionalData:additionalData]; - [self.secondaryButton setWithJSON:secondaryButtonMap delegateObject:delegateObject additionalData:additionalData]; -} - #pragma mark - Inits - (instancetype)init { diff --git a/MVMCoreUI/Legacy/Views/SeparatorView.h b/MVMCoreUI/Legacy/Views/SeparatorView.h index 40489b16..89a9197d 100644 --- a/MVMCoreUI/Legacy/Views/SeparatorView.h +++ b/MVMCoreUI/Legacy/Views/SeparatorView.h @@ -41,7 +41,4 @@ typedef enum : NSUInteger { - (void)setAsLight; - (void)setAsMedium; -/// Returns if the separator should be visible based on the type. -- (BOOL)shouldBeVisible; - @end diff --git a/MVMCoreUI/Legacy/Views/SeparatorView.m b/MVMCoreUI/Legacy/Views/SeparatorView.m index 9f85c311..0a86559d 100644 --- a/MVMCoreUI/Legacy/Views/SeparatorView.m +++ b/MVMCoreUI/Legacy/Views/SeparatorView.m @@ -95,32 +95,6 @@ } } -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - [super setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; - if (json) { - NSString *type = [json string:KeyType]; - if ([type isEqualToString:@"none"]) { - self.hidden = YES; - } else { - self.hidden = NO; - if ([type isEqualToString:@"medium"]) { - [self setAsMedium]; - } else if ([type isEqualToString:@"heavy"]) { - [self setAsHeavy]; - } else if ([type isEqualToString:@"standard"]) { - [self setAsLight]; - } - } - } else { - self.hidden = YES; - } - - NSString *colorString = [json string:KeyBackgroundColor]; - if (colorString) { - self.backgroundColor = [UIColor mfGetColorForHex:colorString]; - } -} - #pragma mark - helper - (void)hide { @@ -162,17 +136,9 @@ [self setNeedsLayout]; [self layoutIfNeeded]; } - -- (BOOL)shouldBeVisible { - return ![[self.json string:KeyType] isEqualToString:@"none"]; -} -#pragma mark - Molecule +#pragma mark - Constraining Protocol -- (void)reset { - [self setAsLight]; -} - - (BOOL)needsToBeConstrained { return YES; } @@ -181,19 +147,4 @@ return NO; } -+ (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject { - NSString *type = [json string:KeyType]; - if ([type isEqualToString:@"none"]) { - return 0; - } else { - if ([type isEqualToString:@"medium"]) { - return 2; - } else if ([type isEqualToString:@"heavy"]) { - return 4; - } else { - return 1; - } - } -} - @end diff --git a/MVMCoreUI/Legacy/Views/ViewConstrainingView.h b/MVMCoreUI/Legacy/Views/ViewConstrainingView.h index 30f5a552..7ba4fd21 100644 --- a/MVMCoreUI/Legacy/Views/ViewConstrainingView.h +++ b/MVMCoreUI/Legacy/Views/ViewConstrainingView.h @@ -39,9 +39,6 @@ @property (nonatomic) CGFloat topMarginPadding; @property (nonatomic) CGFloat bottomMarginPadding; -/// A molecule if we constrain one. -@property (weak, nullable, nonatomic) UIView *molecule; - /// A flag for if we should add a molecule from json. @property (nonatomic) BOOL shouldSetupMoleculeFromJSON; @@ -51,9 +48,6 @@ // Returns a view with the provided view as a subview, pinned. + (nonnull ViewConstrainingView *)viewConstrainingView:(nonnull UIView *)view; -// Can be initialized with a molecule to constrain -- (nonnull instancetype)initWithMolecule:(nonnull UIView *)molecule alignment:(UIStackViewAlignment)alignment; - #pragma mark - Constraining // Use these to sets the constants, because subclasses may align differently. @@ -78,9 +72,6 @@ // Add a view to be constrained in this view. - (void)addConstrainedView:(nonnull UIView *)view; -/// Can override to change how the molecule is added when shouldSetupMoleculeFromJSON = true. Inserts the molecule at 0 and calls pinToSuperView. -- (void)addMolecule:(nonnull UIView *)molecule; - // Change the alignment of the label - (void)alignLeft; - (void)alignCenterHorizontal; @@ -97,7 +88,4 @@ /// Convenience function for getting the alignment from a map. + (UIStackViewAlignment)getAlignmentForString:(nullable NSString *)alignmentString defaultAlignment:(UIStackViewAlignment)defaultAlignment; -/// Makes the view isAccessibilityElement false and adds molecule elements to accessbilityElements. -- (void)setMoleculeAccessibility; - @end diff --git a/MVMCoreUI/Legacy/Views/ViewConstrainingView.m b/MVMCoreUI/Legacy/Views/ViewConstrainingView.m index b98e72a5..8d36f351 100644 --- a/MVMCoreUI/Legacy/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Legacy/Views/ViewConstrainingView.m @@ -13,7 +13,6 @@ #import "NSLayoutConstraint+MFConvenience.h" #import "MFStyler.h" #import "MVMCoreUIConstants.h" -#import "MVMCoreUIMoleculeMappingObject.h" #import "MVMCoreUIUtility.h" #import "MVMCoreUIViewConstrainingProtocol.h" @@ -22,18 +21,6 @@ @implementation ViewConstrainingView -- (nonnull instancetype)initWithMolecule:(nonnull UIView *)molecule alignment:(UIStackViewAlignment)alignment { - if (self = [super init]) { - if (!molecule.superview) { - [self addConstrainedView:molecule alignment:alignment]; - [self setAsMolecule]; - } - self.molecule = molecule; - [self setMoleculeAccessibility]; - } - return self; -} - + (nonnull ViewConstrainingView *)emptyView { ViewConstrainingView *view = [[ViewConstrainingView alloc] initWithFrame:CGRectZero]; view.translatesAutoresizingMaskIntoConstraints = NO; @@ -240,11 +227,6 @@ } } -- (void)addMolecule:(nonnull UIView *)molecule { - [self insertSubview:molecule atIndex:0]; - [self pinViewToSuperView:molecule]; -} - #pragma mark - MVMCoreUIViewConstrainingProtocol - (void)alignHorizontal:(UIStackViewAlignment)alignment { @@ -285,114 +267,27 @@ } } -- (void)shouldSetHorizontalMargins:(BOOL)shouldSet { - if (![self.json optionalNumberForKey:@"useHorizontalMargins"]) { - self.updateViewHorizontalDefaults = shouldSet; - } -} - -- (void)shouldSetVerticalMargins:(BOOL)shouldSet { - if (![self.json optionalNumberForKey:@"useVerticalMargins"]) { - self.updateViewVerticalDefaults = shouldSet; - } -} - #pragma mark - MVMCoreViewProtocol - (void)setupView { [super setupView]; self.translatesAutoresizingMaskIntoConstraints = NO; self.backgroundColor = [UIColor clearColor]; - self.topMarginPadding = PaddingDefaultVerticalSpacing3; - self.bottomMarginPadding = PaddingDefaultVerticalSpacing3; - [MVMCoreUIUtility setMarginsForView:self leading:0 top:0 trailing:0 bottom:0]; } - (void)updateView:(CGFloat)size { [super updateView:size]; - [self.molecule updateView:size]; - [MFStyler setMarginsForView:self size:size defaultHorizontal:self.updateViewHorizontalDefaults top:(self.updateViewVerticalDefaults ? self.topMarginPadding : 0) bottom:(self.updateViewVerticalDefaults ? self.bottomMarginPadding : 0)]; - UIEdgeInsets margins = [MVMCoreUIUtility getMarginsForView:self]; + if ([self.constrainedView respondsToSelector:@selector(updateView:)]) { + [((id)self.constrainedView) updateView:size]; + } if (self.updateViewHorizontalDefaults) { - [self setLeftPinConstant:margins.left]; - [self setRightPinConstant:margins.right]; + CGFloat padding = [MFStyler defaultHorizontalPaddingForSize:size]; + [self setLeftPinConstant:padding]; + [self setRightPinConstant:padding]; } if (self.updateViewVerticalDefaults) { - [self setTopPinConstant:margins.top]; - [self setBottomPinConstant:margins.bottom]; - } -} - -#pragma mark - MVMCoreUIMoleculeViewProtocol - -- (void)setMoleculeAccessibility { - if (self.molecule) { - self.isAccessibilityElement = NO; - if (self.molecule.accessibilityElements) { - self.accessibilityElements = self.molecule.accessibilityElements; - } else { - self.accessibilityElements = @[self.molecule]; - } - } -} - -- (void)reset { - [super reset]; - self.updateViewHorizontalDefaults = NO; - self.updateViewVerticalDefaults = NO; - self.topMarginPadding = PaddingDefaultVerticalSpacing3; - self.bottomMarginPadding = PaddingDefaultVerticalSpacing3; - if ([self.molecule respondsToSelector:@selector(horizontalAlignment)]) { - [self alignHorizontal:[(UIView *)self.molecule horizontalAlignment]]; - } - [self alignVertical:UIStackViewAlignmentFill]; - if ([self.molecule respondsToSelector:@selector(reset)]) { - [self.molecule performSelector:@selector(reset)]; - } -} - -- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { - // Only treated as a container if we are constraining a molecule. - if (!self.constrainedView) { - [super setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; - } - if (self.shouldSetupMoleculeFromJSON) { - NSDictionary *moleculeJSON = [json dict:KeyMolecule]; - if (self.molecule) { - [self.molecule setWithJSON:moleculeJSON delegateObject:delegateObject additionalData:additionalData]; - } else if (moleculeJSON) { - UIView *molecule = [[MVMCoreUIMoleculeMappingObject sharedMappingObject] createMoleculeForJSON:moleculeJSON delegateObject:delegateObject constrainIfNeeded:true]; - if (molecule) { - [self addMolecule:molecule]; - } - self.molecule = molecule; - [self setMoleculeAccessibility]; - } - } else { - [self.molecule setWithJSON:json delegateObject:delegateObject additionalData:additionalData]; - } - - NSNumber *useHorizontalMargins = [json optionalNumberForKey:@"useHorizontalMargins"]; - if (useHorizontalMargins) { - self.updateViewHorizontalDefaults = [useHorizontalMargins boolValue]; - } - NSNumber *useVerticalMargins = [json optionalNumberForKey:@"useVerticalMargins"]; - if (useVerticalMargins) { - self.updateViewVerticalDefaults = [useVerticalMargins boolValue]; - } - - // Set the alignment for the stack in the containing view. The json driven value is for the axis direction alignment. - NSString *alignment = [json string:@"horizontalAlignment"]; - if (alignment) { - [self alignHorizontal:[ViewConstrainingView getAlignmentForString:alignment defaultAlignment:UIStackViewAlignmentFill]]; - } - alignment = [json string:@"verticalAlignment"]; - if (alignment) { - [self alignVertical:[ViewConstrainingView getAlignmentForString:alignment defaultAlignment:UIStackViewAlignmentFill]]; - } - - if ([self.molecule respondsToSelector:@selector(copyBackgroundColor)] && [self.molecule performSelector:@selector(copyBackgroundColor)]) { - self.backgroundColor = self.molecule.backgroundColor; + [self setTopPinConstant:self.topMarginPadding]; + [self setBottomPinConstant:self.bottomMarginPadding]; } } diff --git a/MVMCoreUI/MVMCoreUI.h b/MVMCoreUI/MVMCoreUI.h index 5e8c92cc..4075a110 100644 --- a/MVMCoreUI/MVMCoreUI.h +++ b/MVMCoreUI/MVMCoreUI.h @@ -20,7 +20,6 @@ FOUNDATION_EXPORT const unsigned char MVMCoreUIVersionString[]; #import #import #import -#import #pragma mark - TopAlert #import @@ -48,7 +47,7 @@ FOUNDATION_EXPORT const unsigned char MVMCoreUIVersionString[]; #pragma mark - BaseControllers #import #import -#import +#import #import #import #import @@ -106,7 +105,6 @@ FOUNDATION_EXPORT const unsigned char MVMCoreUIVersionString[]; #pragma mark - Molecules #import -#import #import #import diff --git a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift b/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift deleted file mode 100644 index acaa0923..00000000 --- a/MVMCoreUI/Molecules/Doughnut/DoughnutChart.swift +++ /dev/null @@ -1,186 +0,0 @@ -// -// DoughnutChart.swift -// MVMCoreUI -// -// Created by Murugan, Vimal on 07/01/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import UIKit - -open class DoughnutChart: View { - var doughnutLayer = CALayer() - var titleLabel = Label.commonLabelH3(true) - var subTitleLabel = Label.commonLabelB2(true) - var doughnutChartModel: DoughnutChartModel? { - get { return model as? DoughnutChartModel } - } - var labelContainer = MVMCoreUICommonViewsUtility.commonView() - var labelContainerLeftConstraint: NSLayoutConstraint? - var labelContainerTopConstraint: NSLayoutConstraint? - var labelContainerBottomConstraint: NSLayoutConstraint? - var labelContainerRightConstraint: NSLayoutConstraint? - var heightConstraint: NSLayoutConstraint? - - static let heightConstant: CGFloat = 150 - private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: heightConstant)! - var height: CGFloat = heightConstant { - didSet { - if height != oldValue { - sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)! - updateContainer() - drawGraph() - } - } - } - - open override func updateView(_ size: CGFloat) { - super.updateView(size) - titleLabel.updateView(size) - subTitleLabel.updateView(size) - updateContainer() - drawGraph() - } - - open override func reset() { - super.reset() - titleLabel.reset() - subTitleLabel.reset() - clearLayers() - } - - public override func setAsMolecule() { - titleLabel.setAsMolecule() - subTitleLabel.setAsMolecule() - } - - open override func setupView() { - super.setupView() - guard labelContainer.superview == nil else { - return - } - addSubview(labelContainer) - - labelContainer.addSubview(titleLabel) - labelContainer.addSubview(subTitleLabel) - titleLabel.textAlignment = .center - subTitleLabel.textAlignment = .center - - //Make label font size to adjust width if label content is high - titleLabel.numberOfLines = 1 - titleLabel.adjustsFontSizeToFitWidth = true - - layer.addSublayer(doughnutLayer) - heightConstraint = heightAnchor.constraint(equalToConstant: - sizeObject.getValueBasedOnApplicationWidth()) - heightConstraint?.isActive = true - widthAnchor.constraint(equalTo: heightAnchor).isActive = true - - labelContainerLeftConstraint = labelContainer.leftAnchor.constraint(greaterThanOrEqualTo: leftAnchor, constant: lineWidth()) - labelContainerLeftConstraint?.isActive = true - labelContainerTopConstraint = labelContainer.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: lineWidth()) - labelContainerTopConstraint?.isActive = true - labelContainerRightConstraint = rightAnchor.constraint(greaterThanOrEqualTo: labelContainer.rightAnchor, constant: lineWidth()) - labelContainerRightConstraint?.isActive = true - labelContainerBottomConstraint = bottomAnchor.constraint(greaterThanOrEqualTo: labelContainer.bottomAnchor, constant: lineWidth()) - labelContainerBottomConstraint?.isActive = true - labelContainer.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - labelContainer.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true - - NSLayoutConstraint.constraintPinSubview(titleLabel, pinTop: true, pinBottom: false, pinLeft: true, pinRight: true) - NSLayoutConstraint.constraintPinSubview(subTitleLabel, pinTop: false, pinBottom: true, pinLeft: true, pinRight: true) - _ = NSLayoutConstraint(pinFirstView: titleLabel, toSecondView: subTitleLabel, withConstant: 0, directionVertical: true) - //Rotate view for initial draw - doughnutLayer.transform = CATransform3DMakeRotation(1 * .pi, 0.0, 0.0, 1.0) - } - - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - super.set(with: model, delegateObject, additionalData) - clearLayers() - guard let doughnutChartModel = doughnutChartModel else { - return - } - titleLabel.setOptional(with: doughnutChartModel.title, delegateObject, additionalData) - subTitleLabel.setOptional(with: doughnutChartModel.subtitle, delegateObject, additionalData) - titleLabel.textAlignment = .center - subTitleLabel.textAlignment = .center - updateLabelContainer() - drawGraph() - } - - func drawGraph() { - clearLayers() - let widthHeight = sizeObject.getValueBasedOnApplicationWidth() - doughnutLayer.frame = CGRect(x: 0, y: 0, - width: widthHeight, - height: widthHeight) - if let doughnutChart = doughnutChartModel { - var prevPercent: CGFloat = 0.0 - for model in doughnutChart.sections { - prevPercent += drawBar(model, prevPercent) - } - } - } - - func drawBar(_ chartModel: DoughnutChartItemModel, _ prevPercent: CGFloat) -> CGFloat { - - let shapeLayer = CAShapeLayer() - shapeLayer.frame = doughnutLayer.frame - shapeLayer.lineWidth = lineWidth() - shapeLayer.fillColor = nil - shapeLayer.strokeColor = chartModel.color.uiColor.cgColor - - let arcCenter = shapeLayer.position - let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth()/2.0 - - let value: CGFloat = chartModel.percent - let gap: CGFloat = spaceRequired() ? lineGap() : 0.0 - let startAngle = ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi) - let endAngle = ((value / 100.0) * 2 * .pi) + startAngle - gap - let circlePath = UIBezierPath(arcCenter: arcCenter, - radius: radius, - startAngle: startAngle, - endAngle: endAngle, - clockwise: true) - - shapeLayer.path = circlePath.cgPath - doughnutLayer.addSublayer(shapeLayer) - return value - } - - func clearLayers() { - doughnutLayer.sublayers?.forEach({ $0.removeFromSuperlayer() }) - } - - func updateContainer() { - heightConstraint?.constant = sizeObject.getValueBasedOnApplicationWidth() - updateLabelContainer() - setNeedsDisplay() - } - - func lineWidth() -> CGFloat { - return 30 - } - - func lineGap() -> CGFloat { - //If array is having the single item then no space required - return doughnutChartModel?.sections.count == 1 ? 0.0 : 0.05 - } - - func spaceRequired() -> Bool { - return doughnutChartModel?.spaceRequired ?? true - } - - func updateLabelContainer() { - labelContainer.setNeedsDisplay() - labelContainer.layoutIfNeeded() - let radius = sizeObject.getValueBasedOnApplicationWidth()/2 - lineWidth() - let labelheight = labelContainer.frame.height/2 - let padding = sizeObject.getValueBasedOnApplicationWidth()/2 - sqrt(max(0, pow(radius, 2) - pow(labelheight, 2))) - - labelContainerLeftConstraint?.constant = padding - labelContainerRightConstraint?.constant = padding - labelContainerTopConstraint?.constant = max(radius - labelheight, labelheight) - labelContainerBottomConstraint?.constant = max(radius - labelheight, labelheight) - } -} diff --git a/MVMCoreUI/Molecules/FooterView.swift b/MVMCoreUI/Molecules/FooterView.swift deleted file mode 100644 index b9113d1d..00000000 --- a/MVMCoreUI/Molecules/FooterView.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// FooterView.swift -// MVMCoreUI -// -// Created by Scott Pfeil on 3/11/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import UIKit - -open class FooterView: MoleculeContainer { - public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - if let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) { - return height + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing - } - return 42 - } -} diff --git a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift deleted file mode 100644 index 9c63ba64..00000000 --- a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// MoleculeCollectionViewCell.swift -// MVMCoreUI -// -// Created by Scott Pfeil on 7/2/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import UIKit - -open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeViewProtocol, ModelMoleculeViewProtocol { - - open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? - open var json: [AnyHashable: Any]? - public let containerHelper = ContainerHelper() - - // In updateView, will set padding to default. - open var updateViewHorizontalDefaults = true - open var updateViewVerticalDefaults = true - - open var allowsPeaking = false - var peakingLeftArrow = UIImageView(image: MVMCoreUIUtility.imageNamed("peakingRightArrow")?.withRenderingMode(.alwaysTemplate)) - var peakingRightArrow = UIImageView(image: MVMCoreUIUtility.imageNamed("peakingRightArrow")?.withRenderingMode(.alwaysTemplate)) - var peakingCover = MVMCoreUICommonViewsUtility.commonView() - - public override init(frame: CGRect) { - super.init(frame: .zero) - setupView() - } - - public required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - setupView() - } - - public func setupView() { - guard peakingCover.superview == nil else { - return - } - isAccessibilityElement = false - contentView.isAccessibilityElement = false - insetsLayoutMarginsFromSafeArea = false - contentView.insetsLayoutMarginsFromSafeArea = false - contentView.preservesSuperviewLayoutMargins = false - - // Covers the card when peaking. - peakingCover.backgroundColor = .white - peakingCover.alpha = 0 - contentView.addSubview(peakingCover) - NSLayoutConstraint.constraintPinSubview(toSuperview: peakingCover) - - // A small arrow on the next card for when peaking. - let ratio: CGFloat = 0.015 - peakingLeftArrow.translatesAutoresizingMaskIntoConstraints = false - peakingLeftArrow.alpha = 0 - peakingLeftArrow.tintColor = .black - contentView.addSubview(peakingLeftArrow) - peakingLeftArrow.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true - NSLayoutConstraint.scalingPinViewLeft(toSuper: peakingLeftArrow, ratio: ratio, anchor: contentView.widthAnchor) - - peakingRightArrow.translatesAutoresizingMaskIntoConstraints = false - peakingRightArrow.transform = CGAffineTransform(scaleX: -1, y: 1) // Flip - peakingRightArrow.alpha = 0 - peakingRightArrow.tintColor = .black - contentView.addSubview(peakingRightArrow) - peakingRightArrow.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true - NSLayoutConstraint.scalingPinViewRight(toSuper: peakingRightArrow, ratio: ratio, anchor: contentView.widthAnchor) - } - - - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - guard let collectionModel = model as? CarouselItemModel else { - return - } - - if let useHorizontalMargins = collectionModel.useHorizontalMargins { - updateViewHorizontalDefaults = useHorizontalMargins - } - if let useVerticalMargins = collectionModel.useVerticalMargins { - updateViewVerticalDefaults = useVerticalMargins - } - // Handles peaking. - allowsPeaking = collectionModel.peakingUI ?? false - if let peakingArrowColor = collectionModel.peakingArrowColor { - let color = peakingArrowColor.uiColor - peakingLeftArrow.tintColor = color - peakingRightArrow.tintColor = color - } - - if let backgroundColor = collectionModel.backgroundColor { - self.backgroundColor = backgroundColor.uiColor - } - - if molecule == nil { - if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(collectionModel.molecule, delegateObject, false) { - contentView.insertSubview(moleculeView, at: 0) - containerHelper.constrainView(moleculeView) - molecule = moleculeView - } - } else { - (molecule as? ModelMoleculeViewProtocol)?.set(with: collectionModel.molecule, delegateObject, additionalData) - } - - guard let molecule = molecule else { return } - containerHelper.set(with: json, for: molecule) - - accessibilityElements = molecule.subviews - } - - public func reset() { - molecule?.reset?() - updateViewVerticalDefaults = true - updateViewHorizontalDefaults = true - backgroundColor = .white - } - - public class func nameForReuse(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - guard let molecule = (model as? CarouselItemModel)?.molecule, - let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule) as? ModelMoleculeViewProtocol.Type, - let name = moleculeClass.nameForReuse(with: molecule, delegateObject) ?? molecule.moleculeName else { - return nil - } - return name - } - - public func updateView(_ size: CGFloat) { - molecule?.updateView(size) - MFStyler.setDefaultMarginsFor(contentView, size: size, horizontal: updateViewHorizontalDefaults, vertical: updateViewVerticalDefaults) - } - - public func setPeaking(_ peaking: Bool, animated: Bool) { - guard allowsPeaking else { - return - } - let animation = {() in - self.peakingRightArrow.alpha = peaking ? 1 : 0 - self.peakingLeftArrow.alpha = peaking ? 1 : 0 - self.peakingCover.alpha = peaking ? 0.5 : 0 - } - if animated { - UIView.animate(withDuration: 0.4, animations: animation) - } else { - animation() - } - } -} diff --git a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/LabelToggleModel.swift b/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/LabelToggleModel.swift deleted file mode 100644 index 526ac6b9..00000000 --- a/MVMCoreUI/Molecules/LeftRightViews/ToggleMolecules/LabelToggleModel.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// LabelToggle.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 1/15/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import Foundation - -public class LabelToggleModel: MoleculeModelProtocol { - public static var identifier: String = "labelToggle" - public var moleculeName: String? = LabelToggleModel.identifier - public var backgroundColor: Color? - public var label: LabelModel - public var toggle: ToggleModel - - init(_ label: LabelModel, _ toggle: ToggleModel) { - self.label = label - self.toggle = toggle - } -} diff --git a/MVMCoreUI/Molecules/MoleculeViewProtocol.swift b/MVMCoreUI/Molecules/MoleculeViewProtocol.swift deleted file mode 100644 index 63fff68c..00000000 --- a/MVMCoreUI/Molecules/MoleculeViewProtocol.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// MVMCoreUIMoleculeViewProtocol1.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 10/24/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation - -import UIKit -import MVMCore.MVMCoreViewProtocol - -@objc public protocol MoleculeViewProtocol: MVMCoreViewProtocol { - - /// Sets up the ui based on the json - @objc func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) - - /// Called after init to provide an early setter for any molecule specific logic - @objc optional func setAsMolecule() - - - /// Resets to default state before set with json is called again. - @objc optional func reset() - - - /// For the molecule list to load more efficiently. - @objc optional static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat - - - /// Allows the molecule to set its name for reuse. Default could be moleculeName. - @objc optional static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? - - - /// Can return the required modules - @objc optional static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? -} diff --git a/MVMCoreUI/Molecules/RadioButtonSelectionHelper.swift b/MVMCoreUI/Molecules/RadioButtonSelectionHelper.swift deleted file mode 100644 index 263a85a4..00000000 --- a/MVMCoreUI/Molecules/RadioButtonSelectionHelper.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// RadioButtonModel.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 5/14/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation -import UIKit - -@objcMembers public class RadioButtonSelectionHelper: FormFieldProtocol { - - - public var fieldKey: String? - public var groupName: String? = FormValidator.defaultGroupName - private var selectedRadioButton: RadioButton? - private var fieldGroupName: String? - public var baseValue: AnyHashable? - - init(_ fieldKey: String?) { - self.fieldKey = fieldKey - } - - public static func setupForRadioButtonGroup(_ radioButtonModel: RadioButtonModel, formValidator: FormValidator?) -> RadioButtonSelectionHelper { - guard let groupName = radioButtonModel.fieldKey, - let formValidator = formValidator else { - return RadioButtonSelectionHelper(radioButtonModel.fieldKey) - } - - let radioButtonSelectionHelper = formValidator.radioButtonsModelByGroup[groupName] ?? RadioButtonSelectionHelper(radioButtonModel.fieldKey) - radioButtonSelectionHelper.fieldGroupName = radioButtonModel.fieldKey - formValidator.radioButtonsModelByGroup[groupName] = radioButtonSelectionHelper - return radioButtonSelectionHelper - } - - public func selected(_ radioButton: RadioButton) { - selectedRadioButton?.isSelected = false - selectedRadioButton = radioButton - selectedRadioButton?.isSelected = true - } -} - -// MARK: - FormValidationFormFieldProtocol -extension RadioButtonSelectionHelper { - public func formFieldGroupName() -> String? { - return selectedRadioButton?.formFieldGroupName() ?? self.fieldGroupName - } - - public func formFieldValue() -> AnyHashable? { - return selectedRadioButton?.formFieldValue() - } -} diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyButtonModel.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyButtonModel.swift deleted file mode 100644 index ec97fda0..00000000 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyButtonModel.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// HeadlineBodyButtonModel.swift -// MVMCoreUI -// -// Created by Scott Pfeil on 1/22/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import Foundation - -public struct HeadlineBodyButtonModel: MoleculeModelProtocol { - public static var identifier: String = "headlineBodyButton" - public var moleculeName: String? = HeadlineBodyButtonModel.identifier - public var backgroundColor: Color? - - public var headlineBody: HeadlineBodyModel - public var button: ButtonModel - public var buttonHeadlinePadding: CGFloat -} diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift deleted file mode 100644 index 0ec98c65..00000000 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// HeadlineBody.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 10/3/19. -// Copyright © 2019 Suresh, Kamlesh. All rights reserved. -// - -import Foundation - -@objcMembers open class HeadlineBodyModel: MoleculeModelProtocol { - public static var identifier: String = "headlineBody" - public var moleculeName: String? = HeadlineBodyModel.identifier - public var headline: LabelModel? - public var body: LabelModel? - public var style: String? - public var backgroundColor: Color? - - public init(headline: LabelModel) { - self.headline = headline - } - - public init(body: LabelModel) { - self.body = body - } -} diff --git a/MVMCoreUI/OtherHandlers/CoreUIObject.swift b/MVMCoreUI/OtherHandlers/CoreUIObject.swift index d60cdd6a..3553b54b 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIObject.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIObject.swift @@ -9,7 +9,7 @@ import UIKit @objcMembers open class CoreUIObject: MVMCoreObject { - public var moleculeMap: MVMCoreUIMoleculeMappingObject? + public var moleculeMap: MoleculeObjectMapping? open override func defaultInitialSetup() { cache = MVMCoreCache() @@ -18,7 +18,7 @@ import UIKit session = MVMCoreUISession() viewControllerMapping = MVMCoreUIViewControllerMappingObject() loggingDelegate = MVMCoreUILoggingHandler() - moleculeMap = MVMCoreUIMoleculeMappingObject() + moleculeMap = MoleculeObjectMapping() MoleculeObjectMapping.registerObjects() } } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIDelegateObject.swift b/MVMCoreUI/OtherHandlers/MVMCoreUIDelegateObject.swift index 54de3802..c9d5d34c 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIDelegateObject.swift +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIDelegateObject.swift @@ -13,7 +13,7 @@ open class MVMCoreUIDelegateObject: DelegateObject { public weak var buttonDelegate: ButtonDelegateProtocol? public weak var uiTextFieldDelegate: UITextFieldDelegate? public weak var observingTextFieldDelegate: ObservingTextFieldDelegate? - public var moleculeDelegate: MoleculeDelegateProtocol? + public weak var moleculeDelegate: MoleculeDelegateProtocol? open override func setAll(withDelegate delegate: Any) { super.setAll(withDelegate: delegate) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h index 003d67f8..9fec90f5 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h +++ b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h @@ -14,11 +14,11 @@ NS_ASSUME_NONNULL_BEGIN @interface MVMCoreUILoggingHandler : MVMCoreLoggingHandler // Page State Logging -- (void)defaultLogPageStateForController:(nonnull MFViewController *)controller; +- (void)defaultLogPageStateForController:(nonnull id )controller; // Action Logging -- (void)defaultLogActionForController:(nonnull MFViewController *)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; -- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nonnull MFViewController *)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; +- (void)defaultLogActionForController:(nonnull id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; +- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nonnull id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; @end diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m index 6395fbcc..0c694cce 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m @@ -10,13 +10,13 @@ @implementation MVMCoreUILoggingHandler -- (void)defaultLogPageStateForController:(nonnull MFViewController *)controller { +- (void)defaultLogPageStateForController:(nonnull id )controller { } -- (void)defaultLogActionForController:(nonnull MFViewController *)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData { +- (void)defaultLogActionForController:(nonnull id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData { } -- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nonnull MFViewController *)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData { +- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nonnull id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData { return nil; } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject+ModelExtension.swift b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject+ModelExtension.swift deleted file mode 100644 index 8eb0c34d..00000000 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject+ModelExtension.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// MVMCoreUIMoleculeMappingObject+ModelExtension.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 10/24/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation - -public extension MVMCoreUIMoleculeMappingObject { - - func register(viewClass: V.Type, viewModelClass: M.Type) { - try? ModelRegistry.register(viewModelClass) - MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(viewClass, forKey: viewModelClass.identifier as NSString) - } - - func getMoleculeClass(_ model: MoleculeModelProtocol) -> AnyClass? { - if let moleculeName = model.moleculeName { - return moleculeMapping.object(forKey: moleculeName) as? AnyClass - } - return nil - } - - func createMolecule(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> (UIView & MVMCoreUIMoleculeViewProtocol)? { - return createMolecule(model, delegateObject, false) - } - - func createMolecule(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ constrainIfNeeded: Bool) -> (UIView & MVMCoreUIMoleculeViewProtocol)? { - guard let moleculeName = model.moleculeName, - let molecule = createMolecule(forName: moleculeName) else { - return nil - } - - let setData = {() in - if let molecule = molecule as? ModelMoleculeViewProtocol { - molecule.set(with: model, delegateObject, nil) - } else { - molecule.setWithJSON?(model.toJSON(), delegateObject: delegateObject, additionalData: nil) - } - } - - if constrainIfNeeded, let castMolecule = molecule as? MVMCoreUIViewConstrainingProtocol, - castMolecule.needsToBeConstrained?() ?? false { - let view = ViewConstrainingView(molecule: molecule, alignment: castMolecule.horizontalAlignment?() ?? .fill) - setData() - return view - } else { - setData() - return molecule - } - } -} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.h b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.h deleted file mode 100644 index 776566c8..00000000 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// MVMCoreUIMoleculeMappingObject.h -// MVMCoreUI -// -// Created by Scott Pfeil on 2/11/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -#import -#import -@class MVMCoreUIDelegateObject; -@class MVMCoreLoadObject; -@class MVMCoreErrorObject; - -@interface MVMCoreUIMoleculeMappingObject : NSObject - -/// Maps molecule name to class. -@property (nonnull, strong, nonatomic) NSMutableDictionary *moleculeMapping; - -/// Returns the shared instance -+ (nullable instancetype)sharedMappingObject; - -/// Returns the molecule class. -- (nullable Class)getMoleculeClassWithJSON:(nonnull NSDictionary *)json; - -#pragma mark - Molecule Creation - -/// Creates the molecule for the molecule name. -- (nullable UIView *)createMoleculeForName:(nonnull NSString *)name; - -/// Creates the molecule for the molecule json. -- (nullable UIView *)createMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject; - -/// Creates the molecule for the molecule json. Also checks if the molecule needs to be constrained for a stack/list style situation. -- (nullable UIView *)createMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject constrainIfNeeded:(BOOL)constrainIfNeeded; - -#pragma mark - Convenience - -+ (nullable NSArray *)getRequiredModulesForJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error; -+ (void)addRequiredModulesForJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject moduleList:(nullable NSMutableArray *)moduleList errorList:(nullable NSMutableArray *)errorList; - -@end diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m deleted file mode 100644 index ccaa9115..00000000 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ /dev/null @@ -1,101 +0,0 @@ -// -// MVMCoreUIMoleculeMappingObject.m -// MVMCoreUI -// -// Created by Scott Pfeil on 2/11/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -#import "MVMCoreUIMoleculeMappingObject.h" -@import MVMCore.MVMCoreActionUtility; -@import MVMCore.NSDictionary_MFConvenience; -@import MVMCore.MVMCoreLoadObject; -@import MVMCore.MVMCoreErrorObject; -#import -#import "MFTextField.h" -#import "MVMCoreUIPageControl.h" -#import "MVMCoreUIViewConstrainingProtocol.h" - - -@implementation MVMCoreUIMoleculeMappingObject - -- (NSMutableDictionary *)moleculeMapping { - - // Keeps a mapping of the given page type - static dispatch_once_t onceToken; - static NSMutableDictionary *mapping; - dispatch_once(&onceToken, ^{ - mapping = [@{} mutableCopy]; - }); - return mapping; -} - -+ (nullable instancetype)sharedMappingObject { - return [MVMCoreActionUtility initializerClassCheck:[CoreUIObject sharedInstance].moleculeMap classToVerify:self]; -} - -- (nullable Class)getMoleculeClassWithJSON:(nonnull NSDictionary *)json { - NSString *moleculeName = [json string:KeyMoleculeName]; - if (moleculeName) { - return [self.moleculeMapping objectForKey:moleculeName]; - } - return nil; -} - -#pragma mark - Molecule Creation - -- (nullable UIView *)createMoleculeForName:(nonnull NSString *)name { - Class class = [self.moleculeMapping objectForKey:name]; - if (!class) { - return nil; - } - UIView *molecule = [[class alloc] init]; - if ([molecule respondsToSelector:@selector(setAsMolecule)]) { - [molecule setAsMolecule]; - } - return molecule; -} - -- (nullable UIView *)createMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject { - return [self createMoleculeForJSON:json delegateObject:delegateObject constrainIfNeeded:NO]; -} - -- (nullable UIView *)createMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject constrainIfNeeded:(BOOL)constrainIfNeeded { - NSString *moleculeName = [json string:KeyMoleculeName]; - if (!moleculeName) { - return nil; - } - UIView *molecule = [self createMoleculeForName:moleculeName]; - - // Check if we need to constrain this view. - UIView *castMolecule = [molecule conformsToProtocol:@protocol(MVMCoreUIViewConstrainingProtocol)] ? (UIView *)molecule : nil; - if (constrainIfNeeded && [castMolecule respondsToSelector:@selector(needsToBeConstrained)] && [castMolecule needsToBeConstrained]) { - molecule = [[ViewConstrainingView alloc] initWithMolecule:molecule alignment:[castMolecule respondsToSelector:@selector(horizontalAlignment)] ? [castMolecule horizontalAlignment] : UIStackViewAlignmentFill]; - } - [molecule setWithJSON:json delegateObject:delegateObject additionalData:nil]; - return molecule; -} - -#pragma mark - Convenience - -+ (nullable NSArray *)getRequiredModulesForJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error { - Class theClass = [[MVMCoreUIMoleculeMappingObject sharedMappingObject] getMoleculeClassWithJSON:json]; - if ([theClass respondsToSelector:@selector(requiredModules:delegateObject:error:)]) { - return [theClass requiredModules:json delegateObject:delegateObject error:error]; - } else { - return nil; - } -} - -+ (void)addRequiredModulesForJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject moduleList:(nullable NSMutableArray *)moduleList errorList:(nullable NSMutableArray *)errorList { - MVMCoreErrorObject *error = nil; - NSArray *modules = [self getRequiredModulesForJSON:json delegateObject:delegateObject error:&error]; - if (modules) { - [moduleList addObjectsFromArray:modules]; - } - if (error) { - [errorList addObject:error]; - } -} - -@end diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m index d3db2a2d..20fb924e 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject.m @@ -24,7 +24,8 @@ @"list" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeListTemplate class]], @"threeLayer" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[ThreeLayerTemplate class]], @"modalStack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[ModalMoleculeStackTemplate class]], - @"modalList" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[ModalMoleculeListTemplate class]] + @"modalList" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[ModalMoleculeListTemplate class]], + @"collection" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[CollectionTemplate class]] } mutableCopy]; }); return viewControllerMapping; diff --git a/MVMCoreUI/OtherHandlers/ModelMoleculeDelegateProtocol.swift b/MVMCoreUI/OtherHandlers/ModelMoleculeDelegateProtocol.swift deleted file mode 100644 index eb175c3a..00000000 --- a/MVMCoreUI/OtherHandlers/ModelMoleculeDelegateProtocol.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// MoleculeDelegateProtocol.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 11/26/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation - -public protocol MoleculeDelegateProtocol { - - /// returns a module for the corresponding module name. - func getModuleWithName(_ name: String?) -> [AnyHashable : Any]? - func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? - - /// Notifies the delegate that the molecule layout update. Should be called when the layout may change due to an async method. - func moleculeLayoutUpdated(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) //optional - - /// Asks the delegate to add or remove molecules. - //optional - func addMolecules(_ molecules: [[AnyHashable : Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) - func addMolecules(_ molecules: [[AnyHashable : Any]], indexPath: IndexPath, animation: UITableView.RowAnimation) - - func removeMolecules(_ molecules: [[AnyHashable : Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) - - //optional - func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], sender: UITableViewCell, animation: UITableView.RowAnimation) - func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], sender: UITableViewCell, animation: UITableView.RowAnimation) -} - -extension MoleculeDelegateProtocol { - public func moleculeLayoutUpdated(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) { - // Do Nothing - } - - public func addMolecules(_ molecules: [[AnyHashable : Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) { - // Do nothing - } - - public func addMolecules(_ molecules: [[AnyHashable : Any]], indexPath: IndexPath, animation: UITableView.RowAnimation) { - // Do nothing - } - - public func removeMolecules(_ molecules: [[AnyHashable : Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) { - // Do nothing - } - - public func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], sender: UITableViewCell, animation: UITableView.RowAnimation) { - // Do nothing - } - - public func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], sender: UITableViewCell, animation: UITableView.RowAnimation) { - // Do nothing - } -} diff --git a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift b/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift deleted file mode 100644 index 452a1476..00000000 --- a/MVMCoreUI/OtherHandlers/MoleculeObjectMapping.swift +++ /dev/null @@ -1,137 +0,0 @@ -// -// MVMCoreUIMoleculeMappingObject+ModelMapping.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 10/28/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation - -@objcMembers public class MoleculeObjectMapping: NSObject { - public static func registerObjects() { - // Stacks - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: MoleculeStackView.self, viewModelClass: MoleculeStackModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Stack.self, viewModelClass: StackModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: UnOrderedList.self, viewModelClass: UnOrderedListModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: NumberedList.self, viewModelClass: NumberedListModel.self) - - // Label - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Label.self, viewModelClass: LabelModel.self) - // need to move labelattributemodel to different method - try? ModelRegistry.register(LabelAttributeFontModel.self) - try? ModelRegistry.register(LabelAttributeColorModel.self) - try? ModelRegistry.register(LabelAttributeImageModel.self) // We need to separate the registry by types due to collisions... - try? ModelRegistry.register(LabelAttributeUnderlineModel.self) - try? ModelRegistry.register(LabelAttributeStrikeThroughModel.self) - try? ModelRegistry.register(LabelAttributeActionModel.self) - - // Buttons - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: PillButton.self, viewModelClass: ButtonModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: TwoButtonView.self, viewModelClass: TwoButtonViewModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ExternalLink.self, viewModelClass: ExternalLinkModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Link.self, viewModelClass: LinkModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: CaretLink.self, viewModelClass: CaretLinkModel.self) - - // Entry Field - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: TextEntryField.self, viewModelClass: TextEntryFieldModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: MdnEntryField.self, viewModelClass: MdnEntryFieldModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: DigitEntryField.self, viewModelClass: DigitEntryFieldModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ItemDropdownEntryField.self, viewModelClass: ItemDropdownEntryFieldModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: DateDropdownEntryField.self, viewModelClass: DateDropdownEntryFieldModel.self) - - // Other Atoms - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ProgressBar.self, viewModelClass: ProgressBarModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: MultiProgress.self, viewModelClass: MultiProgressBarModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: CaretView.self, viewModelClass: CaretViewModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: DashLine.self, viewModelClass: DashLineModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: MFLoadImageView.self, viewModelClass: ImageViewModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Line.self, viewModelClass: LineModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: GraphView.self, viewModelClass: CircleProgressModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Toggle.self, viewModelClass: ToggleModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Checkbox.self, viewModelClass: CheckboxModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: CheckboxLabel.self, viewModelClass: CheckboxLabelModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Arrow.self, viewModelClass: ArrowModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: RadioButton.self, viewModelClass: RadioButtonModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: RadioButtonLabel.self, viewModelClass: RadioButtonLabelModel.self) - - // Horizontal Combination Molecules - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: StringAndMoleculeView.self, viewModelClass: StringAndMoleculeModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ImageHeadlineBody.self, viewModelClass: ImageHeadlineBodyModel.self) - - // Vertical Combination Molecules - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: HeadlineBody.self, viewModelClass: HeadlineBodyModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: HeadLineBodyCaretLinkImage.self, viewModelClass: HeadlineBodyCaretLinkImageModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: EyebrowHeadlineBodyLink.self, viewModelClass: EyebrowHeadlineBodyLinkModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: HeadlineBodyLink.self, viewModelClass: HeadlineBodyLinkModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: HeadlineBodyButton.self, viewModelClass: HeadlineBodyButtonModel.self) - - // Left Right Molecules - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: CornerLabels.self, viewModelClass: CornerLabelsModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: LeftRightLabelView.self, viewModelClass: LeftRightLabelModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: LabelToggle.self, viewModelClass: LabelToggleModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: HeadlineBodyToggle.self, viewModelClass: HeadlineBodyToggleModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: HeadlineBodyLinkToggle.self, viewModelClass: HeadlineBodyLinkToggleModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ActionDetailWithImage.self, viewModelClass: ActionDetailWithImageModel.self) - - // List items - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: MoleculeTableViewCell.self, viewModelClass: MoleculeListItemModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: DropDownFilterTableViewCell.self, viewModelClass: DropDownListItemModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: AccordionMoleculeTableViewCell.self, viewModelClass: AccordionListItemModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: TabsTableViewCell.self, viewModelClass: TabsListItemModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListProgressBarData.self, viewModelClass: ListProgressBarDataModel.self) - - // Other Items - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: MoleculeStackItem.self, viewModelClass: MoleculeStackItemModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: StackItem.self, viewModelClass: StackItemModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: MoleculeCollectionViewCell.self, viewModelClass: CarouselItemModel.self) - - // Other Container Molecules - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: MoleculeHeaderView.self, viewModelClass: MoleculeHeaderModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: FooterView.self, viewModelClass: FooterModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Scroller.self, viewModelClass: ScrollerModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ModuleMolecule.self, viewModelClass: ModuleMoleculeModel.self) - - // Other Molecules - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: DoughnutChartView.self, viewModelClass: DoughnutChartModel.self) - - // Other Organisms - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Carousel.self, viewModelClass: CarouselModel.self) - - // Designed List Items - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListLeftVariableIconWithRightCaret.self, viewModelClass: ListLeftVariableIconWithRightCaretModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListLeftVariableCheckboxAllTextAndLinks.self, viewModelClass: ListLeftVariableCheckboxAllTextAndLinksModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListLeftVariableRadioButtonAndPaymentMethod.self, viewModelClass: ListLeftVariableRadioButtonAndPaymentMethodModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListRVWheel.self, viewModelClass: ListRVWheelModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListRightVariablePayments.self, viewModelClass: ListRightVariablePaymentsModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListRightVariableTotalData.self, viewModelClass: ListRightVariableTotalDataModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListOneColumnFullWidthTextAllTextAndLinks.self, viewModelClass: ListOneColumnFullWidthTextAllTextAndLinksModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListOneColumnFullWidthTextBodyText.self, viewModelClass: ListOneColumnFullWidthTextBodyTextModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListTwoColumnCompareChanges.self, viewModelClass: ListTwoColumnCompareChangesModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListTwoColumnPriceDetails.self, viewModelClass: ListTwoColumnPriceDetailsModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListTwoColumnPriceDescription.self, viewModelClass: ListTwoColumnPriceDescriptionModel.self) - - // Designed Section Dividers - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListFourColumnDataUsageDivider.self, viewModelClass: ListFourColumnDataUsageDividerModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListThreeColumnPlanDataDivider.self, viewModelClass: ListThreeColumnPlanDataDividerModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: ListOneColumnFullWidthTextDividerSubsection.self, viewModelClass: ListOneColumnFullWidthTextDividerSubsectionModel.self) - - // Designed Headers - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: HeadersH2NoButtonsBodyText.self, viewModelClass: HeadersH2NoButtonsBodyTextModel.self) - MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: HeadersH1Bill.self, viewModelClass: HeadersH1BillModel.self) - - // TODO: Need model - MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(MVMCoreUIPageControl.self, forKey: "barsPager" as NSString) - - // TODO: Need View - try? ModelRegistry.register(TabsModel.self) - - // Helper models - try? ModelRegistry.register(RuleRequiredModel.self) - try? ModelRegistry.register(RuleAnyRequiredModel.self) - try? ModelRegistry.register(RuleAnyValueChangedModel.self) - try? ModelRegistry.register(RuleAllValueChangedModel.self) - try? ModelRegistry.register(RuleEqualsModel.self) - try? ModelRegistry.register(RuleRegexModel.self) - } -} diff --git a/MVMCoreUI/Styles/Padding.swift b/MVMCoreUI/Styles/Padding.swift new file mode 100644 index 00000000..0dd9ff6e --- /dev/null +++ b/MVMCoreUI/Styles/Padding.swift @@ -0,0 +1,48 @@ +// +// Padding.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/1/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + + +/// Padding is a multiple based on the number 4. +public struct Padding { + + public static let OneHalf: CGFloat = 2 + public static let One: CGFloat = 4 + public static let Two: CGFloat = 8 + public static let Three: CGFloat = 12 + public static let Four: CGFloat = 16 + public static let Five: CGFloat = 24 + public static let Eight: CGFloat = 32 + public static let Ten: CGFloat = 40 + public static let Twelve: CGFloat = 48 + public static let Eighteen: CGFloat = 72 + + public struct Component { + public static let Standard: CGFloat = 24 + public static let HorizontalMarginSpacing: CGFloat = 32 + public static let LargeVerticalMarginSpacing: CGFloat = 32 + public static let VerticalMarginSpacing: CGFloat = 24 + + public static var horizontalPaddingForApplicationWidth: CGFloat { + return MFSizeObject(scalingStandardSize: HorizontalMarginSpacing)?.getValueBasedOnApplicationWidth() ?? HorizontalMarginSpacing + } + + public static var verticalPaddingForApplicationWidth: CGFloat { + return MFSizeObject(scalingStandardSize: VerticalMarginSpacing)?.getValueBasedOnApplicationWidth() ?? VerticalMarginSpacing + } + + public static func horizontalPaddingForSize(_ size: CGFloat) -> CGFloat { + return MFSizeObject(scalingStandardSize: HorizontalMarginSpacing)?.getValueBased(onSize: size) ?? HorizontalMarginSpacing + } + + public static func verticalPaddingForSize(_ size: CGFloat) -> CGFloat { + return MFSizeObject(scalingStandardSize: VerticalMarginSpacing)?.getValueBased(onSize: size) ?? VerticalMarginSpacing + } + } +} diff --git a/MVMCoreUI/Styles/Styler.swift b/MVMCoreUI/Styles/Styler.swift new file mode 100644 index 00000000..b5d6627c --- /dev/null +++ b/MVMCoreUI/Styles/Styler.swift @@ -0,0 +1,228 @@ +// +// Styler.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 4/1/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + + +open class Styler { + //-------------------------------------------------- + // MARK: - Enums + //-------------------------------------------------- + + public enum Font: String, Codable { + case Title2XLarge + case TitleXLarge + case BoldTitleLarge + case RegularTitleLarge + case BoldTitleMedium + case RegularTitleMedium + case BoldBodyLarge + case RegularBodyLarge + case BoldBodySmall + case RegularBodySmall + case BoldMicro + case RegularMicro + + // Legacy Fonts + case H1 + case H2 + case H3 + case H32 + case B1 + case B2 + case B3 + case B20 + + /// Returns the font size of the current enum case. + public func pointSize() -> CGFloat { + switch self { + case .H1: + return 40 + + case .Title2XLarge: + return 36 + + case .TitleXLarge, + .H32: + return 32 + + case .H2: + return 25 + + case .BoldTitleLarge, + .RegularTitleLarge: + return 24 + + case .BoldTitleMedium, + .RegularTitleMedium, + .B20: + return 20 + + case .H3: + return 18 + + case .BoldBodyLarge, + .RegularBodyLarge: + return 16 + + case .BoldBodySmall, + .RegularBodySmall, + .B1, + .B2: + return 13 + + case .BoldMicro, + .RegularMicro, + .B3: + return 11 + } + } + + /// Determines if the selected font case is bold or regular. + public func isBold() -> Bool { + + switch self { + case .Title2XLarge, + .TitleXLarge, + .RegularTitleLarge, + .RegularTitleMedium, + .RegularBodyLarge, + .RegularBodySmall, + .RegularMicro, + .B2, + .B3, + .B20: + return false + + case .BoldTitleLarge, + .BoldTitleMedium, + .BoldBodyLarge, + .BoldBodySmall, + .BoldMicro, + .H1, + .H2, + .H3, + .H32, + .B1: + return true + } + } + + /// Determines if the current enum is a legacy or modern font. + public func isLegacyFont() -> Bool { + + switch self { + case .Title2XLarge, + .TitleXLarge, + .RegularTitleLarge, + .RegularTitleMedium, + .RegularBodyLarge, + .RegularBodySmall, + .RegularMicro, + .BoldTitleLarge, + .BoldTitleMedium, + .BoldBodyLarge, + .BoldBodySmall, + .BoldMicro: + return false + + case .H1, + .H2, + .H3, + .H32, + .B1, + .B2, + .B3, + .B20: + return true + } + } + + /// Returns the font based on the declared enum case. + public func getFont(_ genericScaling: Bool = true) -> UIFont? { + + let size = genericScaling ? sizeFontGeneric(forCurrentDevice: pointSize()) : pointSize() + + if isLegacyFont() { + switch self { + case .B2, .B3, .B20: + return MFFonts.mfFont55Rg(size) + + default: + return MFFonts.mfFont75Bd(size) + } + } else { + if isBold() { + return size >= 15 ? MFFonts.mfFontDSBold(size) : MFFonts.mfFontTXBold(size) + + } else { + return size >= 15 ? MFFonts.mfFontDSRegular(size) : MFFonts.mfFontTXRegular(size) + } + } + } + + /// Styles the provided label to the declared enum Font case. + public func styleLabel(_ label: UILabel, textColor: UIColor = .mvmBlack, genericScaling: Bool = true) { + + label.font = getFont(genericScaling) + label.textColor = textColor + } + } + + //-------------------------------------------------- + // MARK: - Functions + //-------------------------------------------------- + + open class func sizeObjectGeneric(forCurrentDevice size: CGFloat) -> MFSizeObject? { + + let sizeObject = MFSizeObject(standardSize: size, standardiPadPortraitSize: size * 1.3) + sizeObject?.addLargerThanCustomSize(size * 1.4, forThreshold: MFSizeStandardiPadLandscapeThreshold) + sizeObject?.addLargerThanCustomSize(size * 1.5, forThreshold: MFSizeiPadProLandscapeThreshold) + + return sizeObject + } + + open class func sizeFontGeneric(forCurrentDevice size: CGFloat) -> CGFloat { + return sizeObjectGeneric(forCurrentDevice: size)?.getValueBasedOnApplicationWidth() ?? size + } + + //-------------------------------------------------- + // MARK: - Spacing + //-------------------------------------------------- + + open class func setDefaultMarginsFor(_ view: UIView?, size: CGFloat?, horizontal: Bool = true, vertical: Bool = false) { + + var horizontalPadding: CGFloat = Padding.Component.HorizontalMarginSpacing + let verticalPadding: CGFloat = vertical ? Padding.Component.VerticalMarginSpacing : 0 + + if let size = size { + horizontalPadding = horizontal ? Padding.Component.horizontalPaddingForSize(size) : 0 + } + + DispatchQueue.main.async { + MVMCoreUIUtility.setMarginsFor(view, + leading: horizontalPadding, + top: verticalPadding, + trailing: horizontalPadding, + bottom: verticalPadding) + } + } + + open class func setMarginsFor(_ view: UIView?, size: CGFloat, horizontal: CGFloat?, top: CGFloat, bottom: CGFloat) { + + let horizontalPadding: CGFloat = horizontal ?? Padding.Component.horizontalPaddingForSize(size) + + DispatchQueue.main.async { + MVMCoreUIUtility.setMarginsFor(view, + leading: horizontalPadding, + top: top, + trailing: horizontalPadding, + bottom: bottom) + } + } +} diff --git a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings index b9d21b67..48cbe3ce 100644 --- a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings @@ -6,52 +6,73 @@ Copyright © 2017 myverizon. All rights reserved. */ -//// Accessibility +// MARK: Accessibility "AccCloseButton" = "Close"; "swipe_to_select_with_action_hint" = "swipe up or down to select action, then double tap to select."; -// Tab + + +// MARK: Tab "AccTab" = ", tab"; "AccTabHint" = "Double tap to select."; "AccTabIndex" = ", %ld of %ld"; -// top alert + + +// MARK: Top alert "toptabbar_tab_selected" = ", tab, Selected"; "AccTopAlertClosed" = "Top alert notification is closed."; "top_alert_notification" = "Top alert notification"; -// Textfield + + +// MARK: Textfield "textfield_today_string" = "Today"; "textfield_error_message" = "%@.\n The error message.\n %@"; "textfield_picker_item" = " picker item"; "textfield_regular" = " regular"; "textfield_disabled_state" = "disabled"; -// MDNTextfield + + +// MARK: MDNTextfield "textfield_contacts_barbutton" = "My Contacts"; "textfield_phone_format_error_message" = "Invalid phone number format."; -// DigitTextfield + + +// MARK: DigitTextfield "mfdigittextfield_regular" = " regular"; -// Camera + + +// MARK: Camera "AccCameraButton" = "Camera Button"; "AccCameraHint" = "Double tap to launch camera for scanning"; -// Checkbox + + +// MARK: Checkbox "checkbox_action_hint" = "Double tap to change state"; "checkbox_checked_state" = "Checked"; "checkbox_unchecked_state" = "Unchecked"; "checkbox_desc_state" = "%@ CheckBox %@"; -// Radio Button + +// MARK: Radio Button "radio_action_hint" = "Double tap to select"; "radio_selected_state" = "Selected"; "radio_not_selected_state" = "Not Selected"; "radio_desc_state" = "Option"; -// Switch + +// MARK: Switch / Toggle "mfswitch_buttonlabel" = "Switch Button"; +"Toggle_buttonlabel" = "Toggle Button"; "AccOn" = "on"; "AccOff" = "off"; "AccToggleHint" = "double tap to toggle"; -// Carousel + + +// MARK: Carousel "MVMCoreUIPageControl_currentpage_index" = "page %ld of %ld"; "MVMCoreUIPageControlslides_currentpage_index" = "slide %ld of %ld"; -//Styler + + +// MARK: Styler "CountDownDay" = " day"; "CountDownHour" = " hour"; "CountDownMin" = " min"; diff --git a/MVMCoreUI/Templates/StackCenteredPageTemplateModel.swift b/MVMCoreUI/Templates/StackCenteredPageTemplateModel.swift deleted file mode 100644 index 66cd0365..00000000 --- a/MVMCoreUI/Templates/StackCenteredPageTemplateModel.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// StackCenteredPageTemplate.swift -// MVMCoreUI -// -// Created by Suresh, Kamlesh on 11/22/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import Foundation - -@objcMembers public class StackCenteredPageTemplateModel: TemplateModelProtocol { - - public var formRules: [FormGroupRule]? - public var formValidator: FormValidator? - - public static var identifier: String = "stackCenterTemplate" - public var pageType: String - public var screenHeading: String? - public var isAtomicTabs: Bool? - - public init(pageType: String) { - self.pageType = pageType - } - - private enum CodingKeys: String, CodingKey { - case pageType - case screenHeading - case isAtomicTabs - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - pageType = try typeContainer.decode(String.self, forKey: .pageType) - screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading) - isAtomicTabs = try typeContainer.decodeIfPresent(Bool.self, forKey: .isAtomicTabs) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(pageType, forKey: .pageType) - try container.encodeIfPresent(screenHeading, forKey: .screenHeading) - try container.encodeIfPresent(isAtomicTabs, forKey: .isAtomicTabs) - } -} diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m index cc25cd9b..6fc7121f 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertExpandableView.m @@ -79,7 +79,7 @@ } - (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nullable id )animationDelegate viewToLayout:(nonnull UIView *)viewTolayout { - if ([self init]) { + if (self = [self init]) { self.animationDelegate = animationDelegate; self.viewToLayout = viewTolayout; @@ -119,7 +119,7 @@ } - (nullable instancetype)initWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle animationDelegate:(nullable id )animationDelegate viewToLayout:(nonnull UIView *)viewTolayout { - if ([self init]) { + if (self = [self init]) { self.animationDelegate = animationDelegate; self.viewToLayout = viewTolayout; [self setupViewWithTopMessage:topMessage message:message subMessage:nil contentColor:contentColor buttonTitle:buttonTitle]; @@ -128,7 +128,7 @@ } - (nullable instancetype)initWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle animationDelegate:(nullable id )animationDelegate viewToLayout:(nonnull UIView *)viewTolayout { - if ([self init]) { + if (self = [self init]) { self.animationDelegate = animationDelegate; self.viewToLayout = viewTolayout; [self setupViewWithTopMessage:topMessage message:message subMessage:subMessage contentColor:contentColor buttonTitle:buttonTitle]; @@ -137,7 +137,7 @@ } - (nullable instancetype)initWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData animationDelegate:(nullable id )animationDelegate viewToLayout:(nonnull UIView *)viewTolayout { - if ([self init]) { + if (self = [self init]) { self.animationDelegate = animationDelegate; self.viewToLayout = viewTolayout; [self setupViewWithTopMessage:topMessage message:message subMessage:subMessage contentColor:contentColor actionMap:actionMap additionalData:additionalData]; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m index 5b87d163..4ebfd013 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m @@ -59,7 +59,7 @@ } - (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nullable id )animationDelegate { - if ([self init]) { + if (self = [self init]) { UIColor *contentColor = topAlertObject.textColor ?: [[MVMCoreUITopAlertView sharedGlobal] getContentColorForType:topAlertObject.type]; self.backgroundColor = topAlertObject.backgroundColor ?: [[MVMCoreUITopAlertView sharedGlobal] getBackgroundColorForType:topAlertObject.type]; [self setupViewWithLabelAndImage:topAlertObject.imageNameOrURL topImage:topAlertObject.aboveTextImageString]; @@ -72,7 +72,7 @@ - (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData closeButton:(BOOL)closeButton animationDelegate:(nullable id )animationDelegate { // Handles all scenarios. - if ([self init]) { + if (self = [self init]) { self.backgroundColor = color; [self setupViewWithLabelAndImage:nil topImage:nil]; [self setupCloseButton:closeButton animationDelegate:animationDelegate]; @@ -86,7 +86,7 @@ - (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor imageURL:(nullable NSString *)imageURL message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData closeButton:(BOOL)closeButton animationDelegate:(nullable id )animationDelegate { // Handles all scenarios. - if ([self init]) { + if (self = [self init]) { self.backgroundColor = color; [self setupViewWithLabelAndImage:imageURL topImage:nil]; [self setupCloseButton:closeButton animationDelegate:animationDelegate]; @@ -98,7 +98,7 @@ - (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor imageURL:(nullable NSString *)imageURL message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage closeButton:(BOOL)closeButton animationDelegate:(nullable id )animationDelegate { // No main button. - if ([self init]) { + if (self = [self init]) { self.backgroundColor = color; [self setupViewWithLabelAndImage:imageURL topImage:nil]; [self setupCloseButton:closeButton animationDelegate:animationDelegate]; @@ -286,7 +286,7 @@ - (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nonnull UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData { // No icon or close button. - if ([self init]) { + if (self = [self init]) { self.backgroundColor = color; [self setupViewWithLabelAndImage:nil topImage:nil]; [self setupWithMessage:message subMessage:subMessage color:contentColor actionMap:actionMap additionalData:additionalData]; @@ -297,7 +297,7 @@ - (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nonnull UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage closeButton:(BOOL)closeButton animationDelegate:(nullable id )animationDelegate { // No main button. - if ([self init]) { + if (self = [self init]) { self.backgroundColor = color; [self setupViewWithLabelAndImage:nil topImage:nil]; [self setupWithMessage:message subMessage:subMessage color:contentColor buttonTitle:nil userActionHandler:NULL]; @@ -309,7 +309,7 @@ - (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nonnull UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler { // No icon or close button. Custom button action. - if ([self init]) { + if (self = [self init]) { self.backgroundColor = color; [self setupViewWithLabelAndImage:nil topImage:nil]; [self setupWithMessage:message subMessage:subMessage color:contentColor buttonTitle:buttonTitle userActionHandler:userActionHandler]; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index 930828f6..4e79ee6f 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -117,7 +117,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; if ([type isEqualToString:ValueTypeError]) { return [UIColor mvmOrange]; } else { - return [UIColor mfShamrock]; + return [UIColor mvmGreen]; } } diff --git a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift new file mode 100644 index 00000000..3ee452b0 --- /dev/null +++ b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift @@ -0,0 +1,23 @@ +// +// MVMCoreUICommonViewsUtility+Extension.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/23/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public extension MVMCoreUICommonViewsUtility { + + static func getToolbarWithDoneButton(delegate: ObservingTextFieldDelegate) -> UIToolbar { + + let toolbar = self.makeEmptyToolbar() + let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + let button = UIBarButtonItem(barButtonSystemItem: .done, target: delegate, action: #selector(ObservingTextFieldDelegate.dismissFieldInput(sender:))) + button.tintColor = .black + toolbar.setItems([space, button], animated: false) + + return toolbar + } +} diff --git a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m index 4a21b1a5..5c796417 100644 --- a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m @@ -260,15 +260,15 @@ static const CGFloat VertialShadowOffset = 6; UIBezierPath *shadowPath = [UIBezierPath bezierPath]; //get the variables for frame - CGFloat x = 0; - CGFloat y = 0; + CGFloat x = rect.origin.x; + CGFloat y = rect.origin.y; CGFloat width = CGRectGetWidth(rect); CGFloat height = CGRectGetHeight(rect); [shadowPath moveToPoint:CGPointMake(x + HorizontalShadowInset, y)]; - [shadowPath addLineToPoint:CGPointMake(width - HorizontalShadowInset, y)]; - [shadowPath addLineToPoint:CGPointMake(width - HorizontalShadowInset, height-VertialShadowOffset/2)]; - [shadowPath addQuadCurveToPoint:CGPointMake(x + HorizontalShadowInset, height - VertialShadowOffset/2) controlPoint:CGPointMake(width/2.f, height - VertialShadowOffset * 1.5)]; + [shadowPath addLineToPoint:CGPointMake(x + width - HorizontalShadowInset, y)]; + [shadowPath addLineToPoint:CGPointMake(x + width - HorizontalShadowInset, height-VertialShadowOffset/2)]; + [shadowPath addQuadCurveToPoint:CGPointMake(x + HorizontalShadowInset, height - VertialShadowOffset/2) controlPoint:CGPointMake((x + width)/2.f, height - VertialShadowOffset * 1.5)]; [shadowPath addLineToPoint:CGPointMake(x + HorizontalShadowInset, y)]; [shadowPath closePath]; diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.h b/MVMCoreUI/Utility/MVMCoreUIUtility.h index 9300de90..21c92c38 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.h +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.h @@ -31,6 +31,9 @@ NS_ASSUME_NONNULL_BEGIN // Returns the margins for a view. + (UIEdgeInsets)getMarginsForView:(nullable UIView *)view; +/// Gets the current visible view controller. Checks presented view controllers first, and then it checks on the NavigationController in the session object. ++ (UIViewController *)getCurrentVisibleController; + #pragma mark - Setters + (void)setMarginsForView:(nullable UIView *)view leading:(CGFloat)leading top:(CGFloat)top trailing:(CGFloat)trailing bottom:(CGFloat)bottom; diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index b760eb36..0841e248 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -10,6 +10,7 @@ #import "MVMCoreUIConstants.h" #import "MVMCoreUISession.h" #import "MVMCoreUISplitViewController.h" +#import @import MVMCore.MVMCoreNavigationHandler; @import MVMCore.MVMCoreGetterUtility; @@ -60,6 +61,26 @@ return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing); } ++ (UIViewController *)getCurrentVisibleController { + UIViewController *baseViewController = [MVMCoreNavigationHandler sharedNavigationHandler].viewControllerToPresentOn ?: [UIApplication sharedApplication].keyWindow.rootViewController; + UIViewController *viewController = nil; + while (baseViewController.presentedViewController && !baseViewController.presentedViewController.isBeingDismissed) { + viewController = baseViewController.presentedViewController; + baseViewController = viewController; + } + if ([viewController isKindOfClass:[UINavigationController class]]) { + viewController = ((UINavigationController *)viewController).topViewController; + } + // if it is not presented viewcontroller, existing BAU logic will be working + if (!viewController) { + viewController = [MVMCoreUISession sharedGlobal].navigationController.topViewController; + if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { + viewController = [viewController performSelector:@selector(getCurrentViewController)]; + } + } + return viewController; +} + #pragma mark - Setters + (void)setMarginsForView:(nullable UIView *)view leading:(CGFloat)leading top:(CGFloat)top trailing:(CGFloat)trailing bottom:(CGFloat)bottom { @@ -129,7 +150,7 @@ } + (CGFloat)getHeightOfView:(nonnull UIView *)view forWidth:(nullable NSNumber *)width { - CGFloat floatWidth = (width ? width.floatValue : [MVMCoreUIUtility getWidth]); + CGFloat floatWidth = (width != nil ? width.floatValue : [MVMCoreUIUtility getWidth]); return [view systemLayoutSizeFittingSize:CGSizeMake(floatWidth, UILayoutFittingCompressedSize.height) withHorizontalFittingPriority:1000 verticalFittingPriority:250].height; } @@ -143,11 +164,10 @@ CGFloat topInset = scrollview.contentInset.top; CGFloat bottomInset = scrollview.contentInset.bottom; - if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) { + if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic || scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAlways) { topInset = scrollview.adjustedContentInset.top; bottomInset = scrollview.adjustedContentInset.bottom; } - CGFloat remainingSpace = frameHeight - contentSizeHeight - topInset - bottomInset; return remainingSpace - 1; } diff --git a/MVMCoreUI/Utility/Sizing/MFSizeObject.m b/MVMCoreUI/Utility/Sizing/MFSizeObject.m index 1acf878b..792e2d81 100644 --- a/MVMCoreUI/Utility/Sizing/MFSizeObject.m +++ b/MVMCoreUI/Utility/Sizing/MFSizeObject.m @@ -30,7 +30,7 @@ CGFloat const MFSizeiPadProLandscapeThreshold = 1300; + (CGFloat)getScaledValue:(CGFloat)value forSize:(CGFloat)size fromBase:(nullable NSNumber *)base { CGFloat baseFloat = 375.0; - if (base) { + if (base != nil) { baseFloat = [base floatValue]; } return value * (size/baseFloat);