This commit is contained in:
Kevin G Christiano 2020-10-07 09:59:59 -04:00
commit 5bc9d7e427
59 changed files with 1385 additions and 185 deletions

View File

@ -301,6 +301,8 @@
D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */; }; D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */; };
D20923592450ECE00044AD09 /* TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20923582450ECE00044AD09 /* TableView.swift */; }; D20923592450ECE00044AD09 /* TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20923582450ECE00044AD09 /* TableView.swift */; };
D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; };
D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C7008250BF99B0095B21C /* TopNotificationModel.swift */; };
D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */; };
D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */; };
D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; };
D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; }; D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */; };
@ -316,6 +318,7 @@
D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */; }; D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */; };
D22D8393241C27B100D3DF69 /* TemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8392241C27B100D3DF69 /* TemplateModel.swift */; }; D22D8393241C27B100D3DF69 /* TemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8392241C27B100D3DF69 /* TemplateModel.swift */; };
D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8394241FB41200D3DF69 /* UIStackView+Extension.swift */; }; D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8394241FB41200D3DF69 /* UIStackView+Extension.swift */; };
D23118B325124E18001C8440 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23118B225124E18001C8440 /* Notification.swift */; };
D2351C7A24A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2351C7924A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift */; }; D2351C7A24A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2351C7924A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift */; };
D2351C7C24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2351C7B24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift */; }; D2351C7C24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2351C7B24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift */; };
D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */; }; D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */; };
@ -375,7 +378,7 @@
D28A838F23CCDEDE00DFE4FC /* TwoButtonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */; }; D28A838F23CCDEDE00DFE4FC /* TwoButtonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */; };
D28A839123CD4FD400DFE4FC /* CornerLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839023CD4FD400DFE4FC /* CornerLabelsModel.swift */; }; D28A839123CD4FD400DFE4FC /* CornerLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839023CD4FD400DFE4FC /* CornerLabelsModel.swift */; };
D28A839323CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */; }; D28A839323CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */; };
D28BA730247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA72F247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift */; }; D28BA730247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA72F247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift */; };
D28BA741248025A300B75CB8 /* TabBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA740248025A300B75CB8 /* TabBarModel.swift */; }; D28BA741248025A300B75CB8 /* TabBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA740248025A300B75CB8 /* TabBarModel.swift */; };
D28BA7432480284E00B75CB8 /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA7422480284E00B75CB8 /* TabBar.swift */; }; D28BA7432480284E00B75CB8 /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA7422480284E00B75CB8 /* TabBar.swift */; };
D28BA7452481652D00B75CB8 /* TabBarProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */; }; D28BA7452481652D00B75CB8 /* TabBarProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */; };
@ -457,6 +460,11 @@
D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; }; D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; };
D2C521A923EDE79E00CA2634 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C521A823EDE79E00CA2634 /* ViewController.swift */; }; D2C521A923EDE79E00CA2634 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C521A823EDE79E00CA2634 /* ViewController.swift */; };
D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */; }; D2C78CD224228BBD00B69FDE /* ActionOpenPanelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */; };
D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7CA251104E100C75681 /* NotificationXButtonModel.swift */; };
D2CAC7CD251104FE00C75681 /* NotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7CC251104FE00C75681 /* NotificationModel.swift */; };
D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */; };
D2CAC7D12511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */; };
D2CAC7D3251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */; };
D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */; }; D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */; };
D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */; }; D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */; };
D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */; }; D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */; };
@ -474,8 +482,12 @@
D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A99E23E07F8A000B42E6 /* PillButton.swift */; }; D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A99E23E07F8A000B42E6 /* PillButton.swift */; };
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */; }; D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */; };
D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */; }; D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */; };
D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D12513EA6900564112 /* NotificationXButton.swift */; };
D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D32514F80C00564112 /* CollapsableNotification.swift */; };
D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */; };
D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; }; D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */; };
D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */; }; D2FB151D23A40F1500C20E10 /* MoleculeStackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */; };
D2FD4A4925199BD9000C28A9 /* AccessibilityProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */; };
DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; }; DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; };
DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; }; DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; };
DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; }; DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; };
@ -782,6 +794,8 @@
D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerModelBase.swift; sourceTree = "<group>"; }; D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerModelBase.swift; sourceTree = "<group>"; };
D20923582450ECE00044AD09 /* TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableView.swift; sourceTree = "<group>"; }; D20923582450ECE00044AD09 /* TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableView.swift; sourceTree = "<group>"; };
D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = "<group>"; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = "<group>"; };
D20C7008250BF99B0095B21C /* TopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopNotificationModel.swift; sourceTree = "<group>"; };
D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertView+Extension.swift"; sourceTree = "<group>"; };
D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = "<group>"; }; D20FB164241A5D75004AFC3A /* NavigationItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModel.swift; sourceTree = "<group>"; };
D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; };
D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = "<group>"; }; D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = "<group>"; };
@ -797,6 +811,7 @@
D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccordionMoleculeTableViewCell.swift; sourceTree = "<group>"; }; D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccordionMoleculeTableViewCell.swift; sourceTree = "<group>"; };
D22D8392241C27B100D3DF69 /* TemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModel.swift; sourceTree = "<group>"; }; D22D8392241C27B100D3DF69 /* TemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModel.swift; sourceTree = "<group>"; };
D22D8394241FB41200D3DF69 /* UIStackView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Extension.swift"; sourceTree = "<group>"; }; D22D8394241FB41200D3DF69 /* UIStackView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Extension.swift"; sourceTree = "<group>"; };
D23118B225124E18001C8440 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
D2351C7924A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableToggleAllTextAndLinksModel.swift; sourceTree = "<group>"; }; D2351C7924A4D433007DF0BC /* ListRightVariableToggleAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableToggleAllTextAndLinksModel.swift; sourceTree = "<group>"; };
D2351C7B24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableToggleAllTextAndLinks.swift; sourceTree = "<group>"; }; D2351C7B24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableToggleAllTextAndLinks.swift; sourceTree = "<group>"; };
D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTwoColumnPriceDescription.swift; sourceTree = "<group>"; }; D236E5B2241FEB1000C38625 /* ListTwoColumnPriceDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTwoColumnPriceDescription.swift; sourceTree = "<group>"; };
@ -854,7 +869,7 @@
D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonViewModel.swift; sourceTree = "<group>"; }; D28A838E23CCDEDE00DFE4FC /* TwoButtonViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonViewModel.swift; sourceTree = "<group>"; };
D28A839023CD4FD400DFE4FC /* CornerLabelsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerLabelsModel.swift; sourceTree = "<group>"; }; D28A839023CD4FD400DFE4FC /* CornerLabelsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerLabelsModel.swift; sourceTree = "<group>"; };
D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyCaretLinkImageModel.swift; sourceTree = "<group>"; }; D28A839223CE828900DFE4FC /* HeadlineBodyCaretLinkImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyCaretLinkImageModel.swift; sourceTree = "<group>"; };
D28BA72F247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtomModelProtocol.swift; sourceTree = "<group>"; }; D28BA72F247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonModelProtocol.swift; sourceTree = "<group>"; };
D28BA740248025A300B75CB8 /* TabBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarModel.swift; sourceTree = "<group>"; }; D28BA740248025A300B75CB8 /* TabBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarModel.swift; sourceTree = "<group>"; };
D28BA7422480284E00B75CB8 /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = "<group>"; }; D28BA7422480284E00B75CB8 /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = "<group>"; };
D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarProtocol.swift; sourceTree = "<group>"; }; D28BA7442481652D00B75CB8 /* TabBarProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarProtocol.swift; sourceTree = "<group>"; };
@ -940,6 +955,11 @@
D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = "<group>"; }; D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = "<group>"; };
D2C521A823EDE79E00CA2634 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; }; D2C521A823EDE79E00CA2634 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionOpenPanelModel.swift; sourceTree = "<group>"; }; D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionOpenPanelModel.swift; sourceTree = "<group>"; };
D2CAC7CA251104E100C75681 /* NotificationXButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationXButtonModel.swift; sourceTree = "<group>"; };
D2CAC7CC251104FE00C75681 /* NotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationModel.swift; sourceTree = "<group>"; };
D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationModel.swift; sourceTree = "<group>"; };
D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertMainView+Extension.swift"; sourceTree = "<group>"; };
D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreUITopAlertExpandableView+Extension.swift"; sourceTree = "<group>"; };
D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scroller.swift; sourceTree = "<group>"; }; D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scroller.swift; sourceTree = "<group>"; };
D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplate.swift; sourceTree = "<group>"; }; D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplate.swift; sourceTree = "<group>"; };
D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeHeaderModel.swift; sourceTree = "<group>"; }; D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeHeaderModel.swift; sourceTree = "<group>"; };
@ -956,8 +976,12 @@
D2E2A99E23E07F8A000B42E6 /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = "<group>"; }; D2E2A99E23E07F8A000B42E6 /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = "<group>"; };
D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModelProtocol.swift; sourceTree = "<group>"; }; D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModelProtocol.swift; sourceTree = "<group>"; };
D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableableModelProtocol.swift; sourceTree = "<group>"; }; D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableableModelProtocol.swift; sourceTree = "<group>"; };
D2FA83D12513EA6900564112 /* NotificationXButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationXButton.swift; sourceTree = "<group>"; };
D2FA83D32514F80C00564112 /* CollapsableNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotification.swift; sourceTree = "<group>"; };
D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationTopView.swift; sourceTree = "<group>"; };
D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.swift; sourceTree = "<group>"; }; D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainer.swift; sourceTree = "<group>"; };
D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackItem.swift; sourceTree = "<group>"; }; D2FB151C23A40F1500C20E10 /* MoleculeStackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackItem.swift; sourceTree = "<group>"; };
D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityProtocol.swift; sourceTree = "<group>"; };
DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = "<group>"; }; DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = "<group>"; };
DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; }; DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = "<group>"; }; DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = "<group>"; };
@ -1045,6 +1069,7 @@
children = ( children = (
D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */, D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */,
0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */, 0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */,
D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */,
); );
path = Protocols; path = Protocols;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1500,7 +1525,7 @@
D23EA7FC247EBB7500D60C34 /* Buttons */ = { D23EA7FC247EBB7500D60C34 /* Buttons */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D28BA72F247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift */, D28BA72F247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift */,
D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */, D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */,
D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */, D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */,
D23EA7FD247EBBB700D60C34 /* NavigationLabelButtonModel.swift */, D23EA7FD247EBBB700D60C34 /* NavigationLabelButtonModel.swift */,
@ -1693,6 +1718,7 @@
D29DF10E21E67A77003B2FB9 /* Molecules */ = { D29DF10E21E67A77003B2FB9 /* Molecules */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D2CAC7C9251104CB00C75681 /* TopNotification */,
D2509ED42472EE0B001BFB9D /* NavigationBar */, D2509ED42472EE0B001BFB9D /* NavigationBar */,
D253BB9A24587023002DE544 /* OtherContainers */, D253BB9A24587023002DE544 /* OtherContainers */,
D22B38E923F4E07800490EF6 /* DesignedComponents */, D22B38E923F4E07800490EF6 /* DesignedComponents */,
@ -1757,6 +1783,8 @@
D29DF11E21E6851E003B2FB9 /* TopAlert */ = { D29DF11E21E6851E003B2FB9 /* TopAlert */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D20C7008250BF99B0095B21C /* TopNotificationModel.swift */,
D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */,
D29DF12021E6851E003B2FB9 /* MVMCoreUITopAlertView.h */, D29DF12021E6851E003B2FB9 /* MVMCoreUITopAlertView.h */,
D29DF12421E6851E003B2FB9 /* MVMCoreUITopAlertView.m */, D29DF12421E6851E003B2FB9 /* MVMCoreUITopAlertView.m */,
D29DF12321E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.h */, D29DF12321E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.h */,
@ -2056,6 +2084,22 @@
path = Protocols; path = Protocols;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D2CAC7C9251104CB00C75681 /* TopNotification */ = {
isa = PBXGroup;
children = (
D2CAC7CA251104E100C75681 /* NotificationXButtonModel.swift */,
D2FA83D12513EA6900564112 /* NotificationXButton.swift */,
D2CAC7CC251104FE00C75681 /* NotificationModel.swift */,
D23118B225124E18001C8440 /* Notification.swift */,
D2CAC7D02511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift */,
D2CAC7CE2511052300C75681 /* CollapsableNotificationModel.swift */,
D2FA83D32514F80C00564112 /* CollapsableNotification.swift */,
D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */,
D2CAC7D2251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift */,
);
path = TopNotification;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */ /* Begin PBXHeadersBuildPhase section */
@ -2191,6 +2235,7 @@
D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */, D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */,
D2509ED62472EE2F001BFB9D /* NavigationImageButtonModel.swift in Sources */, D2509ED62472EE2F001BFB9D /* NavigationImageButtonModel.swift in Sources */,
32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */, 32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */,
D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */,
DBC4391822442197001AB423 /* CaretView.swift in Sources */, DBC4391822442197001AB423 /* CaretView.swift in Sources */,
C07065C42395677300FBF997 /* Link.swift in Sources */, C07065C42395677300FBF997 /* Link.swift in Sources */,
0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */, 0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */,
@ -2234,6 +2279,7 @@
D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */, D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */,
011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */, 011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */,
BB2BF0EA2452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift in Sources */, BB2BF0EA2452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift in Sources */,
D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */,
D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */, D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */,
0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */, 0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */,
D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */, D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */,
@ -2274,6 +2320,7 @@
D29DF12E21E6851E003B2FB9 /* MVMCoreUITopAlertView.m in Sources */, D29DF12E21E6851E003B2FB9 /* MVMCoreUITopAlertView.m in Sources */,
AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */, AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */,
BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */, BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */,
D2CAC7D3251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift in Sources */,
D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */, D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */,
D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */, D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */,
D28BA74D248589C800B75CB8 /* TabPageModelProtocol.swift in Sources */, D28BA74D248589C800B75CB8 /* TabPageModelProtocol.swift in Sources */,
@ -2300,6 +2347,7 @@
D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */, D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */,
8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */, 8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */,
94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */, 94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */,
D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */,
014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */, 014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */,
AAC23FAF24D92A1E009208DF /* ListThreeColumnSpeedTest.swift in Sources */, AAC23FAF24D92A1E009208DF /* ListThreeColumnSpeedTest.swift in Sources */,
D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */,
@ -2355,6 +2403,8 @@
AA633B3124989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift in Sources */, AA633B3124989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift in Sources */,
8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */, 8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */,
D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */, D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */,
D2FD4A4925199BD9000C28A9 /* AccessibilityProtocol.swift in Sources */,
D2CAC7CD251104FE00C75681 /* NotificationModel.swift in Sources */,
0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */, 0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */,
D20923592450ECE00044AD09 /* TableView.swift in Sources */, D20923592450ECE00044AD09 /* TableView.swift in Sources */,
BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */, BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */,
@ -2426,6 +2476,7 @@
C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */, C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */,
32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */, 32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */,
011D958524042432000E3791 /* RulesProtocol.swift in Sources */, 011D958524042432000E3791 /* RulesProtocol.swift in Sources */,
D23118B325124E18001C8440 /* Notification.swift in Sources */,
AA9972502475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift in Sources */, AA9972502475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift in Sources */,
AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */, AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */,
D264FAA3243E632F00D98315 /* ProgrammaticCollectionViewController.swift in Sources */, D264FAA3243E632F00D98315 /* ProgrammaticCollectionViewController.swift in Sources */,
@ -2463,6 +2514,7 @@
D253BB9E2458751F002DE544 /* BGImageMoleculeModel.swift in Sources */, D253BB9E2458751F002DE544 /* BGImageMoleculeModel.swift in Sources */,
AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */, AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */,
0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */, 0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */,
D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */,
8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */, 8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */,
BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */, BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */,
AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */, AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */,
@ -2470,7 +2522,7 @@
323AC96A24C837F000F8E4C4 /* ListThreeColumnBillChangesModel.swift in Sources */, 323AC96A24C837F000F8E4C4 /* ListThreeColumnBillChangesModel.swift in Sources */,
D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */,
525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */, 525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */,
D28BA730247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift in Sources */, D28BA730247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift in Sources */,
0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */, 0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */,
D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */, D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */,
AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */, AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */,
@ -2480,6 +2532,7 @@
D2A92882241AAB67004E01C6 /* ScrollingViewController.swift in Sources */, D2A92882241AAB67004E01C6 /* ScrollingViewController.swift in Sources */,
C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */, C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */,
0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */, 0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */,
D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */,
D2D90B442404789000DD6EC9 /* MoleculeContainerProtocol.swift in Sources */, D2D90B442404789000DD6EC9 /* MoleculeContainerProtocol.swift in Sources */,
0A7ECC5F243CEB1200C828E8 /* ColorViewWithLabel.swift in Sources */, 0A7ECC5F243CEB1200C828E8 /* ColorViewWithLabel.swift in Sources */,
94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */, 94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */,
@ -2514,6 +2567,7 @@
AA45AA0D24BF0276007A6EA7 /* LockUpsPlanNames.swift in Sources */, AA45AA0D24BF0276007A6EA7 /* LockUpsPlanNames.swift in Sources */,
8DE5BECF2456F7B100772E76 /* ListTwoColumnDropdownSelectors.swift in Sources */, 8DE5BECF2456F7B100772E76 /* ListTwoColumnDropdownSelectors.swift in Sources */,
D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */, D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */,
D2CAC7D12511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift in Sources */,
0A21DB83235DFBC500C160A2 /* MdnEntryField.swift in Sources */, 0A21DB83235DFBC500C160A2 /* MdnEntryField.swift in Sources */,
0AE98BB723FF18E9004C5109 /* ArrowModel.swift in Sources */, 0AE98BB723FF18E9004C5109 /* ArrowModel.swift in Sources */,
D28A837D23CCA86A00DFE4FC /* TabsListItemModel.swift in Sources */, D28A837D23CCA86A00DFE4FC /* TabsListItemModel.swift in Sources */,
@ -2524,6 +2578,7 @@
AAB8549824DC01BD00477C40 /* ListThreeColumnBillHistoryDividerModel.swift in Sources */, AAB8549824DC01BD00477C40 /* ListThreeColumnBillHistoryDividerModel.swift in Sources */,
D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */, D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */,
D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */, D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */,
D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */,
0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */, 0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */,
AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */, AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */,
D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */, D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */,
@ -2569,6 +2624,7 @@
C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */, C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */,
D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */, D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */,
BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */, BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */,
D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */,
0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */, 0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */,
0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */, 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */,
D236E5B5241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift in Sources */, D236E5B5241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift in Sources */,

View File

@ -27,6 +27,11 @@ import UIKit
return caret return caret
}() }()
public var baseDropdownEntryFieldModel: BaseDropdownEntryFieldModel? {
return model as? BaseDropdownEntryFieldModel
}
var additionalData: [AnyHashable: Any]?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Property Observers // MARK: - Property Observers
//-------------------------------------------------- //--------------------------------------------------
@ -75,9 +80,21 @@ import UIKit
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
self.additionalData = additionalData
guard let model = model as? BaseDropdownEntryFieldModel else { return } guard let model = model as? BaseDropdownEntryFieldModel else { return }
dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData) dropDownCaretView.setOptional(with: model.caretView, delegateObject, additionalData)
} }
public override func dismissFieldInput(_ sender: Any?) {
performDropdownAction()
super.dismissFieldInput(sender)
}
func performDropdownAction() {
if let baseDropdownEntryFieldModel = baseDropdownEntryFieldModel, let actionModel = baseDropdownEntryFieldModel.action, let actionMap = actionModel.toJSON() {
let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: baseDropdownEntryFieldModel)
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
}
}
} }

