Merge branch 'develop' into feature/new_modern_page_control

This commit is contained in:
Kevin G Christiano 2020-04-27 09:13:20 -04:00
commit 00d196c0db
58 changed files with 941 additions and 447 deletions

View File

@ -200,8 +200,12 @@
AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */; }; AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */; };
AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1EC59624373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift */; }; AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1EC59624373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift */; };
AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */; }; AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */; };
AA2AD116244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA2AD115244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift */; };
AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA2AD117244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift */; };
AA56A20F243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */; }; AA56A20F243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */; };
AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */; }; AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */; };
AA617AB02453010A00910B8F /* ListDeviceComplexLinkSmall.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA617AAF2453010A00910B8F /* ListDeviceComplexLinkSmall.swift */; };
AA617AB22453012400910B8F /* ListDeviceComplexLinkSmallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA617AB12453012400910B8F /* ListDeviceComplexLinkSmallModel.swift */; };
AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69AAF52445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift */; }; AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69AAF52445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift */; };
AA69AAF82445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69AAF72445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift */; }; AA69AAF82445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69AAF72445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift */; };
AA85236C244435A20059CC1E /* RadioSwatchCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */; }; AA85236C244435A20059CC1E /* RadioSwatchCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */; };
@ -245,6 +249,12 @@
D202AFE4242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.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 */; }; D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D202AFE5242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift */; };
D2092349244A51D40044AD09 /* RadioSwatchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092348244A51D40044AD09 /* RadioSwatchModel.swift */; }; D2092349244A51D40044AD09 /* RadioSwatchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092348244A51D40044AD09 /* RadioSwatchModel.swift */; };
D209234F244F77FD0044AD09 /* ThreeLayerCenterTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D209234E244F77FD0044AD09 /* ThreeLayerCenterTemplate.swift */; };
D2092351244F7BE80044AD09 /* ThreeLayerCenterTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092350244F7BE80044AD09 /* ThreeLayerCenterTemplateModel.swift */; };
D2092353244F7D630044AD09 /* MVMCoreUIViewControllerMappingObject+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092352244F7D630044AD09 /* MVMCoreUIViewControllerMappingObject+Extension.swift */; };
D2092355244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092354244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift */; };
D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */; };
D20923592450ECE00044AD09 /* TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20923582450ECE00044AD09 /* TableView.swift */; };
D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; };
D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */; };
D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; };
@ -650,8 +660,12 @@
AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariablePaymentsModel.swift; sourceTree = "<group>"; }; AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariablePaymentsModel.swift; sourceTree = "<group>"; };
AA1EC59624373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnSpeedTestDividerModel.swift; sourceTree = "<group>"; }; AA1EC59624373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnSpeedTestDividerModel.swift; sourceTree = "<group>"; };
AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnSpeedTestDivider.swift; sourceTree = "<group>"; }; AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnSpeedTestDivider.swift; sourceTree = "<group>"; };
AA2AD115244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexLinkMedium.swift; sourceTree = "<group>"; };
AA2AD117244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexLinkMediumModel.swift; sourceTree = "<group>"; };
AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDividerModel.swift; sourceTree = "<group>"; }; AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDividerModel.swift; sourceTree = "<group>"; };
AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDivider.swift; sourceTree = "<group>"; }; AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDivider.swift; sourceTree = "<group>"; };
AA617AAF2453010A00910B8F /* ListDeviceComplexLinkSmall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexLinkSmall.swift; sourceTree = "<group>"; };
AA617AB12453012400910B8F /* ListDeviceComplexLinkSmallModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexLinkSmallModel.swift; sourceTree = "<group>"; };
AA69AAF52445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxBodyText.swift; sourceTree = "<group>"; }; AA69AAF52445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxBodyText.swift; sourceTree = "<group>"; };
AA69AAF72445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxBodyTextModel.swift; sourceTree = "<group>"; }; AA69AAF72445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxBodyTextModel.swift; sourceTree = "<group>"; };
AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchCollectionViewCell.swift; sourceTree = "<group>"; }; AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchCollectionViewCell.swift; sourceTree = "<group>"; };
@ -695,6 +709,12 @@
D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSTextAlignment+Extension.swift"; sourceTree = "<group>"; }; D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSTextAlignment+Extension.swift"; sourceTree = "<group>"; };
D202AFE5242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionViewScrollPosition+Extension.swift"; sourceTree = "<group>"; }; D202AFE5242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionViewScrollPosition+Extension.swift"; sourceTree = "<group>"; };
D2092348244A51D40044AD09 /* RadioSwatchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchModel.swift; sourceTree = "<group>"; }; D2092348244A51D40044AD09 /* RadioSwatchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchModel.swift; sourceTree = "<group>"; };
D209234E244F77FD0044AD09 /* ThreeLayerCenterTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerCenterTemplate.swift; sourceTree = "<group>"; };
D2092350244F7BE80044AD09 /* ThreeLayerCenterTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerCenterTemplateModel.swift; sourceTree = "<group>"; };
D2092352244F7D630044AD09 /* MVMCoreUIViewControllerMappingObject+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUIViewControllerMappingObject+Extension.swift"; sourceTree = "<group>"; };
D2092354244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplateModelProtocol.swift; sourceTree = "<group>"; };
D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerModelBase.swift; sourceTree = "<group>"; };
D20923582450ECE00044AD09 /* TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableView.swift; sourceTree = "<group>"; };
D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = "<group>"; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = "<group>"; };
D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = "<group>"; }; D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = "<group>"; };
D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; };
@ -953,6 +973,7 @@
D28A837823C7D5BC00DFE4FC /* PageModelProtocol.swift */, D28A837823C7D5BC00DFE4FC /* PageModelProtocol.swift */,
011B58EF23A2AA980085F53C /* ListItemModelProtocol.swift */, 011B58EF23A2AA980085F53C /* ListItemModelProtocol.swift */,
D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */, D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */,
D2092354244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift */,
); );
path = ModelProtocols; path = ModelProtocols;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1196,6 +1217,10 @@
children = ( children = (
BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */, BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */,
BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */, BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */,
AA617AB12453012400910B8F /* ListDeviceComplexLinkSmallModel.swift */,
AA617AAF2453010A00910B8F /* ListDeviceComplexLinkSmall.swift */,
AA2AD117244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift */,
AA2AD115244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift */,
); );
path = Device; path = Device;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1555,6 +1580,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D22D8392241C27B100D3DF69 /* TemplateModel.swift */, D22D8392241C27B100D3DF69 /* TemplateModel.swift */,
D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */,
014AA72823C5059B006F3E93 /* StackPageTemplateModel.swift */, 014AA72823C5059B006F3E93 /* StackPageTemplateModel.swift */,
D2A5146022121FBF00345BFB /* MoleculeStackTemplate.swift */, D2A5146022121FBF00345BFB /* MoleculeStackTemplate.swift */,
942C378D2412F5B60066E45E /* ModalMoleculeStackTemplate.swift */, 942C378D2412F5B60066E45E /* ModalMoleculeStackTemplate.swift */,
@ -1566,6 +1592,8 @@
942C378B2412F4FA0066E45E /* ModalMoleculeListTemplate.swift */, 942C378B2412F4FA0066E45E /* ModalMoleculeListTemplate.swift */,
014AA72A23C5059B006F3E93 /* ThreeLayerPageTemplateModel.swift */, 014AA72A23C5059B006F3E93 /* ThreeLayerPageTemplateModel.swift */,
D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */, D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */,
D2092350244F7BE80044AD09 /* ThreeLayerCenterTemplateModel.swift */,
D209234E244F77FD0044AD09 /* ThreeLayerCenterTemplate.swift */,
D264FAA4243F66A500D98315 /* CollectionTemplateItemProtocol.swift */, D264FAA4243F66A500D98315 /* CollectionTemplateItemProtocol.swift */,
D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */, D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */,
D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */, D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */,
@ -1819,6 +1847,7 @@
D29DF27421E79E81003B2FB9 /* MVMCoreUILoggingHandler.m */, D29DF27421E79E81003B2FB9 /* MVMCoreUILoggingHandler.m */,
D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */, D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */,
D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */, D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */,
D2092352244F7D630044AD09 /* MVMCoreUIViewControllerMappingObject+Extension.swift */,
D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */, D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */,
); );
path = OtherHandlers; path = OtherHandlers;
@ -1924,6 +1953,7 @@
D2B18B7E2360913400A9AEDC /* Control.swift */, D2B18B7E2360913400A9AEDC /* Control.swift */,
D2B18B802360945C00A9AEDC /* View.swift */, D2B18B802360945C00A9AEDC /* View.swift */,
0AE14F63238315D2005417F8 /* TextField.swift */, 0AE14F63238315D2005417F8 /* TextField.swift */,
D20923582450ECE00044AD09 /* TableView.swift */,
D2755D7A23689C7500485468 /* TableViewCell.swift */, D2755D7A23689C7500485468 /* TableViewCell.swift */,
D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */, D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */,
D264FAA92440F97600D98315 /* CollectionView.swift */, D264FAA92440F97600D98315 /* CollectionView.swift */,
@ -2145,6 +2175,7 @@
AA11A41F23F15D3100D7962F /* ListRightVariablePayments.swift in Sources */, AA11A41F23F15D3100D7962F /* ListRightVariablePayments.swift in Sources */,
D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */,
0116A4E5228B19640094F3ED /* RadioButtonSelectionHelper.swift in Sources */, 0116A4E5228B19640094F3ED /* RadioButtonSelectionHelper.swift in Sources */,
D2092353244F7D630044AD09 /* MVMCoreUIViewControllerMappingObject+Extension.swift in Sources */,
017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */, 017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */,
D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */,
94382086243238D100B43AF3 /* WebViewModel.swift in Sources */, 94382086243238D100B43AF3 /* WebViewModel.swift in Sources */,
@ -2152,6 +2183,7 @@
D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */, D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */,
D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */, D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */,
0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */, 0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */,
AA2AD116244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift in Sources */,
D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */, D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */,
0A9D09202433796500D2E6C0 /* BarsIndicatorView.swift in Sources */, 0A9D09202433796500D2E6C0 /* BarsIndicatorView.swift in Sources */,
D2E2A99423D8CCBC000B42E6 /* HeadlineBodyLinkModel.swift in Sources */, D2E2A99423D8CCBC000B42E6 /* HeadlineBodyLinkModel.swift in Sources */,
@ -2183,11 +2215,13 @@
D2E2A98323D8B32D000B42E6 /* EyebrowHeadlineBodyLinkModel.swift in Sources */, D2E2A98323D8B32D000B42E6 /* EyebrowHeadlineBodyLinkModel.swift in Sources */,
012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */, 012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */,
BB6C6AC1242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift in Sources */, BB6C6AC1242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift in Sources */,
D2092351244F7BE80044AD09 /* ThreeLayerCenterTemplateModel.swift in Sources */,
D21B7F77243BB70700051ABF /* MoleculeCollectionItemModel.swift in Sources */, D21B7F77243BB70700051ABF /* MoleculeCollectionItemModel.swift in Sources */,
AAB9C10A243496DD00151545 /* RadioSwatch.swift in Sources */, AAB9C10A243496DD00151545 /* RadioSwatch.swift in Sources */,
D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */, D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */,
011D9602240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift in Sources */, 011D9602240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift in Sources */,
D264FAA1243CF66B00D98315 /* ContainerCollectionReusableView.swift in Sources */, D264FAA1243CF66B00D98315 /* ContainerCollectionReusableView.swift in Sources */,
AA617AB22453012400910B8F /* ListDeviceComplexLinkSmallModel.swift in Sources */,
D260106323D0C05000764D80 /* StackItemModel.swift in Sources */, D260106323D0C05000764D80 /* StackItemModel.swift in Sources */,
D2E2A99823D8D63C000B42E6 /* ActionDetailWithImageModel.swift in Sources */, D2E2A99823D8D63C000B42E6 /* ActionDetailWithImageModel.swift in Sources */,
BBBBC87D24374A4900B0F079 /* ListThreeColumnBillChangesDividerModel.swift in Sources */, BBBBC87D24374A4900B0F079 /* ListThreeColumnBillChangesDividerModel.swift in Sources */,
@ -2273,8 +2307,10 @@
0A7EF85B23D8A52800B2AAD1 /* EntryFieldModel.swift in Sources */, 0A7EF85B23D8A52800B2AAD1 /* EntryFieldModel.swift in Sources */,
8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */, 8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */,
94F217B723E0BF6100A47C06 /* PrimaryButtonView.m in Sources */, 94F217B723E0BF6100A47C06 /* PrimaryButtonView.m in Sources */,
D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */,
0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */, 0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */,
0A21DB8B235E06EF00C160A2 /* MFDigitTextBox.m in Sources */, 0A21DB8B235E06EF00C160A2 /* MFDigitTextBox.m in Sources */,
D20923592450ECE00044AD09 /* TableView.swift in Sources */,
D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */, D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */,
BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */, BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */,
AA69AAF82445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift in Sources */, AA69AAF82445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift in Sources */,
@ -2282,6 +2318,7 @@
C6FA7D5423C77A4A00A3614A /* NumberedList.swift in Sources */, C6FA7D5423C77A4A00A3614A /* NumberedList.swift in Sources */,
0A7ECC702441001C00C828E8 /* UIToolbar+Extension.swift in Sources */, 0A7ECC702441001C00C828E8 /* UIToolbar+Extension.swift in Sources */,
D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */, D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */,
D209234F244F77FD0044AD09 /* ThreeLayerCenterTemplate.swift in Sources */,
525019E52406852100EED91C /* ListFourColumnDataUsageDividerModel.swift in Sources */, 525019E52406852100EED91C /* ListFourColumnDataUsageDividerModel.swift in Sources */,
0A7EF86723D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift in Sources */, 0A7EF86723D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift in Sources */,
94FB966323D797DA003D482B /* MFTextButton.m in Sources */, 94FB966323D797DA003D482B /* MFTextButton.m in Sources */,
@ -2374,6 +2411,7 @@
525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */, 525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */,
0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */, 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */,
D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */, D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */,
AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */,
DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */, DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */,
0A21DB89235E06EF00C160A2 /* MFMdnTextField.m in Sources */, 0A21DB89235E06EF00C160A2 /* MFMdnTextField.m in Sources */,
D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */, D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */,
@ -2386,10 +2424,12 @@
94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */, 94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */,
012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */, 012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */,
D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */, D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */,
D2092355244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift in Sources */,
0AE14F64238315D2005417F8 /* TextField.swift in Sources */, 0AE14F64238315D2005417F8 /* TextField.swift in Sources */,
0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */, 0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */,
D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */, D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */,
D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */, D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */,
AA617AB02453010A00910B8F /* ListDeviceComplexLinkSmall.swift in Sources */,
C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */, C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */,
01EB3684236097C0006832FA /* MoleculeModelProtocol.swift in Sources */, 01EB3684236097C0006832FA /* MoleculeModelProtocol.swift in Sources */,
D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */,

