diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 3a9c7efe..4e5aa0d3 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -117,6 +117,8 @@ 27F9736A246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */; }; 31BE15CB23D8924D00452370 /* CheckboxLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31BE15C923D8924C00452370 /* CheckboxLabelModel.swift */; }; 31BE15CC23D8924D00452370 /* CheckboxModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31BE15CA23D8924C00452370 /* CheckboxModel.swift */; }; + 324FB6AA249366F3002552C7 /* ListLeftVariableNumberedListBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324FB6A9249366F3002552C7 /* ListLeftVariableNumberedListBodyTextModel.swift */; }; + 324FB6AC24936717002552C7 /* ListLeftVariableNumberedListBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324FB6AB24936717002552C7 /* ListLeftVariableNumberedListBodyText.swift */; }; 32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */; }; 32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */; }; 522679C123FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */; }; @@ -188,6 +190,10 @@ 94F6516D2437954100631BF9 /* Tabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F6516C2437954100631BF9 /* Tabs.swift */; }; AA0A257824766C8A00862F64 /* ListLeftVariableIconWithRightCaretBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA0A257724766C8A00862F64 /* ListLeftVariableIconWithRightCaretBodyTextModel.swift */; }; AA0A257A24766CA200862F64 /* ListLeftVariableIconWithRightCaretBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA0A257924766CA200862F64 /* ListLeftVariableIconWithRightCaretBodyText.swift */; }; + AA104AC724472DB0004D2810 /* HeadersH1Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA104AC624472DB0004D2810 /* HeadersH1Button.swift */; }; + AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA104AC824472DC7004D2810 /* HeadersH1ButtonModel.swift */; }; + AA104ADA244734DB004D2810 /* HeadersH1LandingPageHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA104AD9244734DB004D2810 /* HeadersH1LandingPageHeader.swift */; }; + AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA104ADB244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift */; }; AA104B1A24474A66004D2810 /* HeadersH2Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA104B1924474A66004D2810 /* HeadersH2Buttons.swift */; }; AA104B1C24474A76004D2810 /* HeadersH2ButtonsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA104B1B24474A76004D2810 /* HeadersH2ButtonsModel.swift */; }; AA11A41F23F15D3100D7962F /* ListRightVariablePayments.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA11A41E23F15D3100D7962F /* ListRightVariablePayments.swift */; }; @@ -202,8 +208,12 @@ AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */; }; AA617AB02453010A00910B8F /* ListDeviceComplexLinkSmall.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA617AAF2453010A00910B8F /* ListDeviceComplexLinkSmall.swift */; }; AA617AB22453012400910B8F /* ListDeviceComplexLinkSmallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA617AB12453012400910B8F /* ListDeviceComplexLinkSmallModel.swift */; }; + AA633B3124989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA633B3024989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift */; }; + AA633B3324989ED500731E80 /* HeadersH2PricingTwoRows.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA633B3224989ED500731E80 /* HeadersH2PricingTwoRows.swift */; }; AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69AAF52445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift */; }; AA69AAF82445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69AAF72445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift */; }; + AA71AD3E24A32FCE00ACA76F /* HeadersH2LinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA71AD3D24A32FCE00ACA76F /* HeadersH2LinkModel.swift */; }; + AA71AD4024A32FE700ACA76F /* HeadersH2Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA71AD3F24A32FE700ACA76F /* HeadersH2Link.swift */; }; AA7F32AB246C0F7900C965BA /* ListLeftVariableRadioButtonAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7F32AA246C0F7900C965BA /* ListLeftVariableRadioButtonAllTextAndLinksModel.swift */; }; AA7F32AD246C0F8C00C965BA /* ListLeftVariableRadioButtonAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7F32AC246C0F8C00C965BA /* ListLeftVariableRadioButtonAllTextAndLinks.swift */; }; AA85236C244435A20059CC1E /* RadioSwatchCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */; }; @@ -283,6 +293,8 @@ D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */; }; D22D8393241C27B100D3DF69 /* TemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8392241C27B100D3DF69 /* TemplateModel.swift */; }; D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8394241FB41200D3DF69 /* UIStackView+Extension.swift */; }; + D2351C7A24A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2351C7924A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift */; }; + D2351C7C24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2351C7B24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift */; }; D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */; }; D236E5B5241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B3241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift */; }; D236E5B7242007C500C38625 /* MVMControllerModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B6242007C500C38625 /* MVMControllerModelProtocol.swift */; }; @@ -322,6 +334,7 @@ D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */; }; D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */; }; D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */; }; + D2874024249BA6F300BE950A /* MVMCoreUISplitViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2874023249BA6F300BE950A /* MVMCoreUISplitViewController+Extension.swift */; }; D28764AA2458980300CB882D /* ThreeLayerFillMiddleTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28764A92458980300CB882D /* ThreeLayerFillMiddleTemplate.swift */; }; D28764AC245898A400CB882D /* ThreeLayerFillMiddleTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28764AB245898A400CB882D /* ThreeLayerFillMiddleTemplateModel.swift */; }; D28764F9245A327200CB882D /* TwoLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28764F8245A327200CB882D /* TwoLinkView.swift */; }; @@ -563,6 +576,8 @@ 27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenBrightnessModifierBehavior.swift; sourceTree = ""; }; 31BE15C923D8924C00452370 /* CheckboxLabelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxLabelModel.swift; sourceTree = ""; }; 31BE15CA23D8924C00452370 /* CheckboxModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxModel.swift; sourceTree = ""; }; + 324FB6A9249366F3002552C7 /* ListLeftVariableNumberedListBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableNumberedListBodyTextModel.swift; sourceTree = ""; }; + 324FB6AB24936717002552C7 /* ListLeftVariableNumberedListBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableNumberedListBodyText.swift; sourceTree = ""; }; 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableNumberedListAllTextAndLinksModel.swift; sourceTree = ""; }; 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableNumberedListAllTextAndLinks.swift; sourceTree = ""; }; 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinks.swift; sourceTree = ""; }; @@ -634,6 +649,10 @@ 94F6516C2437954100631BF9 /* Tabs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tabs.swift; sourceTree = ""; }; AA0A257724766C8A00862F64 /* ListLeftVariableIconWithRightCaretBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconWithRightCaretBodyTextModel.swift; sourceTree = ""; }; AA0A257924766CA200862F64 /* ListLeftVariableIconWithRightCaretBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconWithRightCaretBodyText.swift; sourceTree = ""; }; + AA104AC624472DB0004D2810 /* HeadersH1Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH1Button.swift; sourceTree = ""; }; + AA104AC824472DC7004D2810 /* HeadersH1ButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH1ButtonModel.swift; sourceTree = ""; }; + AA104AD9244734DB004D2810 /* HeadersH1LandingPageHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH1LandingPageHeader.swift; sourceTree = ""; }; + AA104ADB244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH1LandingPageHeaderModel.swift; sourceTree = ""; }; AA104B1924474A66004D2810 /* HeadersH2Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2Buttons.swift; sourceTree = ""; }; AA104B1B24474A76004D2810 /* HeadersH2ButtonsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2ButtonsModel.swift; sourceTree = ""; }; AA11A41E23F15D3100D7962F /* ListRightVariablePayments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariablePayments.swift; sourceTree = ""; }; @@ -648,8 +667,12 @@ AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDivider.swift; sourceTree = ""; }; AA617AAF2453010A00910B8F /* ListDeviceComplexLinkSmall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexLinkSmall.swift; sourceTree = ""; }; AA617AB12453012400910B8F /* ListDeviceComplexLinkSmallModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexLinkSmallModel.swift; sourceTree = ""; }; + AA633B3024989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2PricingTwoRowsModel.swift; sourceTree = ""; }; + AA633B3224989ED500731E80 /* HeadersH2PricingTwoRows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2PricingTwoRows.swift; sourceTree = ""; }; AA69AAF52445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxBodyText.swift; sourceTree = ""; }; AA69AAF72445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxBodyTextModel.swift; sourceTree = ""; }; + AA71AD3D24A32FCE00ACA76F /* HeadersH2LinkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2LinkModel.swift; sourceTree = ""; }; + AA71AD3F24A32FE700ACA76F /* HeadersH2Link.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2Link.swift; sourceTree = ""; }; AA7F32AA246C0F7900C965BA /* ListLeftVariableRadioButtonAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonAllTextAndLinksModel.swift; sourceTree = ""; }; AA7F32AC246C0F8C00C965BA /* ListLeftVariableRadioButtonAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonAllTextAndLinks.swift; sourceTree = ""; }; AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchCollectionViewCell.swift; sourceTree = ""; }; @@ -729,6 +752,8 @@ D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccordionMoleculeTableViewCell.swift; sourceTree = ""; }; D22D8392241C27B100D3DF69 /* TemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModel.swift; sourceTree = ""; }; D22D8394241FB41200D3DF69 /* UIStackView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Extension.swift"; sourceTree = ""; }; + D2351C7924A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableToggleAllTextAndLinksModel.swift; sourceTree = ""; }; + D2351C7B24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableToggleAllTextAndLinks.swift; sourceTree = ""; }; D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTwoColumnPriceDescription.swift; sourceTree = ""; }; D236E5B3241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTwoColumnPriceDescriptionModel.swift; sourceTree = ""; }; D236E5B6242007C500C38625 /* MVMControllerModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMControllerModelProtocol.swift; sourceTree = ""; }; @@ -767,6 +792,7 @@ D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsTableViewCell.swift; sourceTree = ""; }; D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EyebrowHeadlineBodyLink.swift; sourceTree = ""; }; D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFTransparentGIFView.swift; sourceTree = ""; }; + D2874023249BA6F300BE950A /* MVMCoreUISplitViewController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUISplitViewController+Extension.swift"; sourceTree = ""; }; D28764A92458980300CB882D /* ThreeLayerFillMiddleTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerFillMiddleTemplate.swift; sourceTree = ""; }; D28764AB245898A400CB882D /* ThreeLayerFillMiddleTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerFillMiddleTemplateModel.swift; sourceTree = ""; }; D28764F8245A327200CB882D /* TwoLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoLinkView.swift; sourceTree = ""; }; @@ -1034,12 +1060,8 @@ 5206F150241144A900658DC5 /* Headers */ = { isa = PBXGroup; children = ( - AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */, - AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */, - AA26850D244840C300CE34CC /* HeadersH2TinyButtonModel.swift */, - AA26850B244840AE00CE34CC /* HeadersH2TinyButton.swift */, - AA104B1B24474A76004D2810 /* HeadersH2ButtonsModel.swift */, - AA104B1924474A66004D2810 /* HeadersH2Buttons.swift */, + D287955224A2504C0056BF75 /* H1 */, + D287955324A250550056BF75 /* H2 */, ); path = Headers; sourceTree = ""; @@ -1140,6 +1162,8 @@ 8D8067D22444473A00203BE8 /* ListRightVariablePriceChangeAllTextAndLinks.swift */, C7F8012223E846C300396FBD /* ListRVWheelModel.swift */, C7F8012023E8303200396FBD /* ListRVWheel.swift */, + D2351C7924A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift */, + D2351C7B24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift */, ); path = RightVariable; sourceTree = ""; @@ -1356,6 +1380,8 @@ AA997251247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift */, 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */, 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */, + 324FB6A9249366F3002552C7 /* ListLeftVariableNumberedListBodyTextModel.swift */, + 324FB6AB24936717002552C7 /* ListLeftVariableNumberedListBodyText.swift */, ); path = LeftVariable; sourceTree = ""; @@ -1483,6 +1509,34 @@ path = Selectors; sourceTree = ""; }; + D287955224A2504C0056BF75 /* H1 */ = { + isa = PBXGroup; + children = ( + AA104AC824472DC7004D2810 /* HeadersH1ButtonModel.swift */, + AA104AC624472DB0004D2810 /* HeadersH1Button.swift */, + AA104ADB244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift */, + AA104AD9244734DB004D2810 /* HeadersH1LandingPageHeader.swift */, + ); + path = H1; + sourceTree = ""; + }; + D287955324A250550056BF75 /* H2 */ = { + isa = PBXGroup; + children = ( + AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */, + AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */, + AA26850D244840C300CE34CC /* HeadersH2TinyButtonModel.swift */, + AA26850B244840AE00CE34CC /* HeadersH2TinyButton.swift */, + AA104B1B24474A76004D2810 /* HeadersH2ButtonsModel.swift */, + AA104B1924474A66004D2810 /* HeadersH2Buttons.swift */, + AA633B3024989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift */, + AA633B3224989ED500731E80 /* HeadersH2PricingTwoRows.swift */, + AA71AD3D24A32FCE00ACA76F /* HeadersH2LinkModel.swift */, + AA71AD3F24A32FE700ACA76F /* HeadersH2Link.swift */, + ); + path = H2; + sourceTree = ""; + }; D29DF0C221E404D4003B2FB9 = { isa = PBXGroup; children = ( @@ -1809,6 +1863,7 @@ D29DF15321E69760003B2FB9 /* MVMCoreUIPanelButtonProtocol.h */, D29DF11A21E684A9003B2FB9 /* MVMCoreUISplitViewController.h */, D29DF11B21E684A9003B2FB9 /* MVMCoreUISplitViewController.m */, + D2874023249BA6F300BE950A /* MVMCoreUISplitViewController+Extension.swift */, ); path = SplitViewController; sourceTree = ""; @@ -2057,6 +2112,7 @@ files = ( 0A775F2624893916009EFB58 /* ThreeHeadlineBodyLink.swift in Sources */, AAC6F167243332E400F295C1 /* RadioSwatchesModel.swift in Sources */, + 324FB6AA249366F3002552C7 /* ListLeftVariableNumberedListBodyTextModel.swift in Sources */, 5248BFED23F12E350059236A /* ListThreeColumnPlanDataDividerModel.swift in Sources */, AA0A257824766C8A00862F64 /* ListLeftVariableIconWithRightCaretBodyTextModel.swift in Sources */, 0A5D59C223AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift in Sources */, @@ -2103,6 +2159,7 @@ 01004F3022721C3800991ECC /* RadioButton.swift in Sources */, D268C70E238C22D7007F2C1C /* DropDownFilterTableViewCell.swift in Sources */, D236E5B7242007C500C38625 /* MVMControllerModelProtocol.swift in Sources */, + AA71AD4024A32FE700ACA76F /* HeadersH2Link.swift in Sources */, D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, D2B18B7F2360913400A9AEDC /* Control.swift in Sources */, D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */, @@ -2214,6 +2271,7 @@ D2A92886241ACD99004E01C6 /* ProgrammaticTableViewController.swift in Sources */, BBAA4F05243D8E3B005AAD5F /* RadioBoxesModel.swift in Sources */, 01509D952327ED1900EF99AA /* HeadlineBodyLinkToggle.swift in Sources */, + AA104ADA244734DB004D2810 /* HeadersH1LandingPageHeader.swift in Sources */, 31BE15CB23D8924D00452370 /* CheckboxLabelModel.swift in Sources */, D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */, 94F6516D2437954100631BF9 /* Tabs.swift in Sources */, @@ -2221,6 +2279,7 @@ 0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */, D264FA8E243BCD9A00D98315 /* CollectionTemplate.swift in Sources */, 0A7EF85B23D8A52800B2AAD1 /* EntryFieldModel.swift in Sources */, + AA633B3124989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift in Sources */, 8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */, D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */, 0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */, @@ -2258,6 +2317,7 @@ 942C378E2412F5B60066E45E /* ModalMoleculeStackTemplate.swift in Sources */, 8D8067D32444473A00203BE8 /* ListRightVariablePriceChangeAllTextAndLinks.swift in Sources */, 8D4687E4242E2DF300802879 /* ListFourColumnDataUsageListItem.swift in Sources */, + D2874024249BA6F300BE950A /* MVMCoreUISplitViewController+Extension.swift in Sources */, 01F2A03223A4498200D954D8 /* CaretLinkModel.swift in Sources */, 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */, 011B58F023A2AA980085F53C /* ListItemModelProtocol.swift in Sources */, @@ -2267,6 +2327,7 @@ BB55B51D244482C1002001AD /* ListRightVariablePriceChangeBodyText.swift in Sources */, 017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */, D28A837923C7D5BC00DFE4FC /* PageModelProtocol.swift in Sources */, + D2351C7C24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift in Sources */, 017BEB7B236763000024EF95 /* LineModel.swift in Sources */, D256E9932412880000360572 /* Header.swift in Sources */, 94C2D9A523872C350006CF46 /* LabelAttributeFontModel.swift in Sources */, @@ -2313,14 +2374,17 @@ BB6C6AC0242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTallModel.swift in Sources */, 8DEFA95E243DAC2F000D27E5 /* ListThreeColumnDataUsageDivider.swift in Sources */, D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */, + 324FB6AC24936717002552C7 /* ListLeftVariableNumberedListBodyText.swift in Sources */, AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */, 522679C223FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift in Sources */, AA7F32AB246C0F7900C965BA /* ListLeftVariableRadioButtonAllTextAndLinksModel.swift in Sources */, 8D084AD02410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift in Sources */, D253BB9E2458751F002DE544 /* BGImageMoleculeModel.swift in Sources */, + AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */, 0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */, 8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */, BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */, + AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */, BBAA4F03243D8E3B005AAD5F /* RadioBoxes.swift in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, 525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */, @@ -2352,6 +2416,7 @@ 8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */, D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */, D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, + AA71AD3E24A32FCE00ACA76F /* HeadersH2LinkModel.swift in Sources */, 8DD1E36E243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift in Sources */, D243859923A16B1800332775 /* Container.swift in Sources */, D2C521A923EDE79E00CA2634 /* ViewController.swift in Sources */, @@ -2361,6 +2426,7 @@ AA26850E244840C300CE34CC /* HeadersH2TinyButtonModel.swift in Sources */, D260105F23D0BFFC00764D80 /* StackItem.swift in Sources */, 9432A79F23DB47BA00719041 /* EntryFieldContainer.swift in Sources */, + D2351C7A24A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift in Sources */, 01EB369323609801006832FA /* HeaderModel.swift in Sources */, 8DE5BECF2456F7B100772E76 /* ListTwoColumnDropdownSelectors.swift in Sources */, D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */, @@ -2386,6 +2452,7 @@ BB54C5202434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift in Sources */, 948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */, 013F801923FB4A8E00AD8013 /* UIContentMode+Extension.swift in Sources */, + AA104AC724472DB0004D2810 /* HeadersH1Button.swift in Sources */, 525239C22407BD1000454969 /* ListTwoColumnPriceDetails.swift in Sources */, AA997252247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift in Sources */, D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */, @@ -2437,6 +2504,7 @@ 0A25209824645B76000FA9F6 /* TextViewEntryFieldModel.swift in Sources */, 525019DD2406430800EED91C /* ListProgressBarDataModel.swift in Sources */, C6FA7D5223C77A4A00A3614A /* UnOrderedList.swift in Sources */, + AA633B3324989ED500731E80 /* HeadersH2PricingTwoRows.swift in Sources */, 01509D8F2327EC6F00EF99AA /* MoleculeTableViewCell.swift in Sources */, 0A6682A22434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift in Sources */, EA5124FD243601600051A3A4 /* BGImageHeadlineBodyButton.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index cffbb916..f23fd337 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -157,9 +157,9 @@ import MVMCore override public init(frame: CGRect) { super.init(frame: frame) - accessibilityTraits = .button isAccessibilityElement = true accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") + accessibilityTraits = .button updateAccessibilityLabel() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index 43750513..371d3f7a 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -147,8 +147,8 @@ import UIKit addTarget(self, action: #selector(tapAction), for: .touchUpInside) isAccessibilityElement = true - accessibilityTraits = .button accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint") + accessibilityTraits = .button updateAccessibilityLabel() } diff --git a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/BarsIndicatorView.swift b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/BarsIndicatorView.swift index 9dc4a0bd..69048e74 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/BarsIndicatorView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/BarsIndicatorView.swift @@ -115,12 +115,12 @@ open class BarsIndicatorView: CarouselIndicator { for i in 0.. (Bool) }() // Sizes are from InVision design specs. - static let containerSize = CGSize(width: 46, height: 24) - static let knobSize = CGSize(width: 22, height: 22) + static let containerSize = CGSize(width: 51, height: 31) + static let knobSize = CGSize(width: 28, height: 28) private var knobView: View = { let view = View() @@ -62,8 +62,8 @@ public typealias ActionBlockConfirmation = () -> (Bool) didSet { isUserInteractionEnabled = isEnabled changeStateNoAnimation(isEnabled ? isOn : false) - backgroundColor = isEnabled ? (isOn ? containerTintColor.on : containerTintColor.off) : disabledTintColor.container - knobView.backgroundColor = isEnabled ? (isOn ? knobTintColor.on : knobTintColor.off) : disabledTintColor.knob + setToggleAppearanceFromState() + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: isEnabled ? "AccToggleHint" : "AccDisabled") } } @@ -96,8 +96,7 @@ public typealias ActionBlockConfirmation = () -> (Bool) }, completion: nil) } else { - backgroundColor = isOn ? containerTintColor.on : containerTintColor.off - knobView.backgroundColor = isOn ? knobTintColor.on : knobTintColor.off + setToggleAppearanceFromState() self.constrainKnob() } @@ -143,14 +142,14 @@ public typealias ActionBlockConfirmation = () -> (Bool) private func constrainKnobOn() { - knobTrailingConstraint = trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 1) + knobTrailingConstraint = trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 2) knobLeadingConstraint = knobView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor) } private func constrainKnobOff() { knobTrailingConstraint = trailingAnchor.constraint(greaterThanOrEqualTo: knobView.trailingAnchor) - knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) + knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 2) } //-------------------------------------------------- @@ -159,7 +158,6 @@ public typealias ActionBlockConfirmation = () -> (Bool) public override init(frame: CGRect) { super.init(frame: frame) - setupView() } public convenience override init() { @@ -215,17 +213,17 @@ public typealias ActionBlockConfirmation = () -> (Bool) super.setupView() isAccessibilityElement = true - accessibilityTraits = .button accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint") accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") - + accessibilityTraits = .button + heightConstraint = heightAnchor.constraint(equalToConstant: Self.containerSize.height) heightConstraint?.isActive = true widthConstraint = widthAnchor.constraint(equalToConstant: Self.containerSize.width) widthConstraint?.isActive = true - layer.cornerRadius = Self.containerSize.height / 2.0 + layer.cornerRadius = Self.getContainerHeight() / 2.0 backgroundColor = containerTintColor.off addSubview(knobView) @@ -305,6 +303,16 @@ public typealias ActionBlockConfirmation = () -> (Bool) isAnimated = isAnimatedState } + override open func accessibilityActivate() -> Bool { + // Hold state in case User wanted isAnimated to remain off. + guard isUserInteractionEnabled else { return false } + let isAnimatedState = isAnimated + isAnimated = false + sendActions(for: .touchUpInside) + isAnimated = isAnimatedState + return true + } + //-------------------------------------------------- // MARK: - UIResponder //-------------------------------------------------- @@ -342,6 +350,12 @@ public typealias ActionBlockConfirmation = () -> (Bool) // MARK: - Animations //-------------------------------------------------- + public func setToggleAppearanceFromState() { + + backgroundColor = isEnabled ? isOn ? containerTintColor.on : containerTintColor.off : disabledTintColor.container + knobView.backgroundColor = isEnabled ? isOn ? knobTintColor.on : knobTintColor.off : disabledTintColor.knob + } + public func knobReformAnimation() { if isAnimated { diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift b/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift index 25142fa5..7e70d0f0 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift @@ -32,7 +32,7 @@ import Foundation url = try typeContainer.decodeIfPresent(URL.self, forKey: .url) htmlString = try typeContainer.decodeIfPresent(String.self, forKey: .htmlString) if url == nil, htmlString == nil { - throw ModelRegistry.Error.decoderErrorModelNotMapped + throw ModelRegistry.Error.decoderErrorModelNotMapped() } height = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .height) borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index 98b1e6a3..fbb2fe8c 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -38,7 +38,7 @@ import Foundation public func getMoleculeModelForJSON(_ json: [String: Any]) throws -> MoleculeModelProtocol? { guard let moleculeName = json.optionalStringForKey(KeyMoleculeName), let type = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) else { - throw ModelRegistry.Error.decoderErrorModelNotMapped + throw ModelRegistry.Error.decoderErrorModelNotMapped() } guard let model = try type.decode(jsonDict: json) as? MoleculeModelProtocol else { throw ModelRegistry.Error.decoderError @@ -172,6 +172,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableCheckboxBodyText.self, viewModelClass: ListLeftVariableCheckboxBodyTextModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableIconAllTextLinks.self, viewModelClass: ListLeftVariableIconAllTextLinksModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableNumberedListAllTextAndLinks.self, viewModelClass: ListLeftVariableNumberedListAllTextAndLinksModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableNumberedListBodyText.self, viewModelClass: ListLeftVariableNumberedListBodyTextModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRVWheel.self, viewModelClass: ListRVWheelModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariablePayments.self, viewModelClass: ListRightVariablePaymentsModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariableTotalData.self, viewModelClass: ListRightVariableTotalDataModel.self) @@ -179,6 +180,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariableButtonAllTextAndLinks.self, viewModelClass: ListRightVariableButtonAllTextAndLinksModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariablePriceChangeBodyText.self, viewModelClass: ListRightVariablePriceChangeBodyTextModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariablePriceChangeAllTextAndLinks.self, viewModelClass: ListRightVariablePriceChangeAllTextAndLinksModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariableToggleAllTextAndLinks.self, viewModelClass: ListRightVariableToggleAllTextAndLinksModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListOneColumnFullWidthTextAllTextAndLinks.self, viewModelClass: ListOneColumnFullWidthTextAllTextAndLinksModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListOneColumnFullWidthTextBodyText.self, viewModelClass: ListOneColumnFullWidthTextBodyTextModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListTwoColumnCompareChanges.self, viewModelClass: ListTwoColumnCompareChangesModel.self) @@ -203,9 +205,13 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnDataUsageDivider.self, viewModelClass: ListThreeColumnDataUsageDividerModel.self) // Designed Headers + MoleculeObjectMapping.shared()?.register(viewClass: HeadersH1Button.self, viewModelClass: HeadersH1ButtonModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: HeadersH1LandingPageHeader.self, viewModelClass: HeadersH1LandingPageHeaderModel.self) MoleculeObjectMapping.shared()?.register(viewClass: HeadersH2NoButtonsBodyText.self, viewModelClass: HeadersH2NoButtonsBodyTextModel.self) MoleculeObjectMapping.shared()?.register(viewClass: HeadersH2TinyButton.self, viewModelClass: HeadersH2TinyButtonModel.self) MoleculeObjectMapping.shared()?.register(viewClass: HeadersH2Buttons.self, viewModelClass: HeadersH2ButtonsModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: HeadersH2PricingTwoRows.self, viewModelClass: HeadersH2PricingTwoRowsModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: HeadersH2Link.self, viewModelClass: HeadersH2LinkModel.self) // Device Items MoleculeObjectMapping.shared()?.register(viewClass: ListDeviceComplexButtonMedium.self, viewModelClass: ListDeviceComplexButtonMediumModel.self) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1Button.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1Button.swift new file mode 100644 index 00000000..d41b9022 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1Button.swift @@ -0,0 +1,59 @@ +// +// HeadersH1Button.swift +// MVMCoreUI +// +// Created by Lekshmi S on 04/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class HeadersH1Button: HeaderView { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + public let headlineBody = HeadlineBody(frame: .zero) + public let buttons = TwoButtonView(frame: .zero) + public let stack: Stack + + //------------------------------------------------------- + // MARK: - Initializers + //------------------------------------------------------- + public override init(frame: CGRect) { + stack = Stack.createStack(with: [headlineBody, buttons], spacing: Padding.Eighteen) + super.init(frame: frame) + } + + public required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //------------------------------------------------------- + // MARK: - View Lifecycle + //------------------------------------------------------- + open override func setupView() { + super.setupView() + headlineBody.styleLandingPageHeader() + addMolecule(stack) + stack.restack() + } + + //---------------------------------------------------- + // MARK: - Molecule + //------------------------------------------------------ + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? HeadersH1ButtonModel else { return } + headlineBody.set(with: model.headlineBody, delegateObject, additionalData) + buttons.set(with: model.buttons, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 121 + } + + open override func reset() { + super.reset() + headlineBody.styleLandingPageHeader() + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1ButtonModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1ButtonModel.swift new file mode 100644 index 00000000..5df94b85 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1ButtonModel.swift @@ -0,0 +1,54 @@ +// +// HeadersH1ButtonModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 04/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class HeadersH1ButtonModel: HeaderModel, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "headerH1Btn" + public var headlineBody: HeadlineBodyModel + public var buttons: TwoButtonViewModel + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(headlineBody: HeadlineBodyModel, buttons: TwoButtonViewModel) { + self.headlineBody = headlineBody + self.buttons = buttons + super.init() + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { + case moleculeName + case headlineBody + case buttons + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + headlineBody = try typeContainer.decode(HeadlineBodyModel.self, forKey: .headlineBody) + buttons = try typeContainer.decode(TwoButtonViewModel.self, forKey: .buttons) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(headlineBody, forKey: .headlineBody) + try container.encode(buttons, forKey: .buttons) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1LandingPageHeader.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1LandingPageHeader.swift new file mode 100644 index 00000000..c78ada3f --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1LandingPageHeader.swift @@ -0,0 +1,71 @@ +// +// HeadersH1LandingPageHeader.swift +// MVMCoreUI +// +// Created by Lekshmi S on 09/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class HeadersH1LandingPageHeader: HeaderView { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + public let headline = Label(fontStyle: .Title2XLarge) + public let headline2 = Label(fontStyle: .Title2XLarge) + public let subHeadline = Label(fontStyle: .BoldBodySmall) + public let body = Label(fontStyle: .RegularBodySmall) + public let link = Link() + public let buttons = TwoButtonView(frame: .zero) + public var stack: Stack! + + //-------------------------------------------------- + // MARK: - Constants + //-------------------------------------------------- + let spacingBetweenHeadlineHeadline2: CGFloat = 16.0 + let spacingBetweenHeadline2subHeadline: CGFloat = 32.0 + let spacingBetweenSubHeadlineBodyLink: CGFloat = 8.0 + let spacingBetweenLinkButtons: CGFloat = 128.0 + + //------------------------------------------------------- + // MARK: - View Lifecycle + //------------------------------------------------------- + open override func setupView() { + super.setupView() + stack = Stack.createStack(with: [(view: headline, model: StackItemModel()), + (view: headline2, model: StackItemModel(spacing: spacingBetweenHeadlineHeadline2)), + (view: subHeadline, model: StackItemModel(spacing: spacingBetweenHeadline2subHeadline)), + (view: body, model: StackItemModel(spacing: spacingBetweenSubHeadlineBodyLink)), + (view: link, model: StackItemModel(spacing: spacingBetweenSubHeadlineBodyLink, horizontalAlignment: .leading)), + (view: buttons, model: StackItemModel(spacing: spacingBetweenLinkButtons))]) + addMolecule(stack) + stack.restack() + } + + //---------------------------------------------------- + // MARK: - Molecule + //------------------------------------------------------ + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? HeadersH1LandingPageHeaderModel else { return } + headline.set(with: model.headline, delegateObject, additionalData) + headline2.set(with: model.headline2, delegateObject, additionalData) + subHeadline.set(with: model.subHeadline, delegateObject, additionalData) + body.set(with: model.body, delegateObject, additionalData) + link.set(with: model.link, delegateObject, additionalData) + buttons.set(with: model.buttons, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 121 + } + + open override func reset() { + super.reset() + headline.setFontStyle(.Title2XLarge) + headline2.setFontStyle(.Title2XLarge) + subHeadline.setFontStyle(.BoldBodySmall) + body.setFontStyle(.RegularBodySmall) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1LandingPageHeaderModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1LandingPageHeaderModel.swift new file mode 100644 index 00000000..d7603886 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H1/HeadersH1LandingPageHeaderModel.swift @@ -0,0 +1,74 @@ +// +// HeadersH1LandingPageHeaderModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 09/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class HeadersH1LandingPageHeaderModel: HeaderModel, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "headerH1Landing" + public var headline: LabelModel + public var headline2: LabelModel + public var subHeadline: LabelModel + public var body: LabelModel + public var link: LinkModel + public var buttons: TwoButtonViewModel + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(headline: LabelModel, headline2: LabelModel, subHeadline: LabelModel, body: LabelModel, link: LinkModel, buttons: TwoButtonViewModel) { + self.headline = headline + self.headline2 = headline2 + self.subHeadline = subHeadline + self.body = body + self.link = link + self.buttons = buttons + super.init() + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { + case moleculeName + case headline + case headline2 + case subHeadline + case body + case link + case buttons + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + headline = try typeContainer.decode(LabelModel.self, forKey: .headline) + headline2 = try typeContainer.decode(LabelModel.self, forKey: .headline2) + subHeadline = try typeContainer.decode(LabelModel.self, forKey: .subHeadline) + body = try typeContainer.decode(LabelModel.self, forKey: .body) + link = try typeContainer.decode(LinkModel.self, forKey: .link) + buttons = try typeContainer.decode(TwoButtonViewModel.self, forKey: .buttons) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(headline, forKey: .headline) + try container.encode(headline2, forKey: .headline2) + try container.encode(subHeadline, forKey: .subHeadline) + try container.encode(body, forKey: .body) + try container.encode(link, forKey: .link) + try container.encode(buttons, forKey: .buttons) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2Buttons.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2Buttons.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2Buttons.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2Buttons.swift diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2ButtonsModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2ButtonsModel.swift similarity index 91% rename from MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2ButtonsModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2ButtonsModel.swift index 3cb09a0f..d3ea7497 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2ButtonsModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2ButtonsModel.swift @@ -33,9 +33,13 @@ public class HeadersH2ButtonsModel: HeaderModel, MoleculeModelProtocol { //-------------------------------------------------- public override func setDefaults() { + if topPadding == nil { + topPadding = Padding.Component.VerticalMarginSpacing + } + if bottomPadding == nil { + bottomPadding = Padding.Component.VerticalMarginSpacing + } super.setDefaults() - topPadding = PaddingDefaultVerticalSpacing3 - bottomPadding = PaddingDefaultVerticalSpacing3 } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2Link.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2Link.swift new file mode 100644 index 00000000..c5aaefc8 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2Link.swift @@ -0,0 +1,59 @@ +// +// HeadersH2Link.swift +// MVMCoreUI +// +// Created by Lekshmi S on 24/06/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class HeadersH2Link: HeaderView { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + public let headlineBody = HeadlineBody() + public let link = Link() + public let stack: Stack + + //------------------------------------------------------- + // MARK: - Initializers + //------------------------------------------------------- + public override init(frame: CGRect) { + stack = Stack.createStack(with: [headlineBody, link]) + super.init(frame: frame) + } + + public required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //------------------------------------------------------- + // MARK: - Lifecycle + //------------------------------------------------------- + open override func setupView() { + super.setupView() + headlineBody.stylePageHeader() + addMolecule(stack) + stack.restack() + } + + //---------------------------------------------------- + // MARK: - Molecule + //------------------------------------------------------ + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? HeadersH2LinkModel else { return } + headlineBody.set(with: model.headlineBody, delegateObject, additionalData) + link.set(with: model.link, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 121 + } + + open override func reset() { + super.reset() + headlineBody.stylePageHeader() + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2LinkModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2LinkModel.swift new file mode 100644 index 00000000..66189f99 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2LinkModel.swift @@ -0,0 +1,72 @@ +// +// HeadersH2LinkModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 24/06/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class HeadersH2LinkModel: HeaderModel, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "headerH2Link" + public var headlineBody: HeadlineBodyModel + public var link: LinkModel + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(headlineBody: HeadlineBodyModel, link: LinkModel) { + self.headlineBody = headlineBody + self.link = link + super.init() + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + public override func setDefaults() { + if topPadding == nil { + topPadding = Padding.Component.VerticalMarginSpacing + } + if bottomPadding == nil { + bottomPadding = Padding.Component.VerticalMarginSpacing + } + super.setDefaults() + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case headlineBody + case link + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + headlineBody = try typeContainer.decode(HeadlineBodyModel.self, forKey: .headlineBody) + link = try typeContainer.decode(LinkModel.self, forKey: .link) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(headlineBody, forKey: .headlineBody) + try container.encode(link, forKey: .link) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2NoButtonsBodyText.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyText.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2NoButtonsBodyText.swift diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyTextModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2NoButtonsBodyTextModel.swift similarity index 89% rename from MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyTextModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2NoButtonsBodyTextModel.swift index a6cfc0ce..96d30730 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2NoButtonsBodyTextModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2NoButtonsBodyTextModel.swift @@ -27,9 +27,13 @@ public class HeadersH2NoButtonsBodyTextModel: HeaderModel, MoleculeModelProtocol } public override func setDefaults() { + if topPadding == nil { + topPadding = Padding.Component.VerticalMarginSpacing + } + if bottomPadding == nil { + bottomPadding = Padding.Component.VerticalMarginSpacing + } super.setDefaults() - topPadding = PaddingDefaultVerticalSpacing3 - bottomPadding = PaddingDefaultVerticalSpacing3 } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRows.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRows.swift new file mode 100644 index 00000000..12097880 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRows.swift @@ -0,0 +1,121 @@ +// +// HeadersH2PricingTwoRows.swift +// MVMCoreUI +// +// Created by Lekshmi S on 16/06/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class HeadersH2PricingTwoRows: HeaderView { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + public let headline = Label(fontStyle: .RegularTitleLarge) + public let body = Label(fontStyle: .RegularMicro) + public let subBody = Label(fontStyle: .RegularMicro) + public let body2 = Label(fontStyle: .RegularMicro) + public let subBody2 = Label(fontStyle: .RegularMicro) + public let body3 = Label(fontStyle: .RegularMicro) + public let subBody3 = Label(fontStyle: .RegularMicro) + public let verticalLine1 = Line() + public let verticalLine2 = Line() + public let verticalStack1: Stack + public let verticalStack2: Stack + public let verticalStack3: Stack + public let horizontalStack: Stack + public let stack: Stack + + //------------------------------------------------------- + // MARK: - Initializers + //------------------------------------------------------- + public override init(frame: CGRect) { + verticalStack1 = Stack.createStack(with: [body, subBody], spacing: 0) + verticalStack2 = Stack.createStack(with: [body2, subBody2], spacing: 0) + verticalStack3 = Stack.createStack(with: [body3, subBody3], spacing: 0) + horizontalStack = Stack.createStack(with: [(view: verticalStack1, model: StackItemModel(percent: 29, verticalAlignment: .top)), (view: verticalLine1, model: StackItemModel(verticalAlignment: .top)), + (view: verticalStack2, model: StackItemModel(horizontalAlignment: .leading, verticalAlignment: .top)), + (view: verticalLine2, model: StackItemModel(verticalAlignment: .top)), + (view: verticalStack3, model: StackItemModel(percent: 32, verticalAlignment: .top))], + axis: .horizontal) + stack = Stack.createStack(with: [headline, horizontalStack], spacing: 8) + super.init(frame: frame) + } + + public required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //------------------------------------------------------- + // MARK: - Lifecycle + //------------------------------------------------------- + open override func setupView() { + super.setupView() + setDefaults() + body.numberOfLines = 1 + body.lineBreakMode = .byTruncatingTail + body2.numberOfLines = 1 + body2.lineBreakMode = .byTruncatingTail + body3.numberOfLines = 1 + body3.lineBreakMode = .byTruncatingTail + + verticalLine1.widthConstraint?.isActive = true + verticalLine1.backgroundColor = .mvmBlack + verticalLine2.widthConstraint?.isActive = true + verticalLine2.backgroundColor = .mvmBlack + + addMolecule(stack) + stack.restack() + verticalStack1.restack() + verticalStack2.restack() + verticalStack3.restack() + horizontalStack.restack() + setLineHeight() + } + + open func setDefaults() { + headline.setFontStyle(.BoldTitleLarge) + body.setFontStyle(.RegularMicro) + subBody.setFontStyle(.RegularMicro) + body2.setFontStyle(.RegularMicro) + subBody2.setFontStyle(.RegularMicro) + body3.setFontStyle(.RegularMicro) + subBody3.setFontStyle(.RegularMicro) + subBody.textColor = .mvmCoolGray6 + subBody2.textColor = .mvmCoolGray6 + subBody3.textColor = .mvmCoolGray6 + } + + open func setLineHeight() { + verticalLine1.heightConstraint?.isActive = false + verticalLine1.heightConstraint = verticalLine1.heightAnchor.constraint(equalTo: body2.heightAnchor, multiplier: 1) + verticalLine1.heightConstraint?.isActive = true + + verticalLine2.heightConstraint?.isActive = false + verticalLine2.heightConstraint = verticalLine2.heightAnchor.constraint(equalTo: body3.heightAnchor, multiplier: 1) + verticalLine2.heightConstraint?.isActive = true + } + + //---------------------------------------------------- + // MARK: - Molecule + //------------------------------------------------------ + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? HeadersH2PricingTwoRowsModel else { return } + headline.set(with: model.headline, delegateObject, additionalData) + verticalStack1.updateContainedMolecules(with: [model.body, model.subBody], delegateObject, additionalData) + verticalStack2.updateContainedMolecules(with: [model.body2, model.subBody2], delegateObject, additionalData) + verticalStack3.updateContainedMolecules(with: [model.body3, model.subBody3], delegateObject, additionalData) + setLineHeight() + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 121 + } + + open override func reset() { + super.reset() + setDefaults() + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRowsModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRowsModel.swift new file mode 100644 index 00000000..01bd72e4 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2PricingTwoRowsModel.swift @@ -0,0 +1,95 @@ +// +// HeadersH2PricingTwoRowsModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 16/06/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class HeadersH2PricingTwoRowsModel: HeaderModel, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "headerH2TwoRows" + public var headline: LabelModel + public var body: LabelModel + public var subBody: LabelModel? + public var body2: LabelModel + public var subBody2: LabelModel? + public var body3: LabelModel + public var subBody3: LabelModel? + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(headline: LabelModel, body: LabelModel, subBody: LabelModel? = nil, body2: LabelModel, subBody2: LabelModel? = nil, body3: LabelModel, subBody3: LabelModel? = nil) { + self.headline = headline + self.body = body + self.subBody = subBody + self.body2 = body2 + self.subBody2 = subBody2 + self.body3 = body3 + self.subBody3 = subBody3 + super.init() + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + public override func setDefaults() { + if topPadding == nil { + topPadding = Padding.Component.VerticalMarginSpacing + } + if bottomPadding == nil { + bottomPadding = Padding.Component.VerticalMarginSpacing + } + super.setDefaults() + subBody?.attributes = [LabelAttributeStrikeThroughModel(0, subBody?.text.count ?? 0)] + subBody2?.attributes = [LabelAttributeStrikeThroughModel(0, subBody2?.text.count ?? 0)] + subBody3?.attributes = [LabelAttributeStrikeThroughModel(0, subBody3?.text.count ?? 0)] + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { + case moleculeName + case headline + case body + case subBody + case body2 + case subBody2 + case body3 + case subBody3 + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + 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) + subBody = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .subBody) + body2 = try typeContainer.decode(LabelModel.self, forKey: .body2) + subBody2 = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .subBody2) + body3 = try typeContainer.decode(LabelModel.self, forKey: .body3) + subBody3 = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .subBody3) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(headline, forKey: .headline) + try container.encode(body, forKey: .body) + try container.encodeIfPresent(subBody, forKey: .subBody) + try container.encode(body2, forKey: .body2) + try container.encodeIfPresent(subBody2, forKey: .subBody2) + try container.encode(body3, forKey: .body3) + try container.encodeIfPresent(subBody3, forKey: .subBody3) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2TinyButton.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2TinyButton.swift similarity index 100% rename from MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2TinyButton.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2TinyButton.swift diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2TinyButtonModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2TinyButtonModel.swift similarity index 91% rename from MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2TinyButtonModel.swift rename to MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2TinyButtonModel.swift index 55c2c6e4..d4edfd38 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/HeadersH2TinyButtonModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/Headers/H2/HeadersH2TinyButtonModel.swift @@ -33,9 +33,13 @@ public class HeadersH2TinyButtonModel: HeaderModel, MoleculeModelProtocol { //-------------------------------------------------- public override func setDefaults() { + if topPadding == nil { + topPadding = Padding.Component.VerticalMarginSpacing + } + if bottomPadding == nil { + bottomPadding = Padding.Component.VerticalMarginSpacing + } super.setDefaults() - topPadding = PaddingDefaultVerticalSpacing3 - bottomPadding = PaddingDefaultVerticalSpacing3 button.style = .secondary button.size = .tiny } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift index 983cfc59..986ee73b 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonMedium.swift @@ -58,10 +58,8 @@ import Foundation addMolecule(stack) stack.restack() verticalStack.restack() - isAccessibilityElement = true - accessibilityTraits = button.accessibilityTraits accessibilityHint = button.accessibilityHint - updateAccessibilityLabel() + accessibilityTraits = button.accessibilityTraits } //-------------------------------------------------- @@ -100,10 +98,8 @@ import Foundation // MARK: - Accessibility //-------------------------------------------------- - func updateAccessibilityLabel() { - + func getAccessibilityMessage() -> String? { var message = "" - if let eyebrowText = eyebrow.text, !eyebrowText.isEmpty { message += eyebrowText + ", " } @@ -120,11 +116,28 @@ import Foundation message += body2Text + ", " } - if let rightImageViewText = rightImageView.accessibilityLabel, !rightImageViewText.isEmpty { + if let rightImageViewText = rightImageView.imageView.accessibilityLabel, !rightImageViewText.isEmpty { message += rightImageViewText } - - accessibilityLabel = message + return message.count > 0 ? message : nil + } + + func updateAccessibilityLabel() { + if let accessoryView = accessoryView { + // Both caret and button. Read all content on caret. + isAccessibilityElement = false + accessoryView.accessibilityLabel = getAccessibilityMessage() + accessibilityElements = [accessoryView, button] + } else { + // Make whole cell focusable if no action. + isAccessibilityElement = true + if let message = getAccessibilityMessage(), + let buttonTitle = button.titleLabel?.text { + accessibilityLabel = message + ", " + buttonTitle + } else { + accessibilityLabel = getAccessibilityMessage() + } + } } open override func accessibilityActivate() -> Bool { diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift index f0ee4619..cd43215b 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexButtonSmall.swift @@ -58,10 +58,8 @@ import Foundation addMolecule(stack) stack.restack() verticalStack.restack() - isAccessibilityElement = true - accessibilityTraits = button.accessibilityTraits accessibilityHint = button.accessibilityHint - updateAccessibilityLabel() + accessibilityTraits = button.accessibilityTraits } //-------------------------------------------------- @@ -100,10 +98,8 @@ import Foundation // MARK: - Accessibility //-------------------------------------------------- - func updateAccessibilityLabel() { - + func getAccessibilityMessage() -> String? { var message = "" - if let eyebrowText = eyebrow.text, !eyebrowText.isEmpty { message += eyebrowText + ", " } @@ -120,11 +116,28 @@ import Foundation message += body2Text + ", " } - if let rightImageViewText = rightImageView.accessibilityLabel, !rightImageViewText.isEmpty { + if let rightImageViewText = rightImageView.imageView.accessibilityLabel, !rightImageViewText.isEmpty { message += rightImageViewText } - - accessibilityLabel = message + return message.count > 0 ? message : nil + } + + func updateAccessibilityLabel() { + if let accessoryView = accessoryView { + // Both caret and button. Read all content on caret. + isAccessibilityElement = false + accessoryView.accessibilityLabel = getAccessibilityMessage() + accessibilityElements = [accessoryView, button] + } else { + // Make whole cell focusable if no action. + isAccessibilityElement = true + if let message = getAccessibilityMessage(), + let buttonTitle = button.titleLabel?.text { + accessibilityLabel = message + ", " + buttonTitle + } else { + accessibilityLabel = getAccessibilityMessage() + } + } } open override func accessibilityActivate() -> Bool { diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift index 9c75f78c..ee6ade09 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkMedium.swift @@ -114,7 +114,7 @@ import Foundation message += body2Text + ", " } - if let rightImageLabel = rightImage.accessibilityLabel, !rightImageLabel.isEmpty { + if let rightImageLabel = rightImage.imageView.accessibilityLabel, !rightImageLabel.isEmpty { message += rightImageLabel } @@ -145,8 +145,8 @@ import Foundation views.append(twoLinkView.leftLink) views.append(twoLinkView.rightLink) - if let rightImageLabel = rightImage.accessibilityLabel, !rightImageLabel.isEmpty { - views.append(rightImage) + if let rightImageLabel = rightImage.imageView.accessibilityLabel, !rightImageLabel.isEmpty { + views.append(rightImage.imageView) } accessibilityElements = views diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift index 6a469b0d..1791ec9d 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/Device/ListDeviceComplexLinkSmall.swift @@ -111,7 +111,7 @@ import Foundation message += body2Text + ", " } - if let rightImageLabel = rightImage.accessibilityLabel, !rightImageLabel.isEmpty { + if let rightImageLabel = rightImage.imageView.accessibilityLabel, !rightImageLabel.isEmpty { message += rightImageLabel } @@ -142,8 +142,8 @@ import Foundation views.append(twoLinkView.leftLink) views.append(twoLinkView.rightLink) - if let rightImageLabel = rightImage.accessibilityLabel, !rightImageLabel.isEmpty { - views.append(rightImage) + if let rightImageLabel = rightImage.imageView.accessibilityLabel, !rightImageLabel.isEmpty { + views.append(rightImage.imageView) } accessibilityElements = views diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/FourColumn/ListFourColumnDataUsageListItem.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/FourColumn/ListFourColumnDataUsageListItem.swift index 4ae4c4d2..8455a5bf 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/FourColumn/ListFourColumnDataUsageListItem.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/FourColumn/ListFourColumnDataUsageListItem.swift @@ -54,8 +54,6 @@ import Foundation arrow.pinHeightAndWidth() arrowAndLabel2Stack.restack() stack.restack() - isAccessibilityElement = true - updateAccessibilityLabel() } //----------------------------------------------------- @@ -92,7 +90,7 @@ import Foundation //----------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let label1Text = label1.text, !label1Text.isEmpty { diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift index ff1c6e1b..ff625e8b 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxAllTextAndLinks.swift @@ -49,11 +49,9 @@ import Foundation addMolecule(stack) stack.restack() - isAccessibilityElement = true - checkbox.isAccessibilityElement = false - accessibilityTraits = checkbox.accessibilityTraits accessibilityHint = checkbox.accessibilityHint - // Update accessibility label on radio button state change. + accessibilityTraits = checkbox.accessibilityTraits + // Update accessibility label on checkbox state change. observation = observe(\.checkbox.isSelected, options: [.new]) { [weak self] _, _ in self?.updateAccessibilityLabel() } @@ -92,41 +90,19 @@ import Foundation message += checkboxLabel + ", " } - if let eyebrowLabel = eyebrowHeadlineBodyLink.eyebrow.text, !eyebrowLabel.isEmpty { - message += eyebrowLabel + ", " - } - - if let headlineLabel = eyebrowHeadlineBodyLink.headline.text, !headlineLabel.isEmpty { - message += headlineLabel + ", " - } - - if let bodyLabel = eyebrowHeadlineBodyLink.body.text, !bodyLabel.isEmpty { - message += bodyLabel + if let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() { + message += label } let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 - isAccessibilityElement = !linkShowing - checkbox.isAccessibilityElement = linkShowing - eyebrowHeadlineBodyLink.link.isAccessibilityElement = linkShowing - if !linkShowing { // Make whole cell focusable if no link. accessibilityLabel = message } else { - // Allow only radio button and link to be focused on. + // Allow only checkbox and link to be focused on. checkbox.accessibilityLabel = message - var elements: [UIView] = [] - - if message.count > 0 { - elements.append(checkbox) - } - - if linkShowing { - elements.append(eyebrowHeadlineBodyLink.link) - } - - accessibilityElements = elements + accessibilityElements = [checkbox, eyebrowHeadlineBodyLink.link] } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyText.swift index 326c9320..69ad2156 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyText.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyText.swift @@ -49,10 +49,9 @@ import Foundation addMolecule(stack) stack.restack() isAccessibilityElement = true - checkbox.isAccessibilityElement = false - accessibilityTraits = checkbox.accessibilityTraits accessibilityHint = checkbox.accessibilityHint - // Update accessibility label on radio button state change. + accessibilityTraits = checkbox.accessibilityTraits + // Update accessibility label on checkbox state change. observation = observe(\.checkbox.isSelected, options: [.new]) { [weak self] _, _ in self?.updateAccessibilityLabel() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconAllTextLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconAllTextLinks.swift index 1f8a6a43..9655c4dd 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconAllTextLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconAllTextLinks.swift @@ -42,7 +42,6 @@ import Foundation leftImage.imageView.contentMode = .scaleAspectFit addMolecule(stack) stack.restack() - updateAccessibilityLabel() } //-------------------------------------------------- @@ -63,57 +62,37 @@ import Foundation return 140 } + func getAccessibilityMessage() -> String? { + guard let leftImageLabel = leftImage.imageView.accessibilityLabel else { + return eyebrowHeadlineBodyLink.getAccessibilityMessage() + } + guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { + return leftImageLabel + } + return leftImageLabel + ", " + label + } + func updateAccessibilityLabel() { - var message = "" - - if let leftImageLabel = leftImage.accessibilityLabel { - message += leftImageLabel + ", " - } - - if let eyebrowLabel = eyebrowHeadlineBodyLink.eyebrow.text { - message += eyebrowLabel + ", " - } - - if let headlineLabel = eyebrowHeadlineBodyLink.headline.text { - message += headlineLabel + ", " - } - - if let bodyLabel = eyebrowHeadlineBodyLink.body.text { - message += bodyLabel - } - let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 isAccessibilityElement = !linkShowing - eyebrowHeadlineBodyLink.link.isAccessibilityElement = linkShowing - + accessibilityTraits = (isAccessibilityElement && accessoryView != nil) ? .button : .none if !linkShowing { // Make whole cell focusable if no link. - accessibilityLabel = message + accessibilityLabel = getAccessibilityMessage() + } else if let accessoryView = accessoryView { + // Both caret and link. Read all content on caret. + accessoryView.accessibilityLabel = getAccessibilityMessage() + accessibilityElements = [accessoryView, eyebrowHeadlineBodyLink.link] } else { - // Allow only radio button and link to be focused on. - var elements: [UIView] = [] - - if let leftImageLabel = leftImage.accessibilityLabel, !leftImageLabel.isEmpty { - elements.append(leftImage) + // Only link. Manually add accessibility elements to ensure they are read in the right order. + var elements: [Any] = [] + if let leftImageLabel = leftImage.imageView.accessibilityLabel, !leftImageLabel.isEmpty { + elements.append(leftImage.imageView) } - - if let eyeBrowText = eyebrowHeadlineBodyLink.eyebrow.text, !eyeBrowText.isEmpty { - elements.append(eyebrowHeadlineBodyLink.eyebrow) + if let otherElements = eyebrowHeadlineBodyLink.getAccessibilityElements() { + elements.append(otherElements) } - - if let headlineText = eyebrowHeadlineBodyLink.headline.text, !headlineText.isEmpty { - elements.append(eyebrowHeadlineBodyLink.headline) - } - - if let bodyText = eyebrowHeadlineBodyLink.body.text, !bodyText.isEmpty { - elements.append(eyebrowHeadlineBodyLink.body) - } - - if linkShowing { - elements.append(eyebrowHeadlineBodyLink.link) - } - accessibilityElements = elements } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaret.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaret.swift index 297ef31b..2ebe1c98 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaret.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaret.swift @@ -48,8 +48,6 @@ import UIKit rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 902), for: .horizontal) addMolecule(stack) stack.restack() - isAccessibilityElement = true - updateAccessibilityLabel() } //-------------------------------------------------- @@ -78,10 +76,10 @@ import UIKit } func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" - if let leftImageLabel = leftImage.accessibilityLabel { + if let leftImageLabel = leftImage.imageView.accessibilityLabel { message += leftImageLabel + ", " } @@ -94,5 +92,6 @@ import UIKit } accessibilityLabel = message + accessibilityTraits = (accessoryView != nil) ? .button : .none } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyText.swift index 0617b5e8..c69bebb2 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyText.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretBodyText.swift @@ -58,10 +58,8 @@ import Foundation rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.numberOfLines = 1 - isAccessibilityElement = true addMolecule(stack) stack.restack() - updateAccessibilityLabel() } //-------------------------------------------------- @@ -93,10 +91,10 @@ import Foundation //-------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" - if let leftImageLabel = leftImage.accessibilityLabel { + if let leftImageLabel = leftImage.imageView.accessibilityLabel { message += leftImageLabel + ", " } @@ -113,5 +111,6 @@ import Foundation } accessibilityLabel = message + accessibilityTraits = (accessoryView != nil) ? .button : .none } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinks.swift index 5924b3d7..1ec68330 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinks.swift @@ -37,10 +37,11 @@ import Foundation //-------------------------------------------------- override open func setupView() { super.setupView() + leftLabel.setContentCompressionResistancePriority(.required, for: .horizontal) + leftLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) leftLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) addMolecule(stack) stack.restack() - updateAccessibilityLabel() } //--------------------------------------------------- @@ -66,66 +67,41 @@ import Foundation leftLabel.setFontStyle(.Title2XLarge) } - open override func layoutSubviews() { - super.layoutSubviews() - // This fixes a defect body text where it doesn't layout correctly. - eyebrowHeadlineBodyLink.body.preferredMaxLayoutWidth = eyebrowHeadlineBodyLink.frame.width - } - //-------------------------------------------------- // MARK: - Accessibility //-------------------------------------------------- + func getAccessibilityMessage() -> String? { + guard let leftLabel = leftLabel.text else { + return eyebrowHeadlineBodyLink.getAccessibilityMessage() + } + guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { + return leftLabel + } + return leftLabel + ", " + label + } + func updateAccessibilityLabel() { - - var message = "" - - if let leftLabel = leftLabel.text { - message += leftLabel + ", " - } - - if let eyebrowLabel = eyebrowHeadlineBodyLink.eyebrow.text { - message += eyebrowLabel + ", " - } - - if let headlineLabel = eyebrowHeadlineBodyLink.headline.text { - message += headlineLabel + ", " - } - - if let bodyLabel = eyebrowHeadlineBodyLink.body.text { - message += bodyLabel - } - + let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 isAccessibilityElement = !linkShowing - eyebrowHeadlineBodyLink.link.isAccessibilityElement = linkShowing - + accessibilityTraits = (isAccessibilityElement && accessoryView != nil) ? .button : .none if !linkShowing { // Make whole cell focusable if no link. - accessibilityLabel = message + accessibilityLabel = getAccessibilityMessage() + } else if let accessoryView = accessoryView { + // Both caret and link. Read all content on caret. + accessoryView.accessibilityLabel = getAccessibilityMessage() + accessibilityElements = [accessoryView, eyebrowHeadlineBodyLink.link] } else { - var elements: [UIView] = [] - + // Only link. Manually add accessibility elements to ensure they are read in the right order. + var elements: [Any] = [] if let leftLabelText = leftLabel.text, !leftLabelText.isEmpty { elements.append(leftLabel) } - - if let eyeBrowText = eyebrowHeadlineBodyLink.eyebrow.text, !eyeBrowText.isEmpty { - elements.append(eyebrowHeadlineBodyLink.eyebrow) + if let otherElements = eyebrowHeadlineBodyLink.getAccessibilityElements() { + elements.append(otherElements) } - - if let headlineText = eyebrowHeadlineBodyLink.headline.text, !headlineText.isEmpty { - elements.append(eyebrowHeadlineBodyLink.headline) - } - - if let bodyText = eyebrowHeadlineBodyLink.body.text, !bodyText.isEmpty { - elements.append(eyebrowHeadlineBodyLink.body) - } - - if linkShowing { - elements.append(eyebrowHeadlineBodyLink.link) - } - accessibilityElements = elements } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListBodyText.swift new file mode 100644 index 00000000..a0a7a462 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListBodyText.swift @@ -0,0 +1,95 @@ +// +// ListLeftVariableNumberedListBodyText.swift +// MVMCoreUI +// +// Created by Subhankar Acharya on 12/06/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ListLeftVariableNumberedListBodyText: TableViewCell { + //-------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + public let leftLabel = Label(fontStyle: .Title2XLarge) + public let headlineBody = HeadlineBody() + public var stack: Stack + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(horizontalAlignment: .fill)), + (view: headlineBody, model: StackItemModel(horizontalAlignment: .leading))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //-------------------------------------------------- + // MARK: - Life Cycle + //-------------------------------------------------- + override open func setupView() { + super.setupView() + leftLabel.setContentCompressionResistancePriority(.required, for: .horizontal) + leftLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) + leftLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) + addMolecule(stack) + stack.restack() + } + + //--------------------------------------------------- + // MARK: - Molecule + //--------------------------------------------------- + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + + guard let model = model as? ListLeftVariableNumberedListBodyTextModel else { return } + leftLabel.text = String(model.number) + headlineBody.set(with: model.headlineBody, delegateObject, additionalData) + updateAccessibilityLabel() + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 140 + } + + open override func reset() { + super.reset() + leftLabel.setFontStyle(.Title2XLarge) + } + + //-------------------------------------------------- + // MARK: - Accessibility + //-------------------------------------------------- + + func updateAccessibilityLabel() { + + isAccessibilityElement = true + + var message = "" + + if let leftLabel = leftLabel.text { + message += leftLabel + ", " + } + + if let headlineLabel = headlineBody.headlineLabel.text { + message += headlineLabel + ", " + } + + if let messageLabel = headlineBody.messageLabel.text { + message += messageLabel + } + + accessibilityLabel = message + accessibilityTraits = (accessoryView != nil) ? .button : .none + } +} + diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListBodyTextModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListBodyTextModel.swift new file mode 100644 index 00000000..372bab32 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListBodyTextModel.swift @@ -0,0 +1,58 @@ +// +// ListLeftVariableNumberedListBodyTextModel.swift +// MVMCoreUI +// +// Created by Subhankar Acharya on 12/06/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListLeftVariableNumberedListBodyTextModel: ListItemModel, MoleculeModelProtocol { + //----------------------------------------------------- + // MARK: - Properties + //----------------------------------------------------- + + public static var identifier: String = "listLVNLBdy" + public var number: Int + public var headlineBody: HeadlineBodyModel + + //----------------------------------------------------- + // MARK: - Initializer + //----------------------------------------------------- + + public init(number: Int, headlineBody: HeadlineBodyModel) { + self.number = number + self.headlineBody = headlineBody + super.init() + } + + //----------------------------------------------------- + // MARK: - Keys + //----------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case number + case headlineBody + } + + //----------------------------------------------------- + // MARK: - Codec + //----------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + number = try typeContainer.decode(Int.self, forKey: .number) + headlineBody = try typeContainer.decode(HeadlineBodyModel.self, forKey: .headlineBody) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(number, forKey: .number) + try container.encode(headlineBody, forKey: .headlineBody) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAllTextAndLinks.swift index 5756d4f5..e5e9e178 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAllTextAndLinks.swift @@ -89,41 +89,19 @@ import Foundation message += radioButtonLabel + ", " } - if let eyebrowLabel = eyebrowHeadlineBodyLink.eyebrow.text { - message += eyebrowLabel + ", " - } - - if let headlineLabel = eyebrowHeadlineBodyLink.headline.text { - message += headlineLabel + ", " - } - - if let bodyLabel = eyebrowHeadlineBodyLink.body.text { - message += bodyLabel + if let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() { + message += label } let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 isAccessibilityElement = !linkShowing - radioButton.isAccessibilityElement = linkShowing - eyebrowHeadlineBodyLink.link.isAccessibilityElement = linkShowing - if !linkShowing { // Make whole cell focusable if no link. accessibilityLabel = message } else { // Allow only radio button and link to be focused on. radioButton.accessibilityLabel = message - - var elements: [UIView] = [] - - if !message.isEmpty { - elements.append(radioButton) - } - - if linkShowing { - elements.append(eyebrowHeadlineBodyLink.link) - } - - accessibilityElements = elements + accessibilityElements = [radioButton, eyebrowHeadlineBodyLink.link] } } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethod.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethod.swift index 523c6d4e..e14e91e2 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethod.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonAndPaymentMethod.swift @@ -50,9 +50,9 @@ import UIKit eyebrowHeadlineBodyLink.body.textColor = .mvmOrangeAA eyebrowHeadlineBodyLink.headline.setFontStyle(.BoldBodySmall) - accessibilityTraits = radioButton.accessibilityTraits accessibilityHint = radioButton.accessibilityHint - + accessibilityTraits = radioButton.accessibilityTraits + // Update accessibility label on radio button state change. observation = observe(\.radioButton.isSelected, options: [.new]) { [weak self] _, _ in self?.updateAccessibilityLabel() @@ -103,44 +103,23 @@ import UIKit message += radioButtonLabel + ", " } - if let leftImageLabel = leftImage.accessibilityLabel { + if let leftImageLabel = leftImage.imageView.accessibilityLabel { message += leftImageLabel + ", " } - if let eyebrowLabel = eyebrowHeadlineBodyLink.eyebrow.text { - message += eyebrowLabel + ", " - } - - if let headlineLabel = eyebrowHeadlineBodyLink.headline.text { - message += headlineLabel + ", " - } - - if let bodyLabel = eyebrowHeadlineBodyLink.body.text { - message += bodyLabel + if let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() { + message += label } let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 isAccessibilityElement = !linkShowing - radioButton.isAccessibilityElement = linkShowing - eyebrowHeadlineBodyLink.link.isAccessibilityElement = linkShowing - if !linkShowing { // Make whole cell focusable if no link. accessibilityLabel = message } else { // Allow only radio button and link to be focused on. radioButton.accessibilityLabel = message - var elements: [UIView] = [] - - if !message.isEmpty { - elements.append(radioButton) - } - - if linkShowing { - elements.append(eyebrowHeadlineBodyLink.link) - } - - accessibilityElements = elements + accessibilityElements = [radioButton, eyebrowHeadlineBodyLink.link] } } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonBodyText.swift index 860b78f8..a30f3f9e 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonBodyText.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableRadioButtonBodyText.swift @@ -47,9 +47,8 @@ open class ListLeftVariableRadioButtonBodyText: TableViewCell { // Make the whole cell focusable. isAccessibilityElement = true - radioButton.isAccessibilityElement = false - accessibilityTraits = radioButton.accessibilityTraits accessibilityHint = radioButton.accessibilityHint + accessibilityTraits = radioButton.accessibilityTraits updateAccessibilityLabel() // Update accessibility label on radio button state change. diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift index 08255db9..4e0eb83a 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift @@ -78,8 +78,6 @@ import Foundation stack.restack() horizontalStack.restack() labelStack.restack() - isAccessibilityElement = true - updateAccessibilityLabel() } //------------------------------------------------------ @@ -115,7 +113,7 @@ import Foundation //------------------------------------------------------ func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let leftHeadlineText = leftHeadline.text { @@ -135,5 +133,6 @@ import Foundation } accessibilityLabel = message + accessibilityTraits = (accessoryView != nil) ? .button : .none } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift index a5d2a374..6e5a7245 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift @@ -43,7 +43,6 @@ import Foundation super.setupView() addMolecule(stack) stack.restack() - updateAccessibilityLabel() } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { @@ -72,8 +71,7 @@ import Foundation // MARK: - Accessibility //-------------------------------------------------- - func updateAccessibilityLabel() { - + func getAccessibilityMessage() -> String? { var message = "" if let eyebrowLabel = eyebrow.text, !eyebrowLabel.isEmpty { @@ -91,19 +89,31 @@ import Foundation if let bodyLabel = body.text, !bodyLabel.isEmpty { message += bodyLabel } - + return message.count > 0 ? message : nil + } + + // Ensures voice over does not read "selected" after user triggers action on cell. + override public var accessibilityTraits: UIAccessibilityTraits { + get { + return (accessoryView != nil) ? .button : .none + } + set {} + } + + func updateAccessibilityLabel() { + let linkShowing = link.titleLabel?.text?.count ?? 0 > 0 isAccessibilityElement = !linkShowing - link.isAccessibilityElement = linkShowing - if !linkShowing { // Make whole cell focusable if no link. - accessibilityLabel = message + accessibilityLabel = getAccessibilityMessage() + } else if let accessoryView = accessoryView { + // Both caret and link. Read all content on caret. + accessoryView.accessibilityLabel = getAccessibilityMessage() + accessibilityElements = [accessoryView, link] } else { - // Allow only radio button and link to be focused on. - var elements: [UIView] = [] - - + // Only link. Manually add accessibility elements to ensure they are read in the right order. + var elements: [Any] = [] if let eyeBrowText = eyebrow.text, !eyeBrowText.isEmpty { elements.append(eyebrow) } @@ -119,11 +129,7 @@ import Foundation if let bodyText = body.text, !bodyText.isEmpty { elements.append(body) } - - if linkShowing { - elements.append(link) - } - + elements.append(link) accessibilityElements = elements } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift index bad9caca..cced55dd 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift @@ -24,8 +24,6 @@ import Foundation super.setupView() addMolecule(headlineBody) - isAccessibilityElement = true - updateAccessibilityLabel() } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?){ @@ -46,7 +44,7 @@ import Foundation //----------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let headlineLabel = headlineBody.headlineLabel.text { diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRVWheel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRVWheel.swift index f410c635..58b9ada0 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRVWheel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRVWheel.swift @@ -41,8 +41,6 @@ import Foundation rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) addMolecule(stack) stack.restack() - isAccessibilityElement = true - updateAccessibilityLabel() } //------------------------------------------------- @@ -75,7 +73,7 @@ import Foundation //-------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let leftLabelText = leftLabel.text, !leftLabelText.isEmpty { @@ -91,5 +89,6 @@ import Foundation } accessibilityLabel = message + accessibilityTraits = (accessoryView != nil) ? .button : .none } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinks.swift index 6e9503dc..f0d5cdbf 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinks.swift @@ -47,9 +47,9 @@ import Foundation addMolecule(stack) stack.restack() - updateAccessibilityLabel() accessibilityValue = button.accessibilityValue accessibilityHint = button.accessibilityHint + accessibilityTraits = .button } //----------------------------------------------------- @@ -74,48 +74,36 @@ import Foundation // MARK: - Accessibility //---------------------------------------------------- + func getAccessibilityMessage() -> String? { + guard let buttonText = button.titleLabel?.text else { + return eyebrowHeadlineBodyLink.getAccessibilityMessage() + } + guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { + return buttonText + } + return label + ", " + buttonText + } + func updateAccessibilityLabel() { - - var message = "" - - if let eyebrowLabel = eyebrowHeadlineBodyLink.eyebrow.text { - message += eyebrowLabel + ", " - } - - if let headlineLabel = eyebrowHeadlineBodyLink.headline.text { - message += headlineLabel + ", " - } - - if let bodyLabel = eyebrowHeadlineBodyLink.body.text { - message += bodyLabel + ", " - } - - if let buttonLabel = button.accessibilityLabel { - message += buttonLabel - } - let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 - - isAccessibilityElement = !linkShowing - button.isAccessibilityElement = linkShowing - eyebrowHeadlineBodyLink.link.isAccessibilityElement = linkShowing - - if !linkShowing { - // Make whole cell focusable if no link. - accessibilityLabel = message + if !linkShowing && accessoryView == nil { + // Make whole cell focusable if one action + isAccessibilityElement = true + accessibilityLabel = getAccessibilityMessage() } else { - // Allow only radio button and link to be focused on. - button.accessibilityLabel = message - var elements: [UIView] = [] - - if !message.isEmpty { - elements.append(button) + // Make buttons focusable. + isAccessibilityElement = false + var elements: [Any] = [] + if let accessoryView = accessoryView { + accessoryView.accessibilityLabel = eyebrowHeadlineBodyLink.getAccessibilityMessage() + elements.append(accessoryView) + } else { + button.accessibilityLabel = getAccessibilityMessage() } - + elements.append(button) if linkShowing { elements.append(eyebrowHeadlineBodyLink.link) } - accessibilityElements = elements } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePayments.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePayments.swift index 5bb62b90..f6526e10 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePayments.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePayments.swift @@ -40,8 +40,6 @@ import Foundation rightImage.addSizeConstraintsForAspectRatio = true addMolecule(stack) stack.restack() - isAccessibilityElement = true - updateAccessibilityLabel() } //---------------------------------------------------- @@ -72,17 +70,18 @@ import Foundation //-------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let leftLabelText = leftLabel.text, !leftLabelText.isEmpty { message += leftLabelText + ", " } - if let rightImageText = rightImage.accessibilityLabel, !rightImageText.isEmpty { + if let rightImageText = rightImage.imageView.accessibilityLabel, !rightImageText.isEmpty { message += rightImageText } accessibilityLabel = message + accessibilityTraits = (accessoryView != nil) ? .button : .none } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinks.swift index afbdd2b5..65301304 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeAllTextAndLinks.swift @@ -67,7 +67,6 @@ import Foundation arrow.pinHeightAndWidth() addMolecule(stack) stack.restack() - updateAccessibilityLabel() } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { @@ -89,57 +88,36 @@ import Foundation // MARK: - Accessibility //-------------------------------------------------- + func getAccessibilityMessage() -> String? { + guard let rightLabelText = rightLabel.text else { + return eyebrowHeadlineBodyLink.getAccessibilityMessage() + } + guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { + return rightLabelText + } + return label + ", " + rightLabelText + } + func updateAccessibilityLabel() { - let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 isAccessibilityElement = !linkShowing - eyebrowHeadlineBodyLink.link.isAccessibilityElement = linkShowing - + accessibilityTraits = (isAccessibilityElement && accessoryView != nil) ? .button : .none if !linkShowing { // Make whole cell focusable if no link. - var message = "" - - if let eyebrowLabel = eyebrowHeadlineBodyLink.eyebrow.text { - message += eyebrowLabel + ", " - } - - if let headlineLabel = eyebrowHeadlineBodyLink.headline.text { - message += headlineLabel + ", " - } - - if let bodyLabel = eyebrowHeadlineBodyLink.body.text { - message += bodyLabel + ", " - } - - if let rightLabelText = rightLabel.text { - message += rightLabelText - } - - accessibilityLabel = message - + accessibilityLabel = getAccessibilityMessage() + } else if let accessoryView = accessoryView { + // Both caret and link. Read all content on caret. + accessoryView.accessibilityLabel = getAccessibilityMessage() + accessibilityElements = [accessoryView, eyebrowHeadlineBodyLink.link] } else { - var elements: [UIView] = [] - - if let eyebrowText = eyebrowHeadlineBodyLink.eyebrow.text, !eyebrowText.isEmpty { - elements.append(eyebrowHeadlineBodyLink.eyebrow) + // Only link. Manually add accessibility elements to ensure they are read in the right order. + var elements: [Any] = [] + if let otherElements = eyebrowHeadlineBodyLink.getAccessibilityElements() { + elements.append(otherElements) } - - if let headlineText = eyebrowHeadlineBodyLink.headline.text, !headlineText.isEmpty { - elements.append(eyebrowHeadlineBodyLink.headline) - } - - if let bodyText = eyebrowHeadlineBodyLink.body.text, !bodyText.isEmpty { - elements.append(eyebrowHeadlineBodyLink.body) - } - - if linkShowing { - elements.append(eyebrowHeadlineBodyLink.link) - } - if let rightLabelText = rightLabel.text, !rightLabelText.isEmpty { elements.append(rightLabel) } - accessibilityElements = elements } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyText.swift index a93d50f9..8428dccc 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyText.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariablePriceChangeBodyText.swift @@ -54,8 +54,6 @@ import Foundation addMolecule(stack) stack.restack() arrowAndRightLabelStack.restack() - isAccessibilityElement = true - updateAccessibilityLabel() } //-------------------------------------------------- @@ -87,7 +85,7 @@ import Foundation //-------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let headlineText = headlineBody.headlineLabel.text, !headlineText.isEmpty { @@ -103,5 +101,6 @@ import Foundation } accessibilityLabel = message + accessibilityTraits = (accessoryView != nil) ? .button : .none } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTextLinkAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTextLinkAllTextAndLinks.swift index 2dd2ca99..2c2f25bc 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTextLinkAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTextLinkAllTextAndLinks.swift @@ -47,7 +47,6 @@ import Foundation addMolecule(stack) stack.restack() - updateAccessibilityLabel() } //-------------------------------------------------- @@ -72,52 +71,41 @@ import Foundation // MARK: - Accessibility //-------------------------------------------------- + func getAccessibilityMessage() -> String? { + guard let linkText = link.titleLabel?.text else { + return eyebrowHeadlineBodyLink.getAccessibilityMessage() + } + guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { + return linkText + } + return label + ", " + linkText + } + func updateAccessibilityLabel() { - - var message = "" - - if let eyebrowLabel = eyebrowHeadlineBodyLink.eyebrow.text { - message += eyebrowLabel + ", " - } - - if let headlineLabel = eyebrowHeadlineBodyLink.headline.text { - message += headlineLabel + ", " - } - - if let bodyLabel = eyebrowHeadlineBodyLink.body.text { - message += bodyLabel + ", " - } - - if let linkLabel = link.accessibilityLabel { - message += linkLabel - } - let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 - - isAccessibilityElement = !linkShowing - link.isAccessibilityElement = linkShowing - eyebrowHeadlineBodyLink.link.isAccessibilityElement = linkShowing - - if !linkShowing { - // Make whole cell focusable if no link. - accessibilityLabel = message + accessibilityTraits = .button + if !linkShowing && accessoryView == nil { + // Make whole cell focusable if one action + isAccessibilityElement = true + accessibilityLabel = getAccessibilityMessage() } else { - // Allow only radio button and link to be focused on. - link.accessibilityLabel = message - var elements: [UIView] = [] - + // Make buttons focusable. + isAccessibilityElement = false + var elements: [Any] = [] + if let accessoryView = accessoryView { + accessoryView.accessibilityLabel = eyebrowHeadlineBodyLink.getAccessibilityMessage() + elements.append(accessoryView) + } else { + link.accessibilityLabel = getAccessibilityMessage() + } + elements.append(link) if linkShowing { elements.append(eyebrowHeadlineBodyLink.link) } - - if !message.isEmpty { - elements.append(link) - } - accessibilityElements = elements } } - + open override func accessibilityActivate() -> Bool { return link.accessibilityActivate() } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableToggleAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableToggleAllTextAndLinks.swift new file mode 100644 index 00000000..a631616b --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableToggleAllTextAndLinks.swift @@ -0,0 +1,114 @@ +// +// ListRightVariableToggleAllTextAndLinks.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 6/25/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ListRightVariableToggleAllTextAndLinks: TableViewCell { + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + + public let toggle = Toggle() + public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink() + + //----------------------------------------------------- + // MARK: - Properties + //----------------------------------------------------- + + public var stack: Stack! + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: eyebrowHeadlineBodyLink, model: StackItemModel(horizontalAlignment: .leading)), + (view: toggle, model: StackItemModel(horizontalAlignment: .fill))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + //----------------------------------------------------- + // MARK: - Lifecycle + //----------------------------------------------------- + + override open func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + //----------------------------------------------------- + // MARK: - Molecular + //----------------------------------------------------- + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListRightVariableToggleAllTextAndLinksModel else { return } + toggle.set(with: model.toggle, delegateObject, additionalData) + eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData) + updateAccessibilityLabel() + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 90 + } + + //---------------------------------------------------- + // MARK: - Accessibility + //---------------------------------------------------- + + func getAccessibilityMessage() -> String? { + guard let toggleMessage = toggle.accessibilityLabel else { + return eyebrowHeadlineBodyLink.getAccessibilityMessage() + } + guard let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() else { + return toggleMessage + } + return label + ", " + toggleMessage + } + + func updateAccessibilityLabel() { + accessibilityHint = toggle.accessibilityHint + accessibilityTraits = toggle.accessibilityTraits + accessibilityValue = toggle.accessibilityValue + let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 + if !linkShowing && accessoryView == nil { + // Make whole cell focusable if one action + isAccessibilityElement = true + accessibilityLabel = getAccessibilityMessage() + } else { + // Make buttons focusable. + isAccessibilityElement = false + var elements: [Any] = [] + if let accessoryView = accessoryView { + accessoryView.accessibilityLabel = eyebrowHeadlineBodyLink.getAccessibilityMessage() + elements.append(accessoryView) + } else { + toggle.accessibilityLabel = getAccessibilityMessage() + } + elements.append(toggle) + + if linkShowing { + elements.append(eyebrowHeadlineBodyLink.link) + } + accessibilityElements = elements + } + } + + open override func accessibilityActivate() -> Bool { + let activate = toggle.accessibilityActivate() + accessibilityValue = toggle.accessibilityValue + return activate + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableToggleAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableToggleAllTextAndLinksModel.swift new file mode 100644 index 00000000..9fdc16ca --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableToggleAllTextAndLinksModel.swift @@ -0,0 +1,58 @@ +// +// ListRightVariableToggleAllTextAndLinksModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 6/25/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListRightVariableToggleAllTextAndLinksModel: ListItemModel, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "listRVTgl" + public var toggle: ToggleModel + public var eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(toggle: ToggleModel, eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel) { + self.toggle = toggle + self.eyebrowHeadlineBodyLink = eyebrowHeadlineBodyLink + super.init() + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case toggle + case eyebrowHeadlineBodyLink + } + + //-------------------------------------------------- + // MARK: - codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + toggle = try typeContainer.decode(ToggleModel.self, forKey: .toggle) + eyebrowHeadlineBodyLink = try typeContainer.decode(EyebrowHeadlineBodyLinkModel.self, forKey: .eyebrowHeadlineBodyLink) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(toggle, forKey: .toggle) + try container.encode(eyebrowHeadlineBodyLink, forKey: .eyebrowHeadlineBodyLink) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalData.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalData.swift index a42cd81d..fe2c8a24 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalData.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableTotalData.swift @@ -51,8 +51,6 @@ import Foundation rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) addMolecule(stack) stack.restack() - isAccessibilityElement = true - updateAccessibilityLabel() } //-------------------------------------------------- @@ -86,7 +84,7 @@ import Foundation //-------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let leftLabelText = leftLabel.text, !leftLabelText.isEmpty { @@ -98,5 +96,6 @@ import Foundation } accessibilityLabel = message + accessibilityTraits = (accessoryView != nil) ? .button : .none } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift index 00a09527..6e0013d1 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnDataUsage.swift @@ -42,8 +42,6 @@ import Foundation super.setupView() addMolecule(stack) stack.restack() - isAccessibilityElement = true - updateAccessibilityLabel() } //-------------------------------------------------- @@ -77,7 +75,7 @@ import Foundation //-------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let leftText = leftLabel.text, !leftText.isEmpty { diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnInternationalData.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnInternationalData.swift index 20e7378a..c8aaef83 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnInternationalData.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ThreeColumn/ListThreeColumnInternationalData.swift @@ -55,8 +55,6 @@ import Foundation arrow.pinHeightAndWidth() arrowAndLabel2Stack.restack() stack.restack() - isAccessibilityElement = true - updateAccessibilityLabel() } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { @@ -87,7 +85,7 @@ import Foundation //-------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let leftText = leftLabel.text, !leftText.isEmpty { diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift index e0efa546..62048a71 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnCompareChanges.swift @@ -54,7 +54,6 @@ import Foundation ((molecule as? StackItem)?.view as? Stack)?.restack() } containingStack.restack() - isAccessibilityElement = false } //------------------------------------------------------ diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift index 16328584..472c522d 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDescription.swift @@ -67,9 +67,6 @@ import Foundation leftHeadline.numberOfLines = 1 rightLabel.numberOfLines = 1 rightSubLabel.numberOfLines = 1 - - isAccessibilityElement = true - updateAccessibilityLabel() } //---------------------------------------------------- @@ -107,7 +104,7 @@ import Foundation //---------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let leftHeadline = leftHeadline.text, !leftHeadline.isEmpty { diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift index e725bb64..cc59a15a 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/TwoColumn/ListTwoColumnPriceDetails.swift @@ -31,8 +31,6 @@ import UIKit containerHelper.constrainView(view) rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) - isAccessibilityElement = true - updateAccessibilityLabel() } open override func updateView(_ size: CGFloat) { @@ -72,7 +70,7 @@ import UIKit //---------------------------------------------------- func updateAccessibilityLabel() { - + isAccessibilityElement = true var message = "" if let leftLabel = leftLabel.text, !leftLabel.isEmpty { diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift index 94e53790..1068f350 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift @@ -56,7 +56,7 @@ import Foundation // Add buttons var tabs: [UITabBarItem] = [] for (index, tab) in model.tabs.enumerated() { - let tabBarItem = UITabBarItem(title: tab.title, image: UIImage(named: tab.image, in: MVMCoreCache.shared()?.bundleToUseForImages(), compatibleWith: nil), tag: index) + let tabBarItem = UITabBarItem(title: tab.title, image: MVMCoreCache.shared()?.getImageFromRegisteredBundles(tab.image), tag: index) tabs.append(tabBarItem) } setItems(tabs, animated: false) diff --git a/MVMCoreUI/Atomic/Molecules/Items/CarouselItemModel.swift b/MVMCoreUI/Atomic/Molecules/Items/CarouselItemModel.swift index 15a83552..1b9ce0dc 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/CarouselItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/CarouselItemModel.swift @@ -20,6 +20,7 @@ import Foundation public var peakingUI: Bool? public var peakingArrowColor: Color? + public var analyticsData: JSONValueDictionary? //-------------------------------------------------- // MARK: - Keys @@ -28,6 +29,7 @@ import Foundation private enum CodingKeys: String, CodingKey { case peakingUI case peakingArrowColor + case analyticsData } //-------------------------------------------------- @@ -38,6 +40,7 @@ import Foundation let typeContainer = try decoder.container(keyedBy: CodingKeys.self) peakingUI = try typeContainer.decodeIfPresent(Bool.self, forKey: .peakingUI) peakingArrowColor = try typeContainer.decodeIfPresent(Color.self, forKey: .peakingArrowColor) + analyticsData = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .analyticsData) try super.init(from: decoder) } @@ -46,5 +49,6 @@ import Foundation var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(peakingUI, forKey: .peakingUI) try container.encodeIfPresent(peakingArrowColor, forKey: .peakingArrowColor) + try container.encodeIfPresent(analyticsData, forKey: .analyticsData) } } diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift index aff9c58f..4c021d5f 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/Buttons/NavigationImageButtonModel.swift @@ -11,34 +11,34 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule public var backgroundColor: Color? public static var identifier: String = "navigationImageButton" - public var imageName: String + public var image: String public var action: ActionModelProtocol - public init(with imageName: String, action: ActionModelProtocol) { - self.imageName = imageName + public init(with image: String, action: ActionModelProtocol) { + self.image = image self.action = action } private enum CodingKeys: String, CodingKey { - case imageName + case image case action } required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - imageName = try typeContainer.decode(String.self, forKey: .imageName) + image = try typeContainer.decode(String.self, forKey: .image) action = try typeContainer.decodeModel(codingKey: .action) } open func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(imageName, forKey: .imageName) + try container.encode(image, forKey: .image) try container.encodeModel(action, forKey: .action) } /// Convenience function that creates a BarButtonItem for the model. public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> UIBarButtonItem { - let image = UIImage(named: imageName, in: MVMCoreCache.shared()?.bundleToUseForImages(), compatibleWith: nil) - return ImageBarButtonItem.create(with: image, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) + let uiImage = MVMCoreCache.shared()?.getImageFromRegisteredBundles(image) + return ImageBarButtonItem.create(with: uiImage, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) } } diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index bf926878..d7d677fa 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -18,8 +18,8 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt public var backgroundColor: Color? public var tintColor: Color public var line: LineModel? - public var alwaysShowBackButton = false - public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? = NavigationImageButtonModel(with: "back", action: ActionBackModel()) + public var alwaysShowBackButton: Bool? + public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? = NavigationImageButtonModel(with: "nav_back", action: ActionBackModel()) public var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? public var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? @@ -51,8 +51,8 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt hidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .hidden) ?? false backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .white) tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black) - line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) - alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton) ?? false + line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) ?? LineModel(type: .standard) + alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton) if let backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol) = try typeContainer.decodeModelIfPresent(codingKey: .backButton) { self.backButton = backButton } @@ -68,7 +68,7 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encode(tintColor, forKey: .tintColor) try container.encodeIfPresent(line, forKey: .line) - try container.encode(alwaysShowBackButton, forKey: .alwaysShowBackButton) + try container.encodeIfPresent(alwaysShowBackButton, forKey: .alwaysShowBackButton) try container.encodeModelIfPresent(backButton, forKey: .backButton) try container.encodeModelsIfPresent(additionalLeftButtons, forKey: .additionalLeftButtons) try container.encodeModelsIfPresent(additionalRightButtons, forKey: .additionalRightButtons) diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift index 7d176ba2..12b42e9f 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift @@ -69,4 +69,44 @@ import UIKit open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 65 } + + //-------------------------------------------------- + // MARK: - Accessibility Helpers + //-------------------------------------------------- + + /// Returns the labels text in one message. + func getAccessibilityMessage() -> String? { + var message = "" + + if let eyebrowLabel = eyebrow.text { + message += eyebrowLabel + ", " + } + + if let headlineLabel = headline.text { + message += headlineLabel + ", " + } + + if let bodyLabel = body.text { + message += bodyLabel + } + return message.count > 0 ? message : nil + } + + /// Returns an array of the appropriate accessibility elements. + func getAccessibilityElements() -> [Any]? { + var elements: [UIView] = [] + if eyebrow.hasText { + elements.append(eyebrow) + } + if headline.hasText { + elements.append(headline) + } + if body.hasText { + elements.append(body) + } + if link.titleLabel?.text?.count ?? 0 > 0 { + elements.append(link) + } + return elements.count > 0 ? elements : nil + } } diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/ThreeHeadlineBodyLink.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/ThreeHeadlineBodyLink.swift index 474907dd..5926951f 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/ThreeHeadlineBodyLink.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/ThreeHeadlineBodyLink.swift @@ -73,8 +73,8 @@ open class ThreeHeadlineBodyLink: View { bottomAnchor.constraint(equalTo: link.bottomAnchor).isActive = true isAccessibilityElement = true - accessibilityTraits = link.accessibilityTraits accessibilityHint = link.accessibilityHint + accessibilityTraits = link.accessibilityTraits updateAccessibilityLabel() } diff --git a/MVMCoreUI/Atomic/Organisms/Carousel.swift b/MVMCoreUI/Atomic/Organisms/Carousel.swift index abc0e74b..808c370e 100644 --- a/MVMCoreUI/Atomic/Organisms/Carousel.swift +++ b/MVMCoreUI/Atomic/Organisms/Carousel.swift @@ -297,6 +297,12 @@ open class Carousel: View { cell.accessibilityElementsHidden = true } } + + func trackSwipeActionAnalyticsforIndex(_ index : Int){ + guard let itemModel = molecules?[index], + let viewControllerObject = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else { return } + MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: viewControllerObject, actionInformation: itemModel.toJSON(), additionalData: nil) + } } extension Carousel: UICollectionViewDelegateFlowLayout { @@ -454,5 +460,7 @@ extension Carousel: UIScrollViewDelegate { // Cycle to other end if on buffer cell. pagingView?.currentIndex = pageIndex showPeaking(true) + // track analyticsData + trackSwipeActionAnalyticsforIndex(pageIndex) } } diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift index 198cf80d..c2ade02d 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift @@ -10,4 +10,12 @@ import Foundation public protocol CarouselItemModelProtocol: ContainerModelProtocol { + var analyticsData: JSONValueDictionary? { get set } +} +public extension CarouselItemModelProtocol { + + var analyticsData: JSONValueDictionary? { + get { return nil } + set { analyticsData = newValue } + } } diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift index 30db2aa6..084509e0 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift @@ -14,7 +14,7 @@ public protocol NavigationItemModelProtocol { var backgroundColor: Color? { get set } var tintColor: Color { get set } var line: LineModel? { get set } - var alwaysShowBackButton: Bool { get set } + var alwaysShowBackButton: Bool? { get set } var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? { get set } var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? { get set } var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? { get set } diff --git a/MVMCoreUI/BaseClasses/TableViewCell.swift b/MVMCoreUI/BaseClasses/TableViewCell.swift index caaeec85..300150e8 100644 --- a/MVMCoreUI/BaseClasses/TableViewCell.swift +++ b/MVMCoreUI/BaseClasses/TableViewCell.swift @@ -205,8 +205,8 @@ import UIKit let caret = CaretView(lineWidth: 1) caret.translatesAutoresizingMaskIntoConstraints = true caret.isAccessibilityElement = true - caret.accessibilityTraits = .button caret.accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "AccTabHint") + caret.accessibilityTraits = .button caret.size = .small(.vertical) if let size = caret.size?.dimensions() { caret.frame = CGRect(origin: CGPoint.zero, size: size) diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 395587ec..8c63ed51 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -8,7 +8,7 @@ import UIKit -@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate { +@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol { @objc public var pageType: String? @objc public var loadObject: MVMCoreLoadObject? public var pageModel: MVMControllerModelProtocol? @@ -119,6 +119,7 @@ import UIKit return true } catch let parsingError { // Log all parsing errors and fail load. + handleLoggingFor(parsingError: parsingError) if let errorObject = MVMCoreErrorObject.createErrorObject(for: parsingError, location: MVMCoreLoadHandler.sharedGlobal()?.errorLocation(forRequest: loadObject)) { errorObject.messageToDisplay = MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess) error.pointee = errorObject @@ -127,6 +128,33 @@ import UIKit } } + func handleLoggingFor(parsingError: Error) { + if let registryError = parsingError as? ModelRegistry.Error { + switch (registryError) { + case .decoderErrorModelNotMapped(let identifier, let codingKey, let codingPath) where identifier != nil && codingKey != nil && codingPath != nil: + MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Model identifier \"\(identifier!)\" is not mapped for \"\(codingKey!.stringValue)\" @ \(codingPath!.map { return $0.stringValue })") + case .decoderErrorObjectNotPresent(let codingKey, codingPath: let codingPath): + MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Required model \"\(codingKey.stringValue)\" was not found @ \(codingPath.map { return $0.stringValue })") + default: + MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Registry error: \(registryError)") + } + } + if let decodingError = parsingError as? DecodingError { + switch (decodingError) { + case .keyNotFound(let codingKey, let context): + MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Key \(codingKey.stringValue) was not found @ \(context.codingPath.map { return $0.stringValue })") + case .valueNotFound(_, let context): + MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Value not found @ \(context.codingPath.map { return $0.stringValue })") + case .typeMismatch(_, let context): + MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Type mismatch @ \(context.codingPath.map { return $0.stringValue })") + case .dataCorrupted(let context): + MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Data corrupted @ \(context.codingPath.map { return $0.stringValue })") + @unknown default: + MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: \(parsingError)") + } + } + } + open func parsePageJSON() throws { } @@ -163,9 +191,6 @@ import UIKit open func createDefaultLegacyNavigationModel() -> NavigationItemModel { let navigationModel = NavigationItemModel() navigationModel.title = pageModel?.screenHeading - if /*(self as? MVMCoreUITabBarPageControlViewController) != nil ||*/ manager != nil || loadObject?.requestParameters?.tabWasPressed ?? false == true { - navigationModel.line = LineModel(type: .none) - } return navigationModel } @@ -199,18 +224,8 @@ import UIKit // We additionally want our left items navigationItem.leftItemsSupplementBackButton = true - NavigationController.set(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) - - // Special logic when using the split view controller. Legacy Update Panels. Change how this is done. - guard navigationController == MVMCoreUISplitViewController.main()?.navigationController, - navigationController.topViewController == viewController else { return } - - MVMCoreUISession.sharedGlobal()?.splitViewController?.setupPanels() - showBottomProgressBar() - - MVMCoreUISplitViewController.main()?.setLeftPanelIsAccessible(isMasterInitiallyAccessible(), for: viewController) - MVMCoreUISplitViewController.main()?.setRightPanelIsAccessible(isSupportInitiallyAccessible(), for: viewController) - MVMCoreUISession.sharedGlobal()?.splitViewController?.setNavigationIconColor(navigationItemModel.tintColor.uiColor) + // Utilize helper function to set the split view and navigation item state. + MVMCoreUISplitViewController.setSplitViewController(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel, leftPanelAccessible: isMasterInitiallyAccessible(), rightPanelAccessible: isSupportInitiallyAccessible(), progress: bottomProgress() ?? 0) } // Eventually will be moved to server @@ -252,12 +267,10 @@ import UIKit } // Eventually will be moved to Model - open func showBottomProgressBar() { - if MVMCoreUISplitViewController.main()?.getCurrentVisibleController() == self, - let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), - let progress = Float(progressString) { - MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(progress / Float(100)) - } + open func bottomProgress() -> Float? { + guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), + let progress = Float(progressString) else { return nil } + return (progress / Float(100)) } // MARK: - TabBar @@ -321,24 +334,23 @@ import UIKit super.viewDidLayoutSubviews() } - open override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) + open func pageShown() { + // Update the navigation bar ui when view is appearing. + setNavigationController() - // Update the navigation bar ui when view is appearing. Can remove check in the future, see viewControllerReady - if manager == nil { - setNavigationController() - } + // Update tab if needed. + updateTabBar() + + // Track. + MVMCoreUISession.sharedGlobal()?.currentPageType = pageType + MVMCoreUILoggingHandler.shared()?.defaultLogPageState(forController: self) } open override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - // Update tab if needed. - updateTabBar() - if manager == nil { - MVMCoreUISession.sharedGlobal()?.currentPageType = pageType - MVMCoreUILoggingHandler.shared()?.defaultLogPageState(forController: self) + pageShown() } executeBehaviors { (behavior: PageVisibilityBehavior) in @@ -375,13 +387,7 @@ import UIKit // MARK: - MVMCoreViewManagerViewControllerProtocol open func viewControllerReady(inManager manager: UIViewController & MVMCoreViewManagerProtocol) { - // TODO: This check and set aren't technically needed anymore. The one in viewwillappear should be enough. However, there is a timing issue with the manager where the screen lays out before the menu shows, so the screen grows off the screen. Can fix in the future. - if let _ = self.view { - setNavigationController() - } - // Janky way to track current page. - MVMCoreUISession.sharedGlobal()?.currentPageType = pageType - MVMCoreUILoggingHandler.shared()?.defaultLogPageState(forController: self) + pageShown() } // MARK: - MVMCoreLoadDelegateProtocol @@ -443,6 +449,13 @@ import UIKit open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) {} open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) {} + // MARK: - MVMCoreUIDetailViewProtocol + + // Reset the navigation state. + public func splitViewDidReset() { + setNavigationController() + } + // MARK: - UITextFieldDelegate (Check if this is still needed) // To Remove TextFields Bug: Keyboard is not dismissing after reaching textfield max length limit open func textFieldShouldReturn(_ textField: UITextField) -> Bool { diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 0d4d0a56..2803792f 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -47,13 +47,15 @@ import UIKit public static func setNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject var leftItems: [UIBarButtonItem] = [] - if let backButtonModel = navigationItemModel.backButton, - navigationController.viewControllers.count > 1 || navigationItemModel.alwaysShowBackButton { - leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) - } - if let leftItemModels = navigationItemModel.additionalLeftButtons { - for item in leftItemModels { - leftItems.append(item.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + if navigationItemModel.alwaysShowBackButton != false { + if let backButtonModel = navigationItemModel.backButton, + navigationController.viewControllers.count > 1 || navigationItemModel.alwaysShowBackButton ?? false { + leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } + if let leftItemModels = navigationItemModel.additionalLeftButtons { + for item in leftItemModels { + leftItems.append(item.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } } } viewController.navigationItem.leftBarButtonItems = leftItems.count > 0 ? leftItems : nil @@ -67,8 +69,8 @@ import UIKit viewController.navigationItem.rightBarButtonItems = rightItems.count > 0 ? rightItems : nil } - /// Convenience function for setting navigation bar with model. - public static func set(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { + /// Convenience function for setting the navigation bar ui, except for the buttons. + public static func setNavigationUI(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { viewController.navigationItem.title = navigationItemModel.title viewController.navigationItem.accessibilityLabel = navigationItemModel.title viewController.navigationItem.hidesBackButton = (navigationItemModel.backButton != nil) @@ -86,8 +88,11 @@ import UIKit if let navigationController = navigationController as? NavigationController { navigationController.separatorView?.isHidden = navigationItemModel.line?.type ?? .standard == .none } + } - // Sets up the navigation buttons. + /// Convenience function for setting navigation bar with model. + public static func set(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { + setNavigationUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h index 377508c7..2ec18da9 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUIDetailViewProtocol.h @@ -42,6 +42,9 @@ NS_ASSUME_NONNULL_BEGIN - (UIStatusBarStyle)defaultStatusBarStyle; - (nullable UIColor *)defaultStatusBarBackgroundColor; +/// Called when the split view did reset. If this function found, the splitview assumes it is handling the split view state and does not do anything. If you have navigation item buttons, override this function and handle the panels manually. +- (void)splitViewDidReset; + @end NS_ASSUME_NONNULL_END diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift new file mode 100644 index 00000000..e62efa29 --- /dev/null +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -0,0 +1,126 @@ +// +// MVMCoreUISplitViewController+Extension.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 6/18/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +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 setSplitViewController(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, leftPanelAccessible: Bool? = nil, rightPanelAccessible: Bool? = nil, progress: Float? = nil) { + guard let splitView = MVMCoreUISplitViewController.main(), + navigationController == splitView.navigationController, + navigationController.topViewController == viewController else { + NavigationController.set(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + return + } + splitView.set(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel, leftPanelAccessible: leftPanelAccessible, rightPanelAccessible: rightPanelAccessible, progress: progress) + } + + /// Sets the navigation item for the view controller based on the model and splitview controller + func set(for viewController: UIViewController, navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, leftPanelAccessible: Bool? = nil, rightPanelAccessible: Bool? = nil, progress: Float? = nil) { + + // Setup the panels. + setupPanels() + + NavigationController.setNavigationUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + + setLeftPanelIsAccessible(leftPanelAccessible ?? leftPanelIsAccessible, for: viewController, updateNavigationButtons: false) + setRightPanelIsAccessible(rightPanelAccessible ?? rightPanelIsAccessible, 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. + func setLeftNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) { + var leftItems: [UIBarButtonItem] = [] + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject + + // Add back button first. + if navigationItemModel?.alwaysShowBackButton != false { + if let backButtonModel = navigationItemModel?.backButton { + if navigationController.viewControllers.count > 1 || navigationItemModel!.alwaysShowBackButton ?? false { + leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } + } else if let backButton = backButton, + navigationController.viewControllers.count > 1 { + leftItems.append(backButton) + } + } + + // Add the panel button after the back button. + if let panelButton = leftPanelButton, + leftPanelIsAccessible, + !leftPanelStaysExtended() { + leftItems.append(panelButton) + } + + // Add other model buttons + if let leftItemModels = navigationItemModel?.additionalLeftButtons { + for item in leftItemModels { + leftItems.append(item.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } + } + + // Add any buttons added by the splitview. + if let additionalLeftButtons = additionalLeftButtons(for: viewController) { + leftItems.append(contentsOf: additionalLeftButtons) + } + + viewController.navigationItem.setLeftBarButtonItems(leftItems.count > 0 ? leftItems : nil, animated: !DisableAnimations.boolValue) + } + + /// Sets the right navigation items for the view controller based on model and splitview. + func setRightNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) { + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject + var rightItems: [UIBarButtonItem] = [] + + // Add the panel button first. + if let panelButton = rightPanelButton, + rightPanelIsAccessible, + !rightPanelStaysExtended() { + rightItems.append(panelButton) + } + + // Add other model buttons + if let rightItemModels = navigationItemModel?.additionalRightButtons { + for item in rightItemModels { + rightItems.append(item.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } + } + + // Add any buttons added by the splitview. + if let additionalRightButtons = additionalRightButtons(for: viewController) { + rightItems.append(contentsOf: additionalRightButtons) + } + + 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 setSplitViewController(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.set(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.h b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h index 4d6b2cff..c48c9e8c 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h @@ -67,10 +67,10 @@ typedef NS_ENUM(NSInteger, MFNumberOfDrawers) { #pragma mark - Panel Functions // Sets if the left panel accessible for the view controller. Will show or hide the button. -- (void)setLeftPanelIsAccessible:(BOOL)leftPanelIsAccessible forViewController:(nonnull UIViewController *)viewController; +- (void)setLeftPanelIsAccessible:(BOOL)leftPanelIsAccessible forViewController:(nonnull UIViewController *)viewController updateNavigationButtons:(BOOL)updateNavigationButtons; // Sets if the right panel accessible for the view controller. Will show or hide the button. -- (void)setRightPanelIsAccessible:(BOOL)rightPanelIsAccessible forViewController:(nonnull UIViewController *)viewController; +- (void)setRightPanelIsAccessible:(BOOL)rightPanelIsAccessible forViewController:(nonnull UIViewController *)viewController updateNavigationButtons:(BOOL)updateNavigationButtons; - (void)hideLeftPanelIfNeededAnimated:(BOOL)animated; - (void)showLeftPanelAnimated:(BOOL)animated; @@ -94,6 +94,12 @@ typedef NS_ENUM(NSInteger, MFNumberOfDrawers) { /// Updates the panels that are used. - (void)setupPanels; +/// Returns if the left panel is staying extended (usually do to screen size threshold) +- (BOOL)leftPanelStaysExtended; + +/// Returns if the right panel is staying extended (usually do to screen size threshold) +- (BOOL)rightPanelStaysExtended; + #pragma mark - Bottom Progress Bar - (void)setBottomProgressBarProgress:(float)progress; diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index fe8036d2..2a98a418 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -110,11 +110,11 @@ CGFloat const PanelAnimationDuration = 0.2; } - (nullable NSArray *)additionalLeftButtonsForViewController:(nonnull UIViewController *)viewController { - return viewController.navigationItem.leftBarButtonItems; + return nil; } - (nullable NSArray *)additionalRightButtonsForViewController:(nonnull UIViewController *)viewController { - return viewController.navigationItem.rightBarButtonItems; + return nil; } - (CGFloat)leftPanelExtendedWidth { @@ -146,7 +146,7 @@ CGFloat const PanelAnimationDuration = 0.2; } - (nullable UIImage *)imageForBackButton { - return [MVMCoreUIUtility imageNamed:@"back"]; + return [MVMCoreUIUtility imageNamed:@"nav_back"]; } #pragma mark - Button Presses @@ -234,19 +234,27 @@ CGFloat const PanelAnimationDuration = 0.2; } } +- (BOOL)leftPanelStaysExtended { + MFNumberOfDrawers numberOfDrawers = [self numberOfDrawersShouldShow:self.transitionWidth]; + return [self shouldExtendLeftPanel:numberOfDrawers] && (numberOfDrawers != MFOneDrawer || self.explictlyShowingPanel != self.rightPanel); +} + - (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended { NSMutableArray *leftBarButtonItems = [NSMutableArray array]; + if ([viewController.navigationController.viewControllers count] > 1) { + [leftBarButtonItems addObject:self.backButton]; + } + if ((accessible && !extended) && self.leftPanelButton) { + [leftBarButtonItems addObject:self.leftPanelButton]; + } NSArray *extraButtons = [self additionalLeftButtonsForViewController:viewController]; if (extraButtons) { [leftBarButtonItems addObjectsFromArray:extraButtons]; } - if ((accessible && !extended) && self.leftPanelButton && ![extraButtons containsObject:self.leftPanelButton]) { - [leftBarButtonItems addObject:self.leftPanelButton]; - } [viewController.navigationItem setLeftBarButtonItems:(leftBarButtonItems.count > 0 ? leftBarButtonItems : nil) animated:!DisableAnimations]; } -- (void)setLeftPanelIsAccessible:(BOOL)leftPanelIsAccessible forViewController:(UIViewController *)viewController { +- (void)setLeftPanelIsAccessible:(BOOL)leftPanelIsAccessible forViewController:(UIViewController *)viewController updateNavigationButtons:(BOOL)updateNavigationButtons { if ([self.leftPanel respondsToSelector:@selector(panelAvailable)]) { self.leftPanelIsAccessible = leftPanelIsAccessible && [self.leftPanel panelAvailable]; } else { @@ -262,8 +270,7 @@ CGFloat const PanelAnimationDuration = 0.2; } else { // Determine if we should show the panel (extended based on width or from explicit action). - MFNumberOfDrawers numberOfDrawers = [self numberOfDrawersShouldShow:self.transitionWidth]; - extended = [self shouldExtendLeftPanel:numberOfDrawers] && (numberOfDrawers != MFOneDrawer || self.explictlyShowingPanel != self.rightPanel); + extended = [self leftPanelStaysExtended]; if (extended || self.explictlyShowingPanel == self.leftPanel) { [self showLeftPanelAnimated:NO explict:NO]; } @@ -272,7 +279,9 @@ CGFloat const PanelAnimationDuration = 0.2; [self.leftPanel resetIconToDefault]; } } - [self setLeftNavigationItemForViewController:viewController accessible:self.leftPanelIsAccessible extended:extended]; + if (updateNavigationButtons) { + [self setLeftNavigationItemForViewController:viewController accessible:self.leftPanelIsAccessible extended:extended]; + } }]; } @@ -407,10 +416,10 @@ CGFloat const PanelAnimationDuration = 0.2; - (void)setRightNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended { NSMutableArray *navigationItems = [[NSMutableArray alloc] init]; - NSArray *extraButtons = [self additionalRightButtonsForViewController:viewController]; - if ((accessible && !extended) && self.rightPanelButton && ![extraButtons containsObject:self.rightPanelButton]) { + if ((accessible && !extended) && self.rightPanelButton) { [navigationItems addObject:self.rightPanelButton]; } + NSArray *extraButtons = [self additionalRightButtonsForViewController:viewController]; if (extraButtons) { [navigationItems addObjectsFromArray:extraButtons]; } @@ -440,7 +449,12 @@ CGFloat const PanelAnimationDuration = 0.2; } } -- (void)setRightPanelIsAccessible:(BOOL)rightPanelIsAccessible forViewController:(UIViewController *)viewController { +- (BOOL)rightPanelStaysExtended { + MFNumberOfDrawers numberOfDrawers = [self numberOfDrawersShouldShow:self.transitionWidth]; + return [self shouldExtendRightPanel:numberOfDrawers] && (numberOfDrawers != MFOneDrawer || self.explictlyShowingPanel != self.leftPanel); +} + +- (void)setRightPanelIsAccessible:(BOOL)rightPanelIsAccessible forViewController:(UIViewController *)viewController updateNavigationButtons:(BOOL)updateNavigationButtons { if ([self.rightPanel respondsToSelector:@selector(panelAvailable)]) { self.rightPanelIsAccessible = rightPanelIsAccessible && [self.rightPanel panelAvailable]; @@ -458,8 +472,7 @@ CGFloat const PanelAnimationDuration = 0.2; } else { // Determine if we should show the right panel (extended based on width or from explicit action). - MFNumberOfDrawers numberOfDrawers = [self numberOfDrawersShouldShow:self.transitionWidth]; - extended = [self shouldExtendRightPanel:numberOfDrawers] && (numberOfDrawers != MFOneDrawer || self.explictlyShowingPanel != self.leftPanel); + extended = [self rightPanelStaysExtended]; if (extended || self.explictlyShowingPanel == self.rightPanel) { [self showRightPanelAnimated:NO explict:NO]; } @@ -468,7 +481,10 @@ CGFloat const PanelAnimationDuration = 0.2; [self.rightPanel resetIconToDefault]; } } - [self setRightNavigationItemForViewController:viewController accessible:rightPanelIsAccessible extended:extended]; + + if (updateNavigationButtons) { + [self setRightNavigationItemForViewController:viewController accessible:rightPanelIsAccessible extended:extended]; + } }]; } @@ -918,7 +934,7 @@ CGFloat const PanelAnimationDuration = 0.2; self.extendedDrawers = MFExtendedDrawerLeft | MFExtendedDrawerRight; self.prioritizedExtendedPanel = MFExtendedDrawerLeft; - self.view.backgroundColor = [UIColor blackColor]; + self.view.backgroundColor = [UIColor whiteColor]; } - (void)viewDidLayoutSubviews { @@ -974,11 +990,15 @@ CGFloat const PanelAnimationDuration = 0.2; } - (void)resetDrawers { - [self setLeftPanelIsAccessible:self.leftPanelIsAccessible forViewController:self.navigationItemViewController]; - [self setRightPanelIsAccessible:self.rightPanelIsAccessible forViewController:self.navigationItemViewController]; - - [self.view setNeedsLayout]; - [self.view layoutIfNeeded]; + if ([self.navigationItemViewController respondsToSelector:@selector(splitViewDidReset)]) { + [((UIViewController *)self.navigationItemViewController) splitViewDidReset]; + } else { + [self setLeftPanelIsAccessible:self.leftPanelIsAccessible forViewController:self.navigationItemViewController updateNavigationButtons:YES]; + [self setRightPanelIsAccessible:self.rightPanelIsAccessible forViewController:self.navigationItemViewController updateNavigationButtons:YES]; + + [self.view setNeedsLayout]; + [self.view layoutIfNeeded]; + } } - (UIStatusBarStyle)preferredStatusBarStyle { @@ -1069,6 +1089,7 @@ CGFloat const PanelAnimationDuration = 0.2; if ([self.rightPanelButton respondsToSelector:@selector(setIconColor:)]) { [((UIBarButtonItem *)self.rightPanelButton) setIconColor:color]; } + [self.backButton setTintColor:color]; } @end diff --git a/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift b/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift index 5e45b6ed..f88ca5ec 100644 --- a/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift +++ b/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift @@ -13,7 +13,7 @@ public extension Dictionary { throw ModelRegistry.Error.decoderOther(message: "Dictionary is not of type [String: Any]") } guard let actionType = ModelRegistry.getType(for: castedSelf.stringForkey(KeyActionType), with: ActionModelProtocol.self) else { - throw ModelRegistry.Error.decoderErrorModelNotMapped + throw ModelRegistry.Error.decoderErrorModelNotMapped() } guard let actionModel = try actionType.decode(jsonDict: castedSelf) as? ActionModelProtocol else { throw ModelRegistry.Error.decoderOther(message: "Could not decode to ActionModelProtocol") diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/back.png b/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/back.png deleted file mode 100644 index 3d2734b6..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/back.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/back@2x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/back@2x.png deleted file mode 100644 index 1d64be8b..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/back@2x.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/back@3x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/back@3x.png deleted file mode 100644 index 147e2c6a..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/back@3x.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/closeXBlack.png b/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/closeXBlack.png deleted file mode 100644 index 5f31bd28..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/closeXBlack.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/closeXBlack@2x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/closeXBlack@2x.png deleted file mode 100644 index d966cb74..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/closeXBlack@2x.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/closeXBlack@3x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/closeXBlack@3x.png deleted file mode 100644 index 59847565..00000000 Binary files a/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/closeXBlack@3x.png and /dev/null differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/Contents.json b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Contents.json similarity index 70% rename from MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/Contents.json rename to MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Contents.json index 3bad3d0c..65a64dfd 100644 --- a/MVMCoreUI/SupportingFiles/Media.xcassets/back.imageset/Contents.json +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Contents.json @@ -2,17 +2,17 @@ "images" : [ { "idiom" : "universal", - "filename" : "back.png", + "filename" : "nav_back.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "back@2x.png", + "filename" : "nav_back@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "back@3x.png", + "filename" : "nav_back@3x.png", "scale" : "3x" } ], diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back.png new file mode 100644 index 00000000..2592ddda Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back.png differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@2x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@2x.png new file mode 100644 index 00000000..077a411d Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@2x.png differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@3x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@3x.png new file mode 100644 index 00000000..f2ac8c73 Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/nav_back@3x.png differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/Contents.json b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Contents.json similarity index 69% rename from MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/Contents.json rename to MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Contents.json index d7771f8a..d328ed8e 100644 --- a/MVMCoreUI/SupportingFiles/Media.xcassets/closeXBlack.imageset/Contents.json +++ b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Contents.json @@ -2,17 +2,17 @@ "images" : [ { "idiom" : "universal", - "filename" : "closeXBlack.png", + "filename" : "nav_close.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "closeXBlack@2x.png", + "filename" : "nav_close@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "closeXBlack@3x.png", + "filename" : "nav_close@3x.png", "scale" : "3x" } ], diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/nav_close.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/nav_close.png new file mode 100644 index 00000000..e04a9996 Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/nav_close.png differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/nav_close@2x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/nav_close@2x.png new file mode 100644 index 00000000..f8876428 Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/nav_close@2x.png differ diff --git a/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/nav_close@3x.png b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/nav_close@3x.png new file mode 100644 index 00000000..b2e9d7df Binary files /dev/null and b/MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/nav_close@3x.png differ diff --git a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings index da55d597..74b907e3 100644 --- a/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/en.lproj/Localizable.strings @@ -9,7 +9,7 @@ // MARK: Accessibility "AccCloseButton" = "Close"; "swipe_to_select_with_action_hint" = "swipe up or down to select action, then double tap to select."; - +"AccDisabled" = "Disabled"; // MARK: Tab "AccTab" = ", tab"; diff --git a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings index 0a9eadb9..b3b6298c 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es-MX.lproj/Localizable.strings @@ -8,6 +8,7 @@ // Accessibility "swipe_to_select_with_action_hint" = "deslízate hacia arriba o hacia abajo para seleccionar la acción, luego toca dos veces para seleccionar."; +"AccDisabled" = "desactivado"; "AccCloseButton" = "Cerrar"; // Tab diff --git a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings index bbc45f4a..8e0dbd02 100644 --- a/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings +++ b/MVMCoreUI/SupportingFiles/Strings/es.lproj/Localizable.strings @@ -8,6 +8,7 @@ // Accessibility "swipe_to_select_with_action_hint" = "deslízate hacia arriba o hacia abajo para seleccionar la acción, luego toca dos veces para seleccionar."; +"AccDisabled" = "desactivado"; "AccCloseButton" = "Cerrar"; // Tab diff --git a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift index cf1603bb..d1173250 100644 --- a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift +++ b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility+Extension.swift @@ -13,7 +13,7 @@ public extension MVMCoreUICommonViewsUtility { /// Add the close button (x) to the top right of the view. TODO: update this logic. @objc static func addCloseButton(to view: UIView, action: @escaping ButtonAction, centeredVertically: Bool = false) -> Button { let button = Button() - if let image = MVMCoreUIUtility.imageNamed("closeXBlack")?.withRenderingMode(.alwaysTemplate) { + if let image = MVMCoreUIUtility.imageNamed("nav_close")?.withRenderingMode(.alwaysTemplate) { button.setImage(image, for: .normal) } button.tintColor = .black