View File

@ -12,6 +12,7 @@
//-------------------------------------------------- //--------------------------------------------------
public var caretView: CaretViewModel? public var caretView: CaretViewModel?
public var action: ActionModelProtocol?
public override class var identifier: String { public override class var identifier: String {
return "" return ""
@ -24,6 +25,7 @@
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case caretView case caretView
case action
} }
//-------------------------------------------------- //--------------------------------------------------
@ -34,6 +36,7 @@
try super.init(from: decoder) try super.init(from: decoder)
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
caretView = try typeContainer.decodeIfPresent(CaretViewModel.self, forKey: .caretView) caretView = try typeContainer.decodeIfPresent(CaretViewModel.self, forKey: .caretView)
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
@ -41,5 +44,6 @@
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(caretView, forKey: .caretView) try container.encode(caretView, forKey: .caretView)
try container.encodeModelIfPresent(action, forKey: .action)
} }
} }

View File

@ -109,8 +109,8 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
pickerData = model.options pickerData = model.options
setPickerDelegates(delegate: self) setPickerDelegates(delegate: self)
if let pickerView = pickerView { if let pickerView = pickerView, let index = model.selectedIndex {
self.pickerView(pickerView, didSelectRow: model.selectedIndex, inComponent: 0) self.pickerView(pickerView, didSelectRow: index, inComponent: 0)
} }
} }
} }

View File

@ -16,11 +16,14 @@
} }
public var options: [String] = [] public var options: [String] = []
public var selectedIndex: Int = 0 public var selectedIndex: Int?
public override func formFieldValue() -> AnyHashable? { public override func formFieldValue() -> AnyHashable? {
guard !options.isEmpty else { return nil } guard !options.isEmpty,
return options[selectedIndex] let index = selectedIndex
else { return nil }
return options[index]
} }
//-------------------------------------------------- //--------------------------------------------------
@ -45,13 +48,16 @@
if let selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .selectedIndex) { if let selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .selectedIndex) {
self.selectedIndex = selectedIndex self.selectedIndex = selectedIndex
} }
baseValue = options.indices.contains(selectedIndex) ? options[selectedIndex] : nil
if let index = selectedIndex {
baseValue = options.indices.contains(index) ? options[index] : nil
}
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(options, forKey: .options) try container.encode(options, forKey: .options)
try container.encode(options, forKey: .selectedIndex) try container.encodeIfPresent(options, forKey: .selectedIndex)
} }
} }

View File

@ -24,6 +24,10 @@ import MVMCore
var groupName: String? var groupName: String?
var delegateObject: MVMCoreUIDelegateObject? var delegateObject: MVMCoreUIDelegateObject?
public var checkboxModel: CheckboxModel? {
return model as? CheckboxModel
}
public static let defaultHeightWidth: CGFloat = 18.0 public static let defaultHeightWidth: CGFloat = 18.0
/// If true the border of this checkbox will be circular. /// If true the border of this checkbox will be circular.
@ -381,7 +385,7 @@ import MVMCore
checkWidth = 2 checkWidth = 2
checkAndBypassAnimations(selected: false) checkAndBypassAnimations(selected: false)
} }
public override func updateView(_ size: CGFloat) { public override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
@ -391,6 +395,14 @@ import MVMCore
} }
} }
private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
if let actionMap = actionModel.toJSON() {
var additionalDatatoUpdate = additionalData ?? [:]
additionalDatatoUpdate[KeySourceModel] = checkboxModel
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDatatoUpdate, delegateObject: delegateObject)
}
}
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
self.delegateObject = delegateObject self.delegateObject = delegateObject
@ -422,10 +434,15 @@ import MVMCore
isEnabled = model.enabled isEnabled = model.enabled
if let action = model.action { if (model.action != nil || model.offAction != nil) {
actionBlock = { actionBlock = { [weak self] in
if let actionMap = action.toJSON() { guard let self = self else { return }
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
if let offAction = model.offAction, !self.isSelected {
self.performCheckboxAction(with: offAction, delegateObject: delegateObject, additionalData: additionalData)
} else if let action = model.action {
self.performCheckboxAction(with: action, delegateObject: delegateObject, additionalData: additionalData)
} }
} }
} }

View File