View File

@ -11,7 +11,12 @@ import UIKit
@objcMembers public class ActionCollapseNotificationModel: ActionModelProtocol { @objcMembers public class ActionCollapseNotificationModel: ActionModelProtocol {
public static var identifier: String = "collapseNotification" public static var identifier: String = "collapseNotification"
public var actionType: String public var actionType: String = ActionCollapseNotificationModel.identifier
public var extraParameters: JSONValueDictionary? public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary? public var analyticsData: JSONValueDictionary?
public init(_ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
} }

View File

@ -23,7 +23,9 @@ public class ActionOpenPanelModel: ActionModelProtocol {
public var extraParameters: JSONValueDictionary? public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary? public var analyticsData: JSONValueDictionary?
public init(panel: Panel) { public init(panel: Panel, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.panel = panel self.panel = panel
self.extraParameters = extraParameters
self.analyticsData = analyticsData
} }
} }

View File

@ -16,7 +16,9 @@ import Foundation
public var extraParameters: JSONValueDictionary? public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary? public var analyticsData: JSONValueDictionary?
public init(pageType: String) { public init(pageType: String, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.pageType = pageType self.pageType = pageType
self.extraParameters = extraParameters
self.analyticsData = analyticsData
} }
} }

View File

@ -19,13 +19,14 @@ import UIKit
widthConstraint?.constant = diameter widthConstraint?.constant = diameter
} }
} }
public override var isSelected: Bool { public override var isSelected: Bool {
didSet { didSet {
radioModel?.state = isSelected radioModel?.state = isSelected
updateAccessibilityLabel()
} }
} }
public var enabledColor: UIColor = .mvmBlack public var enabledColor: UIColor = .mvmBlack
public var disabledColor: UIColor = .mvmCoolGray3 public var disabledColor: UIColor = .mvmCoolGray3
public var delegateObject: MVMCoreUIDelegateObject? public var delegateObject: MVMCoreUIDelegateObject?
@ -39,12 +40,12 @@ import UIKit
}() }()
lazy public var radioButtonSelectionHelper: RadioButtonSelectionHelper? = { lazy public var radioButtonSelectionHelper: RadioButtonSelectionHelper? = {
if let radioGroupName = radioGroupName,
let radioButtonModel = delegateObject?.formHolderDelegate?.formValidator?.radioButtonsModelByGroup[radioGroupName] { guard let radioGroupName = radioGroupName,
return radioButtonModel let radioButtonModel = delegateObject?.formHolderDelegate?.formValidator?.radioButtonsModelByGroup[radioGroupName]
} else { else { return nil }
return nil
} return radioButtonModel
}() }()
public override var isEnabled: Bool { public override var isEnabled: Bool {
@ -118,6 +119,18 @@ import UIKit
return radioModel?.fieldValue return radioModel?.fieldValue
} }
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Adjust accessibility label based on state of RadioButton.
func updateAccessibilityLabel() {
if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "radio_selected_state" : "radio_not_selected_state") {
accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "radio_desc_state") ?? "%@%@", "", state)
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - MVMViewProtocol // MARK: - MVMViewProtocol
//-------------------------------------------------- //--------------------------------------------------
@ -136,14 +149,15 @@ import UIKit
isAccessibilityElement = true isAccessibilityElement = true
accessibilityTraits = .button accessibilityTraits = .button
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint")
updateAccessibilityLabel()
} }
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) super.set(with: model, delegateObject, additionalData)
self.delegateObject = delegateObject
guard let model = model as? RadioButtonModel else { return } guard let model = model as? RadioButtonModel else { return }
self.delegateObject = delegateObject
isSelected = model.state isSelected = model.state
isEnabled = model.enabled isEnabled = model.enabled
RadioButtonSelectionHelper.setupForRadioButtonGroup(model, self, delegateObject: delegateObject) RadioButtonSelectionHelper.setupForRadioButtonGroup(model, self, delegateObject: delegateObject)

View File

@ -120,6 +120,7 @@ import Foundation
// Other Container Molecules // Other Container Molecules
MoleculeObjectMapping.shared()?.register(viewClass: MoleculeContainer.self, viewModelClass: MoleculeContainerModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: MoleculeHeaderView.self, viewModelClass: MoleculeHeaderModel.self) MoleculeObjectMapping.shared()?.register(viewClass: MoleculeHeaderView.self, viewModelClass: MoleculeHeaderModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: FooterView.self, viewModelClass: FooterModel.self) MoleculeObjectMapping.shared()?.register(viewClass: FooterView.self, viewModelClass: FooterModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: Scroller.self, viewModelClass: ScrollerModel.self) MoleculeObjectMapping.shared()?.register(viewClass: Scroller.self, viewModelClass: ScrollerModel.self)
@ -170,9 +171,10 @@ import Foundation
// Designed Headers // Designed Headers
MoleculeObjectMapping.shared()?.register(viewClass: HeadersH2NoButtonsBodyText.self, viewModelClass: HeadersH2NoButtonsBodyTextModel.self) MoleculeObjectMapping.shared()?.register(viewClass: HeadersH2NoButtonsBodyText.self, viewModelClass: HeadersH2NoButtonsBodyTextModel.self)
// Device Items // Device Items
MoleculeObjectMapping.shared()?.register(viewClass: ListDeviceComplexButtonMedium.self, viewModelClass: ListDeviceComplexButtonMediumModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListDeviceComplexButtonMedium.self, viewModelClass: ListDeviceComplexButtonMediumModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: ListDeviceComplexLinkSmall.self, viewModelClass: ListDeviceComplexLinkSmallModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: ListDeviceComplexLinkMedium.self, viewModelClass: ListDeviceComplexLinkMediumModel.self)
// TODO: Need View // TODO: Need View
try? ModelRegistry.register(TabsModel.self) try? ModelRegistry.register(TabsModel.self)

View File

@ -25,8 +25,8 @@ public class HeadersH2NoButtonsBodyTextModel: HeaderModel, MoleculeModelProtocol
public override func setDefaults() { public override func setDefaults() {
super.setDefaults() super.setDefaults()
topMarginPadding = PaddingDefaultVerticalSpacing3 topPadding = PaddingDefaultVerticalSpacing3
bottomMarginPadding = PaddingDefaultVerticalSpacing3 bottomPadding = PaddingDefaultVerticalSpacing3
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -0,0 +1,73 @@
//
// ListDeviceComplexLinkMedium.swift
// MVMCoreUI
//
// Created by Lekshmi S on 21/04/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers open class ListDeviceComplexLinkMedium: TableViewCell {
//-----------------------------------------------------
// MARK: - Outlets
//-----------------------------------------------------
public let eyebrow = Label.createLabelRegularMicro(true)
public let headline = Label.createLabelBoldTitleMedium(true)
public let body = Label.createLabelRegularBodySmall(true)
public let body2 = Label.createLabelRegularBodySmall(true)
public let link = Link()
public let rightImage = MFLoadImageView()
let verticalStack: Stack<StackModel>
public let stack: Stack<StackModel>
//------------------------------------------------------
// MARK: - Initializers
//------------------------------------------------------
public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
rightImage.addSizeConstraintsForAspectRatio = true
rightImage.heightAnchor.constraint(equalToConstant: 116.0).isActive = true
rightImage.widthAnchor.constraint(equalToConstant: 116.0).isActive = true
verticalStack = Stack<StackModel>.createStack(with: [(view: eyebrow, model: StackItemModel(horizontalAlignment: .leading)), (view: headline, model: StackItemModel(horizontalAlignment: .leading)), (view: body, model: StackItemModel(horizontalAlignment: .leading)), (view: body2, model: StackItemModel(horizontalAlignment: .leading)), (view: link, model: StackItemModel(spacing: 16, horizontalAlignment: .leading))], axis: .vertical, spacing: 0)
stack = Stack<StackModel>.createStack(with: [(view: verticalStack, model: StackItemModel(horizontalAlignment: .leading)), (view: rightImage, model: StackItemModel(verticalAlignment: .center))], 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()
verticalStack.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? ListDeviceComplexLinkMediumModel else { return }
verticalStack.updateContainedMolecules(with: [model.eyebrow, model.headline, model.body, model.body2, model.link], delegateObject, additionalData)
rightImage.set(with: model.image, delegateObject, additionalData)
}
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return 120
}
open override func reset() {
super.reset()
eyebrow.styleRegularMicro(true)
headline.styleBoldTitleMedium(true)
body.styleRegularBodySmall(true)
body2.styleRegularBodySmall(true)
eyebrow.textColor = .mvmCoolGray6
}
}

View File

@ -0,0 +1,61 @@
//
// ListDeviceComplexLinkMediumModel.swift
// MVMCoreUI
//
// Created by Lekshmi S on 21/04/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public class ListDeviceComplexLinkMediumModel: ListItemModel, MoleculeModelProtocol {
public static var identifier: String = "listDvcLnkM"
public var eyebrow: LabelModel?
public var headline: LabelModel?
public var body: LabelModel?
public var body2: LabelModel?
public var link: LinkModel
public var image: ImageViewModel
public init(eyebrow: LabelModel, headline: LabelModel, body: LabelModel, body2: LabelModel, link: LinkModel, image: ImageViewModel) {
self.eyebrow = eyebrow
self.headline = headline
self.body = body
self.body2 = body2
self.link = link
self.image = image
super.init()
}
private enum CodingKeys: String, CodingKey {
case moleculeName
case eyebrow
case headline
case body
case body2
case link
case image
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
eyebrow = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .eyebrow)
headline = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .headline)
body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body)
body2 = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body2)
link = try typeContainer.decode(LinkModel.self, forKey: .link)
image = try typeContainer.decode(ImageViewModel.self, forKey: .image)
try super.init(from: decoder)
}
public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(eyebrow, forKey: .eyebrow)
try container.encodeIfPresent(headline, forKey: .headline)
try container.encodeIfPresent(body, forKey: .body)
try container.encodeIfPresent(body2, forKey: .body2)
try container.encode(link, forKey: .link)
try container.encode(image, forKey: .image)
}
}

View File

@ -0,0 +1,73 @@
//
// ListDeviceComplexLinkSmall.swift
// MVMCoreUI
//
// Created by Lekshmi S on 24/04/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers open class ListDeviceComplexLinkSmall: TableViewCell {
//-----------------------------------------------------
// MARK: - Outlets
//-----------------------------------------------------
public let eyebrow = Label.createLabelRegularMicro(true)
public let headline = Label.createLabelBoldTitleMedium(true)
public let body = Label.createLabelRegularBodySmall(true)
public let body2 = Label.createLabelRegularBodySmall(true)
public let link = Link()
public let rightImage = MFLoadImageView()
let verticalStack: Stack<StackModel>
public let stack: Stack<StackModel>
//------------------------------------------------------
// MARK: - Initializers
//------------------------------------------------------
public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
rightImage.addSizeConstraintsForAspectRatio = true
rightImage.heightAnchor.constraint(equalToConstant: 71.0).isActive = true
rightImage.widthAnchor.constraint(equalToConstant: 71.0).isActive = true
verticalStack = Stack<StackModel>.createStack(with: [(view: eyebrow, model: StackItemModel(horizontalAlignment: .leading)), (view: headline, model: StackItemModel(horizontalAlignment: .leading)), (view: body, model: StackItemModel(horizontalAlignment: .leading)), (view: body2, model: StackItemModel(horizontalAlignment: .leading)), (view: link, model: StackItemModel(spacing: 16, horizontalAlignment: .leading))], axis: .vertical, spacing: 0)
stack = Stack<StackModel>.createStack(with: [(view: verticalStack, model: StackItemModel(horizontalAlignment: .leading)), (view: rightImage, model: StackItemModel(verticalAlignment: .center))], 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()
verticalStack.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? ListDeviceComplexLinkSmallModel else { return }
verticalStack.updateContainedMolecules(with: [model.eyebrow, model.headline, model.body, model.body2, model.link], delegateObject, additionalData)
rightImage.set(with: model.image, delegateObject, additionalData)
}
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return 120
}
open override func reset() {
super.reset()
eyebrow.styleRegularMicro(true)
headline.styleBoldTitleMedium(true)
body.styleRegularBodySmall(true)
body2.styleRegularBodySmall(true)
eyebrow.textColor = .mvmCoolGray6
}
}

View File

