diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index ba954d23..9079fc39 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -114,6 +114,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 */; }; + 32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */; }; + 32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */; }; 522679C123FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */; }; 522679C223FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */; }; 52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */; }; @@ -253,7 +255,7 @@ D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */; }; D20923592450ECE00044AD09 /* TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20923582450ECE00044AD09 /* TableView.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; - D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */; }; + D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; }; D21B7F71243BAC1600051ABF /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */; }; @@ -271,7 +273,11 @@ D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */; }; D236E5B5241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B3241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift */; }; D236E5B7242007C500C38625 /* MVMControllerModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B6242007C500C38625 /* MVMControllerModelProtocol.swift */; }; + D23EA7E82473654300D60C34 /* PanelNavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA7E72473654300D60C34 /* PanelNavigationItemModelProtocol.swift */; }; + D23EA7FB2475F09800D60C34 /* CarouselItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */; }; D243859923A16B1800332775 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = D243859823A16B1800332775 /* Container.swift */; }; + D2509ED12472ED9B001BFB9D /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */; }; + D2509ED62472EE2F001BFB9D /* NavigationItemButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2509ED52472EE2F001BFB9D /* NavigationItemButtonModel.swift */; }; D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D260106423D0CEA700764D80 /* StackModel.swift */; }; D253BB9C245874F8002DE544 /* BGImageMolecule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D253BB9B245874F8002DE544 /* BGImageMolecule.swift */; }; D253BB9E2458751F002DE544 /* BGImageMoleculeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D253BB9D2458751F002DE544 /* BGImageMoleculeModel.swift */; }; @@ -295,6 +301,7 @@ D268C70C2386DFFD007F2C1C /* MoleculeStackItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */; }; D268C70E238C22D7007F2C1C /* DropDownFilterTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */; }; D26C5A6B23F4A40D007AEECE /* ListItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D26C5A6A23F4A40D007AEECE /* ListItemModel.swift */; }; + D272F5F92473163100BD1A8F /* BarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D272F5F82473163100BD1A8F /* BarButtonItem.swift */; }; D274CA332236A78900B01B62 /* FooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D274CA322236A78900B01B62 /* FooterView.swift */; }; D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2755D7A23689C7500485468 /* TableViewCell.swift */; }; D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */; }; @@ -533,6 +540,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 = ""; }; + 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableNumberedListAllTextAndLinksModel.swift; sourceTree = ""; }; + 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableNumberedListAllTextAndLinks.swift; sourceTree = ""; }; 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinks.swift; sourceTree = ""; }; 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinksModel.swift; sourceTree = ""; }; 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextAllTextAndLinks.swift; sourceTree = ""; }; @@ -672,7 +681,7 @@ D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerModelBase.swift; sourceTree = ""; }; D20923582450ECE00044AD09 /* TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableView.swift; sourceTree = ""; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; - D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = ""; }; + D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = ""; }; D21B7F70243BAC1600051ABF /* CollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = ""; }; @@ -690,7 +699,11 @@ D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTwoColumnPriceDescription.swift; sourceTree = ""; }; D236E5B3241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTwoColumnPriceDescriptionModel.swift; sourceTree = ""; }; D236E5B6242007C500C38625 /* MVMControllerModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMControllerModelProtocol.swift; sourceTree = ""; }; + D23EA7E72473654300D60C34 /* PanelNavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanelNavigationItemModelProtocol.swift; sourceTree = ""; }; + D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselItemProtocol.swift; sourceTree = ""; }; D243859823A16B1800332775 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; + D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = ""; }; + D2509ED52472EE2F001BFB9D /* NavigationItemButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationItemButtonModel.swift; sourceTree = ""; }; D253BB9B245874F8002DE544 /* BGImageMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageMolecule.swift; sourceTree = ""; }; D253BB9D2458751F002DE544 /* BGImageMoleculeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageMoleculeModel.swift; sourceTree = ""; }; D256E9922412880000360572 /* Header.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = ""; }; @@ -713,6 +726,7 @@ D264FAAB2441009400D98315 /* RadioBoxCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioBoxCollectionViewCell.swift; sourceTree = ""; }; D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDownFilterTableViewCell.swift; sourceTree = ""; }; D26C5A6A23F4A40D007AEECE /* ListItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListItemModel.swift; sourceTree = ""; }; + D272F5F82473163100BD1A8F /* BarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarButtonItem.swift; sourceTree = ""; }; D274CA322236A78900B01B62 /* FooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooterView.swift; sourceTree = ""; }; D2755D7A23689C7500485468 /* TableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = ""; }; D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsTableViewCell.swift; sourceTree = ""; }; @@ -860,6 +874,7 @@ children = ( D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */, 014AA72323C501E2006F3E93 /* ContainerModelProtocol.swift */, + D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */, 012A88C3238D86E600FE3DA1 /* CarouselItemModelProtocol.swift */, 012A88B0238C880100FE3DA1 /* CarouselPagingModelProtocol.swift */, 01EB3683236097C0006832FA /* MoleculeModelProtocol.swift */, @@ -868,6 +883,8 @@ 011B58EF23A2AA980085F53C /* ListItemModelProtocol.swift */, D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */, D2092354244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift */, + D2509ED02472ED9B001BFB9D /* NavigationItemModelProtocol.swift */, + D23EA7E72473654300D60C34 /* PanelNavigationItemModelProtocol.swift */, ); path = ModelProtocols; sourceTree = ""; @@ -1289,6 +1306,8 @@ 0A6682A12434DB4F00AD3CA1 /* ListLeftVariableRadioButtonBodyText.swift */, AA99724F2475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift */, AA997251247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift */, + 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */, + 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */, ); path = LeftVariable; sourceTree = ""; @@ -1345,6 +1364,15 @@ path = TwoColumn; sourceTree = ""; }; + D2509ED42472EE0B001BFB9D /* NavigationBar */ = { + isa = PBXGroup; + children = ( + D2509ED52472EE2F001BFB9D /* NavigationItemButtonModel.swift */, + D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */, + ); + path = NavigationBar; + sourceTree = ""; + }; D253BB9A24587023002DE544 /* OtherContainers */ = { isa = PBXGroup; children = ( @@ -1486,6 +1514,7 @@ D29DF10E21E67A77003B2FB9 /* Molecules */ = { isa = PBXGroup; children = ( + D2509ED42472EE0B001BFB9D /* NavigationBar */, D253BB9A24587023002DE544 /* OtherContainers */, D22B38E923F4E07800490EF6 /* DesignedComponents */, D22479912316A9EF003FCCF9 /* Items */, @@ -1499,7 +1528,6 @@ 012A88EB238F084D00FE3DA1 /* FooterModel.swift */, D274CA322236A78900B01B62 /* FooterView.swift */, D260105723CF9CC500764D80 /* Doughnut */, - D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */, ); path = Molecules; sourceTree = ""; @@ -1792,6 +1820,7 @@ D264FAA92440F97600D98315 /* CollectionView.swift */, 0A5D59C323AD488600EFD9E9 /* Protocols */, 0A7918F423F5E7EA00772FF4 /* ImageView.swift */, + D272F5F82473163100BD1A8F /* BarButtonItem.swift */, ); path = BaseClasses; sourceTree = ""; @@ -1950,6 +1979,8 @@ 8D3BA9BF2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift in Sources */, 94C661DA23CCF4FB00D9FE5B /* UIColor+Extension.swift in Sources */, D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */, + D2509ED62472EE2F001BFB9D /* NavigationItemButtonModel.swift in Sources */, + 32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */, DBC4391822442197001AB423 /* CaretView.swift in Sources */, C07065C42395677300FBF997 /* Link.swift in Sources */, 0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */, @@ -1976,6 +2007,7 @@ D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */, 0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */, AA2AD116244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift in Sources */, + D272F5F92473163100BD1A8F /* BarButtonItem.swift in Sources */, 0A9D09202433796500D2E6C0 /* BarsIndicatorView.swift in Sources */, D2E2A99423D8CCBC000B42E6 /* HeadlineBodyLinkModel.swift in Sources */, 01004F3022721C3800991ECC /* RadioButton.swift in Sources */, @@ -2092,6 +2124,7 @@ 01509D952327ED1900EF99AA /* HeadlineBodyLinkToggle.swift in Sources */, 31BE15CB23D8924D00452370 /* CheckboxLabelModel.swift in Sources */, D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */, + D23EA7E82473654300D60C34 /* PanelNavigationItemModelProtocol.swift in Sources */, 94F6516D2437954100631BF9 /* Tabs.swift in Sources */, 5248BFEC23F12E350059236A /* ListThreeColumnPlanDataDivider.swift in Sources */, 0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */, @@ -2151,11 +2184,13 @@ 014AA72423C501E2006F3E93 /* MoleculeContainerModel.swift in Sources */, D29DF28321E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.m in Sources */, 011B58F223A2AE2C0085F53C /* DropDownListItemModel.swift in Sources */, + D2509ED12472ED9B001BFB9D /* NavigationItemModelProtocol.swift in Sources */, 8D448E5524050A46006211BB /* ListOneColumnFullWidthTextAllTextAndLinksModel.swift in Sources */, 94C2D9842386F3F80006CF46 /* LabelAttributeModel.swift in Sources */, 944589212385D6E900DE9FD4 /* DashLineModel.swift in Sources */, D2E2A99623D8CF85000B42E6 /* HeadlineBodyLinkToggleModel.swift in Sources */, C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */, + 32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */, 011D958524042432000E3791 /* RulesProtocol.swift in Sources */, AA9972502475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift in Sources */, AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */, @@ -2193,7 +2228,7 @@ D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, 525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */, 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */, - D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */, + D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */, AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */, DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */, D224798A2314445E003FCCF9 /* LabelToggle.swift in Sources */, @@ -2253,6 +2288,7 @@ 525239C22407BD1000454969 /* ListTwoColumnPriceDetails.swift in Sources */, AA997252247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift in Sources */, D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */, + D23EA7FB2475F09800D60C34 /* CarouselItemProtocol.swift in Sources */, D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */, D29DF11821E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.m in Sources */, 27F973532466074500CAB5C5 /* PageBehavior.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift index d23a2606..b0822480 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLink.swift @@ -9,14 +9,13 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { - //------------------------------------------------------ // MARK: - Constants //------------------------------------------------------ private let CARET_VIEW_HEIGHT: Float = 10.5 private let CARET_VIEW_WIDTH: Float = 6.5 - + //------------------------------------------------------ // MARK: - Properties //------------------------------------------------------ @@ -82,7 +81,7 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { setTitleColor(enabledColor, for: .normal) setTitleColor(disabledColor, for: .disabled) - + if let rightCaretView = rightView as? CaretView { rightCaretView.enabledColor = enabledColor rightCaretView.disabledColor = disabledColor @@ -123,7 +122,7 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { bottomAnchor.constraint(greaterThanOrEqualTo: caretView.bottomAnchor).isActive = true contentHorizontalAlignment = .left - //set correct color after layout + // Set correct color after layout changeCaretColor() } @@ -135,6 +134,7 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { //------------------------------------------------------ // MARK: - Atomization //------------------------------------------------------ + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let model = model as? CaretLinkModel else { return } @@ -143,10 +143,8 @@ open class CaretLink: Button, MVMCoreUIViewConstrainingProtocol { backgroundColor = color.uiColor } - enabledColor = model.enabledColor.uiColor - if let color = model.disabledColor { - disabledColor = color.uiColor - } + enabledColor = (model.inverted ? model.enabledColor_inverted : model.enabledColor).uiColor + disabledColor = (model.inverted ? model.disabledColor_inverted : model.disabledColor).uiColor isEnabled = model.enabled set(with: model.action, delegateObject: delegateObject, additionalData: additionalData) diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLinkModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLinkModel.swift index 8f3ee74d..5a4a4c14 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/CaretLinkModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/CaretLinkModel.swift @@ -9,46 +9,86 @@ import Foundation import MVMCore + public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "caretLink" public var backgroundColor: Color? public var title: String public var action: ActionModelProtocol - public var enabledColor: Color = Color(uiColor: .black) - public var disabledColor: Color? = Color(uiColor: .mvmCoolGray6) + public var enabledColor: Color = Color(uiColor: .mvmBlack) + public var disabledColor: Color = Color(uiColor: .mvmCoolGray6) + public var enabledColor_inverted: Color = Color(uiColor: .mvmWhite) + public var disabledColor_inverted: Color = Color(uiColor: .mvmCoolGray10) public var enabled = true - + public var inverted = false + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(title: String, action: ActionModelProtocol) { self.title = title self.action = action } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case backgroundColor case title case action + case enabledColor_inverted + case disabledColor_inverted case enabledColor case disabledColor case enabled + case inverted case moleculeName } - + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) title = try typeContainer.decode(String.self, forKey: .title) + + if let enabledColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor_inverted) { + self.enabledColor_inverted = enabledColor_inverted + } + + if let disabledColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor_inverted) { + self.disabledColor_inverted = disabledColor_inverted + } + if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor) { enabledColor = color } + if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor) { disabledColor = color } + if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { self.enabled = enabled } + + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted + } + action = try typeContainer.decodeModel(codingKey: .action) } - + public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) @@ -58,5 +98,8 @@ public class CaretLinkModel: ButtonModelProtocol, MoleculeModelProtocol { try container.encode(enabled, forKey: .enabledColor) try container.encodeIfPresent(disabledColor, forKey: .disabledColor) try container.encode(enabled, forKey: .enabled) + try container.encode(enabledColor_inverted, forKey: .enabledColor_inverted) + try container.encode(disabledColor_inverted, forKey: .disabledColor_inverted) + try container.encode(inverted, forKey: .inverted) } } diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLink.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLink.swift index c65698c3..e051cf32 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLink.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLink.swift @@ -25,7 +25,7 @@ open class ExternalLink: Link { guard let model = model as? ExternalLinkModel else { return } - exportImageView?.tintColor = model.enabledColor.uiColor + exportImageView?.tintColor = titleColor(for: model.enabled ? .normal : .disabled) } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLinkModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLinkModel.swift index f492a546..19d6643c 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLinkModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/ExternalLinkModel.swift @@ -8,6 +8,7 @@ import UIKit + open class ExternalLinkModel: LinkModel { override open class var identifier: String { diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift index 5f5e6e12..7413fcbb 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/Link.swift @@ -37,9 +37,7 @@ import UIKit } open override var intrinsicContentSize: CGSize { - guard let size = titleLabel?.intrinsicContentSize else { - return super.intrinsicContentSize - } + guard let size = titleLabel?.intrinsicContentSize else { return super.intrinsicContentSize } return CGSize(width: size.width, height: size.height + 2) } @@ -49,17 +47,18 @@ import UIKit public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) + guard let model = model as? LinkModel else { return } setTitle(model.title, for: .normal) - setTitleColor(model.enabledColor.uiColor, for: .normal) - setTitleColor(model.disabledColor.uiColor, for: .disabled) + setTitleColor((model.inverted ? model.enabledColor_inverted : model.enabledColor).uiColor, for: .normal) + setTitleColor((model.inverted ? model.disabledColor_inverted : model.disabledColor).uiColor, for: .disabled) isEnabled = model.enabled set(with: model.action, delegateObject: delegateObject, additionalData: additionalData) } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return 31.0 + return 31 } } @@ -68,12 +67,15 @@ extension Link { open override func updateView(_ size: CGFloat) { super.updateView(size) + DispatchQueue.main.async { [weak self] in guard let self = self else { return } + var width = size - if MVMCoreGetterUtility.fequal(a: Float(CGFloat.leastNormalMagnitude), b: Float(size)) { + if MVMCoreGetterUtility.fequal(a: Float.leastNormalMagnitude, b: Float(size)) { width = MVMCoreUIUtility.getWidth() } + self.titleLabel?.font = MFStyler.fontB2(forWidth: width) } } diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift index a54f23e9..d51bddf4 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/Link/LinkModel.swift @@ -8,6 +8,7 @@ import UIKit + open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties @@ -22,7 +23,10 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol { public var action: ActionModelProtocol public var enabled = true public var enabledColor = Color(uiColor: .mvmBlack) + public var enabledColor_inverted = Color(uiColor: .mvmWhite) public var disabledColor = Color(uiColor: .mvmCoolGray6) + public var disabledColor_inverted = Color(uiColor: .mvmCoolGray10) + public var inverted = false //-------------------------------------------------- // MARK: - Initializer @@ -44,7 +48,10 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol { case action case enabled case enabledColor + case enabledColor_inverted case disabledColor + case disabledColor_inverted + case inverted } //-------------------------------------------------- @@ -53,6 +60,7 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol { required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) title = try typeContainer.decode(String.self, forKey: .title) action = try typeContainer.decodeModel(codingKey: .action) @@ -60,12 +68,25 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol { if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { self.enabled = enabled } - if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor) { - enabledColor = color + + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted } - if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor) { - disabledColor = color + if let enabledColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor) { + self.enabledColor = enabledColor + } + + if let enabledColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor_inverted) { + self.enabledColor_inverted = enabledColor_inverted + } + + if let disabledColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor) { + self.disabledColor = disabledColor + } + + if let disabledColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor_inverted) { + self.disabledColor_inverted = disabledColor_inverted } } @@ -75,8 +96,11 @@ open class LinkModel: ButtonModelProtocol, MoleculeModelProtocol { try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeModel(action, forKey: .action) + try container.encode(inverted, forKey: .inverted) try container.encode(enabled, forKey: .enabled) try container.encode(enabledColor, forKey: .enabledColor) + try container.encode(enabledColor_inverted, forKey: .enabledColor_inverted) try container.encode(disabledColor, forKey: .disabledColor) + try container.encode(disabledColor_inverted, forKey: .disabledColor_inverted) } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index cb404f5a..cffbb916 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -97,7 +97,7 @@ import MVMCore public var disabledCheckColor: UIColor = .mvmCoolGray3 /// Color of the check mark. - public var checkColor: UIColor = .black { + public var checkColor: UIColor = .mvmBlack { didSet { setShapeLayerStrokeColor(checkColor) } @@ -111,7 +111,7 @@ import MVMCore } /// border color of the Checkbox - public var borderColor: UIColor = .black { + public var borderColor: UIColor = .mvmBlack { didSet { layer.borderColor = borderColor.cgColor } @@ -126,7 +126,7 @@ import MVMCore didSet { if !updateSelectionOnly { layoutIfNeeded() - (model as? CheckboxModel)?.isChecked = isSelected + (model as? CheckboxModel)?.checked = isSelected shapeLayer?.removeAllAnimations() updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) @@ -375,10 +375,10 @@ import MVMCore shapeLayer?.removeFromSuperlayer() shapeLayer = nil backgroundColor = .clear - borderColor = .black - borderWidth = 1.0 - checkColor = .black - checkWidth = 2.0 + borderColor = .mvmBlack + borderWidth = 1 + checkColor = .mvmBlack + checkWidth = 2 checkAndBypassAnimations(selected: false) } @@ -393,32 +393,34 @@ import MVMCore public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - guard let model = model as? CheckboxModel else { return } self.delegateObject = delegateObject + + guard let model = model as? CheckboxModel else { return } + FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) if let fieldKey = model.fieldKey { self.fieldKey = fieldKey } - borderColor = model.borderColor.uiColor + borderColor = (model.inverted ? model.invertedColor : model.borderColor).uiColor borderWidth = model.borderWidth - checkColor = model.checkColor.uiColor - unCheckedBackgroundColor = model.unCheckedBackgroundColor.uiColor - checkedBackgroundColor = model.checkedBackgroundColor.uiColor - disabledCheckColor = model.disabledCheckColor.uiColor - disabledBorderColor = model.disabledBorderColor.uiColor - disabledBackgroundColor = model.disabledBackgroundColor.uiColor + checkColor = (model.inverted ? model.invertedColor : model.checkColor).uiColor + unCheckedBackgroundColor = (model.inverted ? model.invertedBackgroundColor : model.unCheckedBackgroundColor).uiColor + checkedBackgroundColor = (model.inverted ? model.invertedBackgroundColor : model.checkedBackgroundColor).uiColor + disabledCheckColor = (model.inverted ? model.invertedColor : model.disabledCheckColor).uiColor + disabledBorderColor = (model.inverted ? model.invertedColor : model.disabledBorderColor).uiColor + disabledBackgroundColor = (model.inverted ? model.invertedColor : model.disabledBackgroundColor).uiColor - isAnimated = model.isAnimated - isRound = model.isRound + isAnimated = model.animated + isRound = model.round - if model.isChecked { - checkAndBypassAnimations(selected: model.isChecked) + if model.checked { + checkAndBypassAnimations(selected: model.checked) } - isEnabled = model.isEnabled + isEnabled = model.enabled if let action = model.action { actionBlock = { diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift index 423ed1ee..43a08aa6 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CheckboxModel.swift @@ -8,6 +8,7 @@ import Foundation + @objcMembers public class CheckboxModel: MoleculeModelProtocol, FormFieldProtocol { //-------------------------------------------------- // MARK: - Properties @@ -15,20 +16,23 @@ import Foundation public static var identifier: String = "checkbox" public var backgroundColor: Color? - public var isChecked: Bool = false - public var isEnabled: Bool = true - public var isAnimated: Bool = true - public var isRound: Bool = false + public var checked: Bool = false + public var enabled: Bool = true + public var animated: Bool = true + public var inverted: Bool = false + public var round: Bool = false public var borderWidth: CGFloat = 1 - public var borderColor: Color = Color(uiColor: .black) - public var checkColor: Color = Color(uiColor: .black) + public var borderColor: Color = Color(uiColor: .mvmBlack) + public var checkColor: Color = Color(uiColor: .mvmBlack) public var unCheckedBackgroundColor: Color = Color(uiColor: .clear) public var checkedBackgroundColor: Color = Color(uiColor: .clear) public var disabledBackgroundColor: Color = Color(uiColor: .clear) public var disabledBorderColor: Color = Color(uiColor: .mvmCoolGray3) public var disabledCheckColor: Color = Color(uiColor: .mvmCoolGray3) + public var invertedColor: Color = Color(uiColor: .mvmWhite) + public var invertedBackgroundColor: Color = Color(uiColor: .mvmBlack) public var action: ActionModelProtocol? - + public var fieldKey: String? public var groupName: String = FormValidator.defaultGroupName public var baseValue: AnyHashable? @@ -39,13 +43,16 @@ import Foundation private enum CodingKeys: String, CodingKey { case moleculeName - case isChecked = "checked" - case isEnabled = "enabled" - case isAnimated = "animated" - case isRound = "round" + case checked + case enabled + case inverted + case animated + case round case borderWidth case borderColor case checkColor + case invertedColor + case invertedBackgroundColor case unCheckedBackgroundColor case checkedBackgroundColor case disabledBackgroundColor @@ -56,12 +63,20 @@ import Foundation case groupName } + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + public func formFieldValue() -> AnyHashable? { - return isChecked + return checked } + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(isChecked: Bool = false) { - self.isChecked = isChecked + self.checked = isChecked baseValue = isChecked } @@ -71,22 +86,72 @@ import Foundation required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) ?? 1 - borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) ?? Color(uiColor: .black) - checkColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkColor) ?? Color(uiColor: .black) - unCheckedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .unCheckedBackgroundColor) ?? Color(uiColor: .clear) - checkedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkedBackgroundColor) ?? Color(uiColor: .clear) - disabledBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBackgroundColor) ?? Color(uiColor: .clear) - disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor) ?? Color(uiColor: .mvmCoolGray3) - disabledCheckColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledCheckColor) ?? Color(uiColor: .mvmCoolGray3) - isChecked = try typeContainer.decodeIfPresent(Bool.self, forKey: .isChecked) ?? false - isAnimated = try typeContainer.decodeIfPresent(Bool.self, forKey: .isAnimated) ?? true - isRound = try typeContainer.decodeIfPresent(Bool.self, forKey: .isRound) ?? false - isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? true - action = try typeContainer.decodeModelIfPresent(codingKey: .action) - baseValue = isChecked + if let borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) { + self.borderWidth = borderWidth + } + + if let borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) { + self.borderColor = borderColor + } + + if let checkColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkColor) { + self.checkColor = checkColor + } + + if let unCheckedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .unCheckedBackgroundColor) { + self.unCheckedBackgroundColor = unCheckedBackgroundColor + } + + if let checkedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkedBackgroundColor) { + self.checkedBackgroundColor = checkedBackgroundColor + } + + if let disabledBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBackgroundColor) { + self.disabledBackgroundColor = disabledBackgroundColor + } + + if let disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor) { + self.disabledBorderColor = disabledBorderColor + } + + if let disabledCheckColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledCheckColor) { + self.disabledCheckColor = disabledCheckColor + } + + if let invertedColor = try typeContainer.decodeIfPresent(Color.self, forKey: .invertedColor) { + self.invertedColor = invertedColor + } + + if let invertedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .invertedBackgroundColor) { + self.invertedBackgroundColor = invertedBackgroundColor + } + + if let checked = try typeContainer.decodeIfPresent(Bool.self, forKey: .checked) { + self.checked = checked + } + + baseValue = checked + + if let animated = try typeContainer.decodeIfPresent(Bool.self, forKey: .animated) { + self.animated = animated + } + + if let round = try typeContainer.decodeIfPresent(Bool.self, forKey: .round) { + self.round = round + } + + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted + } + + if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { + self.enabled = enabled + } + + action = try typeContainer.decodeModelIfPresent(codingKey: .action) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) + if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { self.groupName = groupName } @@ -99,16 +164,19 @@ import Foundation try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(borderColor, forKey: .borderColor) try container.encode(borderWidth, forKey: .borderWidth) - try container.encode(isChecked, forKey: .isChecked) + try container.encode(checked, forKey: .checked) + try container.encode(inverted, forKey: .inverted) try container.encodeIfPresent(checkColor, forKey: .checkColor) + try container.encodeIfPresent(invertedColor, forKey: .invertedColor) + try container.encodeIfPresent(invertedBackgroundColor, forKey: .invertedBackgroundColor) try container.encodeIfPresent(unCheckedBackgroundColor, forKey: .unCheckedBackgroundColor) try container.encodeIfPresent(checkedBackgroundColor, forKey: .checkedBackgroundColor) try container.encodeIfPresent(disabledBorderColor, forKey: .disabledBorderColor) try container.encodeIfPresent(disabledBackgroundColor, forKey: .disabledBackgroundColor) try container.encodeIfPresent(disabledCheckColor, forKey: .disabledCheckColor) - try container.encodeIfPresent(isAnimated, forKey: .isAnimated) - try container.encodeIfPresent(isRound, forKey: .isRound) - try container.encodeIfPresent(isEnabled, forKey: .isEnabled) + try container.encodeIfPresent(animated, forKey: .animated) + try container.encodeIfPresent(round, forKey: .round) + try container.encodeIfPresent(enabled, forKey: .enabled) try container.encodeModelIfPresent(action, forKey: .action) try container.encodeIfPresent(groupName, forKey: .groupName) } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Arrow.swift b/MVMCoreUI/Atomic/Atoms/Views/Arrow.swift index ea5023c8..a437f9d8 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Arrow.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Arrow.swift @@ -41,8 +41,17 @@ open class Arrow: View { } open var color: UIColor { - get { return arrowModel?.color.uiColor ?? .mvmBlack } - set { arrowModel?.color = Color(uiColor: newValue) } + get { + guard let model = arrowModel else { return .mvmBlack } + return model.inverted ? model.color_inverted.uiColor : model.color.uiColor + } + set { + if let model = arrowModel, model.inverted { + model.color_inverted = Color(uiColor: newValue) + } else { + arrowModel?.color = Color(uiColor: newValue) + } + } } open var degrees: Float { diff --git a/MVMCoreUI/Atomic/Atoms/Views/ArrowModel.swift b/MVMCoreUI/Atomic/Atoms/Views/ArrowModel.swift index ac2b2d90..35dbd906 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/ArrowModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/ArrowModel.swift @@ -22,11 +22,13 @@ open class ArrowModel: MoleculeModelProtocol { public var backgroundColor: Color? public var disabledColor: Color = Color(uiColor: .mvmCoolGray3) public var color: Color = Color(uiColor: .mvmBlack) + public var color_inverted: Color = Color(uiColor: .mvmWhite) public var degrees: Float = 0 public var lineWidth: CGFloat = 1 public var height: CGFloat = 12 public var width: CGFloat = 12 public var enabled: Bool = true + public var inverted: Bool = false //-------------------------------------------------- // MARK: - Enum @@ -64,6 +66,7 @@ open class ArrowModel: MoleculeModelProtocol { case height case width case enabled + case inverted } //-------------------------------------------------- @@ -79,6 +82,10 @@ open class ArrowModel: MoleculeModelProtocol { self.disabledColor = disabledColor } + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted + } + if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) { self.color = color } diff --git a/MVMCoreUI/Atomic/Atoms/Views/CaretView.swift b/MVMCoreUI/Atomic/Atoms/Views/CaretView.swift index 93874287..20023091 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CaretView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CaretView.swift @@ -13,14 +13,13 @@ open class CaretView: View { //------------------------------------------------------ private var caretPath: UIBezierPath = UIBezierPath() - public var strokeColor: UIColor = .black public var lineWidth: CGFloat = 1 public var direction: Direction = .right public var size: CaretSize? - public var enabledColor: UIColor = .black - public var disabledColor: UIColor = .mfSilver() + public var enabledColor: UIColor = .mvmBlack + public var disabledColor: UIColor = .mvmCoolGray3 //------------------------------------------------------ // MARK: - Property Observer @@ -28,7 +27,7 @@ open class CaretView: View { public var isEnabled: Bool = true { didSet { - strokeColor = isEnabled ? enabledColor : disabledColor + guard isEnabled != oldValue else { return } setNeedsDisplay() } } @@ -49,7 +48,7 @@ open class CaretView: View { case horizontal } - // Dimensions of container; provided by InVision design. + /// Dimensions of container; provided by InVision design. func dimensions() -> CGSize { switch self { @@ -92,7 +91,7 @@ open class CaretView: View { //------------------------------------------------------ @objc override open func setupView() { - + super.setupView() defaultState() } @@ -141,7 +140,7 @@ open class CaretView: View { caretPath.addLine(to: CGPoint(x: frame.size.width - inset, y: frame.size.height - inset)) } - strokeColor.setStroke() + enabledColor.setStroke() caretPath.stroke() } @@ -151,17 +150,16 @@ open class CaretView: View { @objc public func setLineColor(_ color: UIColor) { - strokeColor = color + enabledColor = color setNeedsDisplay() } @objc public func defaultState() { - translatesAutoresizingMaskIntoConstraints = false isOpaque = false isHidden = false backgroundColor = .clear - strokeColor = .black + enabledColor = .mvmBlack } /// Ensure you have defined a CaretSize with Orientation before calling. @@ -177,8 +175,8 @@ open class CaretView: View { // MARK: - Atomization //------------------------------------------------------ - // Default values for view. - public required init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + /// Default values for view. + public required init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.init(frame: .zero) defaultState() set(with: model, delegateObject, additionalData) @@ -186,20 +184,21 @@ open class CaretView: View { override public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - guard let caretModel = model as? CaretViewModel else { - return - } - strokeColor = caretModel.strokeColor.uiColor - isHidden = caretModel.isHidden ?? false - isOpaque = caretModel.isOpaque ?? false - - if let lineWidthValue = caretModel.lineWidth { - lineWidth = lineWidthValue + + guard let model = model as? CaretViewModel else { return } + + enabledColor = (model.inverted ? model.strokeColor_inverted : model.strokeColor).uiColor + isHidden = model.isHidden + isOpaque = model.isOpaque + + if let lineWidthValue = model.lineWidth { + lineWidth = lineWidthValue } } } extension CaretView: MVMCoreUIViewConstrainingProtocol { + open func needsToBeConstrained() -> Bool { return true } diff --git a/MVMCoreUI/Atomic/Atoms/Views/CaretViewModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CaretViewModel.swift index cf90496f..9dc43286 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CaretViewModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CaretViewModel.swift @@ -8,32 +8,65 @@ import Foundation -@objcMembers public class CaretViewModel: MoleculeModelProtocol { +@objcMembers public class CaretViewModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "caretView" public var backgroundColor: Color? - public var strokeColor: Color = Color(uiColor: .black) - public var isHidden: Bool? - public var isOpaque: Bool? + public var strokeColor: Color = Color(uiColor: .mvmBlack) + public var strokeColor_inverted: Color = Color(uiColor: .mvmWhite) + public var isHidden: Bool = false + public var isOpaque: Bool = false + public var inverted: Bool = false public var lineWidth: CGFloat? + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor case strokeColor + case strokeColor_inverted case isHidden case isOpaque case lineWidth + case inverted } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + if let strokeColor = try typeContainer.decodeIfPresent(Color.self, forKey: .strokeColor) { self.strokeColor = strokeColor } + + if let strokeColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .strokeColor_inverted) { + self.strokeColor_inverted = strokeColor_inverted + } + + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted + } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - isHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .isHidden) - isOpaque = try typeContainer.decodeIfPresent(Bool.self, forKey: .isOpaque) + + if let isHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .isHidden) { + self.isHidden = isHidden + } + + if let isOpaque = try typeContainer.decodeIfPresent(Bool.self, forKey: .isOpaque) { + self.isOpaque = isOpaque + } + lineWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .lineWidth) } @@ -41,6 +74,8 @@ import Foundation var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(strokeColor, forKey: .strokeColor) + try container.encode(strokeColor_inverted, forKey: .strokeColor_inverted) + try container.encode(inverted, forKey: .inverted) try container.encodeIfPresent(isHidden, forKey: .isHidden) try container.encodeIfPresent(isOpaque, forKey: .isOpaque) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/BarsIndicatorView.swift b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/BarsIndicatorView.swift index 972ea7ca..9dc4a0bd 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/BarsIndicatorView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/BarsIndicatorView.swift @@ -68,12 +68,7 @@ open class BarsIndicatorView: CarouselIndicator { get { return super.indicatorColor } set (newColor) { super.indicatorColor = newColor - - if isEnabled { - for (i, barTuple) in barReferences.enumerated() { - barTuple.view.backgroundColor = i == currentIndex ? currentIndicatorColor : newColor - } - } + refreshBarColors(with: newColor) } } @@ -102,6 +97,15 @@ open class BarsIndicatorView: CarouselIndicator { // MARK: - Methods //-------------------------------------------------- + private func refreshBarColors(with color: UIColor) { + + if isEnabled { + for (i, barTuple) in barReferences.enumerated() { + barTuple.view.backgroundColor = i == currentIndex ? currentIndicatorColor : color + } + } + } + func generateBars() { var bars = [(View, NSLayoutConstraint)]() @@ -142,7 +146,7 @@ open class BarsIndicatorView: CarouselIndicator { guard let model = model as? BarsCarouselIndicatorModel else { return } - currentIndicatorColor = model.currentIndicatorColor.uiColor + currentIndicatorColor = model.inverted ? model.indicatorColor_inverted.uiColor : model.currentIndicatorColor.uiColor } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicator.swift b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicator.swift index 9380e8c0..6bff6e6b 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicator.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicator.swift @@ -78,8 +78,17 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol { } public var indicatorColor: UIColor { - get { return carouselIndicatorModel?.indicatorColor.uiColor ?? .mvmBlack } - set { carouselIndicatorModel?.indicatorColor = Color(uiColor: newValue) } + get { + guard let model = carouselIndicatorModel else { return .mvmBlack } + return model.inverted ? model.indicatorColor_inverted.uiColor : model.indicatorColor.uiColor + } + set { + if let model = carouselIndicatorModel, model.inverted { + model.indicatorColor_inverted = Color(uiColor: newValue) + } else { + carouselIndicatorModel?.indicatorColor = Color(uiColor: newValue) + } + } } var accessibilityValueFormat: String? { @@ -191,7 +200,7 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol { guard let model = model as? CarouselIndicatorModel else { return } - indicatorColor = model.indicatorColor.uiColor + indicatorColor = model.inverted ? model.indicatorColor_inverted.uiColor : model.indicatorColor.uiColor disabledIndicatorColor = model.disabledIndicatorColor.uiColor currentIndex = model.currentIndex isEnabled = model.enabled diff --git a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicatorModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicatorModel.swift index a0ac2273..359fe98a 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicatorModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CarouselIndicator/CarouselIndicatorModel.swift @@ -26,11 +26,13 @@ open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelPro public var currentIndex: Int = 0 public var animated: Bool = true public var hidesForSinglePage: Bool = false + public var inverted: Bool = false /// Set true to make the accessibility value as "Slide #currentPage of #totalPage", otherwise will be "Page #currentPage of #totalPage", default is false public var accessibilityHasSlidesInsteadOfPage: Bool = false public var enabled: Bool = true public var disabledIndicatorColor: Color = Color(uiColor: .mvmCoolGray3) public var indicatorColor: Color = Color(uiColor: .mvmBlack) + public var indicatorColor_inverted: Color = Color(uiColor: .mvmWhite) public var position: Float? /// Allows sendActions() to trigger even if index is already at min/max index. @@ -53,6 +55,7 @@ open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelPro case disabledIndicatorColor case indicatorColor case position + case inverted } //-------------------------------------------------- @@ -72,6 +75,10 @@ open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelPro self.alwaysSendAction = alwaysSendAction } + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted + } + if let position = try typeContainer.decodeIfPresent(Float.self, forKey: .position) { self.position = position } @@ -112,6 +119,7 @@ open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelPro try container.encode(hidesForSinglePage, forKey: .hidesForSinglePage) try container.encode(accessibilityHasSlidesInsteadOfPage, forKey: .accessibilityHasSlidesInsteadOfPage) try container.encode(enabled, forKey: .enabled) + try container.encode(inverted, forKey: .inverted) try container.encode(disabledIndicatorColor, forKey: .disabledIndicatorColor) try container.encode(indicatorColor, forKey: .indicatorColor) try container.encodeIfPresent(position, forKey: .position) diff --git a/MVMCoreUI/Atomic/Atoms/Views/DashLine.swift b/MVMCoreUI/Atomic/Atoms/Views/DashLine.swift index 86d2fd61..c8a59c6d 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/DashLine.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/DashLine.swift @@ -18,10 +18,10 @@ open class DashLine: View { var dashModel: DashLineModel? { get { return model as? DashLineModel } } - + //TODO: Need this for BAU. Can remove once we fix BAU public var dashColor: UIColor? - + @objc private var dashLayer: CAShapeLayer? //------------------------------------------------------ @@ -65,15 +65,15 @@ open class DashLine: View { path.addLine(to: CGPoint(x: dashLayer.frame.size.width, y: 0)) path.stroke() - dashLayer.strokeStart = 0.0 + dashLayer.strokeStart = 0 dashLayer.lineWidth = 1 dashLayer.lineJoin = .miter dashLayer.lineCap = .round dashLayer.lineDashPattern = [NSNumber(value: 2), NSNumber(value: 2)] dashLayer.path = path.cgPath - dashLayer.strokeColor = dashModel?.dashColor.cgColor ?? dashColor?.cgColor + dashLayer.strokeColor = dashColor?.cgColor dashLayer.fillColor = UIColor.clear.cgColor - dashLayer.backgroundColor = backgroundColor?.cgColor ?? UIColor.white.cgColor + dashLayer.backgroundColor = (dashModel?.inverted ?? false) ? UIColor.mvmBlack.cgColor : backgroundColor?.cgColor ?? UIColor.mvmWhite.cgColor self.dashLayer = dashLayer } @@ -81,21 +81,21 @@ open class DashLine: View { // MARK: - Atomization //------------------------------------------------------ - // Default values for view. @objc open override func reset() { - backgroundColor = .clear + super.reset() + isHidden = false } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - guard let dashLineModel = dashModel else { - return - } - if let isHiddenValue = dashLineModel.isHidden { - isHidden = isHiddenValue - } - if let backgroundColor = dashLineModel.backgroundColor { + + guard let model = dashModel else { return } + + isHidden = model.isHidden + dashColor = (model.inverted ? model.dashColor_inverted : model.dashColor).uiColor + + if let backgroundColor = model.backgroundColor { dashLayer?.backgroundColor = backgroundColor.uiColor.cgColor } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/DashLineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/DashLineModel.swift index 7f5ac992..523d83a6 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/DashLineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/DashLineModel.swift @@ -8,38 +8,66 @@ import Foundation + @objcMembers public class DashLineModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "dashLine" public var backgroundColor: Color? + public var dashColor: Color = Color(uiColor: .mvmCoolGray3) + public var dashColor_inverted: Color = Color(uiColor: .mvmWhite) + public var isHidden: Bool = false + public var inverted: Bool = false - public var dashColor: Color = Color(uiColor: .mfLighterGray()) - public var isHidden: Bool? + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- public init(dashColor: Color) { self.dashColor = dashColor } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor + case dashColor_inverted case dashColor case isHidden } + + //-------------------------------------------------- + // MARK: - codec + //-------------------------------------------------- required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + if let dashColor = try typeContainer.decodeIfPresent(Color.self, forKey: .dashColor) { self.dashColor = dashColor } + + if let dashColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .dashColor_inverted) { + self.dashColor_inverted = dashColor_inverted + } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - isHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .isHidden) + + if let isHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .isHidden) { + self.isHidden = isHidden + } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(dashColor, forKey: .dashColor) - try container.encodeIfPresent(isHidden, forKey: .isHidden) + try container.encode(isHidden, forKey: .isHidden) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 087e893c..d47a374f 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -402,11 +402,11 @@ public typealias ActionBlock = () -> () attributedString.insert(mutableString, at: imageAtt.location) case let fontAtt as LabelAttributeFontModel: - if let fontStyle = fontAtt.style?.rawValue { - let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle) + if let fontStyle = fontAtt.style { attributedString.removeAttribute(.font, range: range) attributedString.removeAttribute(.foregroundColor, range: range) - attributedString.addAttributes(styles.attributes(at: 0, effectiveRange: nil), range: range) + attributedString.addAttribute(.font, value: fontStyle.getFont(), range: range) + attributedString.addAttribute(.foregroundColor, value: fontStyle.color(), range: range) } else { let fontSize = fontAtt.size var font: UIFont? diff --git a/MVMCoreUI/Atomic/Atoms/Views/Line.swift b/MVMCoreUI/Atomic/Atoms/Views/Line.swift index 42043c8f..22d33b1f 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Line.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Line.swift @@ -8,14 +8,27 @@ import UIKit + @objcMembers open class Line: View { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + var lineModel: LineModel? { get { return model as? LineModel } } + var lineBackgroundColor: Color? { + return (lineModel?.inverted ?? false) ? lineModel?.backgroundColor_inverted : lineModel?.backgroundColor + } + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + public var heightConstraint: NSLayoutConstraint? public var widthConstraint: NSLayoutConstraint? - + open func updateLineConstraints(constant: CGFloat) { if let useVerticalLine = lineModel?.useVerticalLine, useVerticalLine { heightConstraint?.isActive = false @@ -28,7 +41,21 @@ import UIKit } } + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public convenience init(pinTo view: UIView, edge: UIRectEdge, useMargin: Bool) { + self.init(frame: .zero) + addLine(to: view, edge: edge, useMargin: useMargin) + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + open func setStyle(_ style: LineModel.Style) { + switch style { case .standard: updateLineConstraints(constant: 1) @@ -47,21 +74,23 @@ import UIKit } } - // MARK: - Helpers open func shouldBeVisible() -> Bool { guard let type = lineModel?.type else { return false } return type != .none } - public convenience init(pinTo view: UIView, edge: UIRectEdge, useMargin: Bool) { - self.init(frame: .zero) + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + + open func addLine(to view: UIView, edge: UIRectEdge, useMargin: Bool) { view.addSubview(self) NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: self, useMargins: useMargin, pinTop: edge != .bottom, pinBottom: edge != .top, pinLeft: edge != .right, pinRight: edge != .left).values)) } - // MARK: - MVMCoreViewProtocol open override func setupView() { super.setupView() + heightConstraint = heightAnchor.constraint(equalToConstant: 1) heightConstraint?.isActive = true widthConstraint = widthAnchor.constraint(equalToConstant: 1) @@ -69,9 +98,9 @@ import UIKit setStyle(.standard) } - // MARK: - MoleculeViewProtocol open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) + if let lineModel = model as? LineModel { setStyle(lineModel.type) } @@ -82,7 +111,9 @@ import UIKit } public override static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + guard let type = (model as? LineModel)?.type else { return 1 } + switch type { case .none: return 0 diff --git a/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift b/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift index 7412230d..3189deab 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/LineModel.swift @@ -8,15 +8,19 @@ import UIKit + @objcMembers public class LineModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Enums + //-------------------------------------------------- - /* - The frequency of the line in a moleculeList. - all (between all cells, above top, below bottom) - allExceptTop (between all cells, below bottom) - allExceptBottom (between all cells, above top) - between (between all cells) - */ + /** + The frequency of the line in a moleculeList: + - all (between all cells, above top, below bottom) + - allExceptTop (between all cells, below bottom) + - allExceptBottom (between all cells, above top) + - between (between all cells) + */ public enum Frequency: String, Codable { case all case allExceptTop @@ -24,14 +28,14 @@ import UIKit case between } - /* - The style of the line. - standard (1 height, silver) - thin (1 height, black) - medium (2 height, black) - heavy (4 height, black) - none (hidden) - */ + /** + The style of the line: + - standard (1 height, silver) + - thin (1 height, black) + - medium (2 height, black) + - heavy (4 height, black) + - none (hidden) + */ public enum Style: String, Codable { case standard case thin @@ -40,50 +44,83 @@ import UIKit case none } + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "line" public var type: Style = .standard public var frequency: Frequency? = .allExceptTop - + //TODO: use color insted of backgroundColor. Needs server changes -// public var color: Color? + // public var color: Color? public var backgroundColor: Color? + public var backgroundColor_inverted: Color = Color(uiColor: .mvmWhite) + public var inverted: Bool = false // Use this to show vertical line // Default is false public var useVerticalLine: Bool? + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(type: Style) { self.type = type self.useVerticalLine = false } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case type case backgroundColor + case backgroundColor_inverted case color case frequency + case inverted case useVerticalLine } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + if let type = try typeContainer.decodeIfPresent(Style.self, forKey: .type) { self.type = type } + if let frequency = try typeContainer.decodeIfPresent(Frequency.self, forKey: .frequency) { self.frequency = frequency } + + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted + } + + if let backgroundColor_inverted = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor_inverted) { + self.backgroundColor_inverted = backgroundColor_inverted + } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) useVerticalLine = try typeContainer.decodeIfPresent(Bool.self, forKey: .useVerticalLine) } - + public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(type, forKey: .type) + try container.encode(inverted, forKey: .inverted) try container.encodeIfPresent(frequency, forKey: .frequency) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(backgroundColor_inverted, forKey: .backgroundColor_inverted) try container.encodeIfPresent(useVerticalLine, forKey: .useVerticalLine) } } diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index e46cfed0..18b3fee7 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -147,6 +147,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableRadioButtonBodyText.self, viewModelClass: ListLeftVariableRadioButtonBodyTextModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableCheckboxBodyText.self, viewModelClass: ListLeftVariableCheckboxBodyTextModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableIconAllTextLinks.self, viewModelClass: ListLeftVariableIconAllTextLinksModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableNumberedListAllTextAndLinks.self, viewModelClass: ListLeftVariableNumberedListAllTextAndLinksModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRVWheel.self, viewModelClass: ListRVWheelModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariablePayments.self, viewModelClass: ListRightVariablePaymentsModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListRightVariableTotalData.self, viewModelClass: ListRightVariableTotalDataModel.self) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinks.swift new file mode 100644 index 00000000..d623311b --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinks.swift @@ -0,0 +1,67 @@ +// +// ListLeftVariableNumberedListAllTextAndLinks.swift +// MVMCoreUI +// +// Created by Subhankar Acharya on 21/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ListLeftVariableNumberedListAllTextAndLinks: TableViewCell { + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + public let leftLabel = Label.createLabelTitle2XLarge(true) + public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink(frame: .zero) + public var stack: Stack + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(horizontalAlignment: .fill)), + (view: eyebrowHeadlineBodyLink, model: StackItemModel(horizontalAlignment: .fill))], + 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.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? ListLeftVariableNumberedListAllTextAndLinksModel else { return } + leftLabel.text = String(model.number) + eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 140 + } + + open override func reset() { + super.reset() + leftLabel.styleTitle2XLarge(true) + } + + open override func layoutSubviews() { + super.layoutSubviews() + // This fixes a defect body text where it doesn't layout correctly. + eyebrowHeadlineBodyLink.body.preferredMaxLayoutWidth = eyebrowHeadlineBodyLink.frame.width + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinksModel.swift new file mode 100644 index 00000000..78a85a8e --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableNumberedListAllTextAndLinksModel.swift @@ -0,0 +1,42 @@ +// +// ListLeftVariableNumberedListAllTextAndLinksModel.swift +// MVMCoreUI +// +// Created by Subhankar Acharya on 21/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListLeftVariableNumberedListAllTextAndLinksModel: ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "listLVNAll" + public var number: Int + public var eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel + + public init(number: Int, eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel) { + self.number = number + self.eyebrowHeadlineBodyLink = eyebrowHeadlineBodyLink + super.init() + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case number + case eyebrowHeadlineBodyLink + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + number = try typeContainer.decode(Int.self, forKey: .number) + eyebrowHeadlineBodyLink = try typeContainer.decode(EyebrowHeadlineBodyLinkModel.self, forKey: .eyebrowHeadlineBodyLink) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(number, forKey: .number) + try container.encode(eyebrowHeadlineBodyLink, forKey: .eyebrowHeadlineBodyLink) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabel.swift index 91034095..d415971e 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabel.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabel.swift @@ -8,6 +8,7 @@ import UIKit + @objcMembers public class RadioButtonLabel: View { public let radioButton = RadioButton() @@ -35,9 +36,6 @@ import UIKit open override func setupView() { super.setupView() - guard subviews.count == 0 else { - return - } addSubview(radioButton) radioButton.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 0).isActive = true @@ -72,5 +70,4 @@ import UIKit radioButton.set(with: radioButtonLabelModel.radioButton, delegateObject, additionalData) label.set(with: radioButtonLabelModel.label, delegateObject, additionalData) } - } diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabelModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabelModel.swift index 62df9794..014030ce 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabelModel.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/RadioButtonLabelModel.swift @@ -8,7 +8,12 @@ import Foundation + @objcMembers public class RadioButtonLabelModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "radioButtonLabel" public var backgroundColor: Color? public var moleculeName: String diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift index dc8b9cae..8a6cb82d 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift @@ -155,8 +155,8 @@ import UIKit super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject self.additionalData = additionalData - self.selectedIndex = tabsModel?.selectedIndex ?? 0 - self.bottomLine.backgroundColor = tabsModel?.selectedColor.uiColor + selectedIndex = tabsModel?.selectedIndex ?? 0 + bottomLine.backgroundColor = tabsModel?.selectedColor.uiColor reloadData() } } @@ -236,13 +236,13 @@ extension Tabs: UICollectionViewDelegateFlowLayout { collect.selectItem(at: indexPath, animated: animated, scrollPosition: .centeredHorizontally) guard let tabCell = collect.cellForItem(at: indexPath) as? TabItemCell, let tabsModel = self.tabsModel else { return } - self.moveBottomLine(toIndex: indexPath, animated: animated, cell: tabCell) + moveBottomLine(toIndex: indexPath, animated: animated, cell: tabCell) tabCell.label.textColor = tabsModel.selectedColor.uiColor tabCell.updateAccessibility(indexPath: indexPath, selected: true, tabsModel: tabsModel) tabCell.setNeedsDisplay() tabCell.setNeedsLayout() tabCell.layoutIfNeeded() - self.delegate?.didSelectItem(indexPath, tabs: self) + delegate?.didSelectItem(indexPath, tabs: self) } } diff --git a/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift b/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift index 3089596a..a37fe016 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/CarouselItem.swift @@ -8,7 +8,7 @@ import Foundation -open class CarouselItem: MoleculeCollectionViewCell { +open class CarouselItem: MoleculeCollectionViewCell, CarouselItemProtocol { open var allowsPeaking = false var peakingLeftArrow = UIImageView(image: MVMCoreUIUtility.imageNamed("peakingRightArrow")?.withRenderingMode(.alwaysTemplate)) diff --git a/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift index 10b51bff..2306dc94 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/MoleculeCollectionViewCell.swift @@ -10,10 +10,11 @@ import UIKit /// A collection item that is a container for any molecule. open class MoleculeCollectionViewCell: CollectionViewCell { - + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let collectionModel = model as? MoleculeCollectionItemModel else { return } + if molecule == nil { if let moleculeView = MoleculeObjectMapping.shared()?.createMolecule(collectionModel.molecule, delegateObject: delegateObject, additionalData: additionalData) { addMolecule(moleculeView) @@ -21,16 +22,15 @@ open class MoleculeCollectionViewCell: CollectionViewCell { } else { molecule?.set(with: collectionModel.molecule, delegateObject, additionalData) } - + guard let molecule = molecule as? (UIView & MVMCoreUIViewConstrainingProtocol) else { return } containerHelper.set(with: collectionModel, for: molecule) - accessibilityElements = molecule.subviews } open override func reset() { super.reset() molecule?.reset() - backgroundColor = .white + backgroundColor = .mvmWhite } open class func nameForReuse(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { diff --git a/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemButtonModel.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemButtonModel.swift new file mode 100644 index 00000000..26be6952 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemButtonModel.swift @@ -0,0 +1,41 @@ +// +// NavigationItemButtonModel.swift +// +// +// Created by Scott Pfeil on 5/18/20. +// + +import Foundation + +public class NavigationItemButtonModel: Codable { + var imageName: String + var action: ActionModelProtocol + + public init(with imageName: String, action: ActionModelProtocol) { + self.imageName = imageName + self.action = action + } + + private enum CodingKeys: String, CodingKey { + case imageName + case action + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + imageName = try typeContainer.decode(String.self, forKey: .imageName) + action = try typeContainer.decodeModel(codingKey: .action) + } + + open func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(imageName, forKey: .imageName) + try container.encodeModel(action, forKey: .action) + } + + /// Convenience function that creates a BarButtonItem for the model. + public func createNavigationItemButton(delegateObject: MVMCoreUIDelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) -> BarButtonItem { + let image = UIImage(named: imageName, in: MVMCoreCache.shared()?.bundleToUseForImages(), compatibleWith: nil) + return BarButtonItem.create(with: image, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/NavigationItemModelProtocol.swift b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift similarity index 66% rename from MVMCoreUI/Atomic/Molecules/NavigationItemModelProtocol.swift rename to MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift index f15a847a..41a35182 100644 --- a/MVMCoreUI/Atomic/Molecules/NavigationItemModelProtocol.swift +++ b/MVMCoreUI/Atomic/Molecules/NavigationBar/NavigationItemModel.swift @@ -8,48 +8,7 @@ import Foundation -public protocol NavigationItemModelProtocol { - var title: String? { get set } - var hidden: Bool { get set } - var backgroundColor: Color? { get set } - var translucent: Bool { get set } - var tintColor: Color { get set } - var line: LineModel? { get set } - var showLeftPanelButton: Bool { get set } - var showRightPanelButton: Bool { get set } - var backButton: NavigationItemButtonModel? { get set } - var additionalLeftButtons: [NavigationItemButtonModel]? { get set } - var additionalRightButtons: [NavigationItemButtonModel]? { get set } -} - -public class NavigationItemButtonModel: Codable { - var imageName: String - var action: ActionModelProtocol - - public init(with imageName: String, action: ActionModelProtocol) { - self.imageName = imageName - self.action = action - } - - private enum CodingKeys: String, CodingKey { - case imageName - case action - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - imageName = try typeContainer.decode(String.self, forKey: .imageName) - action = try typeContainer.decodeModel(codingKey: .action) - } - - open func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(imageName, forKey: .imageName) - try container.encodeModel(action, forKey: .action) - } -} - -public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtocol { +public class NavigationItemModel: NavigationItemModelProtocol, PanelNavigationItemModelProtocol, MoleculeModelProtocol { public class var identifier: String { return "navigationBar" } @@ -57,18 +16,18 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt public var title: String? public var hidden: Bool public var backgroundColor: Color? - public var translucent: Bool public var tintColor: Color public var line: LineModel? - public var showLeftPanelButton: Bool - public var showRightPanelButton: Bool public var backButton: NavigationItemButtonModel? public var additionalLeftButtons: [NavigationItemButtonModel]? public var additionalRightButtons: [NavigationItemButtonModel]? + // Legacy, will remove once menu is gone. + public var showLeftPanelButton: Bool + public var showRightPanelButton: Bool + public init() { hidden = false - translucent = false backgroundColor = Color(uiColor: .white) tintColor = Color(uiColor: .black) line = LineModel(type: .standard) @@ -81,7 +40,6 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt case title case hidden case backgroundColor - case translucent case tintColor case line case backButton @@ -96,7 +54,6 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt title = try typeContainer.decodeIfPresent(String.self, forKey: .title) hidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .hidden) ?? false backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) ?? Color(uiColor: .white) - translucent = try typeContainer.decodeIfPresent(Bool.self, forKey: .translucent) ?? false tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) backButton = try typeContainer.decodeIfPresent(NavigationItemButtonModel.self, forKey: .backButton) ?? NavigationItemButtonModel(with: "back", action: ActionBackModel()) @@ -111,7 +68,6 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt try container.encodeIfPresent(title, forKey: .title) try container.encode(hidden, forKey: .hidden) try container.encode(backgroundColor, forKey: .backgroundColor) - try container.encode(translucent, forKey: .translucent) try container.encode(tintColor, forKey: .tintColor) try container.encodeIfPresent(line, forKey: .line) try container.encodeIfPresent(backButton, forKey: .backButton) diff --git a/MVMCoreUI/Atomic/Organisms/Carousel.swift b/MVMCoreUI/Atomic/Organisms/Carousel.swift index 30d4c21e..9fa72b5d 100644 --- a/MVMCoreUI/Atomic/Organisms/Carousel.swift +++ b/MVMCoreUI/Atomic/Organisms/Carousel.swift @@ -36,7 +36,7 @@ open class Carousel: View { open var numberOfPages = 0 /// The models for the molecules. - var molecules: [MoleculeModelProtocol]? + var molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]? /// The horizontal alignment of the cell in the collection view. Only noticeable if the itemWidthPercent is less than 100%. public var itemAlignment = UICollectionView.ScrollPosition.left @@ -176,11 +176,9 @@ open class Carousel: View { if carouselModel?.loop ?? false && newMolecules.count > 1 { // Sets up the row data with buffer cells on each side (for illusion of endless scroll... also has one more buffer cell on each side in case we can peek that cell). loop = true - - molecules?.insert(newMolecules.last!, at: 0) - molecules?.insert(newMolecules[(newMolecules.count - 2)], at: 0) - molecules?.append(newMolecules.first!) - molecules?.append(newMolecules[1]) + + molecules?.insert(contentsOf: newMolecules.suffix(2), at: 0) + molecules?.append(contentsOf: newMolecules.prefix(2)) } pageIndex = 0 @@ -259,15 +257,15 @@ open class Carousel: View { let visibleItemsPaths = collectionView.indexPathsForVisibleItems.sorted { $0.row < $1.row } if let firstItem = visibleItemsPaths.first, firstItem.row != currentIndex { - (collectionView.cellForItem(at: firstItem) as? CarouselItem)?.setPeaking(true, animated: true) + (collectionView.cellForItem(at: firstItem) as? CarouselItemProtocol)?.setPeaking(true, animated: true) } if let lastItem = visibleItemsPaths.last, lastItem.row != currentIndex { - (collectionView.cellForItem(at: lastItem) as? CarouselItem)?.setPeaking(true, animated: true) + (collectionView.cellForItem(at: lastItem) as? CarouselItemProtocol)?.setPeaking(true, animated: true) } } else { // Hide peaking. for item in collectionView.visibleCells { - (item as? CarouselItem)?.setPeaking(false, animated: true) + (item as? CarouselItemProtocol)?.setPeaking(false, animated: true) } } } @@ -301,7 +299,7 @@ extension Carousel: UICollectionViewDelegateFlowLayout { } open func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - (cell as? CarouselItem)?.setPeaking(false, animated: false) + (cell as? CarouselItemProtocol)?.setPeaking(false, animated: false) } } diff --git a/MVMCoreUI/Atomic/Organisms/CarouselModel.swift b/MVMCoreUI/Atomic/Organisms/CarouselModel.swift index 857901e8..5ff753aa 100644 --- a/MVMCoreUI/Atomic/Organisms/CarouselModel.swift +++ b/MVMCoreUI/Atomic/Organisms/CarouselModel.swift @@ -19,7 +19,7 @@ import UIKit } public var backgroundColor: Color? - public var molecules: [CarouselItemModel] + public var molecules: [MoleculeModelProtocol & CarouselItemModelProtocol] public var index: Int = 0 public var spacing: Float? public var border: Bool? @@ -29,7 +29,7 @@ import UIKit public var itemAlignment: UICollectionView.ScrollPosition? public var pagingMolecule: (CarouselPagingModelProtocol & MoleculeModelProtocol)? - public init(molecules: [CarouselItemModel]) { + public init(molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]) { self.molecules = molecules } @@ -57,7 +57,7 @@ import UIKit required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules) + molecules = try typeContainer.decodeModels(codingKey: .molecules) index = try typeContainer.decodeIfPresent(Int.self, forKey: .index) ?? 0 backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) spacing = try typeContainer.decodeIfPresent(Float.self, forKey: .spacing) @@ -73,7 +73,7 @@ import UIKit var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) - try container.encode(molecules, forKey: .molecules) + try container.encodeModels(molecules, forKey: .molecules) try container.encode(spacing, forKey: .spacing) try container.encode(border, forKey: .border) try container.encode(loop, forKey: .loop) diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift index 093c9337..198cf80d 100644 --- a/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemModelProtocol.swift @@ -9,7 +9,5 @@ import Foundation -public protocol CarouselItemModelProtocol: ContainerModelProtocol, MoleculeModelProtocol { - var peakingUI: Bool? { get } - var peakingArrowColor: Color? { get } +public protocol CarouselItemModelProtocol: ContainerModelProtocol { } diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemProtocol.swift new file mode 100644 index 00000000..4f49e5f7 --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/CarouselItemProtocol.swift @@ -0,0 +1,15 @@ +// +// CarouselItemProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 5/20/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol CarouselItemProtocol: UICollectionViewCell { + + /// Notifies the cell if it is peaking or not. + func setPeaking(_ peaking: Bool, animated: Bool) +} diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift new file mode 100644 index 00000000..0bd192e3 --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/NavigationItemModelProtocol.swift @@ -0,0 +1,20 @@ +// +// NavigationItemModelProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 5/18/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol NavigationItemModelProtocol { + var title: String? { get set } + var hidden: Bool { get set } + var backgroundColor: Color? { get set } + var tintColor: Color { get set } + var line: LineModel? { get set } + var backButton: NavigationItemButtonModel? { get set } + var additionalLeftButtons: [NavigationItemButtonModel]? { get set } + var additionalRightButtons: [NavigationItemButtonModel]? { get set } +} diff --git a/MVMCoreUI/Atomic/Protocols/ModelProtocols/PanelNavigationItemModelProtocol.swift b/MVMCoreUI/Atomic/Protocols/ModelProtocols/PanelNavigationItemModelProtocol.swift new file mode 100644 index 00000000..8f599be4 --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/ModelProtocols/PanelNavigationItemModelProtocol.swift @@ -0,0 +1,14 @@ +// +// PanelNavigationItemModelProtocol.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 5/18/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public protocol PanelNavigationItemModelProtocol { + var showLeftPanelButton: Bool { get set } + var showRightPanelButton: Bool { get set } +} diff --git a/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift index a141ef0e..63140e6e 100644 --- a/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift @@ -8,6 +8,7 @@ import UIKit + open class ModalMoleculeListTemplate: MoleculeListTemplate { //-------------------------------------------------- // MARK: - Properties @@ -15,13 +16,20 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate { public var closeButton: Button? + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + override open func handleNewData() { super.handleNewData() + closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: { _ in MVMCoreNavigationHandler.shared()?.removeCurrentViewController() }) - + } + + open override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() accessibilityElements = [closeButton as Any, tableView as Any] - UIAccessibility.post(notification: .layoutChanged, argument: closeButton) } } diff --git a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift index 521a0d45..4be4e025 100644 --- a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift @@ -8,6 +8,7 @@ import UIKit + open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol { //-------------------------------------------------- // MARK: - Stored Properties diff --git a/MVMCoreUI/BaseClasses/BarButtonItem.swift b/MVMCoreUI/BaseClasses/BarButtonItem.swift new file mode 100644 index 00000000..ac26c040 --- /dev/null +++ b/MVMCoreUI/BaseClasses/BarButtonItem.swift @@ -0,0 +1,82 @@ +// +// BarButtonItem.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 5/18/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +public typealias BarButtonAction = (BarButtonItem) -> () + +@objc fileprivate class ActionDelegate: NSObject { + fileprivate var buttonAction: BarButtonAction? + @objc fileprivate func callActionBlock(_ sender: BarButtonItem) { + buttonAction?(sender) + } +} + +@objcMembers open class BarButtonItem: UIBarButtonItem, MFButtonProtocol { + + //-------------------------------------------------- + // MARK: - Delegate + //-------------------------------------------------- + + open weak var buttonDelegate: ButtonDelegateProtocol? + private var actionDelegate: ActionDelegate? + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + public static func create(with image: UIImage?) -> Self { + let actionObject = ActionDelegate() + let button = self.init(image: image, style: .plain, target: actionObject, action: #selector(actionObject.callActionBlock(_:))) + button.actionDelegate = actionObject + return button + } + + /// Creates the item with the passed in action. + public static func create(with image: UIImage?, actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { + let button = create(with: image) + button.set(with: actionModel, delegateObject: delegateObject, additionalData: additionalData) + return button + } + + /// Creates the item with the passed in action map. + public static func create(with image: UIImage?, actionMap: [AnyHashable : Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Self { + let button = create(with: image) + button.set(with: actionMap, delegateObject: delegateObject, additionalData: additionalData) + return button + } + + /// Creates the item with the passed in action. + public static func create(with image: UIImage?, action: @escaping BarButtonAction) -> Self { + let button = create(with: image) + button.actionDelegate?.buttonAction = action + return button + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + open func set(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + buttonDelegate = delegateObject?.buttonDelegate + actionDelegate?.buttonAction = { sender in + if let data = try? actionModel.encode(using: JSONEncoder()), + let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any], + delegateObject?.buttonDelegate?.button?(sender, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } + } + + open func set(with actionMap: [AnyHashable : Any], delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + buttonDelegate = delegateObject?.buttonDelegate + actionDelegate?.buttonAction = { sender in + guard delegateObject?.buttonDelegate?.button?(sender, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true else { return } + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } +} + diff --git a/MVMCoreUI/BaseClasses/CollectionViewCell.swift b/MVMCoreUI/BaseClasses/CollectionViewCell.swift index e11b350c..aa74d229 100644 --- a/MVMCoreUI/BaseClasses/CollectionViewCell.swift +++ b/MVMCoreUI/BaseClasses/CollectionViewCell.swift @@ -8,9 +8,13 @@ import Foundation + /// A base collection view cell with basic mvm functionality. open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCoreViewProtocol, CollectionTemplateItemProtocol { - + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + // Convenience helpers open var molecule: MoleculeViewProtocol? public let containerHelper = ContainerHelper() @@ -18,9 +22,13 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo /// The width, used for establishing columns open var width: CGFloat? - + private var initialSetupPerformed = false - + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + // MARK: - Inits public override init(frame: CGRect) { super.init(frame: .zero) @@ -39,6 +47,10 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo } } + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + // MARK: - MVMCoreViewProtocol open func setupView() { isAccessibilityElement = false @@ -58,15 +70,19 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo open func reset() { molecule?.reset() - backgroundColor = .white + backgroundColor = .mvmWhite width = nil } + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let model = model as? CollectionItemModelProtocol else { return } self.model = model - + if let moleculeModel = model as? MoleculeModelProtocol, let backgroundColor = moleculeModel.backgroundColor { self.backgroundColor = backgroundColor.uiColor @@ -84,8 +100,11 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo containerHelper.constrainView(molecule) self.molecule = molecule } - + + //-------------------------------------------------- // MARK: - CollectionTemplateItemProtocol + //-------------------------------------------------- + public func set(width: CGFloat) { self.width = width } @@ -94,11 +113,11 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo override open func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes) guard let width = width else { return autoLayoutAttributes } - + let targetSize = CGSize(width: width, height: 0) - let newSize = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.defaultLow) + let newSize = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow) let newFrame = CGRect(origin: autoLayoutAttributes.frame.origin, size: newSize) autoLayoutAttributes.frame = newFrame return autoLayoutAttributes - } + } } diff --git a/MVMCoreUI/BaseClasses/TableViewCell.swift b/MVMCoreUI/BaseClasses/TableViewCell.swift index f07b1ee2..254a09f0 100644 --- a/MVMCoreUI/BaseClasses/TableViewCell.swift +++ b/MVMCoreUI/BaseClasses/TableViewCell.swift @@ -206,7 +206,6 @@ import UIKit caret.translatesAutoresizingMaskIntoConstraints = true caret.isAccessibilityElement = true caret.accessibilityTraits = .button - caret.accessibilityLabel = "Caret," caret.accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "AccTabHint") caret.size = .small(.vertical) if let size = caret.size?.dimensions() { diff --git a/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift b/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift index 4ec8ed2c..64242fc3 100644 --- a/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift +++ b/MVMCoreUI/BaseControllers/MVMControllerModelProtocol.swift @@ -5,6 +5,7 @@ // Created by Scott Pfeil on 3/16/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // +// A convenience aggregate for common MVM templates. import Foundation diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index c44a8d47..782deae2 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -158,18 +158,27 @@ import UIKit needsUpdateUI = true view.setNeedsLayout() } - - /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, - open func handleNewData() { - // TODO: remove legacy. Temporary, convert legacy to navigation model. - var navigationModel = pageModel?.navigationItem ?? NavigationItemModel() + + /// Creates a legacy navigation model. + open func createDefaultLegacyNavigationModel() -> NavigationItemModel { + let navigationModel = NavigationItemModel() navigationModel.title = pageModel?.screenHeading navigationModel.showLeftPanelButton = isMasterInitiallyAccessible() navigationModel.showRightPanelButton = isSupportInitiallyAccessible() if /*(self as? MVMCoreUITabBarPageControlViewController) != nil ||*/ manager != nil || loadObject?.requestParameters?.tabWasPressed ?? false == true { navigationModel.line = LineModel(type: .none) } - pageModel?.navigationItem = navigationModel + return navigationModel + } + + /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, + open func handleNewData() { + // TODO: remove legacy. Temporary, convert legacy to navigation model. + if pageModel?.navigationItem == nil { + let navigationItem = createDefaultLegacyNavigationModel() + pageModel?.navigationItem = navigationItem + } + if formValidator == nil { let rules = pageModel?.formRules formValidator = FormValidator(rules) diff --git a/MVMCoreUI/Categories/UIColor+Extension.swift b/MVMCoreUI/Categories/UIColor+Extension.swift index 1ef2e217..27816af2 100644 --- a/MVMCoreUI/Categories/UIColor+Extension.swift +++ b/MVMCoreUI/Categories/UIColor+Extension.swift @@ -34,6 +34,7 @@ extension UIColor { "green33": (.mvmGreen33, "#ABE4BF"), "green66": (.mvmGreen66, "#57C880"), "greenShade2": (.mvmGreenShade2, "#0F5B25"), + "greenInverted": (.mvmGreenInverted, "#00AC3E"), "orange": (.mvmOrange, "#ED7000"), "orange66": (.mvmOrange66, "#F3A157"), "orange33": (.mvmOrange33, "#F9D0AB"), @@ -45,6 +46,7 @@ extension UIColor { "blue66": (.mvmBlue66, "#57B1DF"), "blueShade1": (.mvmBlueShade1, "#136598"), "blueShade2": (.mvmBlueShade2, "#0B4467"), + "blueInverted": (.mvmBlueInverted, "#0088CE"), "yellow": (.mvmYellow, "#FFBC3D"), "coolGray1": (.mvmCoolGray1, "#F6F6F6"), "coolGray3": (.mvmCoolGray3, "#D8DADA"), @@ -147,6 +149,9 @@ extension UIColor { /// HEX: #0F5B25 public static let mvmGreenShade2 = UIColor.color8Bits(red: 15, green: 91, blue: 37) + /// HEX: #00AC3E + public static let mvmGreenInverted = UIColor.color8Bits(red: 0, green: 172, blue: 62) + //-------------------------------------------------- // MARK: - Blue //-------------------------------------------------- @@ -166,6 +171,9 @@ extension UIColor { /// HEX: #0B4467 public static let mvmBlueShade2 = UIColor.color8Bits(red: 11, green: 68, blue: 103) + /// HEX: #0088CE + public static let mvmBlueInverted = UIColor.color8Bits(red: 0, green: 136, blue: 206) + //-------------------------------------------------- // MARK: - Yellow //-------------------------------------------------- @@ -269,31 +277,6 @@ extension UIColor { return .white } - public class func setBackgroundColor(_ color: UIColor, for navigationBar: UINavigationBar, isTransparent: Bool) { - - DispatchQueue.main.async { - - let view = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 1)) - view.backgroundColor = color - - UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0) - - if let context = UIGraphicsGetCurrentContext() { - view.layer.render(in: context) - } - - let image = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - - if isTransparent { - navigationBar.setBackgroundImage(UIImage(), for: .default) - navigationBar.isTranslucent = false - } else { - navigationBar.setBackgroundImage(image, for: .default) - } - } - } - /// - parameter color: The UIColor intended to retrieve its hex value. public class func hexString(for color: UIColor) -> String? { diff --git a/MVMCoreUI/Categories/UIColor+MFConvenience.h b/MVMCoreUI/Categories/UIColor+MFConvenience.h index 92b28a36..97280ba1 100644 --- a/MVMCoreUI/Categories/UIColor+MFConvenience.h +++ b/MVMCoreUI/Categories/UIColor+MFConvenience.h @@ -183,9 +183,6 @@ // Returns a gradient lighter color; + (nonnull UIColor *)mfGradientColor:(nullable UIColor *)color; -// Sets the background color for the nav bar. -+ (void)mfSetBackgroundColorForNavigationBar:(nonnull UIColor *)color navigationBar:(nonnull UINavigationBar *)navigationBar transparent:(BOOL)transparent; - #pragma mark - Hex String + (nullable NSString *)hexStringForColor:(nonnull UIColor*)color; diff --git a/MVMCoreUI/Categories/UIColor+MFConvenience.m b/MVMCoreUI/Categories/UIColor+MFConvenience.m index 33df3848..8786df01 100644 --- a/MVMCoreUI/Categories/UIColor+MFConvenience.m +++ b/MVMCoreUI/Categories/UIColor+MFConvenience.m @@ -389,27 +389,6 @@ return [UIColor whiteColor]; } - -+ (void)mfSetBackgroundColorForNavigationBar:(nonnull UIColor *)color navigationBar:(nonnull UINavigationBar *)navigationBar transparent:(BOOL)transparent { - - [MVMCoreDispatchUtility performBlockOnMainThread:^{ - UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)]; - view.backgroundColor = color; - UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0); - [view.layer renderInContext:UIGraphicsGetCurrentContext()]; - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - if (transparent) { - [navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; - [navigationBar setTranslucent:NO]; - - } else { - [navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault]; - - } - }]; -} - #pragma mark - Hex String + (nullable NSString *)hexStringForColor:(nonnull UIColor*)color { diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 05e5c406..4367f2e1 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -11,42 +11,68 @@ import UIKit @objcMembers open class NavigationController: UINavigationController { public var separatorView: Line? + /// Getter for the main navigation controller public static func navigationController() -> Self? { return MVMCoreActionUtility.initializerClassCheck(MVMCoreUISession.sharedGlobal()?.navigationController, classToVerify: self) as? Self } - public static func style(_ navigationBar: UINavigationBar) { - UIColor.mfSetBackgroundColor(forNavigationBar: .white, navigationBar: navigationBar, transparent: false) + /// Provides MVM styling to the navigation bar. Returns a reference to the line. + public static func style(_ navigationBar: UINavigationBar) -> Line { + navigationBar.backgroundColor = .white navigationBar.shadowImage = UIImage() navigationBar.isOpaque = true navigationBar.tintColor = .black navigationBar.titleTextAttributes = [NSAttributedString.Key.font: MFStyler.fontBoldBodySmall(false)]; + return Line(pinTo: navigationBar, edge: .bottom, useMargin: false) } + /// Sets up the application with a navigation controller public static func setupNavigationController() -> Self? { let navigationController = self.init() - style(navigationController.navigationBar) - navigationController.separatorView = Line(pinTo: navigationController.navigationBar, edge: .bottom, useMargin: false) - navigationController.separatorView?.setStyle(.standard) + navigationController.separatorView = style(navigationController.navigationBar) MVMCoreUISession.sharedGlobal()?.navigationController = navigationController MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController MVMCoreNavigationHandler.shared()?.navigationController = navigationController return navigationController } + /// Sets up the application with a navigation controller as the main container. public static func setupNavigationControllerAsMainController() -> Self? { guard let navigationController = setupNavigationController() else { return nil } MVMCoreUISession.sharedGlobal()?.setup(asStandardLoadViewDelegate: navigationController) return navigationController } + /// Convenience function for setting the navigation buttons. + public static func setNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { + let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject + var items: [UIBarButtonItem] = [] + if let backButtonModel = navigationItemModel.backButton, + navigationController.viewControllers.count > 1 { + items.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } + if let leftItemModels = navigationItemModel.additionalLeftButtons { + for item in leftItemModels { + items.append(item.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } + viewController.navigationItem.leftBarButtonItems = items + } + if let rightItemModels = navigationItemModel.additionalRightButtons { + for item in rightItemModels { + items.append(item.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) + } + viewController.navigationItem.rightBarButtonItems = items + } + } + + /// Convenience function for setting navigation bar with model. public static func set(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { viewController.navigationItem.title = navigationItemModel.title viewController.navigationItem.accessibilityLabel = navigationItemModel.title viewController.navigationItem.hidesBackButton = (navigationItemModel.backButton != nil) navigationController.setNavigationBarHidden(navigationItemModel.hidden, animated: true) - UIColor.setBackgroundColor(navigationItemModel.backgroundColor?.uiColor ?? .white, for: navigationController.navigationBar, isTransparent: navigationItemModel.translucent) + navigationController.navigationBar.barTintColor = navigationItemModel.backgroundColor?.uiColor ?? .white let tint = navigationItemModel.tintColor.uiColor navigationController.navigationBar.tintColor = tint @@ -54,19 +80,26 @@ import UIKit // Have the navigation title match the tint color navigationController.navigationBar.titleTextAttributes?.updateValue(tint, forKey: .foregroundColor) - // Update icons if main navigation controller. - if navigationController == MVMCoreUISession.sharedGlobal()?.navigationController, - navigationController.topViewController == viewController { - // Update line. - MVMCoreUISession.sharedGlobal()?.navigationController?.separatorView?.isHidden = navigationItemModel.line?.type ?? .standard == .none + // Update line. + if let navigationController = navigationController as? NavigationController { + navigationController.separatorView?.isHidden = navigationItemModel.line?.type ?? .standard == .none } - if navigationController == MVMCoreUISplitViewController.main()?.navigationController, - navigationController.topViewController == viewController { - // Update Panels - MVMCoreUISplitViewController.main()?.setLeftPanelIsAccessible(navigationItemModel.showLeftPanelButton , for: viewController) - MVMCoreUISplitViewController.main()?.setRightPanelIsAccessible(navigationItemModel.showRightPanelButton , for: viewController) - MVMCoreUISession.sharedGlobal()?.splitViewController?.setNavigationIconColor(tint) + // Let legacy splitview controller handle buttons for now. + guard navigationController == MVMCoreUISplitViewController.main()?.navigationController, + navigationController.topViewController == viewController else { + // Not the main split view controller, add buttons. + setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + return + } + + // Update icons if main navigation controller. + MVMCoreUISession.sharedGlobal()?.splitViewController?.setNavigationIconColor(tint) + + // Update Panels + if let model = navigationItemModel as? PanelNavigationItemModelProtocol { + MVMCoreUISplitViewController.main()?.setLeftPanelIsAccessible(model.showLeftPanelButton, for: viewController) + MVMCoreUISplitViewController.main()?.setRightPanelIsAccessible(model.showRightPanelButton, for: viewController) } } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h index 0c252a08..e70a8b8b 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.h @@ -124,8 +124,8 @@ typedef NS_ENUM(NSInteger, MFNumberOfDrawers) { - (nullable UIImage *)imageForBackButton; // Can overide to provide other global buttons to be on the navigation bar. -- (nullable NSArray *)additionalLeftButtons; -- (nullable NSArray *)additionalRightButtons; +- (nullable NSArray *)additionalLeftButtonsForViewController:(nonnull UIViewController *)viewController; +- (nullable NSArray *)additionalRightButtonsForViewController:(nonnull UIViewController *)viewController; // The width of the panel when it is permanently extended. Default 320. - (CGFloat)leftPanelExtendedWidth; diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m index e93ad613..d9814a1f 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController.m @@ -12,6 +12,7 @@ @import MVMCore.MVMCoreViewManagerProtocol; @import MVMCore.MVMCoreObject; @import MVMCore.MVMCoreActionUtility; +@import MVMCore.NSDictionary_MFConvenience; #import "MVMCoreUIUtility.h" #import "UIColor+MFConvenience.h" #import "NSLayoutConstraint+MFConvenience.h" @@ -106,12 +107,29 @@ CGFloat const PanelAnimationDuration = 0.2; return (width > 2000 ? MFTwoDrawer : (width > 1000 ? MFOneDrawer : MFNoDrawer)); } -- (nullable NSArray *)additionalLeftButtons { - return nil; +- (nullable UIBarButtonItem *)getBackButtonForViewController:(nonnull UIViewController *)viewController { + if (![viewController conformsToProtocol:@protocol(MVMCoreViewControllerProtocol)]) { return self.backButton; } + UIViewController *controller = (UIViewController *)viewController; + NSDictionary *item = [controller.loadObject.pageJSON dictWithChainOfKeysOrIndexes:@[@"navigationItem",@"backButton"]]; + if (!item) { return self.backButton; } + DelegateObject *delegate = [controller delegateObject]; + return [self createNavigationItemButtonFrom:item delegateObject:[delegate isKindOfClass:[MVMCoreUIDelegateObject class]] ? (MVMCoreUIDelegateObject *)delegate : nil]; } -- (nullable NSArray *)additionalRightButtons { - return nil; +- (nullable NSArray *)additionalLeftButtonsForViewController:(nonnull UIViewController *)viewController { + if (![viewController conformsToProtocol:@protocol(MVMCoreViewControllerProtocol)]) { return nil; } + UIViewController *controller = (UIViewController *)viewController; + NSArray *items = [controller.loadObject.pageJSON arrayForChainOfKeysOrIndexes:@[@"navigationItem",@"additionalLeftButtons"]]; + DelegateObject *delegate = [controller delegateObject]; + return [self createNavigationItemButtonsFrom:items delegateObject:[delegate isKindOfClass:[MVMCoreUIDelegateObject class]] ? (MVMCoreUIDelegateObject *)delegate : nil]; +} + +- (nullable NSArray *)additionalRightButtonsForViewController:(nonnull UIViewController *)viewController { + if (![viewController conformsToProtocol:@protocol(MVMCoreViewControllerProtocol)]) { return nil; } + UIViewController *controller = (UIViewController *)viewController; + NSArray *items = [controller.loadObject.pageJSON arrayForChainOfKeysOrIndexes:@[@"navigationItem",@"additionalRightButtons"]]; + DelegateObject *delegate = [controller delegateObject]; + return [self createNavigationItemButtonsFrom:items delegateObject:[delegate isKindOfClass:[MVMCoreUIDelegateObject class]] ? (MVMCoreUIDelegateObject *)delegate : nil]; } - (CGFloat)leftPanelExtendedWidth { @@ -234,12 +252,18 @@ CGFloat const PanelAnimationDuration = 0.2; - (void)setLeftNavigationItemForViewController:(UIViewController * _Nonnull)viewController accessible:(BOOL)accessible extended:(BOOL)extended { NSMutableArray *leftBarButtonItems = [NSMutableArray array]; if ([viewController.navigationController.viewControllers count] > 1) { - [leftBarButtonItems addObject:self.backButton]; + UIBarButtonItem *button = [self getBackButtonForViewController:viewController]; + if (button) { + viewController.navigationItem.hidesBackButton = YES; + [leftBarButtonItems addObject:button]; + } else { + viewController.navigationItem.hidesBackButton = NO; + } } if ((accessible && !extended) && self.leftPanelButton) { [leftBarButtonItems addObject:self.leftPanelButton]; } - NSArray *extraButtons = [self additionalLeftButtons]; + NSArray *extraButtons = [self additionalLeftButtonsForViewController:viewController]; if (extraButtons) { [leftBarButtonItems addObjectsFromArray:extraButtons]; } @@ -410,7 +434,7 @@ CGFloat const PanelAnimationDuration = 0.2; if ((accessible && !extended) && self.rightPanelButton) { [navigationItems addObject:self.rightPanelButton]; } - NSArray *extraButtons = [self additionalRightButtons]; + NSArray *extraButtons = [self additionalRightButtonsForViewController:viewController]; if (extraButtons) { [navigationItems addObjectsFromArray:extraButtons]; } @@ -601,6 +625,24 @@ CGFloat const PanelAnimationDuration = 0.2; #pragma mark - Other Panel Functions +/// Convenience function, creates a BarButtonItem from button json +- (nonnull UIBarButtonItem *)createNavigationItemButtonFrom:(nonnull NSDictionary *)JSON delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject { + UIImage *image = [UIImage imageNamed:[JSON string:@"imageName"] inBundle:[[MVMCoreCache sharedCache] bundleToUseForImages] compatibleWithTraitCollection:nil]; + BarButtonItem *item = [BarButtonItem createWith:image actionMap:[JSON dict:@"action"] delegateObject:delegateObject additionalData:nil]; + return item; +} + +/// Convenience function, creates a list of BarButtonItems list of json +- (nullable NSArray *)createNavigationItemButtonsFrom:(nullable NSArray *)JSONlist delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject { + if (JSONlist.count == 0) { return nil; } + NSMutableArray *items = [NSMutableArray arrayWithCapacity:JSONlist.count]; + for (NSDictionary *itemData in JSONlist) { + UIBarButtonItem *item = [self createNavigationItemButtonFrom:itemData delegateObject:delegateObject]; + [items addObject:item]; + } + return items; +} + - (void)forceHideBothDrawers { [self hideBothDrawersShouldForceHide:YES]; } diff --git a/MVMCoreUI/Styles/Styler.swift b/MVMCoreUI/Styles/Styler.swift index d8d0a519..43e4d319 100644 --- a/MVMCoreUI/Styles/Styler.swift +++ b/MVMCoreUI/Styles/Styler.swift @@ -81,6 +81,16 @@ open class Styler { } } + public func color() -> UIColor { + switch self { + case .B3: + return .mvmCoolGray6 + + default: + return .mvmBlack + } + } + /// Determines if the selected font case is bold or regular. public func isBold() -> Bool { diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m index eeff9cfe..a882035e 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m @@ -248,6 +248,8 @@ // Sets the color if (color) { + self.button.layer.borderColor = color.CGColor; + [self.button setTitleColor:color forState:UIControlStateNormal]; [self.closeButton setTintColor:color]; }