@ -32,6 +32,7 @@ import Foundation
public var invertedColor: Color = Color(uiColor: .mvmWhite) public var invertedColor: Color = Color(uiColor: .mvmWhite)
public var invertedBackgroundColor: Color = Color(uiColor: .mvmBlack) public var invertedBackgroundColor: Color = Color(uiColor: .mvmBlack)
public var action: ActionModelProtocol? public var action: ActionModelProtocol?
public var offAction: ActionModelProtocol?
public var fieldKey: String? public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName public var groupName: String = FormValidator.defaultGroupName
@ -61,6 +62,7 @@ import Foundation
case action case action
case fieldKey case fieldKey
case groupName case groupName
case offAction
} }
//-------------------------------------------------- //--------------------------------------------------
@ -155,6 +157,7 @@ import Foundation
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName self.groupName = groupName
} }
offAction = try typeContainer.decodeModelIfPresent(codingKey: .offAction)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -179,5 +182,6 @@ import Foundation
try container.encodeIfPresent(enabled, forKey: .enabled) try container.encodeIfPresent(enabled, forKey: .enabled)
try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(action, forKey: .action)
try container.encodeIfPresent(groupName, forKey: .groupName) try container.encodeIfPresent(groupName, forKey: .groupName)
try container.encodeModelIfPresent(offAction, forKey: .offAction)
} }
} }

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
open class RadioBox: Control { open class RadioBox: Control, MFButtonProtocol {
public let label = Label(fontStyle: .RegularBodySmall) public let label = Label(fontStyle: .RegularBodySmall)
public let subTextLabel = Label(fontStyle: .RegularMicro) public let subTextLabel = Label(fontStyle: .RegularMicro)
public var isOutOfStock = false public var isOutOfStock = false
@ -22,6 +22,9 @@ open class RadioBox: Control {
public var subTextLabelHeightConstraint: NSLayoutConstraint? public var subTextLabelHeightConstraint: NSLayoutConstraint?
private var delegateObject: MVMCoreUIDelegateObject?
var additionalData: [AnyHashable: Any]?
public var radioBoxModel: RadioBoxModel? { public var radioBoxModel: RadioBoxModel? {
return model as? RadioBoxModel return model as? RadioBoxModel
} }
@ -77,6 +80,8 @@ open class RadioBox: Control {
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
guard let model = model as? RadioBoxModel else { return } guard let model = model as? RadioBoxModel else { return }
self.delegateObject = delegateObject
self.additionalData = additionalData
label.text = model.text label.text = model.text
subTextLabel.text = model.subText subTextLabel.text = model.subText
isOutOfStock = model.strikethrough isOutOfStock = model.strikethrough
@ -132,9 +137,12 @@ open class RadioBox: Control {
} }
@objc open func selectBox() { @objc open func selectBox() {
guard isEnabled else { return } guard isEnabled, !isSelected else { return }
isSelected = true isSelected = true
radioBoxModel?.selected = isSelected radioBoxModel?.selected = isSelected
if let radioBoxModel = radioBoxModel, let actionModel = radioBoxModel.action {
Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioBoxModel)
}
layer.setNeedsDisplay() layer.setNeedsDisplay()
} }

View File

@ -17,6 +17,7 @@ import Foundation
public var enabled: Bool = true public var enabled: Bool = true
public var strikethrough: Bool = false public var strikethrough: Bool = false
public var fieldValue: String? public var fieldValue: String?
public var action: ActionModelProtocol?
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
@ -28,6 +29,7 @@ import Foundation
case enabled case enabled
case strikethrough case strikethrough
case fieldValue case fieldValue
case action
} }
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
@ -47,6 +49,7 @@ import Foundation
} }
fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue)
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -60,5 +63,6 @@ import Foundation
try container.encode(enabled, forKey: .enabled) try container.encode(enabled, forKey: .enabled)
try container.encode(strikethrough, forKey: .strikethrough) try container.encode(strikethrough, forKey: .strikethrough)
try container.encodeIfPresent(fieldValue, forKey: .fieldValue) try container.encodeIfPresent(fieldValue, forKey: .fieldValue)
try container.encodeModelIfPresent(action, forKey: .action)
} }
} }

View File

@ -9,7 +9,7 @@
import UIKit import UIKit
@objcMembers open class RadioButton: Control { @objcMembers open class RadioButton: Control, MFButtonProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -30,6 +30,7 @@ import UIKit
public var enabledColor: UIColor = .mvmBlack public var enabledColor: UIColor = .mvmBlack
public var disabledColor: UIColor = .mvmCoolGray3 public var disabledColor: UIColor = .mvmCoolGray3
public var delegateObject: MVMCoreUIDelegateObject? public var delegateObject: MVMCoreUIDelegateObject?
var additionalData: [AnyHashable: Any]?
public var radioModel: RadioButtonModel? { public var radioModel: RadioButtonModel? {
return model as? RadioButtonModel return model as? RadioButtonModel
@ -94,11 +95,15 @@ import UIKit
if !isEnabled { if !isEnabled {
return return
} }
let wasPreviouslySelected = isSelected
if let radioButtonModel = radioButtonSelectionHelper { if let radioButtonModel = radioButtonSelectionHelper {
radioButtonModel.selected(self) radioButtonModel.selected(self)
} else { } else {
isSelected = !isSelected isSelected = !isSelected
} }
if let radioModel = radioModel, let actionModel = radioModel.action, isSelected, !wasPreviouslySelected {
Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioModel)
}
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
setNeedsDisplay() setNeedsDisplay()
} }
@ -155,6 +160,7 @@ import UIKit
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
self.delegateObject = delegateObject self.delegateObject = delegateObject
self.additionalData = additionalData
guard let model = model as? RadioButtonModel else { return } guard let model = model as? RadioButtonModel else { return }

View File

@ -26,6 +26,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
public var baseValue: AnyHashable? public var baseValue: AnyHashable?
public var groupName: String = FormValidator.defaultGroupName public var groupName: String = FormValidator.defaultGroupName
public var fieldKey: String? public var fieldKey: String?
public var action: ActionModelProtocol?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
@ -39,6 +40,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
case fieldValue case fieldValue
case fieldKey case fieldKey
case groupName case groupName
case action
} }
//-------------------------------------------------- //--------------------------------------------------
@ -81,6 +83,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
self.groupName = groupName self.groupName = groupName
} }
fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue)
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -92,5 +95,6 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(groupName, forKey: .groupName) try container.encodeIfPresent(groupName, forKey: .groupName)
try container.encodeIfPresent(fieldValue, forKey: .fieldValue) try container.encodeIfPresent(fieldValue, forKey: .fieldValue)
try container.encodeModelIfPresent(action, forKey: .action)
} }
} }

View File

@ -9,7 +9,7 @@
import UIKit import UIKit
open class RadioSwatch: Control { open class RadioSwatch: Control, MFButtonProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -20,6 +20,9 @@ open class RadioSwatch: Control {
private var strikeLayer: CALayer? private var strikeLayer: CALayer?
private var maskLayer: CALayer? private var maskLayer: CALayer?
private var delegateObject: MVMCoreUIDelegateObject?
var additionalData: [AnyHashable: Any]?
public var radioSwatchModel: RadioSwatchModel? { public var radioSwatchModel: RadioSwatchModel? {
return model as? RadioSwatchModel return model as? RadioSwatchModel
} }
@ -57,6 +60,8 @@ open class RadioSwatch: Control {
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
guard let model = model as? RadioSwatchModel else { return } guard let model = model as? RadioSwatchModel else { return }
self.delegateObject = delegateObject
self.additionalData = additionalData
bottomText.text = model.text bottomText.text = model.text
isSelected = model.selected isSelected = model.selected
isEnabled = model.enabled isEnabled = model.enabled
@ -114,9 +119,12 @@ open class RadioSwatch: Control {
} }
@objc open func selectSwatch() { @objc open func selectSwatch() {
guard isEnabled else { return } guard isEnabled, !isSelected else { return }
isSelected = true isSelected = true
radioSwatchModel?.selected = isSelected radioSwatchModel?.selected = isSelected
if let radioSwatchModel = radioSwatchModel, let actionModel = radioSwatchModel.action {
Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioSwatchModel)
}
layer.setNeedsDisplay() layer.setNeedsDisplay()
} }

View File

@ -17,6 +17,7 @@ import Foundation
public var enabled: Bool = true public var enabled: Bool = true
public var strikethrough: Bool = false public var strikethrough: Bool = false
public var fieldValue: String? public var fieldValue: String?
public var action: ActionModelProtocol?
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
@ -27,6 +28,7 @@ import Foundation
case enabled case enabled
case strikethrough case strikethrough
case fieldValue case fieldValue
case action
} }
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
@ -46,6 +48,7 @@ import Foundation
self.strikethrough = strikethrough self.strikethrough = strikethrough
} }
fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue)
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -58,6 +61,7 @@ import Foundation
try container.encode(enabled, forKey: .enabled) try container.encode(enabled, forKey: .enabled)
try container.encode(strikethrough, forKey: .strikethrough) try container.encode(strikethrough, forKey: .strikethrough)
try container.encodeIfPresent(fieldValue, forKey: .fieldValue) try container.encodeIfPresent(fieldValue, forKey: .fieldValue)
try container.encodeModelIfPresent(action, forKey: .action)
} }
} }

View File