@ -0,0 +1,61 @@
//
// ListDeviceComplexLinkSmallModel.swift
// MVMCoreUI
//
// Created by Lekshmi S on 24/04/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public class ListDeviceComplexLinkSmallModel: ListItemModel, MoleculeModelProtocol {
public static var identifier: String = "listDvcLnkS"
public var eyebrow: LabelModel?
public var headline: LabelModel?
public var body: LabelModel?
public var body2: LabelModel?
public var link: LinkModel
public var image: ImageViewModel
public init(eyebrow: LabelModel, headline: LabelModel, body: LabelModel, body2: LabelModel, link: LinkModel, image: ImageViewModel) {
self.eyebrow = eyebrow
self.headline = headline
self.body = body
self.body2 = body2
self.link = link
self.image = image
super.init()
}
private enum CodingKeys: String, CodingKey {
case moleculeName
case eyebrow
case headline
case body
case body2
case link
case image
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
eyebrow = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .eyebrow)
headline = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .headline)
body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body)
body2 = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body2)
link = try typeContainer.decode(LinkModel.self, forKey: .link)
image = try typeContainer.decode(ImageViewModel.self, forKey: .image)
try super.init(from: decoder)
}
public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(eyebrow, forKey: .eyebrow)
try container.encodeIfPresent(headline, forKey: .headline)
try container.encodeIfPresent(body, forKey: .body)
try container.encodeIfPresent(body2, forKey: .body2)
try container.encode(link, forKey: .link)
try container.encode(image, forKey: .image)
}
}

View File

@ -9,47 +9,24 @@
import Foundation import Foundation
@objcMembers public class FooterModel: MoleculeContainerModel, MoleculeModelProtocol { @objcMembers public class FooterModel: MoleculeContainerModel {
public static var identifier: String = "footer" public override class var identifier: String {
public var backgroundColor: Color? return "footer"
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
} }
/// Defaults to set /// Defaults to set
func setDefaults() { public override func setDefaults() {
if useHorizontalMargins == nil { if useHorizontalMargins == nil {
useHorizontalMargins = true useHorizontalMargins = true
} }
if useVerticalMargins == nil { if useVerticalMargins == nil {
useVerticalMargins = true useVerticalMargins = true
} }
if topMarginPadding == nil { if topPadding == nil {
topMarginPadding = PaddingDefaultVerticalSpacing topPadding = PaddingDefaultVerticalSpacing
} }
if bottomMarginPadding == nil { if bottomPadding == nil {
bottomMarginPadding = PaddingDefaultVerticalSpacing bottomPadding = PaddingDefaultVerticalSpacing
} }
} }
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)
}
} }

View File

@ -18,18 +18,18 @@ import Foundation
} }
/// Defaults to set /// Defaults to set
public func setDefaults() { public override func setDefaults() {
if useHorizontalMargins == nil { if useHorizontalMargins == nil {
useHorizontalMargins = true useHorizontalMargins = true
} }
if useVerticalMargins == nil { if useVerticalMargins == nil {
useVerticalMargins = true useVerticalMargins = true
} }
if topMarginPadding == nil { if topPadding == nil {
topMarginPadding = PaddingDefaultVerticalSpacing topPadding = PaddingDefaultVerticalSpacing
} }
if bottomMarginPadding == nil { if bottomPadding == nil {
bottomMarginPadding = PaddingDefaultVerticalSpacing bottomPadding = PaddingDefaultVerticalSpacing
} }
if line == nil { if line == nil {
line = LineModel(type: .heavy) line = LineModel(type: .heavy)
@ -38,7 +38,6 @@ import Foundation
public override init() { public override init() {
super.init() super.init()
setDefaults()
} }
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
@ -46,7 +45,6 @@ import Foundation
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
setDefaults()
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {

View File

@ -38,7 +38,7 @@ import Foundation
//-------------------------------------------------- //--------------------------------------------------
/// Defaults to set /// Defaults to set
open func setDefaults() { open override func setDefaults() {
if useHorizontalMargins == nil { if useHorizontalMargins == nil {
useHorizontalMargins = true useHorizontalMargins = true
} }
@ -54,9 +54,12 @@ import Foundation
// MARK: - Initializer // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------
public override init(horizontalAlignment: UIStackView.Alignment? = nil, verticalAlignment: UIStackView.Alignment? = nil, useHorizontalMargins: Bool? = nil, leftPadding: CGFloat? = nil, rightPadding: CGFloat? = nil, useVerticalMargins: Bool? = nil, topPadding: CGFloat? = nil, bottomPadding: CGFloat? = nil) {
super.init(horizontalAlignment: horizontalAlignment, verticalAlignment: verticalAlignment, useHorizontalMargins: useHorizontalMargins, leftPadding: leftPadding, rightPadding: rightPadding, useVerticalMargins: useVerticalMargins, topPadding: topPadding, bottomPadding: bottomPadding)
}
public override init() { public override init() {
super.init() super.init()
setDefaults()
} }
//-------------------------------------------------- //--------------------------------------------------
@ -71,7 +74,6 @@ import Foundation
line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line)
style = try typeContainer.decodeIfPresent(String.self, forKey: .style) style = try typeContainer.decodeIfPresent(String.self, forKey: .style)
try super.init(from: decoder) try super.init(from: decoder)
setDefaults()
} }
open override func encode(to encoder: Encoder) throws { open override func encode(to encoder: Encoder) throws {

View File

@ -9,49 +9,36 @@
import Foundation import Foundation
/// A model for a collection item that is a container for any molecule. /// A model for a collection item that is a container for any molecule.
@objcMembers public class MoleculeCollectionItemModel: MoleculeContainerModel, CollectionItemModelProtocol, MoleculeModelProtocol { @objcMembers public class MoleculeCollectionItemModel: MoleculeContainerModel, CollectionItemModelProtocol {
open class var identifier: String { open override class var identifier: String {
return "collectionItem" return "collectionItem"
} }
public var backgroundColor: Color?
/// Defaults to set /// Defaults to set
public func setDefaults() { public override func setDefaults() {
if useHorizontalMargins == nil { if useHorizontalMargins == nil {
useHorizontalMargins = true useHorizontalMargins = true
} }
if useVerticalMargins == nil { if useVerticalMargins == nil {
useVerticalMargins = true useVerticalMargins = true
} }
if topMarginPadding == nil { if topPadding == nil {
topMarginPadding = PaddingDefault topPadding = PaddingDefault
} }
if bottomMarginPadding == nil { if bottomPadding == nil {
bottomMarginPadding = PaddingDefault bottomPadding = PaddingDefault
} }
} }
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
}
public override init(with moleculeModel: MoleculeModelProtocol) { public override init(with moleculeModel: MoleculeModelProtocol) {
super.init(with: moleculeModel) super.init(with: moleculeModel)
setDefaults()
} }
required public init(from decoder: Decoder) throws { 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) try super.init(from: decoder)
setDefaults()
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
} }
} }

View File

@ -54,6 +54,6 @@ open class MoleculeCollectionViewCell: CollectionViewCell {
let height = classType.estimatedHeight(with: model.molecule, delegateObject) let height = classType.estimatedHeight(with: model.molecule, delegateObject)
else { return 100 } else { return 100 }
return height + (model.topMarginPadding ?? 0) + (model.bottomMarginPadding ?? 0) return height + (model.topPadding ?? 0) + (model.bottomPadding ?? 0)
} }
} }

View File

@ -8,16 +8,15 @@
import Foundation import Foundation
@objcMembers public class MoleculeStackItemModel: MoleculeContainerModel, MoleculeModelProtocol, StackItemModelProtocol { @objcMembers public class MoleculeStackItemModel: MoleculeContainerModel, StackItemModelProtocol {
public static var identifier: String = "stackItem" public override class var identifier: String {
public var backgroundColor: Color? return "stackItem"
}
public var spacing: CGFloat? public var spacing: CGFloat?
public var percent: Int? public var percent: Int?
public var gone: Bool = false public var gone: Bool = false
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
case spacing case spacing
case percent case percent
case gone case gone
@ -34,17 +33,14 @@ import Foundation
if let gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) { if let gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) {
self.gone = gone self.gone = gone
} }
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
try super.init(from: decoder) try super.init(from: decoder)
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(spacing, forKey: .spacing) try container.encodeIfPresent(spacing, forKey: .spacing)
try container.encodeIfPresent(percent, forKey: .percent) try container.encodeIfPresent(percent, forKey: .percent)
try container.encode(gone, forKey: .gone) try container.encode(gone, forKey: .gone)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
} }
} }

View File

@ -24,16 +24,17 @@ import Foundation
// MARK: - Initializer // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------
public convenience init(spacing: CGFloat? = nil, percent: Int? = nil, horizontalAlignment: UIStackView.Alignment? = nil, verticalAlignment: UIStackView.Alignment? = nil, gone: Bool? = nil) { public 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.spacing = spacing
self.percent = percent self.percent = percent
if let gone = gone { if let gone = gone {
self.gone = gone self.gone = gone
} }
super.init(horizontalAlignment: horizontalAlignment, verticalAlignment: verticalAlignment)
}
required public init(from decoder: Decoder) throws {
fatalError("init(from:) has not been implemented")
} }
} }

View File

@ -24,8 +24,8 @@ public class TabsListItemModel: ListItemModel, MoleculeModelProtocol {
hideArrow = true hideArrow = true
action = nil action = nil
style = nil style = nil
topMarginPadding = 8 topPadding = 8
bottomMarginPadding = 0 bottomPadding = 0
} }
public init(with tabs: TabsModel, molecules: [[ListItemModelProtocol & MoleculeModelProtocol]]) { public init(with tabs: TabsModel, molecules: [[ListItemModelProtocol & MoleculeModelProtocol]]) {

View File

@ -10,17 +10,16 @@ import Foundation
public protocol NavigationItemModelProtocol { public protocol NavigationItemModelProtocol {
var title: String? { get set } var title: String? { get set }
var titleView: MoleculeModelProtocol? { get set }
var hidden: Bool { get set } var hidden: Bool { get set }
var backgroundColor: Color? { get set } var backgroundColor: Color? { get set }
var transparent: Bool { get set } var translucent: Bool { get set }
var tintColor: Color { get set } var tintColor: Color { get set }
var line: LineModel? { get set } var line: LineModel? { get set }
var systemBackButton: Bool { get set } var showLeftPanelButton: Bool { get set }
var showLeftPanelButton: Bool? { get set } var showRightPanelButton: Bool { get set }
var showRightPanelButton: Bool? { get set } var backButton: NavigationItemButtonModel? { get set }
var additionalLeftItems: [NavigationItemButtonModel]? { get set } var additionalLeftButtons: [NavigationItemButtonModel]? { get set }
var additionalRightItems: [NavigationItemButtonModel]? { get set } var additionalRightButtons: [NavigationItemButtonModel]? { get set }
} }
public class NavigationItemButtonModel: Codable { public class NavigationItemButtonModel: Codable {
@ -52,74 +51,73 @@ public class NavigationItemButtonModel: Codable {
public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol {
public class var identifier: String { public class var identifier: String {
return "navigationItem" return "navigationBar"
} }
public var title: String? public var title: String?
public var titleView: MoleculeModelProtocol?
public var hidden: Bool public var hidden: Bool
public var backgroundColor: Color? public var backgroundColor: Color?
public var transparent: Bool public var translucent: Bool
public var tintColor: Color public var tintColor: Color
public var line: LineModel? public var line: LineModel?
public var systemBackButton = false public var showLeftPanelButton: Bool
public var showLeftPanelButton: Bool? public var showRightPanelButton: Bool
public var showRightPanelButton: Bool? public var backButton: NavigationItemButtonModel?
public var additionalLeftItems: [NavigationItemButtonModel]? public var additionalLeftButtons: [NavigationItemButtonModel]?
public var additionalRightItems: [NavigationItemButtonModel]? public var additionalRightButtons: [NavigationItemButtonModel]?
init() { public init() {
hidden = false hidden = false
transparent = false translucent = false
backgroundColor = Color(uiColor: .white) backgroundColor = Color(uiColor: .white)
tintColor = Color(uiColor: .black) tintColor = Color(uiColor: .black)
line = LineModel(type: .standard) line = LineModel(type: .standard)
showLeftPanelButton = true
showRightPanelButton = true
backButton = NavigationItemButtonModel(with: "back", action: ActionBackModel())
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case title case title
case titleView
case hidden case hidden
case backgroundColor case backgroundColor
case transparent case translucent
case tintColor case tintColor
case line case line
case systemBackButton case backButton
case showLeftPanelButton case showLeftPanelButton
case showRightPanelButton case showRightPanelButton
case additionalLeftItems case additionalLeftButtons
case additionalRightItems case additionalRightButtons
} }
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
title = try typeContainer.decodeIfPresent(String.self, forKey: .title) title = try typeContainer.decodeIfPresent(String.self, forKey: .title)
titleView = try typeContainer.decodeModelIfPresent(codingKey: .titleView)
hidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .hidden) ?? false hidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .hidden) ?? false
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .white) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .white)
transparent = try typeContainer.decodeIfPresent(Bool.self, forKey: .transparent) ?? false translucent = try typeContainer.decodeIfPresent(Bool.self, forKey: .translucent) ?? false
tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black) tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black)
line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line)
systemBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .systemBackButton) ?? false backButton = try typeContainer.decodeIfPresent(NavigationItemButtonModel.self, forKey: .backButton) ?? NavigationItemButtonModel(with: "back", action: ActionBackModel())
showLeftPanelButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .showLeftPanelButton) showLeftPanelButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .showLeftPanelButton) ?? true
showRightPanelButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .showRightPanelButton) showRightPanelButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .showRightPanelButton) ?? true
additionalLeftItems = try typeContainer.decodeIfPresent([NavigationItemButtonModel].self, forKey: .additionalLeftItems) additionalLeftButtons = try typeContainer.decodeIfPresent([NavigationItemButtonModel].self, forKey: .additionalLeftButtons)
additionalRightItems = try typeContainer.decodeIfPresent([NavigationItemButtonModel].self, forKey: .additionalRightItems) additionalRightButtons = try typeContainer.decodeIfPresent([NavigationItemButtonModel].self, forKey: .additionalRightButtons)
} }
open func encode(to encoder: Encoder) throws { open func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(title, forKey: .title) try container.encodeIfPresent(title, forKey: .title)
try container.encodeModelIfPresent(titleView, forKey: .titleView)
try container.encode(hidden, forKey: .hidden) try container.encode(hidden, forKey: .hidden)
try container.encode(backgroundColor, forKey: .backgroundColor) try container.encode(backgroundColor, forKey: .backgroundColor)
try container.encode(transparent, forKey: .transparent) try container.encode(translucent, forKey: .translucent)
try container.encode(tintColor, forKey: .tintColor) try container.encode(tintColor, forKey: .tintColor)
try container.encodeIfPresent(line, forKey: .line) try container.encodeIfPresent(line, forKey: .line)
try container.encode(systemBackButton, forKey: .systemBackButton) try container.encodeIfPresent(backButton, forKey: .backButton)
try container.encode(showLeftPanelButton, forKey: .showLeftPanelButton) try container.encode(showLeftPanelButton, forKey: .showLeftPanelButton)
try container.encode(showRightPanelButton, forKey: .showRightPanelButton) try container.encode(showRightPanelButton, forKey: .showRightPanelButton)
try container.encodeIfPresent(additionalLeftItems, forKey: .additionalLeftItems) try container.encodeIfPresent(additionalLeftButtons, forKey: .additionalLeftButtons)
try container.encodeIfPresent(additionalRightItems, forKey: .additionalRightItems) try container.encodeIfPresent(additionalRightButtons, forKey: .additionalRightButtons)
} }
} }

