merge
This commit is contained in:
commit
5bc9d7e427
@ -301,6 +301,8 @@
|
||||
D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092356244FA1EF0044AD09 /* ThreeLayerModelBase.swift */; };
|
||||
D20923592450ECE00044AD09 /* TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20923582450ECE00044AD09 /* TableView.swift */; };
|
||||
D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; };
|
||||
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 */; };
|
||||
D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.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 */; };
|
||||
D22D8393241C27B100D3DF69 /* TemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22D8392241C27B100D3DF69 /* TemplateModel.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 */; };
|
||||
D2351C7C24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2351C7B24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.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 */; };
|
||||
D28A839123CD4FD400DFE4FC /* CornerLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A839023CD4FD400DFE4FC /* CornerLabelsModel.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 */; };
|
||||
D28BA7432480284E00B75CB8 /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28BA7422480284E00B75CB8 /* TabBar.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 */; };
|
||||
D2C521A923EDE79E00CA2634 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C521A823EDE79E00CA2634 /* ViewController.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 */; };
|
||||
D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.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 */; };
|
||||
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.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 */; };
|
||||
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 */; };
|
||||
DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -797,6 +811,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -854,7 +869,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -940,6 +955,11 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -956,8 +976,12 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -1045,6 +1069,7 @@
|
||||
children = (
|
||||
D21B7F72243BAC6800051ABF /* CollectionItemModelProtocol.swift */,
|
||||
0A5D59C123AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift */,
|
||||
D2FD4A4825199BD9000C28A9 /* AccessibilityProtocol.swift */,
|
||||
);
|
||||
path = Protocols;
|
||||
sourceTree = "<group>";
|
||||
@ -1500,7 +1525,7 @@
|
||||
D23EA7FC247EBB7500D60C34 /* Buttons */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D28BA72F247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift */,
|
||||
D28BA72F247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift */,
|
||||
D2509ED52472EE2F001BFB9D /* NavigationImageButtonModel.swift */,
|
||||
D23EA801247EBED400D60C34 /* ImageBarButtonItem.swift */,
|
||||
D23EA7FD247EBBB700D60C34 /* NavigationLabelButtonModel.swift */,
|
||||
@ -1693,6 +1718,7 @@
|
||||
D29DF10E21E67A77003B2FB9 /* Molecules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D2CAC7C9251104CB00C75681 /* TopNotification */,
|
||||
D2509ED42472EE0B001BFB9D /* NavigationBar */,
|
||||
D253BB9A24587023002DE544 /* OtherContainers */,
|
||||
D22B38E923F4E07800490EF6 /* DesignedComponents */,
|
||||
@ -1757,6 +1783,8 @@
|
||||
D29DF11E21E6851E003B2FB9 /* TopAlert */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D20C7008250BF99B0095B21C /* TopNotificationModel.swift */,
|
||||
D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */,
|
||||
D29DF12021E6851E003B2FB9 /* MVMCoreUITopAlertView.h */,
|
||||
D29DF12421E6851E003B2FB9 /* MVMCoreUITopAlertView.m */,
|
||||
D29DF12321E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.h */,
|
||||
@ -2056,6 +2084,22 @@
|
||||
path = Protocols;
|
||||
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 */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@ -2191,6 +2235,7 @@
|
||||
D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */,
|
||||
D2509ED62472EE2F001BFB9D /* NavigationImageButtonModel.swift in Sources */,
|
||||
32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */,
|
||||
D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */,
|
||||
DBC4391822442197001AB423 /* CaretView.swift in Sources */,
|
||||
C07065C42395677300FBF997 /* Link.swift in Sources */,
|
||||
0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */,
|
||||
@ -2234,6 +2279,7 @@
|
||||
D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */,
|
||||
011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */,
|
||||
BB2BF0EA2452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift in Sources */,
|
||||
D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */,
|
||||
D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */,
|
||||
0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */,
|
||||
D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */,
|
||||
@ -2274,6 +2320,7 @@
|
||||
D29DF12E21E6851E003B2FB9 /* MVMCoreUITopAlertView.m in Sources */,
|
||||
AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */,
|
||||
BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */,
|
||||
D2CAC7D3251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift in Sources */,
|
||||
D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */,
|
||||
D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */,
|
||||
D28BA74D248589C800B75CB8 /* TabPageModelProtocol.swift in Sources */,
|
||||
@ -2300,6 +2347,7 @@
|
||||
D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */,
|
||||
8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */,
|
||||
94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */,
|
||||
D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */,
|
||||
014AA73123C5059B006F3E93 /* ListPageTemplateModel.swift in Sources */,
|
||||
AAC23FAF24D92A1E009208DF /* ListThreeColumnSpeedTest.swift in Sources */,
|
||||
D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */,
|
||||
@ -2355,6 +2403,8 @@
|
||||
AA633B3124989EC000731E80 /* HeadersH2PricingTwoRowsModel.swift in Sources */,
|
||||
8DEFA95C243DAC20000D27E5 /* ListThreeColumnDataUsageDividerModel.swift in Sources */,
|
||||
D2092357244FA1EF0044AD09 /* ThreeLayerModelBase.swift in Sources */,
|
||||
D2FD4A4925199BD9000C28A9 /* AccessibilityProtocol.swift in Sources */,
|
||||
D2CAC7CD251104FE00C75681 /* NotificationModel.swift in Sources */,
|
||||
0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */,
|
||||
D20923592450ECE00044AD09 /* TableView.swift in Sources */,
|
||||
BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */,
|
||||
@ -2426,6 +2476,7 @@
|
||||
C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */,
|
||||
32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */,
|
||||
011D958524042432000E3791 /* RulesProtocol.swift in Sources */,
|
||||
D23118B325124E18001C8440 /* Notification.swift in Sources */,
|
||||
AA9972502475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift in Sources */,
|
||||
AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */,
|
||||
D264FAA3243E632F00D98315 /* ProgrammaticCollectionViewController.swift in Sources */,
|
||||
@ -2463,6 +2514,7 @@
|
||||
D253BB9E2458751F002DE544 /* BGImageMoleculeModel.swift in Sources */,
|
||||
AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */,
|
||||
0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */,
|
||||
D20C7009250BF99B0095B21C /* TopNotificationModel.swift in Sources */,
|
||||
8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */,
|
||||
BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */,
|
||||
AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */,
|
||||
@ -2470,7 +2522,7 @@
|
||||
323AC96A24C837F000F8E4C4 /* ListThreeColumnBillChangesModel.swift in Sources */,
|
||||
D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */,
|
||||
525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */,
|
||||
D28BA730247EC2EB00B75CB8 /* NavigationButtomModelProtocol.swift in Sources */,
|
||||
D28BA730247EC2EB00B75CB8 /* NavigationButtonModelProtocol.swift in Sources */,
|
||||
0AE98BB323FF0934004C5109 /* ExternalLinkModel.swift in Sources */,
|
||||
D20FB165241A5D75004AFC3A /* NavigationItemModel.swift in Sources */,
|
||||
AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */,
|
||||
@ -2480,6 +2532,7 @@
|
||||
D2A92882241AAB67004E01C6 /* ScrollingViewController.swift in Sources */,
|
||||
C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */,
|
||||
0AE98BB523FF18D2004C5109 /* Arrow.swift in Sources */,
|
||||
D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */,
|
||||
D2D90B442404789000DD6EC9 /* MoleculeContainerProtocol.swift in Sources */,
|
||||
0A7ECC5F243CEB1200C828E8 /* ColorViewWithLabel.swift in Sources */,
|
||||
94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */,
|
||||
@ -2514,6 +2567,7 @@
|
||||
AA45AA0D24BF0276007A6EA7 /* LockUpsPlanNames.swift in Sources */,
|
||||
8DE5BECF2456F7B100772E76 /* ListTwoColumnDropdownSelectors.swift in Sources */,
|
||||
D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */,
|
||||
D2CAC7D12511058C00C75681 /* MVMCoreUITopAlertMainView+Extension.swift in Sources */,
|
||||
0A21DB83235DFBC500C160A2 /* MdnEntryField.swift in Sources */,
|
||||
0AE98BB723FF18E9004C5109 /* ArrowModel.swift in Sources */,
|
||||
D28A837D23CCA86A00DFE4FC /* TabsListItemModel.swift in Sources */,
|
||||
@ -2524,6 +2578,7 @@
|
||||
AAB8549824DC01BD00477C40 /* ListThreeColumnBillHistoryDividerModel.swift in Sources */,
|
||||
D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */,
|
||||
D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */,
|
||||
D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */,
|
||||
0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */,
|
||||
AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */,
|
||||
D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */,
|
||||
@ -2569,6 +2624,7 @@
|
||||
C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */,
|
||||
D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */,
|
||||
BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */,
|
||||
D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */,
|
||||
0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */,
|
||||
0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */,
|
||||
D236E5B5241FEB1000C38625 /* ListTwoColumnPriceDescriptionModel.swift in Sources */,
|
||||
|
||||
@ -27,6 +27,11 @@ import UIKit
|
||||
return caret
|
||||
}()
|
||||
|
||||
public var baseDropdownEntryFieldModel: BaseDropdownEntryFieldModel? {
|
||||
return model as? BaseDropdownEntryFieldModel
|
||||
}
|
||||
var additionalData: [AnyHashable: Any]?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Property Observers
|
||||
//--------------------------------------------------
|
||||
@ -75,9 +80,21 @@ import UIKit
|
||||
|
||||
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
|
||||
self.additionalData = additionalData
|
||||
guard let model = model as? BaseDropdownEntryFieldModel else { return }
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
//--------------------------------------------------
|
||||
|
||||
public var caretView: CaretViewModel?
|
||||
public var action: ActionModelProtocol?
|
||||
|
||||
public override class var identifier: String {
|
||||
return ""
|
||||
@ -24,6 +25,7 @@
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case caretView
|
||||
case action
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -34,6 +36,7 @@
|
||||
try super.init(from: decoder)
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
caretView = try typeContainer.decodeIfPresent(CaretViewModel.self, forKey: .caretView)
|
||||
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
@ -41,5 +44,6 @@
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encode(caretView, forKey: .caretView)
|
||||
try container.encodeModelIfPresent(action, forKey: .action)
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,8 +109,8 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
|
||||
pickerData = model.options
|
||||
setPickerDelegates(delegate: self)
|
||||
|
||||
if let pickerView = pickerView {
|
||||
self.pickerView(pickerView, didSelectRow: model.selectedIndex, inComponent: 0)
|
||||
if let pickerView = pickerView, let index = model.selectedIndex {
|
||||
self.pickerView(pickerView, didSelectRow: index, inComponent: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,11 +16,14 @@
|
||||
}
|
||||
|
||||
public var options: [String] = []
|
||||
public var selectedIndex: Int = 0
|
||||
public var selectedIndex: Int?
|
||||
|
||||
public override func formFieldValue() -> AnyHashable? {
|
||||
guard !options.isEmpty else { return nil }
|
||||
return options[selectedIndex]
|
||||
guard !options.isEmpty,
|
||||
let index = selectedIndex
|
||||
else { return nil }
|
||||
|
||||
return options[index]
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -45,13 +48,16 @@
|
||||
if let selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .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 {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(options, forKey: .options)
|
||||
try container.encode(options, forKey: .selectedIndex)
|
||||
try container.encodeIfPresent(options, forKey: .selectedIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,10 @@ import MVMCore
|
||||
var groupName: String?
|
||||
var delegateObject: MVMCoreUIDelegateObject?
|
||||
|
||||
public var checkboxModel: CheckboxModel? {
|
||||
return model as? CheckboxModel
|
||||
}
|
||||
|
||||
public static let defaultHeightWidth: CGFloat = 18.0
|
||||
|
||||
/// If true the border of this checkbox will be circular.
|
||||
@ -381,7 +385,7 @@ import MVMCore
|
||||
checkWidth = 2
|
||||
checkAndBypassAnimations(selected: false)
|
||||
}
|
||||
|
||||
|
||||
public override func updateView(_ size: CGFloat) {
|
||||
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]?) {
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
self.delegateObject = delegateObject
|
||||
@ -422,10 +434,15 @@ import MVMCore
|
||||
|
||||
isEnabled = model.enabled
|
||||
|
||||
if let action = model.action {
|
||||
actionBlock = {
|
||||
if let actionMap = action.toJSON() {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
if (model.action != nil || model.offAction != nil) {
|
||||
actionBlock = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ import Foundation
|
||||
public var invertedColor: Color = Color(uiColor: .mvmWhite)
|
||||
public var invertedBackgroundColor: Color = Color(uiColor: .mvmBlack)
|
||||
public var action: ActionModelProtocol?
|
||||
public var offAction: ActionModelProtocol?
|
||||
|
||||
public var fieldKey: String?
|
||||
public var groupName: String = FormValidator.defaultGroupName
|
||||
@ -61,6 +62,7 @@ import Foundation
|
||||
case action
|
||||
case fieldKey
|
||||
case groupName
|
||||
case offAction
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -155,6 +157,7 @@ import Foundation
|
||||
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
|
||||
self.groupName = groupName
|
||||
}
|
||||
offAction = try typeContainer.decodeModelIfPresent(codingKey: .offAction)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -179,5 +182,6 @@ import Foundation
|
||||
try container.encodeIfPresent(enabled, forKey: .enabled)
|
||||
try container.encodeModelIfPresent(action, forKey: .action)
|
||||
try container.encodeIfPresent(groupName, forKey: .groupName)
|
||||
try container.encodeModelIfPresent(offAction, forKey: .offAction)
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
open class RadioBox: Control {
|
||||
open class RadioBox: Control, MFButtonProtocol {
|
||||
public let label = Label(fontStyle: .RegularBodySmall)
|
||||
public let subTextLabel = Label(fontStyle: .RegularMicro)
|
||||
public var isOutOfStock = false
|
||||
@ -22,6 +22,9 @@ open class RadioBox: Control {
|
||||
|
||||
public var subTextLabelHeightConstraint: NSLayoutConstraint?
|
||||
|
||||
private var delegateObject: MVMCoreUIDelegateObject?
|
||||
var additionalData: [AnyHashable: Any]?
|
||||
|
||||
public var radioBoxModel: 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]?) {
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
guard let model = model as? RadioBoxModel else { return }
|
||||
self.delegateObject = delegateObject
|
||||
self.additionalData = additionalData
|
||||
label.text = model.text
|
||||
subTextLabel.text = model.subText
|
||||
isOutOfStock = model.strikethrough
|
||||
@ -132,9 +137,12 @@ open class RadioBox: Control {
|
||||
}
|
||||
|
||||
@objc open func selectBox() {
|
||||
guard isEnabled else { return }
|
||||
guard isEnabled, !isSelected else { return }
|
||||
isSelected = true
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ import Foundation
|
||||
public var enabled: Bool = true
|
||||
public var strikethrough: Bool = false
|
||||
public var fieldValue: String?
|
||||
public var action: ActionModelProtocol?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
@ -28,6 +29,7 @@ import Foundation
|
||||
case enabled
|
||||
case strikethrough
|
||||
case fieldValue
|
||||
case action
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
@ -47,6 +49,7 @@ import Foundation
|
||||
}
|
||||
|
||||
fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue)
|
||||
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -60,5 +63,6 @@ import Foundation
|
||||
try container.encode(enabled, forKey: .enabled)
|
||||
try container.encode(strikethrough, forKey: .strikethrough)
|
||||
try container.encodeIfPresent(fieldValue, forKey: .fieldValue)
|
||||
try container.encodeModelIfPresent(action, forKey: .action)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
import UIKit
|
||||
|
||||
|
||||
@objcMembers open class RadioButton: Control {
|
||||
@objcMembers open class RadioButton: Control, MFButtonProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
@ -30,6 +30,7 @@ import UIKit
|
||||
public var enabledColor: UIColor = .mvmBlack
|
||||
public var disabledColor: UIColor = .mvmCoolGray3
|
||||
public var delegateObject: MVMCoreUIDelegateObject?
|
||||
var additionalData: [AnyHashable: Any]?
|
||||
|
||||
public var radioModel: RadioButtonModel? {
|
||||
return model as? RadioButtonModel
|
||||
@ -94,11 +95,15 @@ import UIKit
|
||||
if !isEnabled {
|
||||
return
|
||||
}
|
||||
let wasPreviouslySelected = isSelected
|
||||
if let radioButtonModel = radioButtonSelectionHelper {
|
||||
radioButtonModel.selected(self)
|
||||
} else {
|
||||
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)
|
||||
setNeedsDisplay()
|
||||
}
|
||||
@ -155,6 +160,7 @@ import UIKit
|
||||
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
self.delegateObject = delegateObject
|
||||
self.additionalData = additionalData
|
||||
|
||||
guard let model = model as? RadioButtonModel else { return }
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
|
||||
public var baseValue: AnyHashable?
|
||||
public var groupName: String = FormValidator.defaultGroupName
|
||||
public var fieldKey: String?
|
||||
public var action: ActionModelProtocol?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
@ -39,6 +40,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
|
||||
case fieldValue
|
||||
case fieldKey
|
||||
case groupName
|
||||
case action
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -81,6 +83,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
|
||||
self.groupName = groupName
|
||||
}
|
||||
fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue)
|
||||
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
|
||||
}
|
||||
|
||||
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(groupName, forKey: .groupName)
|
||||
try container.encodeIfPresent(fieldValue, forKey: .fieldValue)
|
||||
try container.encodeModelIfPresent(action, forKey: .action)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
import UIKit
|
||||
|
||||
|
||||
open class RadioSwatch: Control {
|
||||
open class RadioSwatch: Control, MFButtonProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
@ -20,6 +20,9 @@ open class RadioSwatch: Control {
|
||||
private var strikeLayer: CALayer?
|
||||
private var maskLayer: CALayer?
|
||||
|
||||
private var delegateObject: MVMCoreUIDelegateObject?
|
||||
var additionalData: [AnyHashable: Any]?
|
||||
|
||||
public var radioSwatchModel: 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]?) {
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
guard let model = model as? RadioSwatchModel else { return }
|
||||
self.delegateObject = delegateObject
|
||||
self.additionalData = additionalData
|
||||
bottomText.text = model.text
|
||||
isSelected = model.selected
|
||||
isEnabled = model.enabled
|
||||
@ -114,9 +119,12 @@ open class RadioSwatch: Control {
|
||||
}
|
||||
|
||||
@objc open func selectSwatch() {
|
||||
guard isEnabled else { return }
|
||||
guard isEnabled, !isSelected else { return }
|
||||
isSelected = true
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ import Foundation
|
||||
public var enabled: Bool = true
|
||||
public var strikethrough: Bool = false
|
||||
public var fieldValue: String?
|
||||
public var action: ActionModelProtocol?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
@ -27,6 +28,7 @@ import Foundation
|
||||
case enabled
|
||||
case strikethrough
|
||||
case fieldValue
|
||||
case action
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
@ -46,6 +48,7 @@ import Foundation
|
||||
self.strikethrough = strikethrough
|
||||
}
|
||||
fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue)
|
||||
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -58,6 +61,7 @@ import Foundation
|
||||
try container.encode(enabled, forKey: .enabled)
|
||||
try container.encode(strikethrough, forKey: .strikethrough)
|
||||
try container.encodeIfPresent(fieldValue, forKey: .fieldValue)
|
||||
try container.encodeModelIfPresent(action, forKey: .action)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -241,16 +241,38 @@ public typealias ActionBlock = () -> ()
|
||||
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
|
||||
clauses = []
|
||||
|
||||
guard let labelModel = model as? LabelModel else {
|
||||
text = ""
|
||||
return
|
||||
}
|
||||
|
||||
text = nil
|
||||
attributedText = nil
|
||||
originalAttributedString = nil
|
||||
text = nil
|
||||
|
||||
guard let labelModel = model as? LabelModel else { return }
|
||||
|
||||
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
|
||||
Label.setLabel(self, withHTML: labelModel.html)
|
||||
isAccessibilityElement = hasText
|
||||
@ -258,8 +280,10 @@ public typealias ActionBlock = () -> ()
|
||||
switch labelModel.textAlignment {
|
||||
case .center:
|
||||
textAlignment = .center
|
||||
|
||||
case .right:
|
||||
textAlignment = .right
|
||||
|
||||
default:
|
||||
textAlignment = .left
|
||||
}
|
||||
@ -291,7 +315,7 @@ public typealias ActionBlock = () -> ()
|
||||
if let color = labelModel.textColor {
|
||||
textColor = color.uiColor
|
||||
}
|
||||
|
||||
|
||||
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])
|
||||
|
||||
@ -360,7 +384,7 @@ public typealias ActionBlock = () -> ()
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
attributedText = attributedString
|
||||
originalAttributedString = attributedText
|
||||
}
|
||||
@ -504,11 +528,11 @@ public typealias ActionBlock = () -> ()
|
||||
textColor = .mvmBlack
|
||||
setScale(scale)
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - 2.0 Styling Methods
|
||||
//------------------------------------------------------
|
||||
|
||||
|
||||
@objc public func styleH1(_ scale: Bool) {
|
||||
MFStyler.styleLabelH1(self, genericScaling: false)
|
||||
setScale(scale)
|
||||
@ -549,6 +573,23 @@ public typealias ActionBlock = () -> ()
|
||||
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) {
|
||||
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.
|
||||
@objc public func makeTextButton(actionBlock: @escaping ActionBlock) {
|
||||
|
||||
|
||||
setTextLinkState(range: getRange, actionBlock: actionBlock)
|
||||
}
|
||||
|
||||
@ -921,7 +962,7 @@ extension Label {
|
||||
}
|
||||
|
||||
@objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) {
|
||||
|
||||
|
||||
for clause in clauses {
|
||||
if action.hash == clause.accessibilityID {
|
||||
clause.performAction()
|
||||
@ -931,7 +972,7 @@ extension Label {
|
||||
}
|
||||
|
||||
open override func accessibilityActivate() -> Bool {
|
||||
|
||||
|
||||
guard let accessibleActions = accessibilityCustomActions else { return false }
|
||||
|
||||
for clause in clauses {
|
||||
|
||||
@ -6,23 +6,39 @@
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class LabelAttributeActionModel: LabelAttributeModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
override public class var identifier: String {
|
||||
return "action"
|
||||
}
|
||||
|
||||
var action: ActionModelProtocol
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
public init(_ location: Int, _ length: Int, action: ActionModelProtocol) {
|
||||
self.action = action
|
||||
super.init(location, length)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case action
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
action = try typeContainer.decodeModel(codingKey: .action)
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class LabelAttributeColorModel: LabelAttributeModel {
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -6,8 +6,6 @@
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
@objcMembers public class LabelAttributeFontModel: LabelAttributeModel {
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -6,13 +6,12 @@
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class LabelAttributeImageModel: LabelAttributeModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
|
||||
override public class var identifier: String {
|
||||
return "image"
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers open class LabelAttributeModel: ModelProtocol {
|
||||
//--------------------------------------------------
|
||||
@ -20,7 +19,7 @@ import Foundation
|
||||
public static var categoryCodingKey: String {
|
||||
return "type"
|
||||
}
|
||||
|
||||
|
||||
public class var identifier: String {
|
||||
return ""
|
||||
}
|
||||
@ -31,12 +30,16 @@ import Foundation
|
||||
|
||||
var location: Int
|
||||
var length: Int
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
public init(_ location: Int, _ length: Int) {
|
||||
self.location = location
|
||||
self.length = length
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -6,16 +6,28 @@
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class LabelAttributeStrikeThroughModel: LabelAttributeModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
override public class var identifier: String {
|
||||
return "strikethrough"
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
}
|
||||
|
||||
@ -6,13 +6,20 @@
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class LabelAttributeUnderlineModel: LabelAttributeModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
override public class var identifier: String {
|
||||
return "underline"
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
@ -7,8 +7,6 @@
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class LabelModel: MoleculeModelProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
|
||||
@ -394,18 +394,19 @@ public typealias ActionBlockConfirmation = () -> (Bool)
|
||||
|
||||
let actionMap = model.action?.toJSON()
|
||||
let alternateActionMap = model.alternateAction?.toJSON()
|
||||
let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model)
|
||||
if actionMap != nil || alternateActionMap != nil {
|
||||
didToggleAction = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
if self.isOn {
|
||||
if actionMap != nil {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +128,9 @@ extension WebView : WKUIDelegate {
|
||||
if !dynamicHeight {
|
||||
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
|
||||
*/
|
||||
if !webView.isLoading {
|
||||
@ -136,14 +138,18 @@ extension WebView : WKUIDelegate {
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
if let height = result as? CGFloat {
|
||||
self.webViewHeight?.constant = height
|
||||
} else {
|
||||
//if failed to get height from javascript, using scrollview.contensize's height
|
||||
let scrollHeight = self.webView?.scrollView.contentSize.height
|
||||
self.webViewHeight?.constant = scrollHeight ?? 44
|
||||
}
|
||||
self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
|
||||
MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
if let height = result as? CGFloat {
|
||||
self.webViewHeight?.constant = height
|
||||
} else {
|
||||
//if failed to get height from javascript, using scrollview.contensize's height
|
||||
self.webViewHeight?.constant = webView.scrollView.contentSize.height
|
||||
}
|
||||
self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,6 +232,10 @@ import Foundation
|
||||
MoleculeObjectMapping.shared()?.register(viewClass: LockUpsPlanNames.self, viewModelClass: LockUpsPlanNamesModel.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
|
||||
try? ModelRegistry.register(RuleRequiredModel.self)
|
||||
try? ModelRegistry.register(RuleAnyRequiredModel.self)
|
||||
|
||||
@ -99,6 +99,10 @@ import Foundation
|
||||
self.tabBar(self, didSelect: newSelectedItem)
|
||||
})
|
||||
}
|
||||
|
||||
public func currentTabIndex() -> Int {
|
||||
return model.selectedTab
|
||||
}
|
||||
}
|
||||
|
||||
extension UITabBarItem: MFButtonProtocol {
|
||||
|
||||
@ -6,8 +6,6 @@
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
@objcMembers public class AccordionMoleculeTableViewCell: MoleculeTableViewCell {
|
||||
//--------------------------------------------------
|
||||
@ -36,6 +34,7 @@ import UIKit
|
||||
customAccessoryView = true
|
||||
super.setupView()
|
||||
accessoryView = accordionButton
|
||||
accessoryView?.isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
override public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
|
||||
@ -18,8 +18,12 @@ public class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProt
|
||||
public var backgroundColor: Color?
|
||||
public var tintColor: Color
|
||||
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 backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? = NavigationImageButtonModel(with: "nav_back", action: ActionBackModel())
|
||||
public var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)?
|
||||
|
||||
public var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]?
|
||||
public var additionalRightButtons: [(NavigationButtonModelProtocol & 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)
|
||||
line = try typeContainer.decodeIfPresent(LineModel.self, forKey: .line) ?? LineModel(type: .standard)
|
||||
alwaysShowBackButton = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysShowBackButton)
|
||||
if let backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol) = try typeContainer.decodeModelIfPresent(codingKey: .backButton) {
|
||||
self.backButton = backButton
|
||||
}
|
||||
backButton = try typeContainer.decodeModelIfPresent(codingKey: .backButton)
|
||||
additionalLeftButtons = try typeContainer.decodeModelsIfPresent(codingKey: .additionalLeftButtons)
|
||||
additionalRightButtons = try typeContainer.decodeModelsIfPresent(codingKey: .additionalRightButtons)
|
||||
titleView = try typeContainer.decodeModelIfPresent(codingKey: .titleView)
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,7 @@ public protocol NavigationItemModelProtocol {
|
||||
var backgroundColor: Color? { get set }
|
||||
var tintColor: Color { get set }
|
||||
var line: LineModel? { get set }
|
||||
var hidesSystemBackButton: Bool { get set }
|
||||
var alwaysShowBackButton: Bool? { get set }
|
||||
var backButton: (NavigationButtonModelProtocol & MoleculeModelProtocol)? { get set }
|
||||
var additionalLeftButtons: [(NavigationButtonModelProtocol & MoleculeModelProtocol)]? { get set }
|
||||
|
||||
@ -17,4 +17,7 @@ import Foundation
|
||||
|
||||
/// Should select the tab index. As if the user selected it.
|
||||
@objc func selectTab(at index: Int)
|
||||
|
||||
/// Returns the current tab
|
||||
@objc func currentTabIndex() -> Int
|
||||
}
|
||||
|
||||
@ -72,21 +72,26 @@ public typealias ButtonAction = (Button) -> ()
|
||||
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
|
||||
buttonDelegate = delegateObject?.buttonDelegate
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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()),
|
||||
let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any],
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
14
MVMCoreUI/BaseClasses/Protocols/AccessibilityProtocol.swift
Normal file
14
MVMCoreUI/BaseClasses/Protocols/AccessibilityProtocol.swift
Normal 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?
|
||||
}
|
||||
@ -39,6 +39,9 @@ import UIKit
|
||||
|
||||
public var selectedField: UIView?
|
||||
|
||||
// Stores the previous tab bar index.
|
||||
public var tabBarIndex: Int?
|
||||
|
||||
/// Checks if the screen width has changed
|
||||
open func screenSizeChanged() -> Bool {
|
||||
return !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1)
|
||||
@ -318,13 +321,27 @@ import UIKit
|
||||
//--------------------------------------------------
|
||||
|
||||
open func updateTabBar() {
|
||||
guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self,
|
||||
let tabModel = pageModel as? TabPageModelProtocol else { return }
|
||||
guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self else { return }
|
||||
MVMCoreUISplitViewController.main()?.tabBar?.delegateObject = delegateObjectIVar
|
||||
if let index = tabModel.tabBarIndex {
|
||||
|
||||
if let index = (pageModel as? TabPageModelProtocol)?.tabBarIndex {
|
||||
MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index)
|
||||
} else if let index = loadObject?.requestParameters?.actionMap?["tabBarIndex"] as? Int {
|
||||
MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index)
|
||||
} 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 {
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -47,7 +47,7 @@ import UIKit
|
||||
public static func setNavigationItem(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) {
|
||||
viewController.navigationItem.title = 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)
|
||||
setNavigationTitleView(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController)
|
||||
}
|
||||
|
||||
@ -44,13 +44,17 @@ public extension MVMCoreUISplitViewController {
|
||||
let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
|
||||
|
||||
// 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 navigationController.viewControllers.count > 1 || navigationItemModel!.alwaysShowBackButton ?? false {
|
||||
leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil))
|
||||
}
|
||||
} else if let backButton = backButton,
|
||||
navigationController.viewControllers.count > 1 {
|
||||
leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil))
|
||||
} else if let backButton = backButton {
|
||||
// Default to legacy if we have default back button.
|
||||
leftItems.append(backButton)
|
||||
}
|
||||
}
|
||||
|
||||
@ -923,7 +923,6 @@ CGFloat const PanelAnimationDuration = 0.2;
|
||||
|
||||
// Creates the back button
|
||||
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.
|
||||
if (!self.tapToDismissGesture) {
|
||||
@ -1073,10 +1072,13 @@ CGFloat const PanelAnimationDuration = 0.2;
|
||||
}
|
||||
|
||||
- (UIViewController *)getCurrentDetailViewController {
|
||||
UIViewController *viewController = self.navigationController.topViewController;
|
||||
if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) {
|
||||
viewController = [viewController performSelector:@selector(getCurrentViewController)];
|
||||
}
|
||||
__block UIViewController *viewController = nil;
|
||||
[MVMCoreDispatchUtility performSyncBlockOnMainThread:^{
|
||||
viewController = self.navigationController.topViewController;
|
||||
if ([viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) {
|
||||
viewController = [viewController performSelector:@selector(getCurrentViewController)];
|
||||
}
|
||||
}];
|
||||
return viewController;
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
@import MVMCore.MVMCoreLoggingHandler;
|
||||
@class MFViewController;
|
||||
@class MVMCoreTopAlertObject;
|
||||
|
||||
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;
|
||||
- (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
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -20,4 +20,7 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)trackTopNotificationShown:(UIView *)topNotification topAlertObject:(MVMCoreTopAlertObject *)topAlertObject additionalData:(NSDictionary *)additionalData {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -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;
|
||||
|
||||
// Convenience change functions
|
||||
- (void)defaultSetup;
|
||||
- (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 buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler;
|
||||
|
||||
@ -143,6 +143,20 @@
|
||||
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 {
|
||||
|
||||
MVMCoreUITopAlertShortView *shortView = [[MVMCoreUITopAlertShortView alloc] initWithColor:[UIColor clearColor] message:nil actionMap:nil additionalData:nil topAlertObject:nil];
|
||||
@ -284,58 +298,64 @@
|
||||
__weak typeof(self) weakSelf = self;
|
||||
MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) {
|
||||
[MVMCoreDispatchUtility performBlockOnMainThread:^{
|
||||
// Must notify animation delegate before animating.
|
||||
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);
|
||||
[weakSelf performExpansion:animated onCompletion:^{
|
||||
[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];
|
||||
}
|
||||
}
|
||||
|
||||
- (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 {
|
||||
if (self.collapseAutomaticallyAfterExpanded) {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
@ -365,30 +385,8 @@
|
||||
__weak typeof(self) weakSelf = self;
|
||||
MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) {
|
||||
[MVMCoreDispatchUtility performBlockOnMainThread:^{
|
||||
// Must notify animation delegate before animating.
|
||||
if (weakSelf.animationDelegate) {
|
||||
[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];
|
||||
}];
|
||||
[weakSelf performCollapseAnimationThen:^{
|
||||
[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 {
|
||||
if (![MVMCoreUIUtility viewContainsAccessiblityFocus:self]) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIAccessibilityElementFocusedNotification object:nil];
|
||||
|
||||
@ -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 closeButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate;
|
||||
|
||||
// Sets up without image
|
||||
- (void)defaultSetup;
|
||||
|
||||
// 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;
|
||||
@ -37,6 +39,9 @@
|
||||
- (void)setupButtonWithActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData;
|
||||
- (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
|
||||
|
||||
// Legacy init: inits with a label and button, no close button or icon.
|
||||
|
||||
@ -110,6 +110,17 @@
|
||||
}
|
||||
|
||||
#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 {
|
||||
|
||||
UIView *centerView = [MVMCoreUICommonViewsUtility commonView];
|
||||
|
||||
82
MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift
Normal file
82
MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,6 @@
|
||||
#import <MVMCoreUI/ButtonDelegateProtocol.h>
|
||||
|
||||
@class MVMCoreTopAlertObject;
|
||||
@class MVMCoreUITopAlertBaseView;
|
||||
|
||||
@interface MVMCoreUITopAlertView : UIView <MVMCoreViewProtocol, MVMCoreTopAlertViewProtocol, MVMCoreLoadDelegateProtocol, MVMCoreActionDelegateProtocol, MVMCorePresentationDelegateProtocol, ButtonDelegateProtocol>
|
||||
|
||||
@ -45,7 +44,7 @@
|
||||
- (void)resetDefaultBackgroundColor:(nullable UIColor *)backgroundColor basedOnStatusBarStyle:(UIStatusBarStyle)style;
|
||||
|
||||
// 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
|
||||
- (nonnull UIColor *)getBackgroundColorForType:(nullable NSString *)type;
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#import "NSLayoutConstraint+MFConvenience.h"
|
||||
#import "MVMCoreUISession.h"
|
||||
#import "MVMCoreUIUtility.h"
|
||||
#import <MVMCoreUI/MVMCoreUI-Swift.h>
|
||||
@import MVMCore.MVMCoreTopAlertObject;
|
||||
@import MVMCore.MVMCoreLoadHandler;
|
||||
@import MVMCore.MVMCoreNavigationHandler;
|
||||
@ -105,6 +106,8 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
|
||||
self.statusBarView = statusBarView;
|
||||
|
||||
[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 {
|
||||
MVMCoreUITopAlertExpandableView *view = [[MVMCoreUITopAlertExpandableView alloc] initWithTopAlertObject:topAlertObject animationDelegate:animationDelegate viewToLayout:self.superview];
|
||||
if (statusBarColor && view.shortView.label.text) {
|
||||
*statusBarColor = view.backgroundColor;
|
||||
|
||||
if (statusBarStyle) {
|
||||
CGFloat greyScale = 0;
|
||||
if ([view.shortView.label.textColor getWhite:&greyScale alpha:nil]) {
|
||||
*statusBarStyle = greyScale > 0.5 ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault;
|
||||
- (nonnull UIView *)topAlertViewForTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate statusBarColor:(UIColor *_Nullable *_Nullable)statusBarColor statusBarStyle:(UIStatusBarStyle *_Nullable)statusBarStyle {
|
||||
if (topAlertObject.json) {
|
||||
return [self moleculeFor:topAlertObject statusBarColor:statusBarColor statusBarStyle:statusBarStyle];
|
||||
} else {
|
||||
MVMCoreUITopAlertExpandableView *view = [[MVMCoreUITopAlertExpandableView alloc] initWithTopAlertObject:topAlertObject animationDelegate:animationDelegate viewToLayout:self.superview];
|
||||
if (statusBarColor && view.shortView.label.text) {
|
||||
*statusBarColor = view.backgroundColor;
|
||||
|
||||
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 {
|
||||
@ -162,8 +169,10 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
|
||||
|
||||
UIColor *statusBarColor = nil;
|
||||
UIStatusBarStyle statusBarStyle = UIStatusBarStyleDefault;
|
||||
MVMCoreUITopAlertBaseView *view = [self topAlertViewForTopAlertObject:topAlertObject animationDelegate:animationDelegate statusBarColor:&statusBarColor statusBarStyle:&statusBarStyle];
|
||||
[view updateView:CGRectGetWidth(self.bounds)];
|
||||
UIView *view = [self topAlertViewForTopAlertObject:topAlertObject animationDelegate:animationDelegate statusBarColor:&statusBarColor statusBarStyle:&statusBarStyle];
|
||||
if ([view conformsToProtocol:@protocol(MVMCoreViewProtocol)]) {
|
||||
[((UIView <MVMCoreViewProtocol>*)view) updateView:CGRectGetWidth(self.bounds)];
|
||||
}
|
||||
if (!statusBarColor) {
|
||||
statusBarColor = [UIColor whiteColor];
|
||||
}
|
||||
@ -178,7 +187,20 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
|
||||
[[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;
|
||||
MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) {
|
||||
@ -198,12 +220,14 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf.superview layoutIfNeeded];
|
||||
[weakSelf.animationDelegate topAlertViewFinishAnimation];
|
||||
[view handleAccessibility];
|
||||
|
||||
[weakSelf updateAccessibilityForTopAlert:view];
|
||||
|
||||
[MVMCoreDispatchUtility performBlockInBackground:^{
|
||||
if ([weakSelf.topAlertObject.delegate respondsToSelector:@selector(topAlertViewShown:topAlertObject:)]) {
|
||||
[weakSelf.topAlertObject.delegate topAlertViewShown:view topAlertObject:topAlertObject];
|
||||
}
|
||||
[[MVMCoreUILoggingHandler sharedLoggingHandler] trackTopNotificationShown:view topAlertObject:topAlertObject additionalData:nil];
|
||||
[operation markAsFinished];
|
||||
completionHandler(finished);
|
||||
}];
|
||||
|
||||
124
MVMCoreUI/TopAlert/TopNotificationModel.swift
Normal file
124
MVMCoreUI/TopAlert/TopNotificationModel.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
@ -42,6 +42,7 @@ extern NSString * const KeyIsOpaque;
|
||||
|
||||
extern NSString * const KeyFieldKey;
|
||||
extern NSString * const KeyRequired;
|
||||
extern NSString * const KeySourceModel;
|
||||
|
||||
#pragma mark - Values
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ NSString * const KeyTextColor = @"textColor";
|
||||
|
||||
NSString * const KeyIsHidden = @"isHidden";
|
||||
NSString * const KeyIsOpaque = @"isOpaque";
|
||||
NSString * const KeySourceModel = @"sourceModel";
|
||||
|
||||
|
||||
#pragma mark - Values
|
||||
|
||||
@ -89,7 +89,11 @@
|
||||
if (![focusedElement isKindOfClass:[UIView class]]) {
|
||||
return NO;
|
||||
}
|
||||
return [(UIView *)focusedElement isDescendantOfView:view];
|
||||
__block BOOL containsFocus;
|
||||
[MVMCoreDispatchUtility performSyncBlockOnMainThread:^{
|
||||
containsFocus = [(UIView *)focusedElement isDescendantOfView:view];
|
||||
}];
|
||||
return containsFocus;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
Loading…
Reference in New Issue
Block a user