@ -241,16 +241,38 @@ public typealias ActionBlock = () -> ()
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
clauses = [] clauses = []
text = nil
guard let labelModel = model as? LabelModel else {
text = ""
return
}
attributedText = nil attributedText = nil
originalAttributedString = nil originalAttributedString = nil
text = nil
guard let labelModel = model as? LabelModel else { return }
text = labelModel.text text = labelModel.text
/*
* This is to address a reuse issue with iOS 13 and up.
* Even if you set text & attributedText to nil, the moment you set text with a value,
* attributedText will hold a dirty value from a previously reused cell even if reset() is
* appropriately called.
* Only other reference found of issue: https://www.thetopsites.net/article/58142205.shtml
*/
if #available(iOS 13, *) {
if let attributedText = attributedText, let text = text, !text.isEmpty {
let attributedString = NSMutableAttributedString(string: text)
let range = NSRange(location: 0, length: text.count)
for attribute in attributedText.attributes(at: 0, effectiveRange: nil) {
if attribute.key == .underlineStyle {
attributedString.addAttribute(.underlineStyle, value: 0, range: range)
}
if attribute.key == .strikethroughStyle {
attributedString.addAttribute(.strikethroughStyle, value: 0, range: range)
}
}
self.attributedText = attributedString
}
}
hero = labelModel.hero hero = labelModel.hero
Label.setLabel(self, withHTML: labelModel.html) Label.setLabel(self, withHTML: labelModel.html)
isAccessibilityElement = hasText isAccessibilityElement = hasText
@ -258,8 +280,10 @@ public typealias ActionBlock = () -> ()
switch labelModel.textAlignment { switch labelModel.textAlignment {
case .center: case .center:
textAlignment = .center textAlignment = .center
case .right: case .right:
textAlignment = .right textAlignment = .right
default: default:
textAlignment = .left textAlignment = .left
} }
@ -291,7 +315,7 @@ public typealias ActionBlock = () -> ()
if let color = labelModel.textColor { if let color = labelModel.textColor {
textColor = color.uiColor textColor = color.uiColor
} }
if let attributes = labelModel.attributes, let labelText = text { if let attributes = labelModel.attributes, let labelText = text {
let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font.updateSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor]) let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font.updateSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor])
@ -360,7 +384,7 @@ public typealias ActionBlock = () -> ()
continue continue
} }
} }
attributedText = attributedString attributedText = attributedString
originalAttributedString = attributedText originalAttributedString = attributedText
} }
@ -504,11 +528,11 @@ public typealias ActionBlock = () -> ()
textColor = .mvmBlack textColor = .mvmBlack
setScale(scale) setScale(scale)
} }
//------------------------------------------------------ //------------------------------------------------------
// MARK: - 2.0 Styling Methods // MARK: - 2.0 Styling Methods
//------------------------------------------------------ //------------------------------------------------------
@objc public func styleH1(_ scale: Bool) { @objc public func styleH1(_ scale: Bool) {
MFStyler.styleLabelH1(self, genericScaling: false) MFStyler.styleLabelH1(self, genericScaling: false)
setScale(scale) setScale(scale)
@ -549,6 +573,23 @@ public typealias ActionBlock = () -> ()
setScale(scale) setScale(scale)
} }
/// Will remove the values contained in attributedText.
func clearAttributes() {
guard let labelText = text,
let attributes = attributedText?.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: labelText.count))
else { return }
let attributedString = NSMutableAttributedString(string: labelText)
for attribute in attributes {
attributedString.removeAttribute(attribute.key, range: NSRange(location: 0, length: labelText.count))
}
attributedText = attributedString
}
@objc public func updateView(_ size: CGFloat) { @objc public func updateView(_ size: CGFloat) {
scaleSize = size as NSNumber scaleSize = size as NSNumber
@ -836,7 +877,7 @@ extension Label {
/// Converts the entire text into a link. All characters will be underlined and the intrinsic bounds will respond to tap. /// Converts the entire text into a link. All characters will be underlined and the intrinsic bounds will respond to tap.
@objc public func makeTextButton(actionBlock: @escaping ActionBlock) { @objc public func makeTextButton(actionBlock: @escaping ActionBlock) {
setTextLinkState(range: getRange, actionBlock: actionBlock) setTextLinkState(range: getRange, actionBlock: actionBlock)
} }
@ -921,7 +962,7 @@ extension Label {
} }
@objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) { @objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) {
for clause in clauses { for clause in clauses {
if action.hash == clause.accessibilityID { if action.hash == clause.accessibilityID {
clause.performAction() clause.performAction()
@ -931,7 +972,7 @@ extension Label {
} }
open override func accessibilityActivate() -> Bool { open override func accessibilityActivate() -> Bool {
guard let accessibleActions = accessibilityCustomActions else { return false } guard let accessibleActions = accessibilityCustomActions else { return false }
for clause in clauses { for clause in clauses {

View File

@ -6,23 +6,39 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
import UIKit
open class LabelAttributeActionModel: LabelAttributeModel { open class LabelAttributeActionModel: LabelAttributeModel {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
override public class var identifier: String { override public class var identifier: String {
return "action" return "action"
} }
var action: ActionModelProtocol var action: ActionModelProtocol
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(_ location: Int, _ length: Int, action: ActionModelProtocol) { public init(_ location: Int, _ length: Int, action: ActionModelProtocol) {
self.action = action self.action = action
super.init(location, length) super.init(location, length)
} }
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case action case action
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
action = try typeContainer.decodeModel(codingKey: .action) action = try typeContainer.decodeModel(codingKey: .action)

View File

@ -6,7 +6,6 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
import UIKit
@objcMembers public class LabelAttributeColorModel: LabelAttributeModel { @objcMembers public class LabelAttributeColorModel: LabelAttributeModel {
//-------------------------------------------------- //--------------------------------------------------

View File

@ -6,8 +6,6 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
import UIKit
@objcMembers public class LabelAttributeFontModel: LabelAttributeModel { @objcMembers public class LabelAttributeFontModel: LabelAttributeModel {
//-------------------------------------------------- //--------------------------------------------------

View File

@ -6,13 +6,12 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
import UIKit
class LabelAttributeImageModel: LabelAttributeModel { class LabelAttributeImageModel: LabelAttributeModel {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
override public class var identifier: String { override public class var identifier: String {
return "image" return "image"
} }

View File

@ -6,7 +6,6 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
import Foundation
@objcMembers open class LabelAttributeModel: ModelProtocol { @objcMembers open class LabelAttributeModel: ModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
@ -20,7 +19,7 @@ import Foundation
public static var categoryCodingKey: String { public static var categoryCodingKey: String {
return "type" return "type"
} }
public class var identifier: String { public class var identifier: String {
return "" return ""
} }
@ -31,12 +30,16 @@ import Foundation
var location: Int var location: Int
var length: Int var length: Int
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(_ location: Int, _ length: Int) { public init(_ location: Int, _ length: Int) {
self.location = location self.location = location
self.length = length self.length = length
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
//-------------------------------------------------- //--------------------------------------------------

View File

@ -6,16 +6,28 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
import UIKit
@objcMembers public class LabelAttributeStrikeThroughModel: LabelAttributeModel { @objcMembers public class LabelAttributeStrikeThroughModel: LabelAttributeModel {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
override public class var identifier: String { override public class var identifier: String {
return "strikethrough" return "strikethrough"
} }
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
try super.init(from: decoder) try super.init(from: decoder)
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder) try super.encode(to: encoder)
} }

View File

@ -6,13 +6,20 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
import UIKit
@objcMembers public class LabelAttributeUnderlineModel: LabelAttributeModel { @objcMembers public class LabelAttributeUnderlineModel: LabelAttributeModel {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
override public class var identifier: String { override public class var identifier: String {
return "underline" return "underline"
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
try super.init(from: decoder) try super.init(from: decoder)
} }

View File

@ -7,8 +7,6 @@
// //
import Foundation
@objcMembers public class LabelModel: MoleculeModelProtocol { @objcMembers public class LabelModel: MoleculeModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties

View File

@ -394,18 +394,19 @@ public typealias ActionBlockConfirmation = () -> (Bool)
let actionMap = model.action?.toJSON() let actionMap = model.action?.toJSON()
let alternateActionMap = model.alternateAction?.toJSON() let alternateActionMap = model.alternateAction?.toJSON()
let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model)
if actionMap != nil || alternateActionMap != nil { if actionMap != nil || alternateActionMap != nil {
didToggleAction = { [weak self] in didToggleAction = { [weak self] in
guard let self = self else { return } guard let self = self else { return }
if self.isOn { if self.isOn {
if actionMap != nil { if actionMap != nil {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
} }
} else { } else {
if alternateActionMap != nil { if alternateActionMap != nil {
MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalData, delegateObject: delegateObject) MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
} else if actionMap != nil { } else if actionMap != nil {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
} }
} }
} }

View File

@ -128,7 +128,9 @@ extension WebView : WKUIDelegate {
if !dynamicHeight { if !dynamicHeight {
return return
} }
/* was using "document.readyState" to check the state, while evaluateJavaScript "document.readyState",only works when webview contains userscrpt.otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred". /*
was using "document.readyState" to check the state,
while evaluateJavaScript "document.readyState",only works when webview contains userscrpt.otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred".
so webView.isLoading to check load finished state so webView.isLoading to check load finished state
*/ */
if !webView.isLoading { if !webView.isLoading {
@ -136,14 +138,18 @@ extension WebView : WKUIDelegate {
guard let self = self else { guard let self = self else {
return return
} }
if let height = result as? CGFloat { MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in
self.webViewHeight?.constant = height guard let self = self else {
} else { return
//if failed to get height from javascript, using scrollview.contensize's height }
let scrollHeight = self.webView?.scrollView.contentSize.height if let height = result as? CGFloat {
self.webViewHeight?.constant = scrollHeight ?? 44 self.webViewHeight?.constant = height
} } else {
self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) //if failed to get height from javascript, using scrollview.contensize's height
self.webViewHeight?.constant = webView.scrollView.contentSize.height
}
self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
})
}) })
} }
} }

View File

@ -232,6 +232,10 @@ import Foundation
MoleculeObjectMapping.shared()?.register(viewClass: LockUpsPlanNames.self, viewModelClass: LockUpsPlanNamesModel.self) MoleculeObjectMapping.shared()?.register(viewClass: LockUpsPlanNames.self, viewModelClass: LockUpsPlanNamesModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: LockupsPlanSMLXL.self, viewModelClass: LockupsPlanSMLXLModel.self) MoleculeObjectMapping.shared()?.register(viewClass: LockupsPlanSMLXL.self, viewModelClass: LockupsPlanSMLXLModel.self)
// MARK: - Top Notifications
MoleculeObjectMapping.shared()?.register(viewClass: NotificationView.self, viewModelClass: NotificationModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: CollapsableNotification.self, viewModelClass: CollapsableNotificationModel.self)
// MARK:- Helper models // MARK:- Helper models
try? ModelRegistry.register(RuleRequiredModel.self) try? ModelRegistry.register(RuleRequiredModel.self)
try? ModelRegistry.register(RuleAnyRequiredModel.self) try? ModelRegistry.register(RuleAnyRequiredModel.self)

View File

@ -99,6 +99,10 @@ import Foundation
self.tabBar(self, didSelect: newSelectedItem) self.tabBar(self, didSelect: newSelectedItem)
}) })
} }
public func currentTabIndex() -> Int {
return model.selectedTab
}
} }
extension UITabBarItem: MFButtonProtocol { extension UITabBarItem: MFButtonProtocol {

View File

@ -6,8 +6,6 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
import UIKit
@objcMembers public class AccordionMoleculeTableViewCell: MoleculeTableViewCell { @objcMembers public class AccordionMoleculeTableViewCell: MoleculeTableViewCell {
//-------------------------------------------------- //--------------------------------------------------
@ -36,6 +34,7 @@ import UIKit
customAccessoryView = true customAccessoryView = true
super.setupView() super.setupView()
accessoryView = accordionButton accessoryView = accordionButton
accessoryView?.isUserInteractionEnabled = false
} }
override public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { override public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {

View File

@ -18,8 +18,12 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt
public var backgroundColor: Color? public var backgroundColor: Color?
public var tintColor: Color public var tintColor: Color
public var line: LineModel? public var line: LineModel?
public var hidesSystemBackButton = true
/// If true, we add the button in the backButton property. If false we do not add the button. If nil, we add the button if the controller is not the bottom of the stack
public var alwaysShowBackButton: Bool? public var alwaysShowBackButton: Bool?
public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? = NavigationImageButtonModel(with: "nav_back", action: ActionBackModel()) public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)?
public var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? public var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]?
public var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? public var additionalRightButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]?
public var titleView: MoleculeModelProtocol? public var titleView: MoleculeModelProtocol?
@ -55,9 +59,7 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt
tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black) tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) ?? Color(uiColor: .black)
line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) ?? LineModel(type: .standard) line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) ?? LineModel(type: .standard)
alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton) alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton)
if let backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol) = try typeContainer.decodeModelIfPresent(codingKey: .backButton) { backButton = try typeContainer.decodeModelIfPresent(codingKey: .backButton)
self.backButton = backButton
}
additionalLeftButtons = try typeContainer.decodeModelsIfPresent(codingKey: .additionalLeftButtons) additionalLeftButtons = try typeContainer.decodeModelsIfPresent(codingKey: .additionalLeftButtons)
additionalRightButtons = try typeContainer.decodeModelsIfPresent(codingKey: .additionalRightButtons) additionalRightButtons = try typeContainer.decodeModelsIfPresent(codingKey: .additionalRightButtons)
titleView = try typeContainer.decodeModelIfPresent(codingKey: .titleView) titleView = try typeContainer.decodeModelIfPresent(codingKey: .titleView)

View File

@ -0,0 +1,187 @@
//
// CollapsableNotification.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/18/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers open class CollapsableNotification: View {
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
public let topView = CollapsableNotificationTopView()
public let bottomView = NotificationView()
public var verticalStack: UIStackView!
//--------------------------------------------------
// MARK: - Life Cycle
//--------------------------------------------------
public override func setupView() {
super.setupView()
verticalStack = UIStackView(arrangedSubviews: [topView, bottomView])
verticalStack.translatesAutoresizingMaskIntoConstraints = false
verticalStack.axis = .vertical
verticalStack.alignment = .fill
verticalStack.distribution = .fill
addSubview(verticalStack)
NSLayoutConstraint.constraintPinSubview(verticalStack, pinTop: true, topConstant: 0, pinBottom: true, bottomConstant: 0, pinLeft: true, leftConstant: 0, pinRight: true, rightConstant: 0)
reset()
}
open override func updateView(_ size: CGFloat) {
super.updateView(size)
verticalStack.updateView(size)
}
open override func reset() {
super.reset()
verticalStack.reset()
backgroundColor = .mvmGreen()
}
//--------------------------------------------------
// 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? CollapsableNotificationModel else { return }
topView.set(with: model, delegateObject, additionalData)
bottomView.set(with: model, delegateObject, additionalData)
// Update top view default noop to expand.
if let topAction = model.topAction,
topAction.actionType == ActionNoopModel.identifier {
topView.button.addActionBlock(event: .touchUpInside) { [weak self] (button) in
Button.performButtonAction(with: topAction, button: button, delegateObject: delegateObject, additionalData: additionalData)
self?.expand(topViewShowing: model.alwaysShowTopLabel)
}
}
// Set initial collapse/expand state.
topView.isHidden = !model.alwaysShowTopLabel && !model.initiallyCollapsed
topView.button.isUserInteractionEnabled = model.initiallyCollapsed
bottomView.isHidden = model.initiallyCollapsed
verticalStack.layoutIfNeeded()
if !model.initiallyCollapsed {
autoCollapse()
}
}
open func performBlockOperation(with block: @escaping (MVMCoreBlockOperation) -> Void) {
let operation = MVMCoreBlockOperation(block: block)!
MVMCoreNavigationHandler.shared()?.addNavigationOperation(operation)
}
/// Collapses after a delay
open func autoCollapse() {
let delay: DispatchTimeInterval = DispatchTimeInterval.seconds((model as? CollapsableNotificationModel)?.collapseTime ?? 5)
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
// If accessibility focused, delay collapse.
guard let self = self else { return }
if MVMCoreUIUtility.viewContainsAccessiblityFocus(self) {
NotificationCenter.default.addObserver(self, selector: #selector(self.accessibilityFocusChanged(notification:)), name: UIAccessibility.elementFocusedNotification, object: nil)
} else {
self.collapse()
}
}
}
/// Collapses to show just the top view.
open func collapse(animated: Bool = true) {
guard !bottomView.isHidden else { return }
performBlockOperation { [weak self] (operation) in
let strongSelf = self
MVMCoreDispatchUtility.performBlock(onMainThread: {
MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded()
let animation = {
strongSelf?.topView.isHidden = false
strongSelf?.bottomView.isHidden = true
strongSelf?.verticalStack.layoutIfNeeded()
}
let completion: (Bool) -> Void = { (finished) in
strongSelf?.topView.button.isUserInteractionEnabled = true
MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded()
UIAccessibility.post(notification: .layoutChanged, argument: strongSelf?.getAccessibilityLayoutChangedArgument())
operation.markAsFinished()
}
if animated {
UIView.animate(withDuration: 0.5, animations: animation, completion: completion)
} else {
animation()
completion(true)
}
})
}
}
/// Expands to show the bottom view.
open func expand(topViewShowing: Bool = false, animated: Bool = true) {
guard bottomView.isHidden else { return }
performBlockOperation { [weak self] (operation) in
let strongSelf = self
MVMCoreDispatchUtility.performBlock(onMainThread: {
MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded()
strongSelf?.topView.button.isUserInteractionEnabled = false
let animation = {
strongSelf?.topView.isHidden = !topViewShowing
strongSelf?.bottomView.isHidden = false
strongSelf?.verticalStack.layoutIfNeeded()
}
let completion: (Bool) -> Void = { (finished) in
MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded()
UIAccessibility.post(notification: .layoutChanged, argument: strongSelf?.getAccessibilityLayoutChangedArgument())
strongSelf?.autoCollapse()
operation.markAsFinished()
}
if animated {
UIView.animate(withDuration: 0.5, animations: animation, completion: completion)
} else {
animation()
completion(true)
}
})
}
}
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return 120
}
/// Collapse if focus is no longer on this top alert.
@objc func accessibilityFocusChanged(notification: Notification) {
if !MVMCoreUIUtility.viewContainsAccessiblityFocus(self) {
NotificationCenter.default.removeObserver(self, name: UIAccessibility.elementFocusedNotification, object: nil)
collapse()
}
}
}
extension CollapsableNotification: StatusBarUI {
func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle) {
let color = backgroundColor ?? UIColor.mvmGreen
var greyScale: CGFloat = 0
topView.label.textColor.getWhite(&greyScale, alpha: nil)
return (color, greyScale > 0.5 ? .lightContent : .default)
}
}
extension CollapsableNotification: AccessibilityProtocol {
public func getAccessibilityLayoutChangedArgument() -> Any? {
if !topView.isHidden {
return topView
} else {
return bottomView.headline
}
}
}