View File

@ -8,8 +8,9 @@
import UIKit import UIKit
public class ScrollerModel: MoleculeContainerModel, MoleculeModelProtocol { public class ScrollerModel: MoleculeContainerModel {
public static var identifier: String = "scroller" public override class var identifier: String {
return "scroller"
}
public var moleculeName: String = ScrollerModel.identifier public var moleculeName: String = ScrollerModel.identifier
public var backgroundColor: Color?
} }

View File

@ -20,22 +20,21 @@ public class BGImageHeadlineBodyButtonModel: ContainerModel, MoleculeModelProtoc
self.headlineBody = headlineBody self.headlineBody = headlineBody
self.image = image self.image = image
super.init() super.init()
setDefaults()
} }
/// Defaults to set /// Defaults to set
func setDefaults() { public override func setDefaults() {
if useHorizontalMargins == nil { if useHorizontalMargins == nil {
useHorizontalMargins = true useHorizontalMargins = true
} }
if useVerticalMargins == nil { if useVerticalMargins == nil {
useVerticalMargins = true useVerticalMargins = true
} }
if topMarginPadding == nil { if topPadding == nil {
topMarginPadding = PaddingDefault topPadding = PaddingDefault
} }
if bottomMarginPadding == nil { if bottomPadding == nil {
bottomMarginPadding = PaddingDefault bottomPadding = PaddingDefault
} }
if image.height == nil { if image.height == nil {
image.height = BGImageHeadlineBodyButton.heightConstant image.height = BGImageHeadlineBodyButton.heightConstant
@ -59,7 +58,6 @@ public class BGImageHeadlineBodyButtonModel: ContainerModel, MoleculeModelProtoc
image = try typeContainer.decode(ImageViewModel.self, forKey: .image) image = try typeContainer.decode(ImageViewModel.self, forKey: .image)
button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button) button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button)
try super.init(from: decoder) try super.init(from: decoder)
setDefaults()
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {

View File

@ -19,22 +19,21 @@ public class HeadlineBodyCaretLinkImageModel: ContainerModel, MoleculeModelProto
self.headlineBody = headlineBody self.headlineBody = headlineBody
self.image = image self.image = image
super.init() super.init()
setDefaults()
} }
/// Defaults to set /// Defaults to set
func setDefaults() { public override func setDefaults() {
if useHorizontalMargins == nil { if useHorizontalMargins == nil {
useHorizontalMargins = true useHorizontalMargins = true
} }
if useVerticalMargins == nil { if useVerticalMargins == nil {
useVerticalMargins = true useVerticalMargins = true
} }
if topMarginPadding == nil { if topPadding == nil {
topMarginPadding = PaddingDefault topPadding = PaddingDefault
} }
if bottomMarginPadding == nil { if bottomPadding == nil {
bottomMarginPadding = PaddingDefault bottomPadding = PaddingDefault
} }
if image.height == nil { if image.height == nil {
image.height = HeadLineBodyCaretLinkImage.heightConstant image.height = HeadLineBodyCaretLinkImage.heightConstant
@ -56,7 +55,6 @@ public class HeadlineBodyCaretLinkImageModel: ContainerModel, MoleculeModelProto
image = try typeContainer.decode(ImageViewModel.self, forKey: .image) image = try typeContainer.decode(ImageViewModel.self, forKey: .image)
caretLink = try typeContainer.decodeIfPresent(CaretLinkModel.self, forKey: .caretLink) caretLink = try typeContainer.decodeIfPresent(CaretLinkModel.self, forKey: .caretLink)
try super.init(from: decoder) try super.init(from: decoder)
setDefaults()
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {

View File

@ -11,10 +11,12 @@ import Foundation
public protocol ContainerModelProtocol { public protocol ContainerModelProtocol {
var horizontalAlignment: UIStackView.Alignment? { get set } var horizontalAlignment: UIStackView.Alignment? { get set }
var verticalAlignment: UIStackView.Alignment? { get set }
var useHorizontalMargins: Bool? { get set } var useHorizontalMargins: Bool? { get set }
var leftPadding: CGFloat? { get set }
var rightPadding: CGFloat? { get set }
var verticalAlignment: UIStackView.Alignment? { get set }
var useVerticalMargins: Bool? { get set } var useVerticalMargins: Bool? { get set }
var topMarginPadding: CGFloat? { get set } var topPadding: CGFloat? { get set }
var bottomMarginPadding: CGFloat? { get set } var bottomPadding: CGFloat? { get set }
} }

View File

@ -12,5 +12,6 @@ public protocol PageModelProtocol {
var pageType: String { get set } var pageType: String { get set }
/// Temporary: for legacy response /// Temporary: for legacy response
var screenHeading: String? { get set } var screenHeading: String? { get set }
var backgroundColor: Color? { get set }
var navigationItem: (NavigationItemModelProtocol & MoleculeModelProtocol)? { get set } var navigationItem: (NavigationItemModelProtocol & MoleculeModelProtocol)? { get set }
} }

View File

@ -0,0 +1,17 @@
//
// ThreeLayerTemplateModelProtocol.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 4/21/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public protocol ThreeLayerTemplateModelProtocol: TemplateModelProtocol {
var anchorHeader: Bool { get set }
var header: MoleculeModelProtocol? { get set }
var anchorFooter: Bool { get set }
var footer: MoleculeModelProtocol? { get set }
}

View File

@ -22,5 +22,8 @@ public extension TemplateProtocol where Self: ViewController {
let templateModel = try decoder.decode(TemplateModel.self, from: data) let templateModel = try decoder.decode(TemplateModel.self, from: data)
self.templateModel = templateModel self.templateModel = templateModel
self.pageModel = templateModel as? MVMControllerModelProtocol self.pageModel = templateModel as? MVMControllerModelProtocol
if let backgroundColor = templateModel.backgroundColor {
view.backgroundColor = backgroundColor.uiColor
}
} }
} }

View File

@ -79,6 +79,8 @@ import Foundation
open override func handleNewData() { open override func handleNewData() {
topViewOutsideOfScrollArea = templateModel?.anchorHeader ?? false
bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false
setup() setup()
registerCells() registerCells()
super.handleNewData() super.handleNewData()

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
@objcMembers public class CollectionTemplateModel: TemplateModel { @objcMembers public class CollectionTemplateModel: ThreeLayerModelBase {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -16,9 +16,7 @@ import Foundation
public override class var identifier: String { public override class var identifier: String {
return "collection" return "collection"
} }
public var header: MoleculeModelProtocol?
public var molecules: [CollectionItemModelProtocol & MoleculeModelProtocol]? public var molecules: [CollectionItemModelProtocol & MoleculeModelProtocol]?
public var footer: MoleculeModelProtocol?
public var columns: Int? public var columns: Int?
//-------------------------------------------------- //--------------------------------------------------
@ -37,8 +35,6 @@ import Foundation
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case molecules case molecules
case header
case footer
case columns case columns
} }
@ -49,8 +45,6 @@ import Foundation
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
molecules = try typeContainer.decodeModelsIfPresent(codingKey: .molecules) 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) columns = try typeContainer.decodeIfPresent(Int.self, forKey: .columns)
try super.init(from: decoder) try super.init(from: decoder)
} }
@ -59,8 +53,6 @@ import Foundation
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeModelsIfPresent(molecules, forKey: .molecules) try container.encodeModelsIfPresent(molecules, forKey: .molecules)
try container.encodeModelIfPresent(header, forKey: .header)
try container.encodeModelIfPresent(footer, forKey: .footer)
try container.encodeIfPresent(columns, forKey: .columns) try container.encodeIfPresent(columns, forKey: .columns)
} }
} }

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
@objcMembers public class ListPageTemplateModel: TemplateModel { @objcMembers public class ListPageTemplateModel: ThreeLayerModelBase {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -16,9 +16,7 @@ import Foundation
public override class var identifier: String { public override class var identifier: String {
return "list" return "list"
} }
public var header: MoleculeModelProtocol?
public var molecules: [ListItemModelProtocol & MoleculeModelProtocol]? public var molecules: [ListItemModelProtocol & MoleculeModelProtocol]?
public var footer: MoleculeModelProtocol?
public var line: LineModel? public var line: LineModel?
//-------------------------------------------------- //--------------------------------------------------
@ -37,8 +35,6 @@ import Foundation
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case molecules case molecules
case header
case footer
case line case line
} }
@ -49,8 +45,6 @@ import Foundation
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
molecules = try typeContainer.decodeModelsIfPresent(codingKey: .molecules) molecules = try typeContainer.decodeModelsIfPresent(codingKey: .molecules)
header = try typeContainer.decodeModelIfPresent(codingKey: .header)
footer = try typeContainer.decodeModelIfPresent(codingKey: .footer)
line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line)
try super.init(from: decoder) try super.init(from: decoder)
} }
@ -59,8 +53,6 @@ import Foundation
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeModelsIfPresent(molecules, forKey: .molecules) try container.encodeModelsIfPresent(molecules, forKey: .molecules)
try container.encodeModelIfPresent(header, forKey: .header)
try container.encodeModelIfPresent(footer, forKey: .footer)
try container.encode(line, forKey: .line) try container.encode(line, forKey: .line)
} }
} }

View File

@ -81,6 +81,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
} }
open override func handleNewData() { open override func handleNewData() {
topViewOutsideOfScrollArea = templateModel?.anchorHeader ?? false
bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false
setup() setup()
registerWithTable() registerWithTable()
super.handleNewData() super.handleNewData()

View File

@ -11,6 +11,12 @@ import UIKit
open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol { open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol {
var observer: NSKeyValueObservation? var observer: NSKeyValueObservation?
public var templateModel: StackPageTemplateModel? public var templateModel: StackPageTemplateModel?
open override func handleNewData() {
topViewOutsideOfScroll = templateModel?.anchorHeader ?? false
bottomViewOutsideOfScroll = templateModel?.anchorFooter ?? false
super.handleNewData()
}
open override func parsePageJSON() throws { open override func parsePageJSON() throws {
try parseTemplate(json: loadObject?.pageJSON) try parseTemplate(json: loadObject?.pageJSON)

View File

@ -9,14 +9,11 @@
import Foundation import Foundation
@objcMembers public class StackPageTemplateModel: TemplateModel { @objcMembers public class StackPageTemplateModel: ThreeLayerModelBase {
public override class var identifier: String { public override class var identifier: String {
return "stack" return "stack"
} }
public var header: MoleculeModelProtocol?
public var moleculeStack: StackModel public var moleculeStack: StackModel
public var footer: MoleculeModelProtocol?
public init(pageType: String, moleculeStack: StackModel) { public init(pageType: String, moleculeStack: StackModel) {
self.moleculeStack = moleculeStack self.moleculeStack = moleculeStack
@ -24,16 +21,12 @@ import Foundation
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case header
case footer
case stack case stack
} }
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
moleculeStack = try typeContainer.decode(StackModel.self, forKey: .stack) moleculeStack = try typeContainer.decode(StackModel.self, forKey: .stack)
header = try typeContainer.decodeModelIfPresent(codingKey: .header)
footer = try typeContainer.decodeModelIfPresent(codingKey: .footer)
try super.init(from: decoder) try super.init(from: decoder)
} }
@ -41,7 +34,5 @@ import Foundation
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeStack, forKey: .stack) try container.encode(moleculeStack, forKey: .stack)
try container.encodeModelIfPresent(header, forKey: .header)
try container.encodeModelIfPresent(footer, forKey: .footer)
} }
} }

View File

@ -17,6 +17,7 @@ import Foundation
// Although this is done in the extension, it is needed for the encoding. // Although this is done in the extension, it is needed for the encoding.
return Self.identifier return Self.identifier
} }
public var backgroundColor: Color?
public var screenHeading: String? public var screenHeading: String?
public var navigationItem: (NavigationItemModelProtocol & MoleculeModelProtocol)? public var navigationItem: (NavigationItemModelProtocol & MoleculeModelProtocol)?
public var formRules: [FormGroupRule]? public var formRules: [FormGroupRule]?
@ -29,6 +30,7 @@ import Foundation
case pageType case pageType
case template case template
case screenHeading case screenHeading
case backgroundColor
case formRules case formRules
case navigationItem case navigationItem
} }
@ -37,6 +39,7 @@ import Foundation
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
pageType = try typeContainer.decode(String.self, forKey: .pageType) pageType = try typeContainer.decode(String.self, forKey: .pageType)
screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading) screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
formRules = try typeContainer.decodeIfPresent([FormGroupRule].self, forKey: .formRules) formRules = try typeContainer.decodeIfPresent([FormGroupRule].self, forKey: .formRules)
navigationItem = try typeContainer.decodeModelIfPresent(codingKey: .navigationItem) navigationItem = try typeContainer.decodeModelIfPresent(codingKey: .navigationItem)
} }
@ -45,6 +48,7 @@ import Foundation
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(pageType, forKey: .pageType) try container.encode(pageType, forKey: .pageType)
try container.encode(template, forKey: .template) try container.encode(template, forKey: .template)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(screenHeading, forKey: .screenHeading) try container.encodeIfPresent(screenHeading, forKey: .screenHeading)
try container.encodeIfPresent(formRules, forKey: .formRules) try container.encodeIfPresent(formRules, forKey: .formRules)
try container.encodeModelIfPresent(navigationItem, forKey: .navigationItem) try container.encodeModelIfPresent(navigationItem, forKey: .navigationItem)

