diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 72326c6c..b25857e7 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -236,6 +236,8 @@ AA997252247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA997251247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift */; }; AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */; }; AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */; }; + AAA7CD69250641F90045B959 /* HeartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA7CD68250641F90045B959 /* HeartModel.swift */; }; + AAA7CD6B250642080045B959 /* Heart.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA7CD6A250642080045B959 /* Heart.swift */; }; AAA905DF24D1758700D1EFAB /* ListThreeColumnBillHistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA905DE24D1758700D1EFAB /* ListThreeColumnBillHistoryModel.swift */; }; AAA905E124D1759A00D1EFAB /* ListThreeColumnBillHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA905E024D1759A00D1EFAB /* ListThreeColumnBillHistory.swift */; }; AAB7EDEF246ADA1600E54929 /* ListProgressBarThinModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB7EDEE246ADA1600E54929 /* ListProgressBarThinModel.swift */; }; @@ -303,8 +305,12 @@ D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C7008250BF99B0095B21C /* TopNotificationModel.swift */; }; D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */; }; + D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20F3B43252E00E4004B3F56 /* PageProtocol.swift */; }; + D20F3B5E252F9B5E004B3F56 /* NavigationBarRefreshProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; + D2169301251E51E7002A6324 /* SectionListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2169300251E51E7002A6324 /* SectionListTemplate.swift */; }; + D2169303251E53D9002A6324 /* SectionListTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2169302251E53D9002A6324 /* SectionListTemplateModel.swift */; }; D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; }; D21B7F71243BAC1600051ABF /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */; }; D21B7F73243BAC6800051ABF /* CollectionItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */; }; @@ -465,6 +471,10 @@ D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */; }; D2CAC7D12511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */; }; D2CAC7D3251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */; }; + D2D2FCF0252B72AF0033EAAA /* MoleculeSectionFooterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D2FCEF252B72AF0033EAAA /* MoleculeSectionFooterModel.swift */; }; + D2D2FCF3252B72CF0033EAAA /* MoleculeSectionFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D2FCF2252B72CF0033EAAA /* MoleculeSectionFooter.swift */; }; + D2D3957A252FDBB300047B11 /* ModalSectionListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D39579252FDBB300047B11 /* ModalSectionListTemplate.swift */; }; + D2D3957D252FDBCD00047B11 /* ModalSectionListTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D3957C252FDBCD00047B11 /* ModalSectionListTemplateModel.swift */; }; D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */; }; D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */; }; D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */; }; @@ -482,6 +492,9 @@ D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A99E23E07F8A000B42E6 /* PillButton.swift */; }; D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */; }; D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */; }; + D2EC7BD52527B7A600F540AF /* MoleculeSectionHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BD42527B7A600F540AF /* MoleculeSectionHeaderModel.swift */; }; + D2EC7BD92527B7CF00F540AF /* MoleculeSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BD82527B7CF00F540AF /* MoleculeSectionHeader.swift */; }; + D2EC7BDD2527B83700F540AF /* SectionHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */; }; D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D12513EA6900564112 /* NotificationXButton.swift */; }; D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D32514F80C00564112 /* CollapsableNotification.swift */; }; D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */; }; @@ -729,6 +742,8 @@ AA997251247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconAllTextLinks.swift; sourceTree = ""; }; AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyText.swift; sourceTree = ""; }; AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyTextModel.swift; sourceTree = ""; }; + AAA7CD68250641F90045B959 /* HeartModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeartModel.swift; sourceTree = ""; }; + AAA7CD6A250642080045B959 /* Heart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Heart.swift; sourceTree = ""; }; AAA905DE24D1758700D1EFAB /* ListThreeColumnBillHistoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnBillHistoryModel.swift; sourceTree = ""; }; AAA905E024D1759A00D1EFAB /* ListThreeColumnBillHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnBillHistory.swift; sourceTree = ""; }; AAB7EDEE246ADA1600E54929 /* ListProgressBarThinModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListProgressBarThinModel.swift; sourceTree = ""; }; @@ -796,8 +811,12 @@ D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; D20C7008250BF99B0095B21C /* TopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopNotificationModel.swift; sourceTree = ""; }; D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertView+Extension.swift"; sourceTree = ""; }; + D20F3B43252E00E4004B3F56 /* PageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageProtocol.swift; sourceTree = ""; }; + D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarRefreshProtocol.swift; sourceTree = ""; }; D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; + D2169300251E51E7002A6324 /* SectionListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionListTemplate.swift; sourceTree = ""; }; + D2169302251E53D9002A6324 /* SectionListTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionListTemplateModel.swift; sourceTree = ""; }; D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = ""; }; D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = ""; }; D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionItemModelProtocol.swift; sourceTree = ""; }; @@ -960,6 +979,10 @@ D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationModel.swift; sourceTree = ""; }; D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertMainView+Extension.swift"; sourceTree = ""; }; D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertExpandableView+Extension.swift"; sourceTree = ""; }; + D2D2FCEF252B72AF0033EAAA /* MoleculeSectionFooterModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionFooterModel.swift; sourceTree = ""; }; + D2D2FCF2252B72CF0033EAAA /* MoleculeSectionFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionFooter.swift; sourceTree = ""; }; + D2D39579252FDBB300047B11 /* ModalSectionListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalSectionListTemplate.swift; sourceTree = ""; }; + D2D3957C252FDBCD00047B11 /* ModalSectionListTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalSectionListTemplateModel.swift; sourceTree = ""; }; D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scroller.swift; sourceTree = ""; }; D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplate.swift; sourceTree = ""; }; D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeHeaderModel.swift; sourceTree = ""; }; @@ -976,6 +999,9 @@ D2E2A99E23E07F8A000B42E6 /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = ""; }; D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModelProtocol.swift; sourceTree = ""; }; D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableableModelProtocol.swift; sourceTree = ""; }; + D2EC7BD42527B7A600F540AF /* MoleculeSectionHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionHeaderModel.swift; sourceTree = ""; }; + D2EC7BD82527B7CF00F540AF /* MoleculeSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionHeader.swift; sourceTree = ""; }; + D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderFooterView.swift; sourceTree = ""; }; D2FA83D12513EA6900564112 /* NotificationXButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationXButton.swift; sourceTree = ""; }; D2FA83D32514F80C00564112 /* CollapsableNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotification.swift; sourceTree = ""; }; D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationTopView.swift; sourceTree = ""; }; @@ -1589,6 +1615,8 @@ D2092348244A51D40044AD09 /* RadioSwatchModel.swift */, AAB9C109243496DD00151545 /* RadioSwatch.swift */, AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */, + AAA7CD68250641F90045B959 /* HeartModel.swift */, + AAA7CD6A250642080045B959 /* Heart.swift */, ); path = Selectors; sourceTree = ""; @@ -1691,6 +1719,10 @@ D264FAA4243F66A500D98315 /* CollectionTemplateItemProtocol.swift */, D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */, D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */, + D2169302251E53D9002A6324 /* SectionListTemplateModel.swift */, + D2169300251E51E7002A6324 /* SectionListTemplate.swift */, + D2D3957C252FDBCD00047B11 /* ModalSectionListTemplateModel.swift */, + D2D39579252FDBB300047B11 /* ModalSectionListTemplate.swift */, ); path = Templates; sourceTree = ""; @@ -1718,6 +1750,7 @@ D29DF10E21E67A77003B2FB9 /* Molecules */ = { isa = PBXGroup; children = ( + D2EC7BD22527A1E400F540AF /* HeadersAndFooters */, D2CAC7C9251104CB00C75681 /* TopNotification */, D2509ED42472EE0B001BFB9D /* NavigationBar */, D253BB9A24587023002DE544 /* OtherContainers */, @@ -1726,12 +1759,6 @@ D224798F2316A99F003FCCF9 /* LeftRightViews */, D224798E2316A995003FCCF9 /* HorizontalCombinationViews */, D224798D2316A988003FCCF9 /* VerticalCombinationViews */, - 01EB368C23609801006832FA /* HeaderModel.swift */, - D256E9922412880000360572 /* Header.swift */, - D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */, - D2A514662213885800345BFB /* MoleculeHeaderView.swift */, - 012A88EB238F084D00FE3DA1 /* FooterModel.swift */, - D274CA322236A78900B01B62 /* FooterView.swift */, D260105723CF9CC500764D80 /* Doughnut */, ); path = Molecules; @@ -2033,6 +2060,7 @@ 0A5D59C323AD488600EFD9E9 /* Protocols */, 0A7918F423F5E7EA00772FF4 /* ImageView.swift */, D272F5F82473163100BD1A8F /* BarButtonItem.swift */, + D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */, ); path = BaseClasses; sourceTree = ""; @@ -2077,8 +2105,10 @@ children = ( 012A88C7238DB02000FE3DA1 /* MoleculeDelegateProtocol.swift */, 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, + D20F3B43252E00E4004B3F56 /* PageProtocol.swift */, 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */, D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */, + D20F3B5D252F9B5D004B3F56 /* NavigationBarRefreshProtocol.swift */, 011B58EE23A2AA850085F53C /* ModelProtocols */, ); path = Protocols; @@ -2100,6 +2130,23 @@ path = TopNotification; sourceTree = ""; }; + D2EC7BD22527A1E400F540AF /* HeadersAndFooters */ = { + isa = PBXGroup; + children = ( + 01EB368C23609801006832FA /* HeaderModel.swift */, + D256E9922412880000360572 /* Header.swift */, + D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */, + D2A514662213885800345BFB /* MoleculeHeaderView.swift */, + 012A88EB238F084D00FE3DA1 /* FooterModel.swift */, + D274CA322236A78900B01B62 /* FooterView.swift */, + D2EC7BD42527B7A600F540AF /* MoleculeSectionHeaderModel.swift */, + D2EC7BD82527B7CF00F540AF /* MoleculeSectionHeader.swift */, + D2D2FCEF252B72AF0033EAAA /* MoleculeSectionFooterModel.swift */, + D2D2FCF2252B72CF0033EAAA /* MoleculeSectionFooter.swift */, + ); + path = HeadersAndFooters; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -2231,6 +2278,7 @@ 943784F5236B77BB006A1E82 /* Wheel.swift in Sources */, 31BE15CC23D8924D00452370 /* CheckboxModel.swift in Sources */, 8D3BA9BF2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift in Sources */, + AAA7CD6B250642080045B959 /* Heart.swift in Sources */, 94C661DA23CCF4FB00D9FE5B /* UIColor+Extension.swift in Sources */, D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */, D2509ED62472EE2F001BFB9D /* NavigationImageButtonModel.swift in Sources */, @@ -2250,9 +2298,11 @@ BBC0C4FF24811DCA0087C44F /* TagModel.swift in Sources */, 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, 3265B30424BCA749000D154B /* HeadersH1NoButtonsBodyText.swift in Sources */, + AAA7CD69250641F90045B959 /* HeartModel.swift in Sources */, D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */, D28BA7452481652D00B75CB8 /* TabBarProtocol.swift in Sources */, AA11A41F23F15D3100D7962F /* ListRightVariablePayments.swift in Sources */, + D2EC7BD92527B7CF00F540AF /* MoleculeSectionHeader.swift in Sources */, D28764AA2458980300CB882D /* ThreeLayerFillMiddleTemplate.swift in Sources */, 0116A4E5228B19640094F3ED /* RadioButtonSelectionHelper.swift in Sources */, D2092353244F7D630044AD09 /* MVMCoreUIViewControllerMappingObject+Extension.swift in Sources */, @@ -2268,6 +2318,7 @@ AA2AD116244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift in Sources */, AA7F32AD246C0F8C00C965BA /* ListLeftVariableRadioButtonAllTextAndLinks.swift in Sources */, D272F5F92473163100BD1A8F /* BarButtonItem.swift in Sources */, + D2D2FCF3252B72CF0033EAAA /* MoleculeSectionFooter.swift in Sources */, 0A9D09202433796500D2E6C0 /* BarsIndicatorView.swift in Sources */, D2E2A99423D8CCBC000B42E6 /* HeadlineBodyLinkModel.swift in Sources */, 01004F3022721C3800991ECC /* RadioButton.swift in Sources */, @@ -2345,6 +2396,7 @@ 525239C02407BCFF00454969 /* ListTwoColumnPriceDetailsModel.swift in Sources */, D2E2A99A23D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift in Sources */, D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */, + D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */, 8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */, 94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */, D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */, @@ -2422,11 +2474,13 @@ D260105323CEA61600764D80 /* ToggleModel.swift in Sources */, 014AA72523C501E2006F3E93 /* ContainerModel.swift in Sources */, 0A7EF86523D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift in Sources */, + D2EC7BD52527B7A600F540AF /* MoleculeSectionHeaderModel.swift in Sources */, AAA905DF24D1758700D1EFAB /* ListThreeColumnBillHistoryModel.swift in Sources */, D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */, 0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */, + D20F3B5E252F9B5E004B3F56 /* NavigationBarRefreshProtocol.swift in Sources */, D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */, D260105D23D0BCD400764D80 /* Stack.swift in Sources */, 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */, @@ -2434,6 +2488,7 @@ D2092349244A51D40044AD09 /* RadioSwatchModel.swift in Sources */, 0A775F2824893937009EFB58 /* ThreeHeadlineBodyLinkModel.swift in Sources */, 8DD1E370243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift in Sources */, + D2EC7BDD2527B83700F540AF /* SectionHeaderFooterView.swift in Sources */, D23EA802247EBED400D60C34 /* ImageBarButtonItem.swift in Sources */, AA45AA0B24BF0263007A6EA7 /* LockUpsPlanNamesModel.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, @@ -2526,6 +2581,7 @@ 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */, D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */, AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */, + D2D3957A252FDBB300047B11 /* ModalSectionListTemplate.swift in Sources */, DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */, D28BA741248025A300B75CB8 /* TabBarModel.swift in Sources */, D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */, @@ -2540,6 +2596,7 @@ D2092355244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift in Sources */, 0AE14F64238315D2005417F8 /* TextField.swift in Sources */, 0A51F3E22475CB73002E08B6 /* LoadingSpinnerModel.swift in Sources */, + D2169303251E53D9002A6324 /* SectionListTemplateModel.swift in Sources */, 0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */, BB105859248DEFF70069D008 /* UICollectionViewLeftAlignedLayout.swift in Sources */, D253BB9C245874F8002DE544 /* BGImageMolecule.swift in Sources */, @@ -2623,6 +2680,7 @@ AA104B1A24474A66004D2810 /* HeadersH2Buttons.swift in Sources */, C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */, D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */, + D2D2FCF0252B72AF0033EAAA /* MoleculeSectionFooterModel.swift in Sources */, BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */, D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */, 0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */, @@ -2636,6 +2694,7 @@ D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */, BB6C6AC924225290005F7224 /* ListOneColumnTextWithWhitespaceDividerShortModel.swift in Sources */, C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */, + D2D3957D252FDBCD00047B11 /* ModalSectionListTemplateModel.swift in Sources */, 8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */, D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */, 012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */, @@ -2665,6 +2724,7 @@ AA0A257A24766CA200862F64 /* ListLeftVariableIconWithRightCaretBodyText.swift in Sources */, 011D95AD2406BB57000E3791 /* FormHolderProtocol.swift in Sources */, 01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */, + D2169301251E51E7002A6324 /* SectionListTemplate.swift in Sources */, 0A6682AA2435125F00AD3CA1 /* Styler.swift in Sources */, D264FAA7243FE13B00D98315 /* RadioBox.swift in Sources */, ); diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift new file mode 100644 index 00000000..796bf617 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift @@ -0,0 +1,114 @@ +// +// Heart.swift +// MVMCoreUI +// +// Created by Lekshmi S on 07/09/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +@objcMembers open class Heart: Control, MFButtonProtocol { + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + private var heartLayer: CAShapeLayer? + @objc public override var isSelected: Bool { + didSet { + heartModel?.isActive = isSelected + updateAccessibilityLabel() + } + } + public var delegateObject: MVMCoreUIDelegateObject? + public var heartModel: HeartModel? { + return model as? HeartModel + } + var additionalData: [AnyHashable: Any]? + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + public var widthConstraint: NSLayoutConstraint? + public var heightConstraint: NSLayoutConstraint? + + //------------------------------------------------------ + // MARK: - State Handling + //------------------------------------------------------ + open override func draw(_ rect: CGRect) { + //Draw the heart + heartLayer?.removeFromSuperlayer() + let heart = drawHeart() + layer.addSublayer(heart) + heartLayer = heart + } + + func drawHeart() -> CAShapeLayer { + let heart = CAShapeLayer() + let rect = bounds + let leftArc = rect.width * 0.4 + let rightArc = rect.height * 0.3 + let arcRadius = sqrt(leftArc*leftArc + rightArc*rightArc)/2 + let heartPath = UIBezierPath() + //Left Hand Curve + heartPath.addArc(withCenter: CGPoint(x: rect.width * 0.3, y: rect.height * 0.35), radius: arcRadius, startAngle: 135.degreesToRadians, endAngle: 315.degreesToRadians, clockwise: true) + //Top Centre Dip + heartPath.addLine(to: CGPoint(x: rect.width/2, y: rect.height * 0.2)) + //Right Hand Curve + heartPath.addArc(withCenter: CGPoint(x: rect.width * 0.7, y: rect.height * 0.35), radius: arcRadius, startAngle: 225.degreesToRadians, endAngle: 45.degreesToRadians, clockwise: true) + //Right Bottom Line + heartPath.addLine(to: CGPoint(x: rect.width * 0.5, y: rect.height * 0.95)) + //Left Bottom Line + heartPath.close() + heart.path = heartPath.cgPath + heart.fillColor = isSelected ? heartModel?.activeColor.cgColor : heartModel?.inActiveColor.cgColor + heart.opacity = 1.0 + heart.lineWidth = 1 + heart.strokeColor = isSelected ? UIColor.clear.cgColor : UIColor.mvmBlack.cgColor + return heart + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func setupView() { + super.setupView() + addTarget(self, action: #selector(tapAction), for: .touchUpInside) + widthConstraint = widthAnchor.constraint(equalToConstant: 22) + widthConstraint?.isActive = true + heightConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1) + heightConstraint?.isActive = true + isAccessibilityElement = true + accessibilityTraits = .button + updateAccessibilityLabel() + } + + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + self.delegateObject = delegateObject + self.additionalData = additionalData + guard let model = model as? HeartModel else { return } + isSelected = model.isActive + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + /// Adjust accessibility label based on selection of Heart. + func updateAccessibilityLabel() { + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "heart_unfavorite_action_hint" : "heart_favorite_action_hint") + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "heart_selected_state" : "heart_not_selected_state") + } + + func tapAction() { + isSelected = !isSelected + if let heartModel = heartModel { + Button.performButtonAction(with: heartModel.action, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: heartModel) + } + setNeedsDisplay() + } +} + +extension Int { + var degreesToRadians: CGFloat { return CGFloat(self) * .pi / 180 } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift new file mode 100644 index 00000000..8001ddd5 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/HeartModel.swift @@ -0,0 +1,65 @@ +// +// HeartModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 07/09/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class HeartModel: MoleculeModelProtocol { + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "heart" + public var backgroundColor: Color? + public var isActive: Bool = false + public var activeColor: Color = Color(uiColor: .mvmRed) + public var inActiveColor: Color = Color(uiColor: .clear) + public var action: ActionModelProtocol = ActionNoopModel() + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case isActive + case activeColor + case inActiveColor + case action + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + if let isActive = try typeContainer.decodeIfPresent(Bool.self, forKey: .isActive) { + self.isActive = isActive + } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + if let activeColor = try typeContainer.decodeIfPresent(Color.self, forKey: .activeColor) { + self.activeColor = activeColor + } + if let inActiveColor = try typeContainer.decodeIfPresent(Color.self, forKey: .inActiveColor) { + self.inActiveColor = inActiveColor + } + if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) { + self.action = action + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(isActive, forKey: .isActive) + try container.encode(activeColor, forKey: .activeColor) + try container.encode(inActiveColor, forKey: .inActiveColor) + try container.encodeModel(action, forKey: .action) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index c9f676a8..efbe3aad 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -322,8 +322,11 @@ public typealias ActionBlock = () -> () for attribute in attributes { let range = NSRange(location: attribute.location, length: attribute.length) switch attribute { - case _ as LabelAttributeUnderlineModel: - attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range) + case let underlineAtt as LabelAttributeUnderlineModel: + attributedString.addAttribute(.underlineStyle, value: underlineAtt.underlineValue.rawValue, range: range) + if let underlineColor = underlineAtt.color?.uiColor { + attributedString.addAttribute(.underlineColor, value: underlineColor, range: range) + } case _ as LabelAttributeStrikeThroughModel: attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift index 597b2491..a3adbe00 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeUnderlineModel.swift @@ -6,6 +6,8 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // +import UIKit + @objcMembers public class LabelAttributeUnderlineModel: LabelAttributeModel { //-------------------------------------------------- @@ -16,15 +18,104 @@ return "underline" } + /// This returns the NSUnderlineStyle used in NSAttributedValue. If there is a pattern, it will return + /// a new NSUnderlineStyle derived from the bitmask of style | pattern. + var underlineValue: NSUnderlineStyle { + + if let pattern = pattern?.value() { + return NSUnderlineStyle(rawValue: style.value() | pattern) + } else { + return NSUnderlineStyle(rawValue: style.value()) + } + } + + var color: Color? + var style: UnderlineStyle = .single + var pattern: UnderlineStyle.Pattern? + //-------------------------------------------------- - // MARK: - Codec + // MARK: - Keys //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { + case color + case style + case pattern + } + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) { + self.color = color + } + + if let style = try typeContainer.decodeIfPresent(UnderlineStyle.self, forKey: .style) { + self.style = style + } + + if let pattern = try typeContainer.decodeIfPresent(UnderlineStyle.Pattern.self, forKey: .pattern) { + self.pattern = pattern + } + 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(color, forKey: .color) + try container.encode(style, forKey: .style) + try container.encodeIfPresent(pattern, forKey: .pattern) + } +} + +public enum UnderlineStyle: String, Codable { + case none + case single + case thick + case double + + func value() -> Int { + switch self { + case .none: + return 0 + + case .single: + return NSUnderlineStyle.single.rawValue + + case .thick: + return NSUnderlineStyle.thick.rawValue + + case .double: + return NSUnderlineStyle.double.rawValue + } + } + + public enum Pattern: String, Codable { + case dot + case dash + case dashDot + case dashDotDot + case byWord + + func value() -> Int { + switch self { + case .dot: + return NSUnderlineStyle.patternDot.rawValue + + case .dash: + return NSUnderlineStyle.patternDash.rawValue + + case .dashDot: + return NSUnderlineStyle.patternDashDot.rawValue + + case .dashDotDot: + return NSUnderlineStyle.patternDashDotDot.rawValue + + case .byWord: + return NSUnderlineStyle.byWord.rawValue + } + } } } diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index 9ebb8cfb..e11c782a 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -35,12 +35,12 @@ import Foundation } /// Convenience function for legacy classes - public func getMoleculeModelForJSON(_ json: [String: Any]) throws -> MoleculeModelProtocol? { + public func getMoleculeModelForJSON(_ json: [String: Any], delegateObject: DelegateObject? = nil) throws -> MoleculeModelProtocol? { guard let moleculeName = json.optionalStringForKey(KeyMoleculeName), let type = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) else { throw ModelRegistry.Error.decoderErrorModelNotMapped() } - guard let model = try type.decode(jsonDict: json) as? MoleculeModelProtocol else { + guard let model = try type.decode(jsonDict: json, delegateObject: delegateObject) as? MoleculeModelProtocol else { throw ModelRegistry.Error.decoderError } return model @@ -87,7 +87,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: RadioSwatches.self, viewModelClass: RadioSwatchesModel.self) MoleculeObjectMapping.shared()?.register(viewClass: Tags.self, viewModelClass: TagsModel.self) MoleculeObjectMapping.shared()?.register(viewClass: Tag.self, viewModelClass: TagModel.self) - + MoleculeObjectMapping.shared()?.register(viewClass: Heart.self, viewModelClass: HeartModel.self) // MARK:- Other Atoms @@ -149,6 +149,9 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: Scroller.self, viewModelClass: ScrollerModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ModuleMolecule.self, viewModelClass: ModuleMoleculeModel.self) MoleculeObjectMapping.shared()?.register(viewClass: BGImageMolecule.self, viewModelClass: BGImageMoleculeModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: MoleculeSectionHeader.self, viewModelClass: MoleculeSectionHeaderModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: MoleculeSectionFooter.self, viewModelClass: MoleculeSectionFooterModel.self) + // MARK:- Other Molecules MoleculeObjectMapping.shared()?.register(viewClass: DoughnutChartView.self, viewModelClass: DoughnutChartModel.self) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift index 986ee73b..7a9e8b7e 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift @@ -77,7 +77,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 120 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift index e8d4df30..7f47929f 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift @@ -75,7 +75,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 120 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift index 9f469d28..79530eb6 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift @@ -94,7 +94,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 120 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift index c9368057..3db13a01 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillChanges.swift @@ -59,7 +59,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillHistory.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillHistory.swift index 98ec315a..b80c1ac6 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillHistory.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnBillHistory.swift @@ -55,7 +55,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift index 24da6877..5ebaf0f6 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift @@ -58,7 +58,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnSpeedTest.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnSpeedTest.swift index ba7c8794..64a7cd9f 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnSpeedTest.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnSpeedTest.swift @@ -64,7 +64,7 @@ updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift index 10c4511f..9f7895af 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift @@ -90,7 +90,7 @@ rightHeadlineBodyLink.body.setFontStyle(.RegularBodySmall) } - public override class func estimatedHeight(with molecule: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(with molecule: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnDropdownSelectors.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnDropdownSelectors.swift index 6b77e709..eb8fb0ea 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnDropdownSelectors.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnDropdownSelectors.swift @@ -54,7 +54,7 @@ rightDropDown.set(with:model.rightDropDown, delegateObject, additionalData) } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift index 95b512b0..3e67b86f 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift @@ -59,7 +59,7 @@ rightLabel.setFontStyle(.RegularBodySmall) } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 15 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift index 96c72865..f97fea4a 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/FourColumn/ListFourColumnDataUsageDivider.swift @@ -66,7 +66,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift index 3ed2065e..3f48c3d8 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsection.swift @@ -53,9 +53,7 @@ import Foundation super.set(with: model, delegateObject, additionalData) guard let model = model as? ListOneColumnFullWidthTextDividerSubsectionModel else { return } - - headline.set(with: model.headline, delegateObject, additionalData) - body.set(with: model.body, delegateObject, additionalData) + stack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, additionalData) updateAccessibilityLabel() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift index 063c8cfa..5040fbd4 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnFullWidthTextDividerSubsectionModel.swift @@ -16,13 +16,13 @@ public class ListOneColumnFullWidthTextDividerSubsectionModel: ListItemModel, Mo public static var identifier: String = "list1CTxtDiv3" public var headline: LabelModel - public var body: LabelModel + public var body: LabelModel? //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- - public init(headline: LabelModel, body: LabelModel) { + public init(headline: LabelModel, body: LabelModel?) { self.headline = headline self.body = body super.init() @@ -54,7 +54,7 @@ public class ListOneColumnFullWidthTextDividerSubsectionModel: ListItemModel, Mo required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) headline = try typeContainer.decode(LabelModel.self, forKey: .headline) - body = try typeContainer.decode(LabelModel.self, forKey: .body) + body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) try super.init(from: decoder) } @@ -63,6 +63,6 @@ public class ListOneColumnFullWidthTextDividerSubsectionModel: ListItemModel, Mo var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(headline, forKey: .headline) - try container.encode(body, forKey: .body) + try container.encodeIfPresent(body, forKey: .body) } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift index 02d320a9..2f023b78 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShort.swift @@ -53,9 +53,7 @@ import Foundation super.set(with: model, delegateObject, additionalData) guard let model = model as? ListOneColumnTextWithWhitespaceDividerShortModel else { return } - - headline.set(with: model.headline, delegateObject, additionalData) - body.set(with: model.body, delegateObject, additionalData) + stack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, additionalData) updateAccessibilityLabel() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift index f6303a85..66025760 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerShortModel.swift @@ -16,13 +16,13 @@ public class ListOneColumnTextWithWhitespaceDividerShortModel: ListItemModel, Mo public static var identifier: String = "list1CTxtDiv1" public var headline: LabelModel - public var body: LabelModel + public var body: LabelModel? //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- - public init(headline: LabelModel, body: LabelModel) { + public init(headline: LabelModel, body: LabelModel?) { self.headline = headline self.body = body super.init() @@ -54,7 +54,7 @@ public class ListOneColumnTextWithWhitespaceDividerShortModel: ListItemModel, Mo required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) headline = try typeContainer.decode(LabelModel.self, forKey: .headline) - body = try typeContainer.decode(LabelModel.self, forKey: .body) + body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) try super.init(from: decoder) } @@ -63,6 +63,6 @@ public class ListOneColumnTextWithWhitespaceDividerShortModel: ListItemModel, Mo var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(headline, forKey: .headline) - try container.encode(body, forKey: .body) + try container.encodeIfPresent(body, forKey: .body) } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift index 704057db..62ee43f5 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTall.swift @@ -52,9 +52,7 @@ import Foundation super.set(with: model, delegateObject, additionalData) guard let model = model as? ListOneColumnTextWithWhitespaceDividerTallModel else { return } - - headline.set(with: model.headline, delegateObject, additionalData) - body.set(with: model.body, delegateObject, additionalData) + stack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, additionalData) updateAccessibilityLabel() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift index e3bb634d..c713b0d2 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/OneColumn/ListOneColumnTextWithWhitespaceDividerTallModel.swift @@ -16,13 +16,13 @@ public class ListOneColumnTextWithWhitespaceDividerTallModel: ListItemModel, Mol public static var identifier: String = "list1CTxtDiv2" public var headline: LabelModel - public var body: LabelModel + public var body: LabelModel? //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- - public init(headline: LabelModel, body: LabelModel) { + public init(headline: LabelModel, body: LabelModel?) { self.headline = headline self.body = body super.init() @@ -54,7 +54,7 @@ public class ListOneColumnTextWithWhitespaceDividerTallModel: ListItemModel, Mol required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) headline = try typeContainer.decode(LabelModel.self, forKey: .headline) - body = try typeContainer.decode(LabelModel.self, forKey: .body) + body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) try super.init(from: decoder) } @@ -63,6 +63,6 @@ public class ListOneColumnTextWithWhitespaceDividerTallModel: ListItemModel, Mol var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(headline, forKey: .headline) - try container.encode(body, forKey: .body) + try container.encodeIfPresent(body, forKey: .body) } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift index c4a42835..32425fca 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillChangesDivider.swift @@ -62,7 +62,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 120 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillHistoryDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillHistoryDivider.swift index 1246e98c..64830441 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillHistoryDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnBillHistoryDivider.swift @@ -49,7 +49,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift index 7fe35b92..dd2ecc03 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnDataUsageDivider.swift @@ -61,7 +61,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift index 1d842266..51913799 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift @@ -60,7 +60,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift index 9e5c2f05..c75c77df 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnPlanDataDivider.swift @@ -62,7 +62,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift index 91a86643..c23a5cdc 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnSpeedTestDivider.swift @@ -61,7 +61,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift index b3972ae6..ddcc27c1 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/TwoColumn/ListTwoColumnSubsectionDivider.swift @@ -58,7 +58,7 @@ import Foundation updateAccessibilityLabel() } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 121 } diff --git a/MVMCoreUI/Atomic/Molecules/FooterModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/FooterModel.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/FooterModel.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/FooterModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/FooterView.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/FooterView.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/FooterView.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/FooterView.swift diff --git a/MVMCoreUI/Atomic/Molecules/Header.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/Header.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/Header.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/Header.swift diff --git a/MVMCoreUI/Atomic/Molecules/HeaderModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/HeaderModel.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/HeaderModel.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/HeaderModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/MoleculeHeaderModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderModel.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/MoleculeHeaderModel.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderModel.swift diff --git a/MVMCoreUI/Atomic/Molecules/MoleculeHeaderView.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderView.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/MoleculeHeaderView.swift rename to MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeHeaderView.swift diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift new file mode 100644 index 00000000..33caba83 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooter.swift @@ -0,0 +1,56 @@ +// +// MoleculeSectionFooter.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/5/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class MoleculeSectionFooter: SectionHeaderFooterView { + open var molecule: MoleculeViewProtocol? + public let containerHelper = ContainerHelper() + + /// Adds the molecule to the view. + open func addMolecule(_ molecule: MoleculeViewProtocol) { + contentView.addSubview(molecule) + containerHelper.constrainView(molecule) + self.molecule = molecule + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + containerHelper.updateViewMargins(self, model: model as? MoleculeSectionFooterModel, size: size) + contentView.directionalLayoutMargins = directionalLayoutMargins + (molecule as? MVMCoreViewProtocol)?.updateView(size) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? MoleculeSectionFooterModel else { return } + if molecule != nil { + molecule?.set(with: model.molecule, delegateObject, additionalData) + } else if let moleculeView = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: additionalData) { + addMolecule(moleculeView) + } + containerHelper.set(with: model, for: molecule as? MVMCoreUIViewConstrainingProtocol) + } + + open override func reset() { + super.reset() + molecule?.reset() + } + + public override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + return MoleculeContainer.nameForReuse(with: model, delegateObject) + } + + public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + return MoleculeContainer.requiredModules(with: model, delegateObject, error: error) + } + + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return MoleculeContainer.estimatedHeight(with: model, delegateObject) ?? 80.0 + } +} diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooterModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooterModel.swift new file mode 100644 index 00000000..edf9fc7a --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionFooterModel.swift @@ -0,0 +1,31 @@ +// +// MoleculeSectionFooterModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/5/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class MoleculeSectionFooterModel: MoleculeContainerModel, SectionListHeaderFooterModel { + public class override var identifier: String { + return "sectionFooter" + } + + /// Defaults to set + public override func setDefaults() { + if useHorizontalMargins == nil { + useHorizontalMargins = true + } + if useVerticalMargins == nil { + useVerticalMargins = true + } + if topPadding == nil { + topPadding = PaddingDefaultVerticalSpacing3 + } + if bottomPadding == nil { + bottomPadding = PaddingDefaultVerticalSpacing3 + } + } +} diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift new file mode 100644 index 00000000..16aaacfe --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeader.swift @@ -0,0 +1,69 @@ +// +// MoleculeSectionHeader.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/2/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class MoleculeSectionHeader: SectionHeaderFooterView { + open var molecule: MoleculeViewProtocol? + public let containerHelper = ContainerHelper() + public let line = Line() + + /// Adds the molecule to the view. + open func addMolecule(_ molecule: MoleculeViewProtocol) { + contentView.addSubview(molecule) + containerHelper.constrainView(molecule) + self.molecule = molecule + } + + public override func setupView() { + super.setupView() + line.setStyle(.thin) + contentView.addSubview(line) + NSLayoutConstraint.pinViewBottom(toSuperview: line, useMargins: false, constant: 0).isActive = true + NSLayoutConstraint.pinViewLeft(toSuperview: line, useMargins: true, constant: 0).isActive = true + NSLayoutConstraint.pinViewRight(toSuperview: line, useMargins: true, constant: 0).isActive = true + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + containerHelper.updateViewMargins(self, model: model as? MoleculeSectionHeaderModel, size: size) + contentView.directionalLayoutMargins = directionalLayoutMargins + (molecule as? MVMCoreViewProtocol)?.updateView(size) + line.updateView(size) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? MoleculeSectionHeaderModel else { return } + if molecule != nil { + molecule?.set(with: model.molecule, delegateObject, additionalData) + } else if let moleculeView = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: additionalData) { + addMolecule(moleculeView) + } + containerHelper.set(with: model, for: molecule as? MVMCoreUIViewConstrainingProtocol) + line.setOptional(with: model.line, delegateObject, additionalData) + } + + open override func reset() { + super.reset() + line.setStyle(.thin) + molecule?.reset() + } + + public override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + return MoleculeContainer.nameForReuse(with: model, delegateObject) + } + + public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + return MoleculeContainer.requiredModules(with: model, delegateObject, error: error) + } + + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return MoleculeContainer.estimatedHeight(with: model, delegateObject) ?? 80.0 + } +} diff --git a/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeaderModel.swift b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeaderModel.swift new file mode 100644 index 00000000..ef6b057e --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/HeadersAndFooters/MoleculeSectionHeaderModel.swift @@ -0,0 +1,52 @@ +// +// MoleculeSectionHeaderModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/2/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class MoleculeSectionHeaderModel: MoleculeContainerModel, SectionListHeaderFooterModel { + public class override var identifier: String { + return "sectionHeader" + } + public var line: LineModel? + + private enum CodingKeys: String, CodingKey { + case line + case backgroundColor + } + + /// Defaults to set + public override func setDefaults() { + if useHorizontalMargins == nil { + useHorizontalMargins = true + } + if useVerticalMargins == nil { + useVerticalMargins = true + } + if topPadding == nil { + topPadding = PaddingDefaultVerticalSpacing3 + } + if bottomPadding == nil { + bottomPadding = PaddingDefaultVerticalSpacing3 + } + if line == nil { + line = LineModel(type: .thin) + } + } + + required public init(from decoder: Decoder) throws { + try super.init(from: decoder) + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(line, forKey: .line) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift index bf3714bd..f6fe580f 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift @@ -34,26 +34,14 @@ open class MoleculeCollectionViewCell: CollectionViewCell { } open override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - guard let moleculeModel = (model as? MoleculeCollectionItemModel)?.molecule else { return "\(self)<>" } - let className = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) - let moleculeName = className?.nameForReuse(with: moleculeModel, delegateObject) ?? moleculeModel.moleculeName - return "\(self)<\(moleculeName)>" + return MoleculeContainer.nameForReuse(with: model, delegateObject) } open override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - guard let moleculeModel = (model as? MoleculeCollectionItemModel)?.molecule, - let theClass = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) - else { return nil } - - return theClass.requiredModules(with: moleculeModel, delegateObject, error: error) + return MoleculeContainer.requiredModules(with: model, delegateObject, error: error) } - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - guard let model = model as? MoleculeCollectionItemModel, - let classType = MoleculeObjectMapping.shared()?.getMoleculeClass(model.molecule), - let height = classType.estimatedHeight(with: model.molecule, delegateObject) - else { return 100 } - - return height + (model.topPadding ?? 0) + (model.bottomPadding ?? 0) + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return MoleculeContainer.estimatedHeight(with: model, delegateObject) ?? 100 } } diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeListItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeListItemModel.swift index bee9d612..1028362e 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeListItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeListItemModel.swift @@ -10,7 +10,7 @@ import Foundation import MVMCore -@objcMembers public class MoleculeListItemModel: ListItemModel, MoleculeModelProtocol { +@objcMembers public class MoleculeListItemModel: ListItemModel, MoleculeContainerModelProtocol, MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift index 8464b520..ee2a2790 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeStackItemModel.swift @@ -27,6 +27,7 @@ case spacing case percent case gone + case moleculeName } //-------------------------------------------------- @@ -54,6 +55,7 @@ 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(spacing, forKey: .spacing) try container.encodeIfPresent(percent, forKey: .percent) try container.encode(gone, forKey: .gone) diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift index 532c21f4..92d04bed 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeTableViewCell.swift @@ -30,28 +30,16 @@ import UIKit } public override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - guard let moleculeModel = (model as? MoleculeListItemModel)?.molecule else { return "\(self)<>" } - - let className = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) - let moleculeName = className?.nameForReuse(with: moleculeModel, delegateObject) ?? moleculeModel.moleculeName - - return "\(self)<\(moleculeName)>" + return MoleculeContainer.nameForReuse(with: model, delegateObject) } public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { - guard let moleculeModel = (model as? MoleculeListItemModel)?.molecule, - let theClass = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel) - else { return nil } - - return theClass.requiredModules(with: moleculeModel, delegateObject, error: error) + return MoleculeContainer.requiredModules(with: model, delegateObject, error: error) } - public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - guard let moleculeModel = (model as? MoleculeListItemModel)?.molecule, - let classType = MoleculeObjectMapping.shared()?.getMoleculeClass(moleculeModel), - let height = classType.estimatedHeight(with: moleculeModel, delegateObject) + public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + guard let height = MoleculeContainer.estimatedHeight(with: model, delegateObject) else { return 80 } - - return max(2 * PaddingDefaultVerticalSpacing3, height) + return height } } diff --git a/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift index be3ac62e..3f8ff8a7 100644 --- a/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/CornerLabels.swift @@ -157,7 +157,7 @@ import UIKit bottomRightLabel.setFontStyle(.RegularMicro) } - public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 34 } diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index d8c8c7ac..15ef632e 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -8,25 +8,25 @@ import Foundation -public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { - public class var identifier: String { +open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { + open class var identifier: String { return "navigationBar" } - public var title: String? - public var hidden: Bool - public var backgroundColor: Color? - public var tintColor: Color - public var line: LineModel? - public var hidesSystemBackButton = true + open var title: String? + open var hidden: Bool + open var backgroundColor: Color? + open var tintColor: Color + open var line: LineModel? + open var hidesSystemBackButton = true /// If true, we add the button in the backButton property. If false we do not add the button. If nil, we add the button if the controller is not the bottom of the stack - public var alwaysShowBackButton: Bool? - public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? + open var alwaysShowBackButton: Bool? + open var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? - public var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? - public var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? - public var titleView: MoleculeModelProtocol? + open var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? + open var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? + open var titleView: MoleculeModelProtocol? public init() { hidden = false @@ -44,8 +44,6 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt case line case alwaysShowBackButton case backButton - case showLeftPanelButton - case showRightPanelButton case additionalLeftButtons case additionalRightButtons case titleView diff --git a/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainer.swift b/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainer.swift index b5d78c85..bc50deba 100644 --- a/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainer.swift +++ b/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainer.swift @@ -12,12 +12,11 @@ import UIKit open class MoleculeContainer: Container { /// Can be overriden to change how the molecule is added to the hierarchy. - public func addMolecule(_ molecule: UIView) { + open func addMolecule(_ molecule: UIView) { addAndContain(molecule) } - public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { if let casteModel = model as? MoleculeContainerModelProtocol { if view != nil { (view as? MoleculeViewProtocol)?.set(with: casteModel.molecule, delegateObject, additionalData) diff --git a/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainerModel.swift b/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainerModel.swift index aa847f88..77b40716 100644 --- a/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainerModel.swift +++ b/MVMCoreUI/Atomic/Molecules/OtherContainers/MoleculeContainerModel.swift @@ -9,7 +9,7 @@ import Foundation open class MoleculeContainerModel: ContainerModel, MoleculeContainerModelProtocol, MoleculeModelProtocol { - public class var identifier: String { + open class var identifier: String { return "container" } public var backgroundColor: Color? @@ -41,7 +41,7 @@ open class MoleculeContainerModel: ContainerModel, MoleculeContainerModelProtoco try super.init(from: decoder) } - public override func encode(to encoder: Encoder) throws { + open 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) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift index 804bfeb6..17b1aef7 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotificationModel.swift @@ -8,7 +8,7 @@ import Foundation -public class CollapsableNotificationModel: NotificationModel { +open class CollapsableNotificationModel: NotificationModel { public class override var identifier: String { return "collapsableNotification" } @@ -19,12 +19,12 @@ public class CollapsableNotificationModel: NotificationModel { public var initiallyCollapsed = false public var pages: [String]? - init(with topLabel: LabelModel, headline: LabelModel) { + public init(with topLabel: LabelModel, headline: LabelModel) { self.topLabel = topLabel super.init(with: headline) } - override func setDefault() { + open override func setDefault() { super.setDefault() if topLabel.textColor == nil { topLabel.textColor = Color(uiColor: .white) @@ -61,7 +61,7 @@ public class CollapsableNotificationModel: NotificationModel { try super.init(from: decoder) } - public override func encode(to encoder: Encoder) throws { + open 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) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift index e2f94326..70ff00bb 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift @@ -8,7 +8,7 @@ import Foundation -public class NotificationModel: MoleculeModelProtocol { +open class NotificationModel: MoleculeModelProtocol { public class var identifier: String { return "notification" } @@ -18,11 +18,11 @@ public class NotificationModel: MoleculeModelProtocol { public var button: ButtonModel? public var closeButton: NotificationXButtonModel? - init(with headline: LabelModel) { + public init(with headline: LabelModel) { self.headline = headline } - func setDefault() { + open func setDefault() { if backgroundColor == nil { backgroundColor = Color(uiColor: .mvmGreen()) } @@ -59,7 +59,7 @@ public class NotificationModel: MoleculeModelProtocol { setDefault() } - public func encode(to encoder: Encoder) throws { + open func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift index 550434a8..9922454b 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyLink.swift @@ -42,6 +42,7 @@ headlineBody.topAnchor.constraint(equalTo: topAnchor).isActive = true headlineBody.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + rightAnchor.constraint(greaterThanOrEqualTo: headlineBody.rightAnchor).isActive = true var constraint = rightAnchor.constraint(equalTo: headlineBody.rightAnchor) constraint.priority = .defaultHigh constraint.isActive = true diff --git a/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift b/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift new file mode 100644 index 00000000..2d4002e3 --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/NavigationBarRefreshProtocol.swift @@ -0,0 +1,34 @@ +// +// NavigationBarRefreshProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/8/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +/// A protocol to inform that we should refresh the navigation bar ui. +@objc public protocol NavigationBarRefreshProtocol { + @objc func refreshNavigationUI() +} + +extension UIViewController: NavigationBarRefreshProtocol { + + /// Convenience function to refresh the navigation bar ui. A separate function for others to use. + @objc public static func refreshNavigationUI(for viewController: UIViewController) { + guard let model = (viewController as? PageProtocol)?.pageModel?.navigationBar else { return } + if let navigationController = viewController.navigationController { + NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: model, viewController: viewController) + MVMCoreUISplitViewController.setNavigationBarUI(for: viewController, navigationController: navigationController, navigationItemModel: model) + } + if let manager = ((viewController as? MVMCoreViewManagerViewControllerProtocol)?.manager as? NavigationBarRefreshProtocol) { + // Refresh the manager if possible. + manager.refreshNavigationUI() + } + } + + public func refreshNavigationUI() { + UIViewController.refreshNavigationUI(for: self) + } +} diff --git a/MVMCoreUI/Atomic/Protocols/PageProtocol.swift b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift new file mode 100644 index 00000000..4117604e --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/PageProtocol.swift @@ -0,0 +1,13 @@ +// +// PageProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/7/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol PageProtocol { + var pageModel: PageModelProtocol? { get set } +} diff --git a/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift b/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift index 4696ce38..04bc080b 100644 --- a/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift @@ -23,7 +23,7 @@ public extension TemplateProtocol where Self: ViewController { let decoder = JSONDecoder() try decoder.add(delegateObject: delegateObjectIVar) self.templateModel = try decodeTemplate(using: decoder, from: data) - self.pageModel = templateModel as? MVMControllerModelProtocol + self.model = templateModel as? MVMControllerModelProtocol } func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> TemplateModel { diff --git a/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift b/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift index b2b52573..14be7809 100644 --- a/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift @@ -18,6 +18,15 @@ import Foundation } public var molecules: [ListItemModelProtocol & MoleculeModelProtocol]? public var line: LineModel? + + /// This template requires content. + func validateModelHasContent() throws { + if header == nil, + molecules?.count ?? 0 == 0, + footer == nil { + throw ModelRegistry.Error.decoderOther(message: "List template requires atleast one of the following: header, footer, molecules") + } + } //-------------------------------------------------- // MARK: - Initializer @@ -47,6 +56,7 @@ import Foundation molecules = try typeContainer.decodeModelsIfPresent(codingKey: .molecules) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) try super.init(from: decoder) + try validateModelHasContent() } open override func encode(to encoder: Encoder) throws { diff --git a/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift new file mode 100644 index 00000000..77ee705c --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift @@ -0,0 +1,30 @@ +// +// ModalSectionListTemplate.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/8/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class ModalSectionListTemplate: SectionListTemplate { + + // For subclassing the model. + open override func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> ModalSectionListTemplateModel { + return try decoder.decode(ModalSectionListTemplateModel.self, from: data) + } + + override open func handleNewData() { + super.handleNewData() + _ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: {[weak self] _ in + guard let self = self else { return } + guard let model = self.templateModel as? ModalSectionListTemplateModel, + let actionMap = model.closeAction else { + MVMCoreActionHandler.shared()?.handleAction(with: ActionBackModel().toJSON(), additionalData: nil, delegateObject: self.delegateObjectIVar) + return + } + MVMCoreActionHandler.shared()?.handleAction(with: actionMap.toJSON(), additionalData: nil, delegateObject: self.delegateObjectIVar) + }) + } +} diff --git a/MVMCoreUI/Atomic/Templates/ModalSectionListTemplateModel.swift b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplateModel.swift new file mode 100644 index 00000000..83f2bb98 --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplateModel.swift @@ -0,0 +1,32 @@ +// +// ModalSectionListTemplateModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/8/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class ModalSectionListTemplateModel: SectionListTemplateModel { + public override class var identifier: String { + return "modalSectionList" + } + public var closeAction: ActionModelProtocol? + + private enum CodingKeys: String, CodingKey { + case closeAction + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + closeAction = try typeContainer.decodeModelIfPresent(codingKey: .closeAction) + 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.encodeModelIfPresent(closeAction, forKey: .closeAction) + } +} diff --git a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift index 12f9ce4d..57760022 100644 --- a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift @@ -14,24 +14,15 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol // MARK: - Stored Properties //-------------------------------------------------- - public var moleculesInfo: [(identifier: String, class: AnyClass, molecule: (ListItemModelProtocol & MoleculeModelProtocol))]? + public var moleculesInfo: [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)]? var observer: NSKeyValueObservation? public var templateModel: ListPageTemplateModel? - + //-------------------------------------------------- // MARK: - Computed Properties //-------------------------------------------------- - open override func parsePageJSON() throws { - try parseTemplate(json: loadObject?.pageJSON) - try super.parsePageJSON() - } - - // For subclassing the model. - open func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> ListPageTemplateModel { - return try decoder.decode(ListPageTemplateModel.self, from: data) - } open override var loadObject: MVMCoreLoadObject? { didSet { @@ -50,6 +41,16 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- + + open override func parsePageJSON() throws { + try parseTemplate(json: loadObject?.pageJSON) + try super.parsePageJSON() + } + + // For subclassing the model. + open func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> ListPageTemplateModel { + return try decoder.decode(ListPageTemplateModel.self, from: data) + } open override func viewForTop() -> UIView { guard let headerModel = templateModel?.header, @@ -72,21 +73,6 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol return molecule } - open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { - - guard super.shouldFinishProcessingLoad(loadObject, error: error) else { return false } - - // This template requires atleast one of the three layers. - if templateModel?.header == nil, - templateModel?.molecules?.count ?? 0 == 0, - templateModel?.footer == nil, - let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), messageToLog: "List template requires atleast one of the following: header, footer, molecules", code: CoreUIErrorCode.ErrorCodeListMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) { - error.pointee = errorObject - return false - } - return true - } - open override func handleNewData() { topViewOutsideOfScrollArea = templateModel?.anchorHeader ?? false bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false @@ -109,7 +95,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { - guard let moleculeInfo = moleculesInfo?[indexPath.row], + guard let moleculeInfo = getMoleculeInfo(for: indexPath), let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) else { return 0 } @@ -122,7 +108,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let moleculeInfo = moleculesInfo?[indexPath.row], + guard let moleculeInfo = getMoleculeInfo(for: indexPath), let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) else { return UITableViewCell() } @@ -175,7 +161,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol var indexPaths: [IndexPath] = [] for molecule in molecules { - if let info = self.getMoleculeInfo(with: molecule) { + if let info = self.createMoleculeInfo(with: molecule) { self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) let index = indexPath.row + 1 + indexPaths.count self.moleculesInfo?.insert(info, at: index) @@ -211,8 +197,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol // MARK: - Convenience //-------------------------------------------------- - /// Returns the (identifier, class) of the molecule for the given map. - func getMoleculeInfo(with listItem: (ListItemModelProtocol & MoleculeModelProtocol)?) -> (identifier: String, class: AnyClass, molecule: ListItemModelProtocol & MoleculeModelProtocol)? { + /// Returns the (identifier, class) of the molecule for the given model. + func createMoleculeInfo(with listItem: MoleculeModelProtocol?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { guard let listItem = listItem, let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem) @@ -223,14 +209,19 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol return (moleculeName, moleculeClass, listItem) } + /// Returns the (identifier, class) of the molecule for the indexPath. + func getMoleculeInfo(for indexPath: IndexPath) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { + return moleculesInfo?[indexPath.row] + } + /// Sets up the molecule list and ensures no errors loading all content. - func getMoleculeInfoList() -> [(identifier: String, class: AnyClass, molecule: (ListItemModelProtocol & MoleculeModelProtocol))]? { + func getMoleculeInfoList() -> [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)]? { - var moleculeList: [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol & MoleculeModelProtocol)] = [] + var moleculeList: [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)] = [] if let molecules = templateModel?.molecules { for molecule in molecules { - if let info = getMoleculeInfo(with: molecule) { + if let info = createMoleculeInfo(with: molecule) { moleculeList.append(info) } } diff --git a/MVMCoreUI/Atomic/Templates/SectionListTemplate.swift b/MVMCoreUI/Atomic/Templates/SectionListTemplate.swift new file mode 100644 index 00000000..bc12146f --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/SectionListTemplate.swift @@ -0,0 +1,137 @@ +// +// SectionListTemplate.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/25/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +open class SectionListTemplate: MoleculeListTemplate { + + public var sectionMoleculesInfo: [(header: (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)?, footer: (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)?, rows: [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)])]? + + open override func viewDidLoad() { + super.viewDidLoad() + tableView.sectionHeaderHeight = UITableView.automaticDimension + } + + override func setup() { + // Create quick reference + var sectionList: [(header: (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)?, footer: (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)?, rows: [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)])] = [] + + if let sections = (templateModel as? SectionListTemplateModel)?.sections { + for section in sections { + let header = createMoleculeInfo(with: section.header) + let footer = createMoleculeInfo(with: section.footer) + var rows: [(identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)] = [] + for row in section.rows { + if let rowInfo = createMoleculeInfo(with: row) { + rows.append(rowInfo) + } + } + sectionList.append((header: header, footer: footer, rows: rows)) + } + } + + sectionMoleculesInfo = sectionList.count > 0 ? sectionList : nil + } + + override func getMoleculeInfo(for indexPath: IndexPath) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { + return sectionMoleculesInfo?[indexPath.section].rows[indexPath.row] + } + + open override func createTableView() -> TableView { + let tableView = TableView(frame: .zero, style: .plain) + tableView.backgroundColor = .clear + tableView.separatorStyle = UITableViewCell.SeparatorStyle.none + tableView.delegate = self + tableView.dataSource = self + tableView.insetsContentViewsToSafeArea = false + return tableView + } + + // For subclassing the model. + open override func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> SectionListTemplateModel { + return try decoder.decode(SectionListTemplateModel.self, from: data) + } + + open override func registerWithTable() { + super.registerWithTable() + guard let sections = sectionMoleculesInfo else { return } + for section in sections { + if let header = section.header { + tableView.register(header.class, forHeaderFooterViewReuseIdentifier: header.identifier) + } + if let footer = section.footer { + tableView.register(footer.class, forHeaderFooterViewReuseIdentifier: footer.identifier) + } + for row in section.rows { + tableView.register(row.class, forCellReuseIdentifier: row.identifier) + } + } + } + + public func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat { + guard let moleculeInfo = sectionMoleculesInfo?[section].header else { + return CGFloat.leastNormalMagnitude + } + let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) + return estimatedHeight ?? 80 + } + + public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + guard (sectionMoleculesInfo?[section].header) != nil else { + return CGFloat.leastNormalMagnitude + } + return UITableView.automaticDimension + } + + public func tableView(_ tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat { + guard let moleculeInfo = sectionMoleculesInfo?[section].footer else { + return CGFloat.leastNormalMagnitude + } + let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) + return estimatedHeight ?? 80 + } + + public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + guard (sectionMoleculesInfo?[section].footer) != nil else { + return CGFloat.leastNormalMagnitude + } + return UITableView.automaticDimension + } + + public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard let sectionInfo = sectionMoleculesInfo?[section], + let headerInfo = sectionInfo.header else { return nil } + let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerInfo.identifier) + (header as? MoleculeViewProtocol)?.reset() + (header as? MoleculeViewProtocol)?.set(with: headerInfo.molecule, delegateObjectIVar, nil) + (header as? MVMCoreViewProtocol)?.updateView(tableView.bounds.width) + // Neded to fix an apple defect where the cell is not the correct size on certain devices for certain cells + header?.layoutIfNeeded() + return header + } + + public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + guard let sectionInfo = sectionMoleculesInfo?[section], + let footerInfo = sectionInfo.footer else { return nil } + let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: footerInfo.identifier) + (footer as? MoleculeViewProtocol)?.reset() + (footer as? MoleculeViewProtocol)?.set(with: footerInfo.molecule, delegateObjectIVar, nil) + (footer as? MVMCoreViewProtocol)?.updateView(tableView.bounds.width) + // Neded to fix an apple defect where the cell is not the correct size on certain devices for certain cells + footer?.layoutIfNeeded() + return footer + } + + open override func getNumberOfSections() -> Int { + return sectionMoleculesInfo?.count ?? 0 + } + + open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return sectionMoleculesInfo?[section].rows.count ?? 0 + } +} diff --git a/MVMCoreUI/Atomic/Templates/SectionListTemplateModel.swift b/MVMCoreUI/Atomic/Templates/SectionListTemplateModel.swift new file mode 100644 index 00000000..280ea4e5 --- /dev/null +++ b/MVMCoreUI/Atomic/Templates/SectionListTemplateModel.swift @@ -0,0 +1,91 @@ +// +// SectionListTemplateModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 9/25/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol SectionListHeaderFooterModel { + +} + +@objcMembers open class SectionModel: Codable { + public var header: (SectionListHeaderFooterModel & MoleculeModelProtocol)? + public var footer: (SectionListHeaderFooterModel & MoleculeModelProtocol)? + public var rows: [ListItemModelProtocol & MoleculeModelProtocol] + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case header + case footer + case rows + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + header = try typeContainer.decodeModelIfPresent(codingKey: .header) + footer = try typeContainer.decodeModelIfPresent(codingKey: .footer) + rows = try typeContainer.decodeModels(codingKey: .rows) + } + + open func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeModelIfPresent(header, forKey: .header) + try container.encodeModelIfPresent(footer, forKey: .footer) + try container.encodeModels(rows, forKey: .rows) + } +} + +@objcMembers open class SectionListTemplateModel: ListPageTemplateModel { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + open override class var identifier: String { + return "sectionList" + } + + public var sections: [SectionModel] + + override func validateModelHasContent() throws { + if header == nil, + sections.count == 0, + footer == nil { + throw ModelRegistry.Error.decoderOther(message: "Section List template requires atleast one of the following: header, footer, sections") + } + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case sections + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + sections = try typeContainer.decode([SectionModel].self, forKey: .sections) + try super.init(from: decoder) + } + + open override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(sections, forKey: .sections) + } +} diff --git a/MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift b/MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift new file mode 100644 index 00000000..b4b7f8eb --- /dev/null +++ b/MVMCoreUI/BaseClasses/SectionHeaderFooterView.swift @@ -0,0 +1,85 @@ +// +// SectionHeaderFooterView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 10/2/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class SectionHeaderFooterView: UITableViewHeaderFooterView, MoleculeViewProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + open var model: MoleculeModelProtocol? + + private var initialSetupPerformed = false + + //-------------------------------------------------- + // MARK: - Initialization + //-------------------------------------------------- + + public override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + initialSetup() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + public func initialSetup() { + if !initialSetupPerformed { + initialSetupPerformed = true + setupView() + } + } + + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + + open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + self.model = model + if let backgroundColor = model.backgroundColor { + contentView.backgroundColor = backgroundColor.uiColor + } + } + + open func reset() { + contentView.backgroundColor = .white + } + + // MARK: Overridables + // Base classes need to implement these functions otherwise swift won't respect the subclass functions and use the ones in the protocol extension instead. + open class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { + return model.moleculeName + } + + open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return nil + } + + open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + return nil + } +} + +// MARK:- MVMCoreViewProtocol +extension SectionHeaderFooterView: MVMCoreViewProtocol { + + open func updateView(_ size: CGFloat) { } + + /// Will be called only once. + open func setupView() { + translatesAutoresizingMaskIntoConstraints = false + insetsLayoutMarginsFromSafeArea = false + preservesSuperviewLayoutMargins = false + contentView.insetsLayoutMarginsFromSafeArea = false + contentView.preservesSuperviewLayoutMargins = false + MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0) + } +} diff --git a/MVMCoreUI/BaseControllers/ScrollingViewController.swift b/MVMCoreUI/BaseControllers/ScrollingViewController.swift index eb1d5999..fcbcf878 100644 --- a/MVMCoreUI/BaseControllers/ScrollingViewController.swift +++ b/MVMCoreUI/BaseControllers/ScrollingViewController.swift @@ -63,6 +63,16 @@ open class ScrollingViewController: ViewController { registerForKeyboardNotifications() } + open override func handleNewData() { + super.handleNewData() + // will change scrollView indicatorStyle automatically on the basis of backgroundColor + var greyScale: CGFloat = 0 + if view.backgroundColor?.getWhite(&greyScale, alpha: nil) ?? false { + scrollView.indicatorStyle = greyScale > 0.5 ? .black : .white + } + scrollView.flashScrollIndicators() + } + //-------------------------------------------------- // MARK: - Keyboard Handling //-------------------------------------------------- diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 1ae988b0..33c47344 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -8,15 +8,23 @@ import UIKit - -@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol { +@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol, PageProtocol { + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @objc public var pageType: String? @objc public var loadObject: MVMCoreLoadObject? - public var pageModel: MVMControllerModelProtocol? + public var model: MVMControllerModelProtocol? + public var pageModel: PageModelProtocol? { + get { + return model + } + set { + model = newValue as? MVMControllerModelProtocol + } + } /// Set if this page is containted in a manager. public var manager: (UIViewController & MVMCoreViewManagerProtocol)? @@ -111,9 +119,15 @@ import UIKit MVMCoreDispatchUtility.performBlock(onMainThread: { self.handleNewDataAndUpdateUI() - // Update navigation bar if showing. if MVMCoreUIUtility.getCurrentVisibleController() == self { + // Update navigation bar if showing. self.setNavigationBar() + self.manager?.refreshNavigationUI() + } + // Update splitview properties + if self == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() { + MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(self.bottomProgress() ?? 0) + self.updateTabBar() } }) } catch { @@ -127,7 +141,7 @@ import UIKit pageType = loadObject.pageType self.loadObject = loadObject - // Verifies all modules needed are loaded. TODO: change to ViewController + // Verifies all modules needed are loaded. guard ViewController.verifyRequiredModulesLoaded(for: loadObject, error: error) else { return false } // Parse the model for the page. @@ -214,18 +228,18 @@ import UIKit /// Creates a legacy navigation model. open func createDefaultLegacyNavigationModel() -> NavigationItemModel { let navigationModel = NavigationItemModel() - navigationModel.title = pageModel?.screenHeading + navigationModel.title = model?.screenHeading return navigationModel } /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, open func handleNewData() { if formValidator == nil { - let rules = pageModel?.formRules + let rules = model?.formRules formValidator = FormValidator(rules) } - if let backgroundColor = pageModel?.backgroundColor { + if let backgroundColor = model?.backgroundColor { view.backgroundColor = backgroundColor.uiColor } @@ -234,16 +248,16 @@ import UIKit } //-------------------------------------------------- - // MARK: - Navigation Item (Move to model base) + // MARK: - Navigation Item //-------------------------------------------------- open func getNavigationModel() -> NavigationItemModelProtocol? { // TODO: remove legacy. Temporary, convert legacy to navigation model. - if pageModel?.navigationBar == nil { + if model?.navigationBar == nil { let navigationItem = createDefaultLegacyNavigationModel() - pageModel?.navigationBar = navigationItem + model?.navigationBar = navigationItem } - return pageModel?.navigationBar + return model?.navigationBar } /// Sets the navigation item for this view controller. @@ -252,70 +266,22 @@ import UIKit let navigationController = navigationController else { return } - // We additionally want our left items - navigationItem.leftItemsSupplementBackButton = true - // Utilize helper function to set the navigation item state. NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self) } /// Sets the appearance of the navigation bar based on the model. open func setNavigationBar() { - let viewController = manager ?? self guard let navigationItemModel = getNavigationModel(), - let navigationController = viewController.navigationController else { + let navigationController = navigationController else { MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() return } // Utilize helper function to set the split view and navigation item state. - MVMCoreUISplitViewController.setNavigationBarUI(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel, leftPanelAccessible: isMasterInitiallyAccessible(), rightPanelAccessible: isSupportInitiallyAccessible(), progress: bottomProgress() ?? 0) + MVMCoreUISplitViewController.setNavigationBarUI(for: self, navigationController: navigationController, navigationItemModel: navigationItemModel) } - // Eventually will be moved to server - open func isMasterInitiallyAccessible() -> Bool { - if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { - return false - } - return MVMCoreUISession.sharedGlobal()?.launchAppLoadedSuccessfully ?? false - } - - // Eventually will be moved to server - open func isSupportInitiallyAccessible() -> Bool { - if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { - return false - } - return (MVMCoreUISession.sharedGlobal()?.launchAppLoadedSuccessfully ?? false) || showRightPanelForScreenBeforeLaunchApp() - } - - open func showRightPanelForScreenBeforeLaunchApp() -> Bool { - return loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false - } - - // Eventually will be moved to separate button in navigation item model - open func isOverridingRightButton() -> Bool { - guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink") - else { return false } - MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject()) - return true - } - - // Eventually will be moved to separate button in navigation item model - open func isOverridingLeftButton() -> Bool { - guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink") - else { return false } - MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject()) - return true - } - - // Eventually will be moved to Model - open func bottomProgress() -> Float? { - guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), - let progress = Float(progressString) - else { return nil } - - return progress / Float(100) - } //-------------------------------------------------- // MARK: - TabBar //-------------------------------------------------- @@ -324,7 +290,7 @@ import UIKit guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self else { return } MVMCoreUISplitViewController.main()?.tabBar?.delegateObject = delegateObjectIVar - if let index = (pageModel as? TabPageModelProtocol)?.tabBarIndex { + if let index = (model as? TabPageModelProtocol)?.tabBarIndex { MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) } else if let index = loadObject?.requestParameters?.actionMap?["tabBarIndex"] as? Int { MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) @@ -335,7 +301,7 @@ import UIKit self.tabBarIndex = index } - if let hidden = (pageModel as? TabPageModelProtocol)?.tabBarHidden { + if let hidden = (model as? TabPageModelProtocol)?.tabBarHidden { MVMCoreUISplitViewController.main()?.updateTabBarShowing(!hidden) } else if let hidden = loadObject?.requestParameters?.actionMap?["tabBarHidden"] as? Bool { MVMCoreUISplitViewController.main()?.updateTabBarShowing(!hidden) @@ -398,14 +364,15 @@ import UIKit } open func pageShown() { - // Update the navigation bar ui when view is appearing. + // Update split view properties if this is the current detail controller. if self == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() { MVMCoreUISplitViewController.main()?.setupPanels() + MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(bottomProgress() ?? 0) + updateTabBar() } - setNavigationBar() - // Update tab if needed. - updateTabBar() + // Update the navigation bar ui when view is appearing. + setNavigationBar() // Track. MVMCoreUISession.sharedGlobal()?.currentPageType = pageType @@ -472,13 +439,17 @@ import UIKit MVMCoreUISession.sharedGlobal()?.splitViewController?.showRightPanel(animated: true) } } - + + /// Override this method to avoid adding form params. + open func addFormParams(_ requestParameters: MVMCoreRequestParameters) { + formValidator?.addFormParams(requestParameters: requestParameters) + } //-------------------------------------------------- // MARK: - MVMCoreActionDelegateProtocol //-------------------------------------------------- open func handleOpenPage(for requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) { - formValidator?.addFormParams(requestParameters: requestParameters) + addFormParams(requestParameters) requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType") MVMCoreActionHandler.defaultHandleOpenPage(for: requestParameters, additionalData: additionalData, delegateObject: delegateObject()) } @@ -526,6 +497,51 @@ import UIKit setNavigationBar() } + public func isLeftPanelAccessible() -> Bool { + // TODO: Remove when hamburger menu is fully phased out. + if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { + return false + } + return MVMCoreUISession.sharedGlobal()?.launchAppLoadedSuccessfully ?? false + } + + public func isRightPanelAccessible() -> Bool { + // TODO: Remove when FAB is 100%. + if loadObject?.pageJSON?.boolForKey(KeyHideMainMenu) ?? false { + return false + } + return (MVMCoreUISession.sharedGlobal()?.launchAppLoadedSuccessfully ?? false) || showRightPanelForScreenBeforeLaunchApp() + } + + open func showRightPanelForScreenBeforeLaunchApp() -> Bool { + return loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false + } + + // TODO: make molecular + open func isOverridingRightButton() -> Bool { + guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink") + else { return false } + MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject()) + return true + } + + // TODO: make molecular + open func isOverridingLeftButton() -> Bool { + guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink") + else { return false } + MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject()) + return true + } + + // Eventually will be moved to Model + open func bottomProgress() -> Float? { + guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), + let progress = Float(progressString) + else { return nil } + + return progress / Float(100) + } + //-------------------------------------------------- // MARK: - UITextFieldDelegate //-------------------------------------------------- @@ -594,6 +610,6 @@ import UIKit //-------------------------------------------------- func executeBehaviors(_ behaviorBlock:(_ behavior:T)->Void) { - pageModel?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) } + model?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) } } } diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 22864027..7a403338 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -48,6 +48,7 @@ import UIKit viewController.navigationItem.title = navigationItemModel.title viewController.navigationItem.accessibilityLabel = navigationItemModel.title viewController.navigationItem.hidesBackButton = navigationItemModel.hidesSystemBackButton + viewController.navigationItem.leftItemsSupplementBackButton = !navigationItemModel.hidesSystemBackButton setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setNavigationTitleView(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) } @@ -58,7 +59,7 @@ import UIKit var leftItems: [UIBarButtonItem] = [] if navigationItemModel.alwaysShowBackButton != false { if let backButtonModel = navigationItemModel.backButton, - navigationController.viewControllers.count > 1 || navigationItemModel.alwaysShowBackButton ?? false { + MVMCoreNavigationHandler.shared()?.getViewControllers(for: navigationController)?.count ?? 0 > 1 || navigationItemModel.alwaysShowBackButton ?? false { leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) } if let leftItemModels = navigationItemModel.additionalLeftButtons { @@ -94,23 +95,7 @@ import UIKit navigationController.separatorView?.isHidden = navigationItemModel.line?.type ?? .standard == .none } } - - /// Convenience setter for legacy files - public static func setNavigationItem(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { - guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { - throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") - } - setNavigationItem(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) - } - - /// Convenience setter for legacy files - public static func setNavigationBarUI(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { - guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { - throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") - } - setNavigationBarUI(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) - } - + /// Convenience function for setting the navigation titleView. public static func setNavigationTitleView(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) { let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h index 2ec18da9..60ce0873 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h @@ -15,6 +15,12 @@ NS_ASSUME_NONNULL_BEGIN @optional +/// Returns if the left panel should be accessible. +- (BOOL)isLeftPanelAccessible; + +/// Returns if the right panel should be accessible. +- (BOOL)isRightPanelAccessible; + - (void)panelWillAppear:(nonnull NSObject *)panel; - (void)panelWillAppear:(nonnull NSObject *)panel overtakingDetail:(BOOL)willOvertake; - (void)panelDidAppear:(nonnull NSObject *)panel; diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index d37d88f1..eb22eccb 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -10,32 +10,29 @@ import Foundation public extension MVMCoreUISplitViewController { - /// Convenience function. Sets the navigation and split view properties for the view controller. Optional parameters use current value if not set. - static func setNavigationBarUI(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, leftPanelAccessible: Bool? = nil, rightPanelAccessible: Bool? = nil, progress: Float? = nil) { + /// Convenience function. Sets the navigation and split view properties for the view controller. Panel access is determined if view controller is a detail view protocol. + static func setNavigationBarUI(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) { guard let splitView = MVMCoreUISplitViewController.main(), navigationController == splitView.navigationController, navigationController.topViewController == viewController else { - NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) - return + /// Not the split view navigation controller, skip split functions. + NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + return } - splitView.set(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel, leftPanelAccessible: leftPanelAccessible, rightPanelAccessible: rightPanelAccessible, progress: progress) + splitView.set(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel) } /// Sets the navigation item for the view controller based on the model and splitview controller - private func set(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, leftPanelAccessible: Bool? = nil, rightPanelAccessible: Bool? = nil, progress: Float? = nil) { + private func set(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol) { NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) - setLeftPanelIsAccessible(leftPanelAccessible ?? leftPanelIsAccessible, for: viewController, updateNavigationButtons: false) - setRightPanelIsAccessible(rightPanelAccessible ?? rightPanelIsAccessible, for: viewController, updateNavigationButtons: false) + setLeftPanelIsAccessible((viewController as? MVMCoreUIDetailViewProtocol)?.isLeftPanelAccessible?() ?? false, for: viewController, updateNavigationButtons: false) + setRightPanelIsAccessible((viewController as? MVMCoreUIDetailViewProtocol)?.isRightPanelAccessible?() ?? false, for: viewController, updateNavigationButtons: false) setLeftNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setRightNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setNavigationIconColor(navigationItemModel.tintColor.uiColor) - - if let progress = progress { - setBottomProgressBarProgress(progress) - } } /// Sets the left navigation items for the view controller based on model and splitview. @@ -48,7 +45,7 @@ public extension MVMCoreUISplitViewController { if let forceBackButton = navigationItemModel?.alwaysShowBackButton { showBackButton = forceBackButton } else { - showBackButton = navigationController.viewControllers.count > 1 + showBackButton = MVMCoreNavigationHandler.shared()?.getViewControllers(for: navigationController)?.count ?? 0 > 1 } if showBackButton { if let backButtonModel = navigationItemModel?.backButton { @@ -107,20 +104,4 @@ public extension MVMCoreUISplitViewController { viewController.navigationItem.setRightBarButtonItems(rightItems.count > 0 ? rightItems : nil, animated: !DisableAnimations.boolValue) } - - // MARK: - Legacy Functions - /// Convenience setter for legacy files. Sets the navigation item for the view controller based on the json and splitview controller - @objc static func setNavigationBarUI(for viewController: UIViewController, navigationController: UINavigationController, navigationJSON: [String: Any], leftPanelAccessible: Bool, rightPanelAccessible: Bool, progress: NSNumber?) throws { - guard let navigationItemModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { - throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") - } - guard let splitView = MVMCoreUISplitViewController.main(), - navigationController == splitView.navigationController, - navigationController.topViewController == viewController else { - NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) - return - } - let progress = progress?.floatValue - splitView.set(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel, leftPanelAccessible: leftPanelAccessible, rightPanelAccessible: rightPanelAccessible, progress: progress) - } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index 177d0b8a..d88641fa 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -241,7 +241,7 @@ CGFloat const PanelAnimationDuration = 0.2; - (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended { NSMutableArray *leftBarButtonItems = [NSMutableArray array]; - if ([viewController.navigationController.viewControllers count] > 1) { + if (viewController.navigationController && [MVMCoreNavigationHandler.sharedNavigationHandler getViewControllersForNavigationController:viewController.navigationController].count > 1) { [leftBarButtonItems addObject:self.backButton]; } if ((accessible && !extended) && self.leftPanelButton) { diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift index 36f13be0..f126799c 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIViewControllerMappingObject+Extension.swift @@ -20,6 +20,8 @@ public extension MVMCoreUIViewControllerMappingObject { register(template: MoleculeListTemplate.self) add(toTemplateViewControllerMapping: ["modalList": MVMCoreViewControllerProgrammaticMappingObject(with: ModalMoleculeListTemplate.self)!]) + add(toTemplateViewControllerMapping: [SectionListTemplateModel.identifier: MVMCoreViewControllerProgrammaticMappingObject(with: SectionListTemplate.self)!]) + add(toTemplateViewControllerMapping: [ModalSectionListTemplateModel.identifier: MVMCoreViewControllerProgrammaticMappingObject(with: ModalSectionListTemplate.self)!]) register(template: ThreeLayerTemplate.self) register(template: ThreeLayerCenterTemplate.self) diff --git a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings index c280b737..ee0b1849 100644 --- a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings @@ -67,6 +67,11 @@ "AccOff" = "off"; "AccToggleHint" = "double tap to toggle"; +// MARK: Heart +"heart_favorite_action_hint" = "Double tap to favorite"; +"heart_unfavorite_action_hint" = "Double tap to unfavorite"; +"heart_selected_state" = "Favorited"; +"heart_not_selected_state" = "Un-favorited"; // MARK: Carousel "MVMCoreUIPageControl_currentpage_index" = "page %@ of %d"; diff --git a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings index f21df078..0a37e1a5 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings @@ -49,6 +49,11 @@ "AccOn" = "encendido"; "AccOff" = "apagado"; "AccToggleHint" = "toca dos veces para alternar"; +// Heart +"heart_favorite_action_hint" = "Toca dos veces en favorito"; +"heart_unfavorite_action_hint" = "Toca dos veces para dejar de marcar como favorito"; +"heart_selected_state" = "Favoritos"; +"heart_not_selected_state" = "No favorito"; // Carousel "MVMCoreUIPageControl_currentpage_index" = "página %@ de %d"; "MVMCoreUIPageControlslides_currentpage_index" = "diapositiva %@ of %d"; diff --git a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings index 47d124a9..a64c3915 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings @@ -51,6 +51,13 @@ "AccOn" = "encendido"; "AccOff" = "apagado"; "AccToggleHint" = "toca dos veces para alternar"; + +// Heart +"heart_favorite_action_hint" = "Toca dos veces en favorito"; +"heart_unfavorite_action_hint" = "Toca dos veces para dejar de marcar como favorito"; +"heart_selected_state" = "Favoritos"; +"heart_not_selected_state" = "No favorito"; + // Carousel "MVMCoreUIPageControl_currentpage_index" = "página %@ de %d"; "MVMCoreUIPageControlslides_currentpage_index" = "diapositiva %@ of %d"; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index 8bf4860f..6a3c78de 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -21,14 +21,17 @@ public extension MVMCoreUITopAlertView { NotificationCenter.default.addObserver(self, selector: #selector(viewControllerChanged(notification:)), name: NSNotification.Name(rawValue: MVMCoreNotificationViewControllerChanged), object: nil) } + @objc func getDelegateObject() -> MVMCoreUIDelegateObject { + // TODO: Top alert view is current delegate. Should move to current view controller eventually? + return MVMCoreUIDelegateObject.create(withDelegateForAll: self) + } + /// Checks for new top alert json @objc func responseJSONUpdated(notification: Notification) { guard let responseJSON = (notification.userInfo?[String(describing: MVMCoreLoadObject.self)] as? MVMCoreLoadObject)?.responseJSON, - let json = responseJSON.optionalDictionaryForKey("TopNotification") else { return } - - // TODO: Top alert view is current delegate. Should move to current view controller eventually? - let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self) - showTopAlert(with: json, delegateObject: delegateObject) + let json = responseJSON.optionalDictionaryForKey("TopNotification"), + let model = decodeTopNotification(with: json, delegateObject: getDelegateObject()) else { return } + showTopAlert(with: model) } /// When a detail page changes, check top alerts. @@ -37,24 +40,55 @@ public extension MVMCoreUITopAlertView { MVMCoreAlertHandler.shared()?.checkPagesDependency(for: controller.pageType) } - /// Shows the top alert with the json payload. - func showTopAlert(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) { + /// Decodes the json into a TopNotificationModel + func decodeTopNotification(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) -> TopNotificationModel? { do { - let model = try TopNotificationModel.decode(json: json, delegateObject: delegateObject) - showTopAlert(with: model, delegateObject: delegateObject) + return try TopNotificationModel.decode(json: json, delegateObject: delegateObject) } catch { if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) } + return nil } } /// Shows the top alert with the model. - func showTopAlert(with model: TopNotificationModel, delegateObject: MVMCoreUIDelegateObject?) { + func showTopAlert(with model: TopNotificationModel) { let object = model.createTopAlertObject() MVMCoreAlertHandler.shared()?.showTopAlert(with: object) } + /// Updates the current top alert molecule with the new object + @objc func updateMolecule(with topAlertObject: MVMCoreTopAlertObject) { + guard topAlertObject.type == self.topAlertObject?.type else { return } + let delegateObject = getDelegateObject() + guard let newJson = topAlertObject.json, + let newModel = decodeTopNotification(with: newJson, delegateObject: delegateObject), + let newModelName = MoleculeObjectMapping.shared()?.getMoleculeClass(newModel.molecule)?.nameForReuse(with: newModel.molecule, delegateObject), + let currentJson = self.topAlertObject?.json, + let currentModel = decodeTopNotification(with: currentJson, delegateObject: delegateObject), + let currentModelName = MoleculeObjectMapping.shared()?.getMoleculeClass(currentModel.molecule)?.nameForReuse(with: currentModel.molecule, delegateObject), + newModelName == currentModelName, + let molecule = currentAlert as? MoleculeViewProtocol else { + // Log that we couldn't update. + if let errorObject = MVMCoreErrorObject(title: nil, message: nil, messageToLog: nil, code: ErrorCode.parsingJSON.rawValue, domain: ErrorDomainNative, location: "TopNotification update \(String(describing: topAlertObject.type))") { + MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) + } + return + } + MVMCoreDispatchUtility.performBlock(onMainThread: { + // Update molecule + molecule.reset() + molecule.set(with: newModel.molecule, delegateObject, nil) + (molecule as? MVMCoreViewProtocol)?.updateView(self.bounds.width) + + // Update status bar. + guard let statusBarDelegate = molecule as? StatusBarUI else { return } + let statusBarUI = statusBarDelegate.getStatusBarUI() + self.setStatusBarColor(statusBarUI.color, statusBarStyle: statusBarUI.style) + }) + } + /// Returns the top alert molecule to use and status bar color legacy style. @objc func molecule(for topAlertObject: MVMCoreTopAlertObject, statusBarColor: AutoreleasingUnsafeMutablePointer?, statusBarStyle: UnsafeMutablePointer?) -> UIView? { do { diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h index 82c0a7e9..1979a850 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.h @@ -27,6 +27,9 @@ // Current top alert object @property (strong, nullable, nonatomic) MVMCoreTopAlertObject *topAlertObject; +/// Current top alert view. +@property (weak, nullable, nonatomic, readonly) UIView *currentAlert; + // Returns the top alert view + (nullable instancetype)sharedGlobal; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index d54190b0..14f4dee2 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -39,7 +39,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; @property (strong, nonatomic) NSLayoutConstraint *statusBarBottomConstraint; @property (weak, nonatomic) UIView *alertView; -@property (weak, nonatomic) UIView *currentAlert; +@property (weak, nullable, nonatomic, readwrite) UIView *currentAlert; @property (strong, nonatomic) NSLayoutConstraint *height; @property (weak, nonatomic) MVMCoreUITopAlertExpandableView *topAlertClearspotView; @@ -181,6 +181,10 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; }); } +- (void)updateTopAlertWith:(MVMCoreTopAlertObject *)topAlertObject { + [self updateMoleculeWith:topAlertObject]; +} + - (void)setStatusBarColor:(nullable UIColor *)statusBarColor statusBarStyle:(UIStatusBarStyle)style { self.statusBarView.backgroundColor = statusBarColor; self.statusBarStyle = style;