View File

@ -0,0 +1,75 @@
//
// CollapsableNotificationModel.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/15/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public class CollapsableNotificationModel: NotificationModel {
public class override var identifier: String {
return "collapsableNotification"
}
public var topLabel: LabelModel
public var topAction: ActionModelProtocol?
public var alwaysShowTopLabel = false
public var collapseTime: Int = 5
public var initiallyCollapsed = false
public var pages: [String]?
init(with topLabel: LabelModel, headline: LabelModel) {
self.topLabel = topLabel
super.init(with: headline)
}
override func setDefault() {
super.setDefault()
if topLabel.textColor == nil {
topLabel.textColor = Color(uiColor: .white)
}
if topLabel.textAlignment == nil {
topLabel.textAlignment = .center
}
}
private enum CodingKeys: String, CodingKey {
case moleculeName
case topLabel
case topAction
case alwaysShowTopLabel
case collapseTime
case initiallyCollapsed
case pages
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
topLabel = try typeContainer.decode(LabelModel.self, forKey: .topLabel)
topAction = try typeContainer.decodeModelIfPresent(codingKey: .topAction)
if let alwaysShowTopLabel = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowTopLabel) {
self.alwaysShowTopLabel = alwaysShowTopLabel
}
if let collapseTime = try typeContainer.decodeIfPresent(Int.self, forKey: .collapseTime) {
self.collapseTime = collapseTime
}
if let initiallyCollapsed = try typeContainer.decodeIfPresent(Bool.self, forKey: .initiallyCollapsed) {
self.initiallyCollapsed = initiallyCollapsed
}
pages = try typeContainer.decodeIfPresent([String].self, forKey: .pages)
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(topLabel, forKey: .topLabel)
try container.encodeModelIfPresent(topAction, forKey: .topAction)
try container.encode(alwaysShowTopLabel, forKey: .alwaysShowTopLabel)
try container.encode(collapseTime, forKey: .collapseTime)
try container.encode(initiallyCollapsed, forKey: .initiallyCollapsed)
try container.encodeIfPresent(pages, forKey: .pages)
}
}

View File

@ -0,0 +1,70 @@
//
// CollapsableNotificationTopView.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/18/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers open class CollapsableNotificationTopView: View {
public let label: Label = {
let label = Label(fontStyle: .BoldBodySmall)
label.numberOfLines = 1
label.textAlignment = .center
label.setContentHuggingPriority(.defaultHigh, for: .vertical)
return label
}()
public let button: Button = {
let button = Button(type: .custom)
button.backgroundColor = .clear
button.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
button.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return button
}()
public override func setupView() {
super.setupView()
addSubview(label)
NSLayoutConstraint.constraintPinSubview(label, pinTop: true, topConstant: 0, pinBottom: true, bottomConstant: 0, pinLeft: true, leftConstant: PaddingThree, pinRight: true, rightConstant: PaddingThree)
addSubview(button)
NSLayoutConstraint.constraintPinSubview(button, pinTop: true, topConstant: 0, pinBottom: true, bottomConstant: -5, pinLeft: true, leftConstant: 0, pinRight: true, rightConstant: 0)
// Listen for status bar touches.
NotificationCenter.default.addObserver(self, selector: #selector(pressed(_:)), name: NSNotification.Name(rawValue: NotificationStatusBarTouched), object: nil)
}
open override func updateView(_ size: CGFloat) {
super.updateView(size)
label.updateView(size)
}
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
guard let model = model as? CollapsableNotificationModel else { return }
label.set(with: model.topLabel, delegateObject, additionalData)
button.set(with: model.topAction, delegateObject: delegateObject, additionalData: additionalData)
updateAccessibility()
}
open override func reset() {
super.reset()
label.setFontStyle(.BoldBodySmall)
label.textColor = .white
label.textAlignment = .center
}
open func updateAccessibility() {
isAccessibilityElement = true
accessibilityLabel = label.text
accessibilityTraits = (button.isUserInteractionEnabled && button.actionModel != nil) ? .button : .none
MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: self)
}
@objc func pressed(_ sender: Notification) {
button.callActionBlock(button)
}
}

View File

@ -0,0 +1,50 @@
//
// MVMCoreUITopAlertExpandableView+Extension.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/15/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
extension MVMCoreUITopAlertExpandableView: MoleculeViewProtocol {
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
defaultSetup()
guard let model = model as? CollapsableNotificationModel else { return }
backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen
collapseTime = model.collapseTime
onlyShowTopMessageWhenCollapsed = !model.alwaysShowTopLabel
var actionMap = model.button?.action.toJSON()
if let title = model.button?.title {
actionMap?.updateValue(title, forKey: KeyTitle)
}
buttonView?.setupCloseButton(model.closeButton != nil, animationDelegate: MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate)
setTopMessage(model.topLabel.text, message: model.headline.text, subMessage: model.body?.text, contentColor: model.headline.textColor?.uiColor ?? .white, actionMap: actionMap, additionalData: nil)
expand(false)
if let button = shortView?.button,
let topActionMap = model.topAction?.toJSON() {
MVMCoreUITopAlertBaseView.addAction(to: button, actionMap: topActionMap, additionalData: nil)
shortView?.label?.accessibilityTraits = .button
}
}
}
extension MVMCoreUITopAlertExpandableView: StatusBarUI {
func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle) {
if shortView?.label?.text?.count ?? 0 > 0 {
let color = backgroundColor ?? UIColor.mvmGreen
var greyScale: CGFloat = 0
if shortView?.label?.textColor.getWhite(&greyScale, alpha: nil) ?? false {
return (color, greyScale > 0.5 ? .lightContent : .default)
} else {
return (color, .default)
}
} else {
return (.white, .default)
}
}
}

View File

@ -0,0 +1,23 @@
//
// MVMCoreUITopAlertMainView+Extension.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/15/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
extension MVMCoreUITopAlertMainView: MoleculeViewProtocol {
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
defaultSetup()
guard let model = model as? NotificationModel else { return }
backgroundColor = model.backgroundColor?.uiColor ?? .mvmGreen
var actionMap = model.button?.action.toJSON()
if let title = model.button?.title {
actionMap?.updateValue(title, forKey: KeyTitle)
}
setupCloseButton(model.closeButton != nil, animationDelegate: MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate)
setup(withMessage: model.headline.text, subMessage: model.body?.text, color: model.headline.textColor?.uiColor ?? .white, actionMap: actionMap, additionalData: nil)
}
}

View File

@ -0,0 +1,93 @@
//
// Notification.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/16/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers open class NotificationView: View {
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
public let headline = Label(fontStyle: .BoldBodySmall)
public let body = Label(fontStyle: .RegularBodySmall)
public let button = PillButton(asPrimaryButton: false, makeTiny: true)
public let closeButton = NotificationXButton()
public var labelStack: Stack<StackModel>!
public var horizontalStack: Stack<StackModel>!
// Legacy constant
private static let viewHeight: CGFloat = 96.0
//--------------------------------------------------
// MARK: - Life Cycle
//--------------------------------------------------
public override func setupView() {
super.setupView()
reset()
// Buttons should have highest priority, then headline, then body
headline.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 500), for: .horizontal)
headline.setContentHuggingPriority(.required, for: .vertical)
body.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 500), for: .horizontal)
body.setContentHuggingPriority(.required, for: .vertical)
headline.setContentCompressionResistancePriority(UILayoutPriority(rawValue: body.contentCompressionResistancePriority(for: .vertical).rawValue + 40), for: .vertical)
headline.lineBreakMode = .byTruncatingTail
body.lineBreakMode = .byTruncatingTail
button.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
labelStack = Stack<StackModel>.createStack(with: [headline, body], spacing: 0)
horizontalStack = Stack<StackModel>.createStack(with: [(view: labelStack, model: StackItemModel()),(view: button, model: StackItemModel(horizontalAlignment: .fill)),(view: closeButton, model: StackItemModel(horizontalAlignment: .fill))], axis: .horizontal)
addSubview(horizontalStack)
NSLayoutConstraint.constraintPinSubview(horizontalStack, pinTop: true, topConstant: PaddingTwo, pinBottom: true, bottomConstant: PaddingTwo, pinLeft: true, leftConstant: PaddingThree, pinRight: true, rightConstant: PaddingThree)
labelStack.restack()
horizontalStack.restack()
heightAnchor.constraint(equalToConstant: Self.viewHeight).isActive = true
}
open override func updateView(_ size: CGFloat) {
super.updateView(size)
horizontalStack.updateView(size)
}
open override func reset() {
super.reset()
backgroundColor = .mvmGreen()
headline.textColor = .white
body.textColor = .white
}
//--------------------------------------------------
// 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? NotificationModel else { return }
labelStack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, nil)
horizontalStack.updateContainedMolecules(with: [labelStack.stackModel, model.button, model.closeButton], delegateObject, nil)
updateAccessibility()
}
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return viewHeight
}
open func updateAccessibility() {
MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: headline)
MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: body)
MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: button)
}
}
extension NotificationView: AccessibilityProtocol {
public func getAccessibilityLayoutChangedArgument() -> Any? {
return headline
}
}

View File