View File

@ -0,0 +1,15 @@
//
// ThreeLayerCenterTemplate.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 4/21/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers open class ThreeLayerCenterTemplate: ThreeLayerTemplate<ThreeLayerCenterPageTemplateModel> {
open override func spaceBetweenTopAndMiddle() -> CGFloat? {
return nil
}
}

View File

@ -0,0 +1,15 @@
//
// ThreeLayerCenterTemplateModel.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 4/21/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers public class ThreeLayerCenterPageTemplateModel: ThreeLayerPageTemplateModel {
public override class var identifier: String {
return "threeLayerCenter"
}
}

View File

@ -0,0 +1,49 @@
//
// ThreeLayerModelBase.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 4/21/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers public class ThreeLayerModelBase: TemplateModel, ThreeLayerTemplateModelProtocol {
public var anchorHeader: Bool = false
public var header: MoleculeModelProtocol?
public var anchorFooter: Bool = false
public var footer: MoleculeModelProtocol?
public override init(pageType: String) {
super.init(pageType: pageType)
}
private enum CodingKeys: String, CodingKey {
case anchorHeader
case header
case anchorFooter
case footer
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let anchor = try typeContainer.decodeIfPresent(Bool.self, forKey: .anchorHeader) {
anchorHeader = anchor
}
header = try typeContainer.decodeModelIfPresent(codingKey: .header)
if let anchor = try typeContainer.decodeIfPresent(Bool.self, forKey: .anchorFooter) {
anchorFooter = anchor
}
footer = try typeContainer.decodeModelIfPresent(codingKey: .footer)
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.encodeIfPresent(anchorHeader, forKey: .anchorHeader)
try container.encodeModelIfPresent(header, forKey: .header)
try container.encodeIfPresent(anchorFooter, forKey: .anchorFooter)
try container.encodeModelIfPresent(footer, forKey: .footer)
}
}

View File

@ -8,13 +8,11 @@
import Foundation import Foundation
@objcMembers public class ThreeLayerPageTemplateModel: TemplateModel { @objcMembers public class ThreeLayerPageTemplateModel: ThreeLayerModelBase {
public override class var identifier: String { public override class var identifier: String {
return "threeLayer" return "threeLayer"
} }
public var header: MoleculeModelProtocol?
public var middle: MoleculeModelProtocol? public var middle: MoleculeModelProtocol?
public var footer: MoleculeModelProtocol?
public init(pageType: String, header: MoleculeModelProtocol?, middle: MoleculeModelProtocol?, footer: MoleculeModelProtocol?) { public init(pageType: String, header: MoleculeModelProtocol?, middle: MoleculeModelProtocol?, footer: MoleculeModelProtocol?) {
super.init(pageType: pageType) super.init(pageType: pageType)
@ -24,24 +22,18 @@ import Foundation
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case header
case footer
case middle case middle
} }
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
header = try typeContainer.decodeModelIfPresent(codingKey: .header)
middle = try typeContainer.decodeModelIfPresent(codingKey: .middle) middle = try typeContainer.decodeModelIfPresent(codingKey: .middle)
footer = try typeContainer.decodeModelIfPresent(codingKey: .footer)
try super.init(from: decoder) try super.init(from: decoder)
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeModelIfPresent(header, forKey: .header)
try container.encodeModelIfPresent(header, forKey: .middle) try container.encodeModelIfPresent(header, forKey: .middle)
try container.encodeModelIfPresent(footer, forKey: .footer)
} }
} }

View File

@ -8,23 +8,18 @@
import UIKit import UIKit
@objcMembers open class ThreeLayerTemplate: ThreeLayerViewController, TemplateProtocol { @objcMembers open class ThreeLayerTemplate<TemplateModel: ThreeLayerPageTemplateModel>: ThreeLayerViewController, TemplateProtocol {
public var templateModel: ThreeLayerPageTemplateModel? public var templateModel: TemplateModel?
open override func parsePageJSON() throws { open override func parsePageJSON() throws {
try parseTemplate(json: loadObject?.pageJSON) try parseTemplate(json: loadObject?.pageJSON)
try super.parsePageJSON() try super.parsePageJSON()
} }
override open func viewDidLoad() {
super.viewDidLoad()
bottomViewOutsideOfScroll = true
// Do any additional setup after loading the view.
}
open override func handleNewData() { open override func handleNewData() {
topViewOutsideOfScroll = templateModel?.anchorHeader ?? false
bottomViewOutsideOfScroll = templateModel?.anchorFooter ?? false
super.handleNewData() super.handleNewData()
heightConstraint?.isActive = true
} }
open override func viewForTop() -> UIView? { open override func viewForTop() -> UIView? {

View File

@ -10,6 +10,11 @@ import Foundation
open class CollectionView: UICollectionView, MVMCoreViewProtocol { open class CollectionView: UICollectionView, MVMCoreViewProtocol {
/// A block that gets called on tableview frame changes
public var frameChangeAction: (() -> Void)?
private var previousFrame = CGRect.zero
private var initialSetupPerformed = false private var initialSetupPerformed = false
private func initialSetup() { private func initialSetup() {
@ -29,6 +34,14 @@ open class CollectionView: UICollectionView, MVMCoreViewProtocol {
initialSetup() initialSetup()
} }
open override func layoutSubviews() {
super.layoutSubviews()
if frame != previousFrame {
frameChangeAction?()
}
previousFrame = frame
}
public func updateView(_ size: CGFloat) { public func updateView(_ size: CGFloat) {
for cell in visibleCells { for cell in visibleCells {
(cell as? MVMCoreViewProtocol)?.updateView(size) (cell as? MVMCoreViewProtocol)?.updateView(size)

View File

@ -0,0 +1,25 @@
//
// TableView.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 4/22/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers open class TableView: UITableView {
/// A block that gets called on tableview frame changes
public var frameChangeAction: (() -> Void)?
private var previousFrame = CGRect.zero
open override func layoutSubviews() {
super.layoutSubviews()
if frame != previousFrame {
frameChangeAction?()
}
previousFrame = frame
}
}

View File

@ -51,36 +51,36 @@ import UIKit
} }
open func styleStandard() { open func styleStandard() {
listItemModel?.topMarginPadding = 24 listItemModel?.topPadding = 24
listItemModel?.bottomMarginPadding = 24 listItemModel?.bottomPadding = 24
topSeparatorView?.setStyle(.none) topSeparatorView?.setStyle(.none)
bottomSeparatorView?.setStyle(.standard) bottomSeparatorView?.setStyle(.standard)
} }
open func styleTallDivider() { open func styleTallDivider() {
listItemModel?.topMarginPadding = 48 listItemModel?.topPadding = 48
listItemModel?.bottomMarginPadding = 16 listItemModel?.bottomPadding = 16
topSeparatorView?.setStyle(.none) topSeparatorView?.setStyle(.none)
bottomSeparatorView?.setStyle(.thin) bottomSeparatorView?.setStyle(.thin)
} }
open func styleShortDivider() { open func styleShortDivider() {
listItemModel?.topMarginPadding = 32 listItemModel?.topPadding = 32
listItemModel?.bottomMarginPadding = 16 listItemModel?.bottomPadding = 16
topSeparatorView?.setStyle(.none) topSeparatorView?.setStyle(.none)
bottomSeparatorView?.setStyle(.thin) bottomSeparatorView?.setStyle(.thin)
} }
open func styleFooter() { open func styleFooter() {
listItemModel?.topMarginPadding = 24 listItemModel?.topPadding = 24
listItemModel?.bottomMarginPadding = 0 listItemModel?.bottomPadding = 0
topSeparatorView?.setStyle(.none) topSeparatorView?.setStyle(.none)
bottomSeparatorView?.setStyle(.none) bottomSeparatorView?.setStyle(.none)
} }
open func styleNone() { open func styleNone() {
listItemModel?.topMarginPadding = 0 listItemModel?.topPadding = 0
listItemModel?.bottomMarginPadding = 0 listItemModel?.bottomPadding = 0
topSeparatorView?.setStyle(.none) topSeparatorView?.setStyle(.none)
bottomSeparatorView?.setStyle(.none) bottomSeparatorView?.setStyle(.none)
} }

View File

@ -20,11 +20,8 @@ public class ContainerCollectionReusableView: UICollectionReusableView {
view.setContentCompressionResistancePriority(.required, for: .vertical) view.setContentCompressionResistancePriority(.required, for: .vertical)
addSubview(view) addSubview(view)
self.view = view self.view = view
topConstraint = view.topAnchor.constraint(equalTo: topAnchor) let constraints = NSLayoutConstraint.constraintPinSubview(toSuperview: view)
topConstraint?.isActive = true topConstraint = constraints?[ConstraintTop] as? NSLayoutConstraint
bottomConstraint = bottomAnchor.constraint(equalTo: view.bottomAnchor) bottomConstraint = constraints?[ConstraintBot] as? NSLayoutConstraint
bottomConstraint?.isActive = true
rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
view.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
} }
} }

View File

@ -11,7 +11,9 @@ import Foundation
/// A base view controller with a collection view. /// A base view controller with a collection view.
@objc open class ProgrammaticCollectionViewController: ScrollingViewController { @objc open class ProgrammaticCollectionViewController: ScrollingViewController {
public var collectionView: UICollectionView? public var collectionView: CollectionView?
public var topConstraint: NSLayoutConstraint?
public var bottomConstraint: NSLayoutConstraint?
open override func loadView() { open override func loadView() {
let view = UIView() let view = UIView()
@ -19,7 +21,9 @@ import Foundation
let collection = createCollectionView() let collection = createCollectionView()
view.addSubview(collection) view.addSubview(collection)
NSLayoutConstraint.constraintPinSubview(toSuperview: collection) let constraints = NSLayoutConstraint.constraintPinSubview(toSuperview: collection)
topConstraint = constraints?[ConstraintTop] as? NSLayoutConstraint
bottomConstraint = constraints?[ConstraintBot] as? NSLayoutConstraint
collectionView = collection collectionView = collection
scrollView = collectionView scrollView = collectionView
@ -45,7 +49,7 @@ import Foundation
} }
/// Creates the collection view. /// Creates the collection view.
open func createCollectionView() -> UICollectionView { open func createCollectionView() -> CollectionView {
let collection = CollectionView(frame: .zero, collectionViewLayout: createCollectionViewLayout()) let collection = CollectionView(frame: .zero, collectionViewLayout: createCollectionViewLayout())
collection.dataSource = self collection.dataSource = self
collection.delegate = self collection.delegate = self

View File

@ -35,19 +35,20 @@ open class ProgrammaticScrollViewController: ScrollingViewController {
view.addSubview(scrollView) view.addSubview(scrollView)
// Sets the constraints for the scroll view // Sets the constraints for the scroll view
let constraints = NSLayoutConstraint.constraintPinSubview(toSuperview: scrollView) scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
topConstraint = constraints?[ConstraintTop] as? NSLayoutConstraint view.rightAnchor.constraint(equalTo: scrollView.rightAnchor).isActive = true
bottomConstraint = constraints?[ConstraintBot] as? NSLayoutConstraint topConstraint = scrollView.topAnchor.constraint(equalTo: view.topAnchor)
topConstraint?.isActive = true
bottomConstraint = view.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
bottomConstraint?.isActive = true
// Sets the constraints for the content view
let contentView = MVMCoreUICommonViewsUtility.commonView() let contentView = MVMCoreUICommonViewsUtility.commonView()
scrollView.addSubview(contentView) scrollView.addSubview(contentView)
contentView.leftAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.leftAnchor).isActive = true
// Sets the constraints for the content view scrollView.safeAreaLayoutGuide.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
NSLayoutConstraint.constraintPinSubview(toSuperview: contentView) contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
// Super will set later.
contentWidthConstraint = contentView.widthAnchor.constraint(equalToConstant: 320.0)
contentWidthConstraint?.isActive = true
self.contentView = contentView self.contentView = contentView
self.scrollView = scrollView self.scrollView = scrollView

View File

@ -9,9 +9,9 @@
import Foundation import Foundation
open class ProgrammaticTableViewController: ProgrammaticScrollViewController, UITableViewDelegate, UITableViewDataSource { open class ProgrammaticTableViewController: ProgrammaticScrollViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet public var tableView: UITableView! @IBOutlet public var tableView: TableView!
public init(with tableView: UITableView) { public init(with tableView: TableView) {
self.tableView = tableView self.tableView = tableView
super.init(with: tableView) super.init(with: tableView)
} }
@ -48,8 +48,8 @@ open class ProgrammaticTableViewController: ProgrammaticScrollViewController, UI
} }
/// This class should create the table view that will be used here. Subclass this for different table styles. /// This class should create the table view that will be used here. Subclass this for different table styles.
open func createTableView() -> UITableView { open func createTableView() -> TableView {
let tableView = UITableView(frame: .zero, style: .grouped) let tableView = TableView(frame: .zero, style: .grouped)
tableView.backgroundColor = .clear tableView.backgroundColor = .clear
tableView.separatorStyle = UITableViewCell.SeparatorStyle.none tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
tableView.delegate = self tableView.delegate = self
@ -67,6 +67,11 @@ open class ProgrammaticTableViewController: ProgrammaticScrollViewController, UI
tableView.sectionFooterHeight = CGFloat.leastNormalMagnitude tableView.sectionFooterHeight = CGFloat.leastNormalMagnitude
} }
open func refreshTable() {
tableView.beginUpdates()
tableView.endUpdates()
}
/// For subclassing, returns the number of sections for table. This function calls numberOfSectionsForTableview aftre ensuring the table is setup properly. /// For subclassing, returns the number of sections for table. This function calls numberOfSectionsForTableview aftre ensuring the table is setup properly.
open func getNumberOfSections() -> Int { open func getNumberOfSections() -> Int {
return 1 return 1

View File

@ -12,7 +12,6 @@ open class ScrollingViewController: ViewController {
public var dismissKeyboardTapGesture: UITapGestureRecognizer? public var dismissKeyboardTapGesture: UITapGestureRecognizer?
@IBOutlet public var scrollView: UIScrollView! @IBOutlet public var scrollView: UIScrollView!
public var contentView: UIView? public var contentView: UIView?
public var contentWidthConstraint: NSLayoutConstraint?
private var keyboardNotificationsAdded = false private var keyboardNotificationsAdded = false
private var keyboardIsShowing = false private var keyboardIsShowing = false
@ -42,13 +41,7 @@ open class ScrollingViewController: ViewController {
scrollView.alwaysBounceVertical = false scrollView.alwaysBounceVertical = false
scrollView.delegate = self 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() { open override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews() super.viewDidLayoutSubviews()
view.setNeedsUpdateConstraints() view.setNeedsUpdateConstraints()

View File

@ -17,10 +17,18 @@ import Foundation
private var footerView: ContainerCollectionReusableView? private var footerView: ContainerCollectionReusableView?
private let headerID = "header" private let headerID = "header"
private let footerID = "footer" private let footerID = "footer"
public var bottomViewOutsideOfScrollArea: Bool = false
public var topViewOutsideOfScrollArea: Bool = false
open override func updateViewConstraints() {
// Update the spacing on constraint update
updateFlexibleSpace()
super.updateViewConstraints()
}
/// Updates the padding for flexible space (header or footer) /// Updates the padding for flexible space (header or footer)
private func updateFlexibleSpace() { private func updateFlexibleSpace() {
guard let tableView = collectionView else { return } guard let collectionView = collectionView else { return }
let minimumSpace: CGFloat = minimumFillSpace() let minimumSpace: CGFloat = minimumFillSpace()
var currentSpace: CGFloat = 0 var currentSpace: CGFloat = 0
@ -41,33 +49,43 @@ import Foundation
} }
guard fillTop || fillBottom else { return } guard fillTop || fillBottom else { return }
let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: collectionView, minimumHeight: totalMinimumSpace)
let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: tableView, minimumHeight: totalMinimumSpace) if !MVMCoreGetterUtility.cgfequalwiththreshold(newSpace, currentSpace, 1) {
// 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 { if fillTop && fillBottom {
// space both // space both
let half = newSpace / 2 let half = newSpace / 2
headerView?.bottomConstraint?.constant = half headerView?.bottomConstraint?.constant = half
footerView?.topConstraint?.constant = half footerView?.topConstraint?.constant = half
collectionView?.collectionViewLayout.invalidateLayout() collectionView.collectionViewLayout.invalidateLayout()
} else if fillTop { } 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. // Only top is spaced.
headerView?.bottomConstraint?.constant = newSpace headerView?.bottomConstraint?.constant = newSpace
collectionView?.collectionViewLayout.invalidateLayout() collectionView.collectionViewLayout.invalidateLayout()
} else if fillBottom { } else if fillBottom {
// Only bottom is spaced. // Only bottom is spaced.
footerView?.topConstraint?.constant = newSpace footerView?.topConstraint?.constant = newSpace
collectionView?.collectionViewLayout.invalidateLayout() collectionView.collectionViewLayout.invalidateLayout()
} }
} }
} }
func addTopViewOutside() {
guard let collectionView = collectionView, let topView = topView else { return }
view.addSubview(topView)
topView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
topView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
view.safeAreaLayoutGuide.rightAnchor.constraint(equalTo: topView.rightAnchor).isActive = true
collectionView.topAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true
}
func addBottomViewOutside() {
guard let collectionView = collectionView, let bottomView = bottomView else { return }
view.addSubview(bottomView)
bottomView.topAnchor.constraint(equalTo: collectionView.bottomAnchor).isActive = true
bottomView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
view.safeAreaLayoutGuide.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true
}
//MARK: - ViewController //MARK: - ViewController
open override func updateViews() { open override func updateViews() {
@ -89,17 +107,35 @@ import Foundation
self.invalidateCollectionLayout() self.invalidateCollectionLayout()
} }
} }
open override func handleNewData() { open override func handleNewData() {
super.handleNewData() super.handleNewData()
createViewForHeader() topView?.removeFromSuperview()
createViewForFooter() bottomView?.removeFromSuperview()
topView = viewForTop()
bottomView = viewForBottom()
if topViewOutsideOfScrollArea {
topConstraint?.isActive = false
addTopViewOutside()
} else {
topConstraint?.isActive = true
}
if bottomViewOutsideOfScrollArea {
bottomConstraint?.isActive = false
addBottomViewOutside()
} else {
bottomConstraint?.isActive = true
}
reloadCollectionData() reloadCollectionData()
} }
override open func viewDidLoad() { override open func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// Do any additional setup after loading the view. // Do any additional setup after loading the view.
collectionView?.frameChangeAction = { [weak self] in
self?.invalidateCollectionLayout()
}
} }
//MARK: - Spacing //MARK: - Spacing
@ -118,27 +154,6 @@ import Foundation
open func minimumFillSpace() -> CGFloat { open func minimumFillSpace() -> CGFloat {
return 0 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 //MARK: - Functions to subclass
/// Subclass for a top view. /// Subclass for a top view.
@ -187,18 +202,14 @@ import Foundation
} }
open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
guard let _ = topView, guard section == 0 else { return .zero }
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. // 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)) 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) 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 { open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
guard let _ = bottomView, guard section == numberOfSections(in: collectionView) - 1 else { return .zero }
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. // 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)) 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) return footer.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, height: UIView.layoutFittingExpandedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
@ -207,13 +218,15 @@ import Foundation
open func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { open func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionFooter, if kind == UICollectionView.elementKindSectionFooter,
let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerID, for: indexPath) as? ContainerCollectionReusableView { let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerID, for: indexPath) as? ContainerCollectionReusableView {
footerView.addAndContain(view: bottomView!) let bottomView = (bottomViewOutsideOfScrollArea ? nil : self.bottomView) ?? MVMCoreUICommonViewsUtility.getView(with: 0.5)
footerView.addAndContain(view: bottomView)
footerView.topConstraint?.constant = spaceAboveBottomView() ?? 0 footerView.topConstraint?.constant = spaceAboveBottomView() ?? 0
self.footerView = footerView self.footerView = footerView
return footerView return footerView
} else if kind == UICollectionView.elementKindSectionHeader, } else if kind == UICollectionView.elementKindSectionHeader,
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerID, for: indexPath) as? ContainerCollectionReusableView { let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerID, for: indexPath) as? ContainerCollectionReusableView {
headerView.addAndContain(view: topView!) let topView = (topViewOutsideOfScrollArea ? nil : self.topView) ?? MVMCoreUICommonViewsUtility.getView(with: 0.5)
headerView.addAndContain(view: topView)
headerView.bottomConstraint?.constant = spaceBelowTopView() ?? 0 headerView.bottomConstraint?.constant = spaceBelowTopView() ?? 0
self.headerView = headerView self.headerView = headerView
return headerView return headerView

View File

@ -15,9 +15,8 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController {
private var bottomView: UIView? private var bottomView: UIView?
private var headerView: UIView? private var headerView: UIView?
private var footerView: UIView? private var footerView: UIView?
private var safeAreaView: UIView?
var useMargins: Bool = true
public var bottomViewOutsideOfScrollArea: Bool = false public var bottomViewOutsideOfScrollArea: Bool = false
public var topViewOutsideOfScrollArea: Bool = false
private var topViewBottomConstraint: NSLayoutConstraint? private var topViewBottomConstraint: NSLayoutConstraint?
private var bottomViewTopConstraint: NSLayoutConstraint? private var bottomViewTopConstraint: NSLayoutConstraint?
@ -33,7 +32,7 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController {
bottomView.updateView(width) bottomView.updateView(width)
showFooter(width) showFooter(width)
} }
tableView?.reloadData() tableView.reloadData()
} }
open override func handleNewData() { open override func handleNewData() {
@ -47,6 +46,11 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController {
super.viewDidLoad() super.viewDidLoad()
// Do any additional setup after loading the view. // Do any additional setup after loading the view.
setNoSectionHeadersFooters() setNoSectionHeadersFooters()
// Ensures the footer and headers are the right size
tableView.frameChangeAction = { [weak self] in
self?.view.setNeedsUpdateConstraints()
}
} }
//MARK: - Spacing //MARK: - Spacing
@ -67,40 +71,37 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController {
} }
open override func updateViewConstraints() { open override func updateViewConstraints() {
super.updateViewConstraints() guard let tableView = tableView else {
super.updateViewConstraints()
guard let tableView = tableView else { return } return
}
let minimumSpace: CGFloat = minimumFillSpace() let minimumSpace: CGFloat = minimumFillSpace()
var currentSpace: CGFloat = 0 var currentSpace: CGFloat = 0
var totalMinimumSpace: CGFloat = 0 var totalMinimumSpace: CGFloat = 0
var fillTop = false var fillTop = false
if spaceBelowTopView() == nil, self.tableView?.tableHeaderView != nil { if spaceBelowTopView() == nil, tableView.tableHeaderView != nil {
fillTop = true fillTop = true
currentSpace += topViewBottomConstraint?.constant ?? 0 currentSpace += topViewBottomConstraint?.constant ?? 0
totalMinimumSpace += minimumSpace totalMinimumSpace += minimumSpace
} }
var fillBottom = false var fillBottom = false
if spaceAboveBottomView() == nil, !bottomViewOutsideOfScrollArea, self.tableView?.tableFooterView != nil { if spaceAboveBottomView() == nil, tableView.tableFooterView != nil {
fillBottom = true fillBottom = true
currentSpace += bottomViewTopConstraint?.constant ?? 0 currentSpace += bottomViewTopConstraint?.constant ?? 0
totalMinimumSpace += minimumSpace totalMinimumSpace += minimumSpace
} }
guard fillTop || fillBottom else { return } guard fillTop || fillBottom else {
super.updateViewConstraints()
let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: tableView, minimumHeight: totalMinimumSpace) return
// 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 && bottomViewOutsideOfScrollArea {
currentSpaceForCompare = currentSpace * 2;
} }
let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: tableView, minimumHeight: totalMinimumSpace)
let width = view.bounds.width let width = view.bounds.width
if !MVMCoreGetterUtility.cgfequalwiththreshold(newSpace, currentSpaceForCompare, 0.1) { if !MVMCoreGetterUtility.cgfequalwiththreshold(newSpace, currentSpace, 0.1) {
if fillTop && fillBottom { if fillTop && fillBottom {
// space both // space both
let half = newSpace / 2 let half = newSpace / 2
@ -109,27 +110,30 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController {
showHeader(width) showHeader(width)
showFooter(width) showFooter(width)
} else if fillTop { } 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. topViewBottomConstraint?.constant = newSpace
if bottomViewOutsideOfScrollArea {
topViewBottomConstraint?.constant = newSpace / 2
} else {
topViewBottomConstraint?.constant = newSpace
}
showHeader(width) showHeader(width)
} else if fillBottom { } else if fillBottom {
// Only bottom is spaced. // Only bottom is spaced.
bottomViewTopConstraint?.constant = newSpace bottomViewTopConstraint?.constant = newSpace
showFooter(width) showFooter(width)
} }
DispatchQueue.main.async {
self.refreshTable()
}
} }
super.updateViewConstraints()
} }
//MARK: - Header Footer //MARK: - Header Footer
/// Gets the top view and adds it to a spacing view, headerView, and then calls showHeader. /// Gets the top view and adds it to a spacing view, headerView, and then calls showHeader.
open func createViewForTableHeader() { open func createViewForTableHeader() {
let topView = viewForTop() var topView = viewForTop()
self.topView = topView self.topView = topView
// If top view is outside of scroll area, create a dummy view for the header. Small height is needed to stop apple from adding padding for grouped tables when no header.
if topViewOutsideOfScrollArea {
topView = MVMCoreUICommonViewsUtility.getView(with: 0.5)
}
let headerView = MVMCoreUICommonViewsUtility.commonView() let headerView = MVMCoreUICommonViewsUtility.commonView()
headerView.addSubview(topView) headerView.addSubview(topView)
topView.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true topView.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
@ -143,9 +147,13 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController {
/// Gets the bottom view and adds it to a spacing view, footerView, and then calls showFooter. /// Gets the bottom view and adds it to a spacing view, footerView, and then calls showFooter.
open func createViewForTableFooter() { open func createViewForTableFooter() {
let bottomView = viewForBottom() var bottomView = viewForBottom()
self.bottomView = bottomView self.bottomView = bottomView
// If bottom view is outside of scroll area, create a dummy view for the header. Small height is needed to stop apple from adding padding for grouped tables when no header.
if bottomViewOutsideOfScrollArea {
bottomView = MVMCoreUICommonViewsUtility.getView(with: 0.5)
}
let footerView = MVMCoreUICommonViewsUtility.commonView() let footerView = MVMCoreUICommonViewsUtility.commonView()
footerView.addSubview(bottomView) footerView.addSubview(bottomView)
bottomViewTopConstraint = bottomView.topAnchor.constraint(equalTo: footerView.topAnchor, constant: spaceAboveBottomView() ?? 0) bottomViewTopConstraint = bottomView.topAnchor.constraint(equalTo: footerView.topAnchor, constant: spaceAboveBottomView() ?? 0)
@ -161,10 +169,20 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController {
func showHeader(_ sizingWidth: CGFloat?) { func showHeader(_ sizingWidth: CGFloat?) {
headerView?.removeFromSuperview() headerView?.removeFromSuperview()
tableView?.tableHeaderView = nil tableView?.tableHeaderView = nil
guard let headerView = headerView else { guard let headerView = headerView else { return }
return
}
if topViewOutsideOfScrollArea,
let topView = topView {
// put top view outside of scrolling area.
topConstraint?.isActive = false
view.addSubview(topView)
topView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
topView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
view.rightAnchor.constraint(equalTo: topView.rightAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true
} else {
topConstraint?.isActive = true
}
// This extra view is needed because of the wonkiness of apple's table header. Things breaks if using autolayout. // This extra view is needed because of the wonkiness of apple's table header. Things breaks if using autolayout.
headerView.setNeedsLayout() headerView.setNeedsLayout()
headerView.layoutIfNeeded() headerView.layoutIfNeeded()
@ -178,56 +196,55 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController {
/// Takes the current footerView and adds it to the tableFooterView /// Takes the current footerView and adds it to the tableFooterView
func showFooter(_ sizingWidth: CGFloat?) { func showFooter(_ sizingWidth: CGFloat?) {
footerView?.removeFromSuperview() footerView?.removeFromSuperview()
safeAreaView?.removeFromSuperview() guard let footerView = footerView,
guard let footerView = footerView, let tableView = tableView else { let tableView = tableView else {
return self.tableView?.tableFooterView = nil
return
} }
if bottomViewOutsideOfScrollArea { if bottomViewOutsideOfScrollArea,
let bottomView = bottomView {
// put bottom view outside of scrolling area. // put bottom view outside of scrolling area.
bottomConstraint?.isActive = false bottomConstraint?.isActive = false
view.addSubview(footerView) view.addSubview(bottomView)
footerView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true bottomView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true
footerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true bottomView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
view.rightAnchor.constraint(equalTo: footerView.rightAnchor).isActive = true view.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true
safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view)
safeAreaView?.backgroundColor = bottomView?.backgroundColor
} else { } else {
bottomConstraint?.isActive = true bottomConstraint?.isActive = true
var y: CGFloat?
if let tableFooterView = tableView.tableFooterView {
// if footer already exists, use the same y location to avoid strange moving animation
y = tableFooterView.frame.minY
}
// This extra view is needed because of the wonkiness of apple's table footer. Things breaks if using autolayout.
MVMCoreUIUtility.sizeView(toFit: footerView)
let tableFooterView = UIView(frame: CGRect(x: 0, y: y ?? 0, width: MVMCoreUIUtility.getWidth(), height: footerView.frame.height))
tableFooterView.addSubview(footerView)
NSLayoutConstraint.constraintPinSubview(toSuperview: footerView)
tableView.tableFooterView = tableFooterView
} }
// if footer already exists, use the same y location to avoid strange moving animation
let y = tableView.tableFooterView?.frame.minY ?? 0.0
// This extra view is needed because of the wonkiness of apple's table footer. Things breaks if using autolayout.
MVMCoreUIUtility.sizeView(toFit: footerView)
let tableFooterView = UIView(frame: CGRect(x: 0, y: y, width: MVMCoreUIUtility.getWidth(), height: footerView.frame.height))
tableFooterView.addSubview(footerView)
NSLayoutConstraint.constraintPinSubview(toSuperview: footerView)
tableView.tableFooterView = tableFooterView
} }
//MARK: - Functions to subclass //MARK: - Functions to subclass
/// Subclass for a top view. /// Subclass for a top view.
open func viewForTop() -> UIView { open func viewForTop() -> UIView {
let view = MVMCoreUICommonViewsUtility.commonView()
// Small height is needed to stop apple from adding padding for grouped tables when no header. // Small height is needed to stop apple from adding padding for grouped tables when no header.
view.heightAnchor.constraint(equalToConstant: 1).isActive = true return MVMCoreUICommonViewsUtility.getView(with: 0.5)
return view
} }
/// Subclass for a bottom view. /// Subclass for a bottom view.
open func viewForBottom() -> UIView { open func viewForBottom() -> UIView {
// Default spacing is standard when no buttons. // Default spacing is standard when no buttons.
let view = MVMCoreUICommonViewsUtility.commonView() return MVMCoreUICommonViewsUtility.getView(with: PaddingDefaultVerticalSpacing)
view.heightAnchor.constraint(equalToConstant: PaddingDefaultVerticalSpacing).isActive = true
return view
} }
deinit { deinit {
tableView?.delegate = nil tableView?.delegate = nil
} }
// Ensures the footer and headers are the right size
func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView) {
view.setNeedsUpdateConstraints()
}
} }

View File

@ -18,10 +18,12 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController {
var bottomView: UIView? var bottomView: UIView?
var useMargins: Bool = false var useMargins: Bool = false
// The top view can be put outside of the scrolling area.
var topViewOutsideOfScroll = false
// The bottom view can be put outside of the scrolling area. // The bottom view can be put outside of the scrolling area.
var bottomViewOutsideOfScroll = false var bottomViewOutsideOfScroll = false
private var safeAreaView: UIView?
var heightConstraint: NSLayoutConstraint? var heightConstraint: NSLayoutConstraint?
open override func updateViews() { open override func updateViews() {
@ -39,24 +41,11 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController {
} }
} }
open override func updateViewConstraints() {
super.updateViewConstraints()
guard let scrollView = scrollView else {
return
}
if scrollView.contentInsetAdjustmentBehavior == UIScrollView.ContentInsetAdjustmentBehavior.automatic {
heightConstraint?.constant = -scrollView.adjustedContentInset.top - scrollView.adjustedContentInset.bottom
} else {
heightConstraint?.constant = -scrollView.contentInset.top - scrollView.contentInset.bottom
}
}
open override func loadView() { open override func loadView() {
super.loadView() super.loadView()
// The height is used to keep the bottom view at the bottom. // The height is used to keep the bottom view at the bottom.
if let contentView = contentView, let scrollView = scrollView { if let contentView = contentView, let scrollView = scrollView {
heightConstraint = contentView.heightAnchor.constraint(equalTo: scrollView.heightAnchor, multiplier: 1.0) heightConstraint = contentView.heightAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.heightAnchor, multiplier: 1.0)
heightConstraint?.priority = UILayoutPriority.defaultLow heightConstraint?.priority = UILayoutPriority.defaultLow
} }
} }
@ -68,7 +57,6 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController {
topView?.removeFromSuperview() topView?.removeFromSuperview()
middleView?.removeFromSuperview() middleView?.removeFromSuperview()
bottomView?.removeFromSuperview() bottomView?.removeFromSuperview()
safeAreaView?.removeFromSuperview()
if let subViews = contentView?.subviews { if let subViews = contentView?.subviews {
for view in subViews { for view in subViews {
view.removeFromSuperview() view.removeFromSuperview()
@ -76,6 +64,7 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController {
} }
// Reset constraints // Reset constraints
topConstraint?.isActive = true
bottomConstraint?.isActive = true bottomConstraint?.isActive = true
heightConstraint?.isActive = false heightConstraint?.isActive = false
@ -111,33 +100,30 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController {
//MARK:-Setup //MARK:-Setup
extension ThreeLayerViewController { extension ThreeLayerViewController {
func setupViewAsTop() -> UIView? { func setupViewAsTop() -> UIView {
if let topView = viewForTop() { var topView = viewForTop() ?? MVMCoreUICommonViewsUtility.getView(with: 0)
self.topView = topView self.topView = topView
// Adds the top view outside the scroll if directed.
if topViewOutsideOfScroll {
topConstraint?.isActive = false
addViewOutsideOfScrollViewTop(topView)
// Adds and returns an empty view to use for the internal logic.
topView = MVMCoreUICommonViewsUtility.getView(with: 0)
addViewInsideOfScrollViewTop(topView)
} else { } else {
topView = MVMCoreUICommonViewsUtility.commonView() topConstraint?.isActive = true
topView?.heightAnchor.constraint(equalToConstant: 0).isActive = true addViewInsideOfScrollViewTop(topView)
} }
guard let topView = topView, let contentView = contentView else {
return nil
}
contentView.addSubview(topView)
NSLayoutConstraint.pinViewTop(toSuperview: topView, useMargins: useMargins, constant: 0).isActive = true
NSLayoutConstraint.pinViewLeft(toSuperview: topView, useMargins: useMargins, constant: 0).isActive = true
NSLayoutConstraint.pinViewRight(toSuperview: topView, useMargins: useMargins, constant: 0).isActive = true
return topView return topView
} }
func setupViewAsMiddle() -> UIView? { func setupViewAsMiddle() -> UIView {
if let middleView = viewForMiddle() { let middleView = viewForMiddle() ?? MVMCoreUICommonViewsUtility.getView(with: 0)
self.middleView = middleView self.middleView = middleView
} else {
middleView = MVMCoreUICommonViewsUtility.commonView() guard let contentView = contentView else { return middleView }
middleView?.heightAnchor.constraint(equalToConstant: 0).isActive = true
}
guard let middleView = middleView, let contentView = contentView else {
return nil
}
contentView.addSubview(middleView) contentView.addSubview(middleView)
NSLayoutConstraint.pinViewLeft(toSuperview: middleView, useMargins: useMargins, constant: 0).isActive = true NSLayoutConstraint.pinViewLeft(toSuperview: middleView, useMargins: useMargins, constant: 0).isActive = true
NSLayoutConstraint.pinViewRight(toSuperview: middleView, useMargins: useMargins, constant: 0).isActive = true NSLayoutConstraint.pinViewRight(toSuperview: middleView, useMargins: useMargins, constant: 0).isActive = true
@ -145,33 +131,30 @@ extension ThreeLayerViewController {
return middleView return middleView
} }
func setupViewAsBottom() -> UIView? { func setupViewAsBottom() -> UIView {
if let bottomView = viewForBottom() { var bottomView = viewForBottom() ?? MVMCoreUICommonViewsUtility.getView(with: 0)
self.bottomView = bottomView self.bottomView = bottomView
} else {
bottomView = MVMCoreUICommonViewsUtility.commonView()
bottomView?.heightAnchor.constraint(equalToConstant: 0).isActive = true
}
guard let bottomView = bottomView else {
return nil
}
// Adds the bottom view outside the scroll if directed. // Adds the bottom view outside the scroll if directed.
if bottomViewOutsideOfScroll { if bottomViewOutsideOfScroll {
bottomConstraint?.isActive = false; bottomConstraint?.isActive = false
addViewInsideOfScrollViewBottom(ViewConstrainingView.empty())
addViewOutsideOfScrollViewBottom(bottomView) addViewOutsideOfScrollViewBottom(bottomView)
// Adds and returns an empty view to use for the internal logic.
bottomView = MVMCoreUICommonViewsUtility.getView(with: 0)
addViewInsideOfScrollViewBottom(bottomView)
} else { } else {
bottomConstraint?.isActive = true; bottomConstraint?.isActive = true
addViewInsideOfScrollViewBottom(bottomView) addViewInsideOfScrollViewBottom(bottomView)
} }
return bottomView return bottomView
} }
func setupLayers() { func setupLayers() {
guard let contentView = contentView, let topView = setupViewAsTop(), let middleView = setupViewAsMiddle(), let bottomView = setupViewAsBottom() else { guard let contentView = contentView else { return }
return let topView = setupViewAsTop()
} let middleView = setupViewAsMiddle()
let bottomView = setupViewAsBottom()
let spaceAbove = spaceBetweenTopAndMiddle() let spaceAbove = spaceBetweenTopAndMiddle()
let spaceBelow = spaceBetweenMiddleAndBottom() let spaceBelow = spaceBetweenMiddleAndBottom()
if let spaceAbove = spaceAbove, let spaceBelow = spaceBelow { if let spaceAbove = spaceAbove, let spaceBelow = spaceBelow {
@ -197,7 +180,7 @@ extension ThreeLayerViewController {
contentView.addSubview(topSpacer) contentView.addSubview(topSpacer)
topSpacer.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true topSpacer.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
contentView.rightAnchor.constraint(equalTo: topSpacer.rightAnchor).isActive = true contentView.rightAnchor.constraint(equalTo: topSpacer.rightAnchor).isActive = true
topSpacer.heightAnchor.constraint(greaterThanOrEqualToConstant: PaddingTwo).isActive = true topSpacer.heightAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true
topSpacer.topAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true topSpacer.topAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true
middleView.topAnchor.constraint(equalTo: topSpacer.bottomAnchor).isActive = true middleView.topAnchor.constraint(equalTo: topSpacer.bottomAnchor).isActive = true
bottomView.topAnchor.constraint(equalTo: middleView.bottomAnchor, constant: spaceBelow).isActive = true bottomView.topAnchor.constraint(equalTo: middleView.bottomAnchor, constant: spaceBelow).isActive = true
@ -212,7 +195,7 @@ extension ThreeLayerViewController {
bottomSpacer.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true bottomSpacer.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
contentView.rightAnchor.constraint(equalTo: bottomSpacer.rightAnchor).isActive = true contentView.rightAnchor.constraint(equalTo: bottomSpacer.rightAnchor).isActive = true
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor).isActive = true topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor).isActive = true
topSpacer.heightAnchor.constraint(greaterThanOrEqualToConstant: PaddingTwo).isActive = true topSpacer.heightAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true
topSpacer.topAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true topSpacer.topAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true
middleView.topAnchor.constraint(equalTo: topSpacer.bottomAnchor).isActive = true middleView.topAnchor.constraint(equalTo: topSpacer.bottomAnchor).isActive = true
bottomSpacer.topAnchor.constraint(equalTo: middleView.bottomAnchor).isActive = true bottomSpacer.topAnchor.constraint(equalTo: middleView.bottomAnchor).isActive = true
@ -221,10 +204,25 @@ extension ThreeLayerViewController {
} }
} }
func addViewInsideOfScrollViewTop(_ view: UIView) {
guard let contentView = contentView else { return }
contentView.addSubview(view)
NSLayoutConstraint.pinViewTop(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
}
func addViewOutsideOfScrollViewTop(_ view: UIView) {
guard let scrollView = scrollView, let parentView = self.view else { return }
parentView.addSubview(view)
scrollView.topAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
view.topAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.topAnchor).isActive = true
}
func addViewInsideOfScrollViewBottom(_ view: UIView) { func addViewInsideOfScrollViewBottom(_ view: UIView) {
guard let contentView = contentView else { guard let contentView = contentView else { return }
return
}
contentView.addSubview(view); contentView.addSubview(view);
NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
@ -232,16 +230,11 @@ extension ThreeLayerViewController {
} }
func addViewOutsideOfScrollViewBottom(_ view: UIView) { func addViewOutsideOfScrollViewBottom(_ view: UIView) {
self.view?.addSubview(view) guard let scrollView = scrollView, let parentView = self.view else { return }
if let scrollView = scrollView, let parentView = self.view { parentView.addSubview(view)
view.topAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true view.topAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
if let safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: parentView) {
safeAreaView.backgroundColor = bottomView?.backgroundColor
self.safeAreaView = safeAreaView
}
}
} }
} }

View File

@ -43,10 +43,10 @@ import UIKit
public static func set(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { public static func set(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) {
viewController.navigationItem.title = navigationItemModel.title viewController.navigationItem.title = navigationItemModel.title
viewController.navigationItem.accessibilityLabel = navigationItemModel.title viewController.navigationItem.accessibilityLabel = navigationItemModel.title
viewController.navigationItem.hidesBackButton = !navigationItemModel.systemBackButton viewController.navigationItem.hidesBackButton = (navigationItemModel.backButton != nil)
navigationController.setNavigationBarHidden(navigationItemModel.hidden, animated: true) navigationController.setNavigationBarHidden(navigationItemModel.hidden, animated: true)
UIColor.setBackgroundColor(navigationItemModel.backgroundColor?.uiColor ?? .white, for: navigationController.navigationBar, isTransparent: navigationItemModel.transparent) UIColor.setBackgroundColor(navigationItemModel.backgroundColor?.uiColor ?? .white, for: navigationController.navigationBar, isTransparent: navigationItemModel.translucent)
let tint = navigationItemModel.tintColor.uiColor let tint = navigationItemModel.tintColor.uiColor
navigationController.navigationBar.tintColor = tint navigationController.navigationBar.tintColor = tint
@ -64,8 +64,8 @@ import UIKit
if navigationController == MVMCoreUISplitViewController.main()?.navigationController, if navigationController == MVMCoreUISplitViewController.main()?.navigationController,
navigationController.topViewController == viewController { navigationController.topViewController == viewController {
// Update Panels // Update Panels
MVMCoreUISplitViewController.main()?.setLeftPanelIsAccessible(navigationItemModel.showLeftPanelButton ?? false, for: viewController) MVMCoreUISplitViewController.main()?.setLeftPanelIsAccessible(navigationItemModel.showLeftPanelButton , for: viewController)
MVMCoreUISplitViewController.main()?.setRightPanelIsAccessible(navigationItemModel.showRightPanelButton ?? false, for: viewController) MVMCoreUISplitViewController.main()?.setRightPanelIsAccessible(navigationItemModel.showRightPanelButton , for: viewController)
MVMCoreUISession.sharedGlobal()?.splitViewController?.setNavigationIconColor(tint) MVMCoreUISession.sharedGlobal()?.splitViewController?.setNavigationIconColor(tint)
} }
} }

View File

@ -169,8 +169,15 @@ open class ContainerHelper: NSObject {
} }
} }
/// Updates the view margins according to model and size. If useHorizontalMargins is true, we will try to use the left and right padding, else we will default to the normal gutters. If useVerticalMargins is true, we will try to use the top and bottom padding. All else, we use zero.
open func updateViewMargins(_ view: UIView, model: ContainerModelProtocol?, size: CGFloat) { open func updateViewMargins(_ view: UIView, model: ContainerModelProtocol?, size: CGFloat) {
MFStyler.setMarginsFor(view, size: size, defaultHorizontal: model?.useHorizontalMargins ?? false, top: (model?.useVerticalMargins ?? false) ? (model?.topMarginPadding ?? 0) : 0, bottom: (model?.useVerticalMargins ?? false) ? (model?.bottomMarginPadding ?? 0) : 0) let left = (model?.useHorizontalMargins ?? false) ? (model?.leftPadding ?? Padding.Component.horizontalPaddingForSize(size)) : 0.0
let right = (model?.useHorizontalMargins ?? false) ? (model?.rightPadding ?? Padding.Component.horizontalPaddingForSize(size)) : 0.0
let top = (model?.useVerticalMargins ?? false) ? (model?.topPadding ?? 0.0) : 0.0
let bottom = (model?.useVerticalMargins ?? false) ? (model?.bottomPadding ?? 0.0) : 0.0
MVMCoreDispatchUtility.performBlock(onMainThread: {
MVMCoreUIUtility.setMarginsFor(view, leading: left, top: top, trailing: right, bottom: bottom)
})
} }
open func set(with model: ContainerModelProtocol, for contained: MVMCoreUIViewConstrainingProtocol?) { open func set(with model: ContainerModelProtocol, for contained: MVMCoreUIViewConstrainingProtocol?) {

View File

@ -9,41 +9,61 @@
import Foundation import Foundation
open class ContainerModel: ContainerModelProtocol, Codable { open class ContainerModel: ContainerModelProtocol, Codable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public var horizontalAlignment: UIStackView.Alignment? public var horizontalAlignment: UIStackView.Alignment?
public var verticalAlignment: UIStackView.Alignment?
public var useHorizontalMargins: Bool? public var useHorizontalMargins: Bool?
public var leftPadding: CGFloat?
public var rightPadding: CGFloat?
public var verticalAlignment: UIStackView.Alignment?
public var useVerticalMargins: Bool? public var useVerticalMargins: Bool?
public var topMarginPadding: CGFloat? public var topPadding: CGFloat?
public var bottomMarginPadding: CGFloat? public var bottomPadding: CGFloat?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
//-------------------------------------------------- //--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case verticalAlignment
case horizontalAlignment case horizontalAlignment
case useHorizontalMargins case useHorizontalMargins
case leftPadding
case rightPadding
case verticalAlignment
case useVerticalMargins case useVerticalMargins
case topMarginPadding case topPadding
case bottomMarginPadding case bottomPadding
} }
//--------------------------------------------------
// MARK: - Subclassable
//--------------------------------------------------
/// Sets the default values. Should be called on init.
public func setDefaults() {}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
public init() {} public init() {
setDefaults()
}
public convenience init(horizontalAlignment: UIStackView.Alignment? = nil, verticalAlignment: UIStackView.Alignment? = nil) { public init(horizontalAlignment: UIStackView.Alignment? = nil, verticalAlignment: UIStackView.Alignment? = nil, useHorizontalMargins: Bool? = nil, leftPadding: CGFloat? = nil, rightPadding: CGFloat? = nil, useVerticalMargins: Bool? = nil, topPadding: CGFloat? = nil, bottomPadding: CGFloat? = nil) {
self.init()
self.horizontalAlignment = horizontalAlignment self.horizontalAlignment = horizontalAlignment
self.verticalAlignment = verticalAlignment self.verticalAlignment = verticalAlignment
self.useHorizontalMargins = useHorizontalMargins
self.leftPadding = leftPadding
self.rightPadding = rightPadding
self.useVerticalMargins = useVerticalMargins
self.topPadding = topPadding
self.bottomPadding = bottomPadding
setDefaults()
} }
//-------------------------------------------------- //--------------------------------------------------
@ -59,9 +79,12 @@ open class ContainerModel: ContainerModelProtocol, Codable {
horizontalAlignment = ContainerHelper.getAlignment(for: horizontalAlignmentString) horizontalAlignment = ContainerHelper.getAlignment(for: horizontalAlignmentString)
} }
useHorizontalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useHorizontalMargins) useHorizontalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useHorizontalMargins)
leftPadding = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .leftPadding)
rightPadding = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .rightPadding)
useVerticalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useVerticalMargins) useVerticalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useVerticalMargins)
topMarginPadding = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .topMarginPadding) topPadding = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .topPadding)
bottomMarginPadding = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .bottomMarginPadding) bottomPadding = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .bottomPadding)
setDefaults()
} }
open func encode(to encoder: Encoder) throws { open func encode(to encoder: Encoder) throws {
@ -69,9 +92,10 @@ open class ContainerModel: ContainerModelProtocol, Codable {
try container.encodeIfPresent(ContainerHelper.getAlignmentString(for: verticalAlignment), forKey: .verticalAlignment) try container.encodeIfPresent(ContainerHelper.getAlignmentString(for: verticalAlignment), forKey: .verticalAlignment)
try container.encodeIfPresent(ContainerHelper.getAlignmentString(for: horizontalAlignment), forKey: .horizontalAlignment) try container.encodeIfPresent(ContainerHelper.getAlignmentString(for: horizontalAlignment), forKey: .horizontalAlignment)
try container.encodeIfPresent(useHorizontalMargins, forKey: .useHorizontalMargins) try container.encodeIfPresent(useHorizontalMargins, forKey: .useHorizontalMargins)
try container.encodeIfPresent(leftPadding, forKey: .leftPadding)
try container.encodeIfPresent(rightPadding, forKey: .rightPadding)
try container.encodeIfPresent(useVerticalMargins, forKey: .useVerticalMargins) try container.encodeIfPresent(useVerticalMargins, forKey: .useVerticalMargins)
// TODO: can add this back once we have type erasures. try container.encodeIfPresent(topPadding, forKey: .topPadding)
//try container.encodeIfPresent(topMarginPadding, forKey: .topMarginPadding) try container.encodeIfPresent(bottomPadding, forKey: .bottomPadding)
//try container.encodeIfPresent(bottomMarginPadding, forKey: .bottomMarginPadding)
} }
} }