@ -0,0 +1,71 @@
//
// NotificationModel.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/15/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public class NotificationModel: MoleculeModelProtocol {
public class var identifier: String {
return "notification"
}
public var backgroundColor: Color?
public var headline: LabelModel
public var body: LabelModel?
public var button: ButtonModel?
public var closeButton: NotificationXButtonModel?
init(with headline: LabelModel) {
self.headline = headline
}
func setDefault() {
if backgroundColor == nil {
backgroundColor = Color(uiColor: .mvmGreen())
}
if headline.textColor == nil {
headline.textColor = Color(uiColor: .white)
}
if body?.textColor == nil {
body?.textColor = Color(uiColor: .white)
}
if button?.style == nil {
button?.style = .secondary
}
button?.size = .tiny
button?.enabledTextColor = Color(uiColor: .white)
button?.enabledBorderColor = Color(uiColor: .white)
}
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
case headline
case body
case button
case closeButton
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
headline = try typeContainer.decode(LabelModel.self, forKey: .headline)
body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body)
button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button)
closeButton = try typeContainer.decodeIfPresent(NotificationXButtonModel.self, forKey: .closeButton)
setDefault()
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encode(headline, forKey: .headline)
try container.encodeIfPresent(body, forKey: .body)
try container.encodeIfPresent(button, forKey: .button)
try container.encodeIfPresent(closeButton, forKey: .closeButton)
}
}

View File

@ -0,0 +1,43 @@
//
// NotificationXButton.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/17/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers open class NotificationXButton: Button {
open func closeTopAlert() {
if let delegate = MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate {
delegate.topAlertCloseButtonPressed()
} else {
MVMCoreUISession.sharedGlobal()?.topAlertView?.hideAlertView(true, completionHandler: nil)
}
}
open override func setupView() {
if let image = MVMCoreUIUtility.imageNamed("nav_close")?.withRenderingMode(.alwaysTemplate) {
setImage(image, for: .normal)
}
tintColor = .white
adjustsImageWhenHighlighted = false
accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "AccCloseButton")
setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
}
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let model = model as? NotificationXButtonModel else { return }
tintColor = model.color.uiColor
// TODO: Temporary, consider action for dismissing top alert
if model.action.actionType == ActionNoopModel.identifier {
addActionBlock(event: .touchUpInside) { (button) in
(button as? NotificationXButton)?.closeTopAlert()
}
}
}
}

View File

@ -0,0 +1,39 @@
//
// NotificationXButtonModel.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/15/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public class NotificationXButtonModel: ButtonModelProtocol, MoleculeModelProtocol {
public static var identifier: String = "notificationXButton"
public var backgroundColor: Color?
public var color = Color(uiColor: .white)
public var action: ActionModelProtocol = ActionNoopModel()
private enum CodingKeys: String, CodingKey {
case moleculeName
case color
case action
}
public init() {}
public required init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) ?? Color(uiColor: .white)
if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) {
self.action = action
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(color, forKey: .color)
try container.encodeModel(action, forKey: .action)
}
}

View File

@ -14,6 +14,7 @@ public protocol NavigationItemModelProtocol {
var backgroundColor: Color? { get set } var backgroundColor: Color? { get set }
var tintColor: Color { get set } var tintColor: Color { get set }
var line: LineModel? { get set } var line: LineModel? { get set }
var hidesSystemBackButton: Bool { get set }
var alwaysShowBackButton: Bool? { get set } var alwaysShowBackButton: Bool? { get set }
var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? { get set } var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? { get set }
var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? { get set } var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? { get set }

View File

@ -17,4 +17,7 @@ import Foundation
/// Should select the tab index. As if the user selected it. /// Should select the tab index. As if the user selected it.
@objc func selectTab(at index: Int) @objc func selectTab(at index: Int)
/// Returns the current tab
@objc func currentTabIndex() -> Int
} }

View File

@ -72,21 +72,26 @@ public typealias ButtonAction = (Button) -> ()
buttonAction?(self) buttonAction?(self)
} }
open func set(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { open func set(with actionModel: ActionModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
self.actionModel = actionModel self.actionModel = actionModel
buttonDelegate = delegateObject?.buttonDelegate buttonDelegate = delegateObject?.buttonDelegate
addActionBlock(event: .touchUpInside) { [weak self] sender in addActionBlock(event: .touchUpInside) { [weak self] sender in
guard let self = self else { return } guard let self = self, let actionModel = actionModel else { return }
Self.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData) Self.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData)
} }
} }
open class func performButtonAction(with model: ActionModelProtocol, button: MFButtonProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { open class func performButtonAction(with model: ActionModelProtocol, button: MFButtonProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, sourceModel: MoleculeModelProtocol? = nil) {
if let data = try? model.encode(using: JSONEncoder()), if let data = try? model.encode(using: JSONEncoder()),
let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any], let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any],
delegateObject?.buttonDelegate?.button?(button, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { delegateObject?.buttonDelegate?.button?(button, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) if let sourceModel = sourceModel {
let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: sourceModel)
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
} else {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
}
} }
} }

View File

@ -0,0 +1,14 @@
//
// AccessibilityProtocol.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/21/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objc public protocol AccessibilityProtocol {
/// Should return the argument to use for posting a layout change.
func getAccessibilityLayoutChangedArgument() -> Any?
}

View File

@ -39,6 +39,9 @@ import UIKit
public var selectedField: UIView? public var selectedField: UIView?
// Stores the previous tab bar index.
public var tabBarIndex: Int?
/// Checks if the screen width has changed /// Checks if the screen width has changed
open func screenSizeChanged() -> Bool { open func screenSizeChanged() -> Bool {
return !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1) return !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1)
@ -318,13 +321,27 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
open func updateTabBar() { open func updateTabBar() {
guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self, guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self else { return }
let tabModel = pageModel as? TabPageModelProtocol else { return }
MVMCoreUISplitViewController.main()?.tabBar?.delegateObject = delegateObjectIVar MVMCoreUISplitViewController.main()?.tabBar?.delegateObject = delegateObjectIVar
if let index = tabModel.tabBarIndex {
if let index = (pageModel as? TabPageModelProtocol)?.tabBarIndex {
MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index)
} else if let index = loadObject?.requestParameters?.actionMap?["tabBarIndex"] as? Int {
MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index)
} else if let index = self.tabBarIndex {
MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index)
} else if let index = MVMCoreUISplitViewController.main()?.tabBar?.currentTabIndex() {
// Store current tab index for cases like back button.
self.tabBarIndex = index
}
if let hidden = (pageModel as? TabPageModelProtocol)?.tabBarHidden {
MVMCoreUISplitViewController.main()?.updateTabBarShowing(!hidden)
} else if let hidden = loadObject?.requestParameters?.actionMap?["tabBarHidden"] as? Bool {
MVMCoreUISplitViewController.main()?.updateTabBarShowing(!hidden)
} else {
MVMCoreUISplitViewController.main()?.updateTabBarShowing(true)
} }
MVMCoreUISplitViewController.main()?.updateTabBarShowing(!tabModel.tabBarHidden)
} }
//-------------------------------------------------- //--------------------------------------------------
@ -454,15 +471,6 @@ import UIKit
loadObject?.requestParameters?.openSupportPanel ?? (loadObject?.systemParametersJSON?.boolForKey(KeyOpenSupport) ?? false) == true { loadObject?.requestParameters?.openSupportPanel ?? (loadObject?.systemParametersJSON?.boolForKey(KeyOpenSupport) ?? false) == true {
MVMCoreUISession.sharedGlobal()?.splitViewController?.showRightPanel(animated: true) MVMCoreUISession.sharedGlobal()?.splitViewController?.showRightPanel(animated: true)
} }
// Selects the tab if needed. Page driven takes priority over action driven (see viewWillAppear)
if let tab: Int = loadObject?.requestParameters?.actionMap?["tabBarIndex"] as? Int,
error == nil,
loadObject?.pageJSON?["tabBarIndex"] == nil {
MVMCoreDispatchUtility.performBlock(onMainThread: {
MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: tab)
})
}
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -47,7 +47,7 @@ import UIKit
public static func setNavigationItem(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { public static func setNavigationItem(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) {
viewController.navigationItem.title = navigationItemModel.title viewController.navigationItem.title = navigationItemModel.title
viewController.navigationItem.accessibilityLabel = navigationItemModel.title viewController.navigationItem.accessibilityLabel = navigationItemModel.title
viewController.navigationItem.hidesBackButton = (navigationItemModel.backButton != nil) viewController.navigationItem.hidesBackButton = navigationItemModel.hidesSystemBackButton
setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController)
setNavigationTitleView(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setNavigationTitleView(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController)
} }

View File

@ -44,13 +44,17 @@ public extension MVMCoreUISplitViewController {
let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
// Add back button first. // Add back button first.
if navigationItemModel?.alwaysShowBackButton != false { var showBackButton: Bool
if let forceBackButton = navigationItemModel?.alwaysShowBackButton {
showBackButton = forceBackButton
} else {
showBackButton = navigationController.viewControllers.count > 1
}
if showBackButton {
if let backButtonModel = navigationItemModel?.backButton { if let backButtonModel = navigationItemModel?.backButton {
if navigationController.viewControllers.count > 1 || navigationItemModel!.alwaysShowBackButton ?? false { leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil))
leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil)) } else if let backButton = backButton {
} // Default to legacy if we have default back button.
} else if let backButton = backButton,
navigationController.viewControllers.count > 1 {
leftItems.append(backButton) leftItems.append(backButton)
} }
} }

View File

@ -923,7 +923,6 @@ CGFloat const PanelAnimationDuration = 0.2;
// Creates the back button // Creates the back button
self.backButton = [[UIBarButtonItem alloc] initWithImage:[self imageForBackButton] style:UIBarButtonItemStylePlain target:self action:@selector(backButtonPressed:)]; self.backButton = [[UIBarButtonItem alloc] initWithImage:[self imageForBackButton] style:UIBarButtonItemStylePlain target:self action:@selector(backButtonPressed:)];
self.backButton.imageInsets = UIEdgeInsetsMake(0, 4, 0, -8);
// Dismisses a panel if the user taps the main view. // Dismisses a panel if the user taps the main view.
if (!self.tapToDismissGesture) { if (!self.tapToDismissGesture) {
@ -1073,10 +1072,13 @@ CGFloat const PanelAnimationDuration = 0.2;
} }
- (UIViewController *)getCurrentDetailViewController { - (UIViewController *)getCurrentDetailViewController {
UIViewController *viewController = self.navigationController.topViewController; __block UIViewController *viewController = nil;
if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{
viewController = [viewController performSelector:@selector(getCurrentViewController)]; viewController = self.navigationController.topViewController;
} if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) {
viewController = [viewController performSelector:@selector(getCurrentViewController)];
}
}];
return viewController; return viewController;
} }

View File

@ -8,6 +8,7 @@
@import MVMCore.MVMCoreLoggingHandler; @import MVMCore.MVMCoreLoggingHandler;
@class MFViewController; @class MFViewController;
@class MVMCoreTopAlertObject;
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -20,6 +21,9 @@ NS_ASSUME_NONNULL_BEGIN
- (void)defaultLogActionForController:(nullable id <MVMCoreViewControllerProtocol>)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; - (void)defaultLogActionForController:(nullable id <MVMCoreViewControllerProtocol>)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData;
- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nullable id <MVMCoreViewControllerProtocol>)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; - (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nullable id <MVMCoreViewControllerProtocol>)controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData;
// Logging top notification.
- (void)trackTopNotificationShown:(nonnull UIView *)topNotification topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject additionalData:(nullable NSDictionary *)additionalData;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@ -20,4 +20,7 @@
return nil; return nil;
} }
- (void)trackTopNotificationShown:(UIView *)topNotification topAlertObject:(MVMCoreTopAlertObject *)topAlertObject additionalData:(NSDictionary *)additionalData {
}
@end @end

View File

@ -39,6 +39,7 @@
- (nullable instancetype)initWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate viewToLayout:(nonnull UIView *)viewTolayout; - (nullable instancetype)initWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate viewToLayout:(nonnull UIView *)viewTolayout;
// Convenience change functions // Convenience change functions
- (void)defaultSetup;
- (void)setTopMessage:(nullable NSString *)topMessage; - (void)setTopMessage:(nullable NSString *)topMessage;
- (void)setTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData; - (void)setTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData;
- (void)setTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler; - (void)setTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler;

View File

@ -143,6 +143,20 @@
return self; return self;
} }
- (void)defaultSetup {
if (!self.shortView) {
self.translatesAutoresizingMaskIntoConstraints = NO;
self.clipsToBounds = YES;
_collapseAutomaticallyAfterExpanded = YES;
self.viewToLayout = MVMCoreUITopAlertView.sharedGlobal.superview;
self.expanded = NO;
[self setupTopMessage:nil];
MVMCoreUITopAlertMainView *topAlertWithButton = [[MVMCoreUITopAlertMainView alloc] init];
[topAlertWithButton defaultSetup];
[self setupTopAlertWithButton:topAlertWithButton];
}
}
- (void)setupTopMessage:(nullable NSString *)topMessage { - (void)setupTopMessage:(nullable NSString *)topMessage {
MVMCoreUITopAlertShortView *shortView = [[MVMCoreUITopAlertShortView alloc] initWithColor:[UIColor clearColor] message:nil actionMap:nil additionalData:nil topAlertObject:nil]; MVMCoreUITopAlertShortView *shortView = [[MVMCoreUITopAlertShortView alloc] initWithColor:[UIColor clearColor] message:nil actionMap:nil additionalData:nil topAlertObject:nil];
@ -284,58 +298,64 @@
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) { MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{ [MVMCoreDispatchUtility performBlockOnMainThread:^{
// Must notify animation delegate before animating. [weakSelf performExpansion:animated onCompletion:^{
if (animated && weakSelf.animationDelegate) {
[weakSelf.animationDelegate topAlertViewBeginAnimation];
}
[weakSelf.viewToLayout layoutIfNeeded];
weakSelf.topLabelConstraintBottom.active = NO;
weakSelf.topConstraint.active = YES;
weakSelf.expanded = YES;
void(^animation)(void) = ^(void) {
weakSelf.buttonView.button.alpha = 1;
weakSelf.buttonView.label.alpha = 1;
if (weakSelf.onlyShowTopMessageWhenCollapsed) {
weakSelf.shortViewHeight.active = YES;
}
[weakSelf.viewToLayout layoutIfNeeded];
};
//accessibility - added to make only top alert label and close button accessible. Posted notification when top alert is displayed
weakSelf.accessibilityElements = @[weakSelf.buttonView];
weakSelf.shortView.isAccessibilityElement = NO;
void(^completion)(void) = ^(void) {
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, weakSelf.buttonView.label);
[operation markAsFinished]; [operation markAsFinished];
}; }];
if (animated) {
[UIView animateWithDuration:.5 animations:animation completion:^(BOOL finished) {
[weakSelf.viewToLayout layoutIfNeeded];
// Must notify animation delegate when animating finished.
[MVMCoreDispatchUtility performBlockInBackground:^{
if (weakSelf.animationDelegate) {
[weakSelf.animationDelegate topAlertViewFinishAnimation];
}
}];
completion();
}];
} else {
animation();
completion();
}
// Collapse after 5 seconds (if the view still exists)
[weakSelf autoCollapse];
}]; }];
}]; }];
[[MVMCoreNavigationHandler sharedNavigationHandler] addNavigationOperation:operation]; [[MVMCoreNavigationHandler sharedNavigationHandler] addNavigationOperation:operation];
} }
} }
- (void)performExpansion:(BOOL)animated onCompletion:(void(^)(void))completionHandler {
// Must notify animation delegate before animating.
if (animated && self.animationDelegate) {
[self.animationDelegate topAlertViewBeginAnimation];
}
[self.viewToLayout layoutIfNeeded];
self.topLabelConstraintBottom.active = NO;
self.topConstraint.active = YES;
self.expanded = YES;
void(^animation)(void) = ^(void) {
self.buttonView.button.alpha = 1;
self.buttonView.label.alpha = 1;
if (self.onlyShowTopMessageWhenCollapsed) {
self.shortViewHeight.active = YES;
}
[self.viewToLayout layoutIfNeeded];
};
//accessibility - added to make only top alert label and close button accessible. Posted notification when top alert is displayed
self.accessibilityElements = @[self.buttonView];
self.shortView.isAccessibilityElement = NO;
void(^completion)(void) = ^(void) {
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.buttonView.label);
completionHandler();
};
if (animated) {
[UIView animateWithDuration:.5 animations:animation completion:^(BOOL finished) {
[self.viewToLayout layoutIfNeeded];
// Must notify animation delegate when animating finished.
[MVMCoreDispatchUtility performBlockInBackground:^{
if (self.animationDelegate) {
[self.animationDelegate topAlertViewFinishAnimation];
}
}];
completion();
}];
} else {
animation();
completion();
}
// Collapse after 5 seconds (if the view still exists)
[self autoCollapse];
}
- (void)autoCollapse { - (void)autoCollapse {
if (self.collapseAutomaticallyAfterExpanded) { if (self.collapseAutomaticallyAfterExpanded) {
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
@ -365,30 +385,8 @@
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) { MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{ [MVMCoreDispatchUtility performBlockOnMainThread:^{
// Must notify animation delegate before animating. [weakSelf performCollapseAnimationThen:^{
if (weakSelf.animationDelegate) { [operation markAsFinished];
[weakSelf.animationDelegate topAlertViewBeginAnimation];
}
[weakSelf.viewToLayout layoutIfNeeded];
weakSelf.topConstraint.active = NO;
weakSelf.topLabelConstraintBottom.active = YES;
weakSelf.expanded = NO;
[UIView animateWithDuration:.5 animations:^{
[weakSelf.viewToLayout layoutIfNeeded];
weakSelf.buttonView.button.alpha = 0;
weakSelf.buttonView.label.alpha = 0;
weakSelf.shortViewHeight.active = NO;
} completion:^(BOOL finished) {
[weakSelf.viewToLayout layoutIfNeeded];
weakSelf.accessibilityElements = @[weakSelf.shortView.label];
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
[MVMCoreDispatchUtility performBlockInBackground:^{
// Must notify animation delegate when animating finished.
if (weakSelf.animationDelegate) {
[weakSelf.animationDelegate topAlertViewFinishAnimation];
}
[operation markAsFinished];
}];
}]; }];
}]; }];
}]; }];
@ -396,6 +394,34 @@
} }
} }
- (void)performCollapseAnimationThen:(void(^)(void))completionHandler {
// Must notify animation delegate before animating.
if (self.animationDelegate) {
[self.animationDelegate topAlertViewBeginAnimation];
}
[self.viewToLayout layoutIfNeeded];
self.topConstraint.active = NO;
self.topLabelConstraintBottom.active = YES;
self.expanded = NO;
[UIView animateWithDuration:.5 animations:^{
[self.viewToLayout layoutIfNeeded];
self.buttonView.button.alpha = 0;
self.buttonView.label.alpha = 0;
self.shortViewHeight.active = NO;
} completion:^(BOOL finished) {
[self.viewToLayout layoutIfNeeded];
self.accessibilityElements = @[self.shortView.label];
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
// Must notify animation delegate when animating finished.
[MVMCoreDispatchUtility performBlockInBackground:^{
if (self.animationDelegate) {
[self.animationDelegate topAlertViewFinishAnimation];
}
}];
completionHandler();
}];
}
- (void)accessibilityFocusChanged:(NSNotification *)notification { - (void)accessibilityFocusChanged:(NSNotification *)notification {
if (![MVMCoreUIUtility viewContainsAccessiblityFocus:self]) { if (![MVMCoreUIUtility viewContainsAccessiblityFocus:self]) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIAccessibilityElementFocusedNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIAccessibilityElementFocusedNotification object:nil];

View File

@ -28,6 +28,8 @@
- (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor imageURL:(nullable NSString *)imageURL message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData closeButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate; - (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor imageURL:(nullable NSString *)imageURL message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData closeButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate;
- (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor imageURL:(nullable NSString *)imageURL message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage closeButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate; - (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor imageURL:(nullable NSString *)imageURL message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage closeButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate;
// Sets up without image
- (void)defaultSetup;
// Setters for label and button. // Setters for label and button.
- (void)setupWithMessage:(nullable NSString *)message subMessage:(nullable NSString *)subMessage color:(nullable UIColor *)color actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData; - (void)setupWithMessage:(nullable NSString *)message subMessage:(nullable NSString *)subMessage color:(nullable UIColor *)color actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData;
@ -37,6 +39,9 @@
- (void)setupButtonWithActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData; - (void)setupButtonWithActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData;
- (void)setupButtonWithButtonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler; - (void)setupButtonWithButtonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler;
// Setters for close button.
- (void)setupCloseButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate;
#pragma mark - legacy inits #pragma mark - legacy inits
// Legacy init: inits with a label and button, no close button or icon. // Legacy init: inits with a label and button, no close button or icon.

View File

@ -110,6 +110,17 @@
} }
#pragma mark - setup #pragma mark - setup
- (void)defaultSetup {
if (!self.label) {
self.translatesAutoresizingMaskIntoConstraints = NO;
self.clipsToBounds = YES;
self.height = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:96];
self.height.active = YES;
[self setupViewWithLabelAndImage:nil topImage:nil];
}
}
- (void)setupViewWithLabelAndImage:(NSString *)imageURL topImage:(NSString *)topImageString { - (void)setupViewWithLabelAndImage:(NSString *)imageURL topImage:(NSString *)topImageString {
UIView *centerView = [MVMCoreUICommonViewsUtility commonView]; UIView *centerView = [MVMCoreUICommonViewsUtility commonView];

View File

@ -0,0 +1,82 @@
//
// MVMCoreUITopAlertView+Extension.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/11/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
/// Allows top alerts to determine the status bar color and style.
protocol StatusBarUI {
func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle)
}
public extension MVMCoreUITopAlertView {
/// Registers with the notification center to know when json is updated.
@objc func registerWithNotificationCenter() {
NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(viewControllerChanged(notification:)), name: NSNotification.Name(rawValue: MVMCoreNotificationViewControllerChanged), object: nil)
}
/// Checks for new top alert json
@objc func responseJSONUpdated(notification: Notification) {
guard let responseJSON = (notification.userInfo?[String(describing: MVMCoreLoadObject.self)] as? MVMCoreLoadObject)?.responseJSON,
let json = responseJSON.optionalDictionaryForKey("TopNotification") else { return }
// TODO: Top alert view is current delegate. Should move to current view controller eventually?
let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self)
showTopAlert(with: json, delegateObject: delegateObject)
}
/// When a detail page changes, check top alerts.
@objc func viewControllerChanged(notification: Notification) {
guard let controller = MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol else { return }
MVMCoreAlertHandler.shared()?.checkPagesDependency(for: controller.pageType)
}
/// Shows the top alert with the json payload.
func showTopAlert(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) {
do {
let model = try TopNotificationModel.decode(json: json, delegateObject: delegateObject)
showTopAlert(with: model, delegateObject: delegateObject)
} catch {
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") {
MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject)
}
}
}
/// Shows the top alert with the model.
func showTopAlert(with model: TopNotificationModel, delegateObject: MVMCoreUIDelegateObject?) {
let object = model.createTopAlertObject()
MVMCoreAlertHandler.shared()?.showTopAlert(with: object)
}
/// Returns the top alert molecule to use and status bar color legacy style.
@objc func molecule(for topAlertObject: MVMCoreTopAlertObject, statusBarColor: AutoreleasingUnsafeMutablePointer<UIColor?>?, statusBarStyle: UnsafeMutablePointer<UIStatusBarStyle>?) -> UIView? {
do {
let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self)
guard let json = topAlertObject.json else { return nil }
let model = try TopNotificationModel.decode(json: json, delegateObject: delegateObject)
guard let molecule = MoleculeObjectMapping.shared()?.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil) else {
throw ModelRegistry.Error.decoderOther(message: "Molecule not mapped")
}
if let castView = molecule as? StatusBarUI {
let (color, style) = castView.getStatusBarUI()
statusBarColor?.pointee = color
statusBarStyle?.pointee = style
}
// TODO: Temporary, waiting for actual restriction from design.
molecule.heightAnchor.constraint(lessThanOrEqualToConstant: 140).isActive = true
return molecule
} catch {
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") {
MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject)
}
return nil
}
}
}

View File