View File

@ -41,9 +41,9 @@ open class MoleculeContainer: Container {
guard let containerModel = model as? MoleculeContainerModelProtocol else { return 0 } guard let containerModel = model as? MoleculeContainerModelProtocol else { return 0 }
guard let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(containerModel.molecule), guard let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(containerModel.molecule),
let moleculeHeight = moleculeClass.estimatedHeight(with: containerModel.molecule, delegateObject) else { let moleculeHeight = moleculeClass.estimatedHeight(with: containerModel.molecule, delegateObject) else {
return (containerModel.topMarginPadding ?? 0) + (containerModel.bottomMarginPadding ?? 0) return (containerModel.topPadding ?? 0) + (containerModel.bottomPadding ?? 0)
} }
return moleculeHeight + (containerModel.topMarginPadding ?? 0) + (containerModel.bottomMarginPadding ?? 0) return moleculeHeight + (containerModel.topPadding ?? 0) + (containerModel.bottomPadding ?? 0)
} }
public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {

View File

@ -8,11 +8,17 @@
import Foundation import Foundation
public class MoleculeContainerModel: ContainerModel, MoleculeContainerModelProtocol { public class MoleculeContainerModel: ContainerModel, MoleculeContainerModelProtocol, MoleculeModelProtocol {
public class var identifier: String {
return "container"
}
public var backgroundColor: Color?
public var molecule: MoleculeModelProtocol public var molecule: MoleculeModelProtocol
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName
case molecule case molecule
case backgroundColor
} }
//-------------------------------------------------- //--------------------------------------------------
@ -31,12 +37,15 @@ public class MoleculeContainerModel: ContainerModel, MoleculeContainerModelProto
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
molecule = try typeContainer.decodeModel(codingKey: .molecule) molecule = try typeContainer.decodeModel(codingKey: .molecule)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
try super.init(from: decoder) try super.init(from: decoder)
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeModel(molecule, forKey: .molecule) try container.encodeModel(molecule, forKey: .molecule)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
} }
} }

View File

@ -0,0 +1,29 @@
//
// MVMCoreUIViewControllerMappingObject+Extension.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 4/21/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public extension MVMCoreUIViewControllerMappingObject {
func register<T: TemplateProtocol>(template: T.Type) {
add(toTemplateViewControllerMapping: [template.TemplateModel.identifier: MVMCoreViewControllerProgrammaticMappingObject(with: template)!])
}
@objc func registerTemplates() {
register(template: MoleculeStackTemplate.self)
add(toTemplateViewControllerMapping: [StackCenteredPageTemplateModel.identifier: MVMCoreViewControllerProgrammaticMappingObject(with: MoleculeStackCenteredTemplate.self)!])
add(toTemplateViewControllerMapping: ["modalStack": MVMCoreViewControllerProgrammaticMappingObject(with: ModalMoleculeStackTemplate.self)!])
register(template: MoleculeListTemplate.self)
add(toTemplateViewControllerMapping: ["modalList": MVMCoreViewControllerProgrammaticMappingObject(with: ModalMoleculeListTemplate.self)!])
register(template: ThreeLayerTemplate.self)
register(template: ThreeLayerCenterTemplate.self)
register(template: CollectionTemplate.self)
}
}

View File

@ -12,23 +12,14 @@
@implementation MVMCoreUIViewControllerMappingObject @implementation MVMCoreUIViewControllerMappingObject
- (NSMutableDictionary *)viewControllerMapping { - (instancetype)init {
if (self = [super init]) {
// Keeps a mapping of the given page type if (self.viewControllerMapping == nil) {
static dispatch_once_t onceToken; self.viewControllerMapping = [NSMutableDictionary dictionary];
static NSMutableDictionary *viewControllerMapping; }
dispatch_once(&onceToken, ^{ [self registerTemplates];
viewControllerMapping = [@{ }
@"stack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeStackTemplate class]], return self;
@"centerMoleculeStack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeStackCenteredTemplate class]],
@"list" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[MoleculeListTemplate class]],
@"threeLayer" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[ThreeLayerTemplate class]],
@"modalStack" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[ModalMoleculeStackTemplate class]],
@"modalList" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[ModalMoleculeListTemplate class]],
@"collection" : [[MVMCoreViewControllerProgrammaticMappingObject alloc] initWithClass:[CollectionTemplate class]]
} mutableCopy];
});
return viewControllerMapping;
} }
@end @end

View File

@ -20,4 +20,10 @@ public extension MVMCoreUICommonViewsUtility {
return toolbar return toolbar
} }
static func getView(with height: CGFloat) -> UIView {
let view = self.commonView()
view.heightAnchor.constraint(equalToConstant: height).isActive = true
return view
}
} }