@ -16,7 +16,6 @@
#import <MVMCoreUI/ButtonDelegateProtocol.h> #import <MVMCoreUI/ButtonDelegateProtocol.h>
@class MVMCoreTopAlertObject; @class MVMCoreTopAlertObject;
@class MVMCoreUITopAlertBaseView;
@interface MVMCoreUITopAlertView : UIView <MVMCoreViewProtocol, MVMCoreTopAlertViewProtocol, MVMCoreLoadDelegateProtocol, MVMCoreActionDelegateProtocol, MVMCorePresentationDelegateProtocol, ButtonDelegateProtocol> @interface MVMCoreUITopAlertView : UIView <MVMCoreViewProtocol, MVMCoreTopAlertViewProtocol, MVMCoreLoadDelegateProtocol, MVMCoreActionDelegateProtocol, MVMCorePresentationDelegateProtocol, ButtonDelegateProtocol>
@ -45,7 +44,7 @@
- (void)resetDefaultBackgroundColor:(nullable UIColor *)backgroundColor basedOnStatusBarStyle:(UIStatusBarStyle)style; - (void)resetDefaultBackgroundColor:(nullable UIColor *)backgroundColor basedOnStatusBarStyle:(UIStatusBarStyle)style;
// Can be subclassed for custom views. // Can be subclassed for custom views.
- (nonnull MVMCoreUITopAlertBaseView *)topAlertViewForTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate statusBarColor:(UIColor *_Nullable *_Nullable)statusBarColor statusBarStyle:(UIStatusBarStyle *_Nullable)statusBarStyle; - (nonnull UIView *)topAlertViewForTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate statusBarColor:(UIColor *_Nullable *_Nullable)statusBarColor statusBarStyle:(UIStatusBarStyle *_Nullable)statusBarStyle;
/// Get the background color based on the type /// Get the background color based on the type
- (nonnull UIColor *)getBackgroundColorForType:(nullable NSString *)type; - (nonnull UIColor *)getBackgroundColorForType:(nullable NSString *)type;

View File

@ -16,6 +16,7 @@
#import "NSLayoutConstraint+MFConvenience.h" #import "NSLayoutConstraint+MFConvenience.h"
#import "MVMCoreUISession.h" #import "MVMCoreUISession.h"
#import "MVMCoreUIUtility.h" #import "MVMCoreUIUtility.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
@import MVMCore.MVMCoreTopAlertObject; @import MVMCore.MVMCoreTopAlertObject;
@import MVMCore.MVMCoreLoadHandler; @import MVMCore.MVMCoreLoadHandler;
@import MVMCore.MVMCoreNavigationHandler; @import MVMCore.MVMCoreNavigationHandler;
@ -105,6 +106,8 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
self.statusBarView = statusBarView; self.statusBarView = statusBarView;
[self setStatusBarColor:[UIColor whiteColor] statusBarStyle:UIStatusBarStyleDefault]; [self setStatusBarColor:[UIColor whiteColor] statusBarStyle:UIStatusBarStyleDefault];
[self registerWithNotificationCenter];
} }
} }
@ -121,19 +124,23 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
} }
} }
- (nonnull MVMCoreUITopAlertBaseView *)topAlertViewForTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate statusBarColor:(UIColor *_Nullable *_Nullable)statusBarColor statusBarStyle:(UIStatusBarStyle *_Nullable)statusBarStyle { - (nonnull UIView *)topAlertViewForTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate statusBarColor:(UIColor *_Nullable *_Nullable)statusBarColor statusBarStyle:(UIStatusBarStyle *_Nullable)statusBarStyle {
MVMCoreUITopAlertExpandableView *view = [[MVMCoreUITopAlertExpandableView alloc] initWithTopAlertObject:topAlertObject animationDelegate:animationDelegate viewToLayout:self.superview]; if (topAlertObject.json) {
if (statusBarColor && view.shortView.label.text) { return [self moleculeFor:topAlertObject statusBarColor:statusBarColor statusBarStyle:statusBarStyle];
*statusBarColor = view.backgroundColor; } else {
MVMCoreUITopAlertExpandableView *view = [[MVMCoreUITopAlertExpandableView alloc] initWithTopAlertObject:topAlertObject animationDelegate:animationDelegate viewToLayout:self.superview];
if (statusBarStyle) { if (statusBarColor && view.shortView.label.text) {
CGFloat greyScale = 0; *statusBarColor = view.backgroundColor;
if ([view.shortView.label.textColor getWhite:&greyScale alpha:nil]) {
*statusBarStyle = greyScale > 0.5 ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault; if (statusBarStyle) {
CGFloat greyScale = 0;
if ([view.shortView.label.textColor getWhite:&greyScale alpha:nil]) {
*statusBarStyle = greyScale > 0.5 ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault;
}
} }
} }
return view;
} }
return view;
} }
- (nonnull UIColor *)getBackgroundColorForType:(nullable NSString *)type { - (nonnull UIColor *)getBackgroundColorForType:(nullable NSString *)type {
@ -162,8 +169,10 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
UIColor *statusBarColor = nil; UIColor *statusBarColor = nil;
UIStatusBarStyle statusBarStyle = UIStatusBarStyleDefault; UIStatusBarStyle statusBarStyle = UIStatusBarStyleDefault;
MVMCoreUITopAlertBaseView *view = [self topAlertViewForTopAlertObject:topAlertObject animationDelegate:animationDelegate statusBarColor:&statusBarColor statusBarStyle:&statusBarStyle]; UIView *view = [self topAlertViewForTopAlertObject:topAlertObject animationDelegate:animationDelegate statusBarColor:&statusBarColor statusBarStyle:&statusBarStyle];
[view updateView:CGRectGetWidth(self.bounds)]; if ([view conformsToProtocol:@protocol(MVMCoreViewProtocol)]) {
[((UIView <MVMCoreViewProtocol>*)view) updateView:CGRectGetWidth(self.bounds)];
}
if (!statusBarColor) { if (!statusBarColor) {
statusBarColor = [UIColor whiteColor]; statusBarColor = [UIColor whiteColor];
} }
@ -178,7 +187,20 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
[[MVMCoreUISession sharedGlobal].splitViewController.parentViewController setNeedsStatusBarAppearanceUpdate]; [[MVMCoreUISession sharedGlobal].splitViewController.parentViewController setNeedsStatusBarAppearanceUpdate];
} }
- (void)showAlertView:(nullable MVMCoreUITopAlertBaseView *)view topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject completionHandler:(void (^ __nullable)(BOOL finished))completionHandler { - (void)updateAccessibilityForTopAlert:(nullable UIView *)view {
// Update accessibility with top alert
if ([view isKindOfClass:[MVMCoreUITopAlertBaseView class]]) {
[((MVMCoreUITopAlertBaseView *)view) handleAccessibility];
} else {
id accessibilityArgument = view;
if ([view conformsToProtocol:@protocol(AccessibilityProtocol)]) {
accessibilityArgument = [((id <AccessibilityProtocol>)view) getAccessibilityLayoutChangedArgument];
}
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, accessibilityArgument);
}
}
- (void)showAlertView:(nullable UIView *)view topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject completionHandler:(void (^ __nullable)(BOOL finished))completionHandler {
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) { MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) {
@ -198,12 +220,14 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
[weakSelf.superview layoutIfNeeded]; [weakSelf.superview layoutIfNeeded];
[weakSelf.animationDelegate topAlertViewFinishAnimation]; [weakSelf.animationDelegate topAlertViewFinishAnimation];
[view handleAccessibility];
[weakSelf updateAccessibilityForTopAlert:view];
[MVMCoreDispatchUtility performBlockInBackground:^{ [MVMCoreDispatchUtility performBlockInBackground:^{
if ([weakSelf.topAlertObject.delegate respondsToSelector:@selector(topAlertViewShown:topAlertObject:)]) { if ([weakSelf.topAlertObject.delegate respondsToSelector:@selector(topAlertViewShown:topAlertObject:)]) {
[weakSelf.topAlertObject.delegate topAlertViewShown:view topAlertObject:topAlertObject]; [weakSelf.topAlertObject.delegate topAlertViewShown:view topAlertObject:topAlertObject];
} }
[[MVMCoreUILoggingHandler sharedLoggingHandler] trackTopNotificationShown:view topAlertObject:topAlertObject additionalData:nil];
[operation markAsFinished]; [operation markAsFinished];
completionHandler(finished); completionHandler(finished);
}]; }];

View File

@ -0,0 +1,124 @@
//
// TopNotification.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/11/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
open class TopNotificationModel: Codable {
public var type: String
public var priority = Operation.QueuePriority.normal
public var molecule: MoleculeModelProtocol
public var persistent = false
public var dismissTime = 5
public var pages: [String]?
public var analyticsData: JSONValueDictionary?
private enum CodingKeys: String, CodingKey {
case type
case priority
case molecule
case persistent
case dismissTime
case pages
case analyticsData
}
//--------------------------------------------------
// MARK: - Convenience Functions
//--------------------------------------------------
/// Set the priority using percent 0-100
open func setPriority(with percent: Float) {
// The new scale
let scale = Operation.QueuePriority.veryHigh.rawValue - Operation.QueuePriority.veryLow.rawValue
// Adjust the percent to the new scale
let scaledPercent = (percent / 100.0) * Float(scale)
// Finish shifting.
priority = Operation.QueuePriority(rawValue: Operation.QueuePriority.veryLow.rawValue + Int(scaledPercent)) ?? .normal
}
/// Gets the priority as a percent (0-100)
open func getPriorityPercent() -> Float {
// Shift the value
let shifted = Float(priority.rawValue - Operation.QueuePriority.veryLow.rawValue)
// The current scale
let scale = Operation.QueuePriority.veryHigh.rawValue - Operation.QueuePriority.veryLow.rawValue
// Adjust to percent
return (shifted / Float(scale)) * 100.0
}
open func createTopAlertObject() -> MVMCoreTopAlertObject {
let object = MVMCoreTopAlertObject()
object.persistent = persistent
object.type = type
object.topAlertDismissTime = dismissTime
object.queuePriority = priority
object.useNewStyle = true
object.json = toJSON()
return object
}
/// Decodes the top alert json to a model.
public static func decode(json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) throws -> Self {
let data = try JSONSerialization.data(withJSONObject: json)
let decoder = JSONDecoder()
if let delegateObject = delegateObject {
try decoder.add(delegateObject: delegateObject)
}
return try decoder.decode(self, from: data)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(with type: String, molecule: MoleculeModelProtocol, priority: Operation.QueuePriority = .normal, persistent: Bool = false, dismissTime: Int = 5, pages: [String]? = nil, analyticsData: JSONValueDictionary? = nil) {
self.type = type
self.molecule = molecule
self.priority = priority
self.persistent = persistent
self.dismissTime = dismissTime
self.pages = pages
self.analyticsData = analyticsData
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
type = try typeContainer.decode(String.self, forKey: .type)
molecule = try typeContainer.decodeModel(codingKey: .molecule)
if let priorityPercent = try typeContainer.decodeIfPresent(Float.self, forKey: .priority) {
setPriority(with: priorityPercent)
}
if let persistent = try typeContainer.decodeIfPresent(Bool.self, forKey: .persistent) {
self.persistent = persistent
}
if let dismissTime = try typeContainer.decodeIfPresent(Int.self, forKey: .dismissTime) {
self.dismissTime = dismissTime
}
pages = try typeContainer.decodeIfPresent([String].self, forKey: .pages)
analyticsData = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .analyticsData)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type, forKey: .type)
try container.encodeModel(molecule, forKey: .molecule)
try container.encode(getPriorityPercent(), forKey: .priority)
try container.encode(persistent, forKey: .persistent)
try container.encode(dismissTime, forKey: .dismissTime)
try container.encodeIfPresent(pages, forKey: .pages)
try container.encodeIfPresent(analyticsData, forKey: .analyticsData)
}
}

View File

@ -42,6 +42,7 @@ extern NSString * const KeyIsOpaque;
extern NSString * const KeyFieldKey; extern NSString * const KeyFieldKey;
extern NSString * const KeyRequired; extern NSString * const KeyRequired;
extern NSString * const KeySourceModel;
#pragma mark - Values #pragma mark - Values

View File

@ -40,6 +40,7 @@ NSString * const KeyTextColor = @"textColor";
NSString * const KeyIsHidden = @"isHidden"; NSString * const KeyIsHidden = @"isHidden";
NSString * const KeyIsOpaque = @"isOpaque"; NSString * const KeyIsOpaque = @"isOpaque";
NSString * const KeySourceModel = @"sourceModel";
#pragma mark - Values #pragma mark - Values

View File

@ -89,7 +89,11 @@
if (![focusedElement isKindOfClass:[UIView class]]) { if (![focusedElement isKindOfClass:[UIView class]]) {
return NO; return NO;
} }
return [(UIView *)focusedElement isDescendantOfView:view]; __block BOOL containsFocus;
[MVMCoreDispatchUtility performSyncBlockOnMainThread:^{
containsFocus = [(UIView *)focusedElement isDescendantOfView:view];
}];
return containsFocus;
} }
#pragma mark - Setters #pragma mark - Setters