Top Alert Swift conversion

This commit is contained in:
Scott Pfeil 2023-06-02 10:36:07 -04:00
parent 47dfb10b62
commit 7da3af94ec
40 changed files with 283 additions and 2106 deletions

View File

@ -204,7 +204,6 @@
9458C3172406C8FD00930963 /* UIFont+FontWrapping.h in Headers */ = {isa = PBXBuildFile; fileRef = 9458C3152406C8FD00930963 /* UIFont+FontWrapping.h */; settings = {ATTRIBUTES = (Public, ); }; };
9458C3182406C8FD00930963 /* UIFont+FontWrapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 9458C3162406C8FD00930963 /* UIFont+FontWrapping.m */; };
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; };
94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C0150924215643005811A9 /* ActionTopAlertModel.swift */; };
94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C0150B2421564A005811A9 /* ActionCollapseNotificationModel.swift */; };
94C2D9842386F3F80006CF46 /* LabelAttributeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9832386F3F80006CF46 /* LabelAttributeModel.swift */; };
94C2D9A123872BCC0006CF46 /* LabelAttributeUnderlineModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C2D9A023872BCC0006CF46 /* LabelAttributeUnderlineModel.swift */; };
@ -281,7 +280,6 @@
AAE96FA525341F7D0037A989 /* ListStoreLocator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE96FA425341F7D0037A989 /* ListStoreLocator.swift */; };
AF1C33652883B5A4006B1001 /* ActionTopNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33642883B5A4006B1001 /* ActionTopNotificationHandler.swift */; };
AF1C336928859778006B1001 /* ActionAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336828859778006B1001 /* ActionAlertHandler.swift */; };
AF1C336B28859C73006B1001 /* ActionTopAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336A28859C73006B1001 /* ActionTopAlertHandler.swift */; };
AF1C336D28859EE1006B1001 /* ActionOpenPanelHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336C28859EE1006B1001 /* ActionOpenPanelHandler.swift */; };
AF1C336F2885A16A006B1001 /* ActionCollapseNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */; };
AF1C33712885AE76006B1001 /* MVMCoreUIActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */; };
@ -462,16 +460,6 @@
D29DF11821E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF11421E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.m */; };
D29DF11C21E684A9003B2FB9 /* MVMCoreUISplitViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF11A21E684A9003B2FB9 /* MVMCoreUISplitViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF11B21E684A9003B2FB9 /* MVMCoreUISplitViewController.m */; };
D29DF12921E6851E003B2FB9 /* MVMCoreUITopAlertMainView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF11F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF12A21E6851E003B2FB9 /* MVMCoreUITopAlertView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF12021E6851E003B2FB9 /* MVMCoreUITopAlertView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF12121E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m */; };
D29DF12C21E6851E003B2FB9 /* MVMCoreUITopAlertShortView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF12221E6851E003B2FB9 /* MVMCoreUITopAlertShortView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF12D21E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF12321E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF12E21E6851E003B2FB9 /* MVMCoreUITopAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF12421E6851E003B2FB9 /* MVMCoreUITopAlertView.m */; };
D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF12521E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m */; };
D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF12621E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m */; };
D29DF13121E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF12721E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF12821E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m */; };
D29DF15421E69760003B2FB9 /* MVMCoreUIPanelButtonProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF15321E69760003B2FB9 /* MVMCoreUIPanelButtonProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF17521E69E1F003B2FB9 /* ButtonDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF16B21E69E1F003B2FB9 /* ButtonDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF25921E6A22D003B2FB9 /* MFButtonProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF25821E6A22D003B2FB9 /* MFButtonProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -532,8 +520,6 @@
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 */; };
D2D2FCF0252B72AF0033EAAA /* MoleculeSectionFooterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D2FCEF252B72AF0033EAAA /* MoleculeSectionFooterModel.swift */; };
D2D2FCF3252B72CF0033EAAA /* MoleculeSectionFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D2FCF2252B72CF0033EAAA /* MoleculeSectionFooter.swift */; };
D2D3957A252FDBB300047B11 /* ModalSectionListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D39579252FDBB300047B11 /* ModalSectionListTemplate.swift */; };
@ -566,11 +552,6 @@
D2ED27EE254B0CE700A1C293 /* ActionAlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2ED27E9254B0CE600A1C293 /* ActionAlertModel.swift */; };
D2ED27EF254B0CE700A1C293 /* AlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2ED27EA254B0CE700A1C293 /* AlertModel.swift */; };
D2ED27FC254B0E0300A1C293 /* AlertObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2ED27F3254B0E0200A1C293 /* AlertObject.swift */; };
D2ED280F254B0EB800A1C293 /* MVMCoreTopAlertViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D2ED2808254B0EB700A1C293 /* MVMCoreTopAlertViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
D2ED2810254B0EB800A1C293 /* MVMCoreTopAlertDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D2ED2809254B0EB700A1C293 /* MVMCoreTopAlertDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
D2ED2811254B0EB800A1C293 /* MVMCoreTopAlertObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2ED280A254B0EB700A1C293 /* MVMCoreTopAlertObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
D2ED2812254B0EB800A1C293 /* MVMCoreTopAlertObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2ED280B254B0EB800A1C293 /* MVMCoreTopAlertObject.m */; };
D2ED2815254B0EE400A1C293 /* MVMCoreGlobalTopAlertDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D2ED2814254B0EE400A1C293 /* MVMCoreGlobalTopAlertDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
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 */; };
@ -809,7 +790,6 @@
9458C3152406C8FD00930963 /* UIFont+FontWrapping.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIFont+FontWrapping.h"; sourceTree = "<group>"; };
9458C3162406C8FD00930963 /* UIFont+FontWrapping.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIFont+FontWrapping.m"; sourceTree = "<group>"; };
948DB67D2326DCD90011F916 /* MultiProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgress.swift; sourceTree = "<group>"; };
94C0150924215643005811A9 /* ActionTopAlertModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionTopAlertModel.swift; sourceTree = "<group>"; };
94C0150B2421564A005811A9 /* ActionCollapseNotificationModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionCollapseNotificationModel.swift; sourceTree = "<group>"; };
94C2D9832386F3F80006CF46 /* LabelAttributeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeModel.swift; sourceTree = "<group>"; };
94C2D9A023872BCC0006CF46 /* LabelAttributeUnderlineModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeUnderlineModel.swift; sourceTree = "<group>"; };
@ -885,7 +865,6 @@
AAE96FA425341F7D0037A989 /* ListStoreLocator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListStoreLocator.swift; sourceTree = "<group>"; };
AF1C33642883B5A4006B1001 /* ActionTopNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionTopNotificationHandler.swift; sourceTree = "<group>"; };
AF1C336828859778006B1001 /* ActionAlertHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionAlertHandler.swift; sourceTree = "<group>"; };
AF1C336A28859C73006B1001 /* ActionTopAlertHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionTopAlertHandler.swift; sourceTree = "<group>"; };
AF1C336C28859EE1006B1001 /* ActionOpenPanelHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionOpenPanelHandler.swift; sourceTree = "<group>"; };
AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCollapseNotificationHandler.swift; sourceTree = "<group>"; };
AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionHandler.swift; sourceTree = "<group>"; };
@ -1066,16 +1045,6 @@
D29DF11421E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSLayoutConstraint+MFConvenience.m"; sourceTree = "<group>"; };
D29DF11A21E684A9003B2FB9 /* MVMCoreUISplitViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUISplitViewController.h; sourceTree = "<group>"; };
D29DF11B21E684A9003B2FB9 /* MVMCoreUISplitViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUISplitViewController.m; sourceTree = "<group>"; };
D29DF11F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUITopAlertMainView.h; sourceTree = "<group>"; };
D29DF12021E6851E003B2FB9 /* MVMCoreUITopAlertView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUITopAlertView.h; sourceTree = "<group>"; };
D29DF12121E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUITopAlertExpandableView.m; sourceTree = "<group>"; };
D29DF12221E6851E003B2FB9 /* MVMCoreUITopAlertShortView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUITopAlertShortView.h; sourceTree = "<group>"; };
D29DF12321E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUITopAlertBaseView.h; sourceTree = "<group>"; };
D29DF12421E6851E003B2FB9 /* MVMCoreUITopAlertView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUITopAlertView.m; sourceTree = "<group>"; };
D29DF12521E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUITopAlertMainView.m; sourceTree = "<group>"; };
D29DF12621E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUITopAlertShortView.m; sourceTree = "<group>"; };
D29DF12721E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUITopAlertExpandableView.h; sourceTree = "<group>"; };
D29DF12821E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUITopAlertBaseView.m; sourceTree = "<group>"; };
D29DF13821E68636003B2FB9 /* MFStyler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFStyler.h; sourceTree = "<group>"; };
D29DF13921E68637003B2FB9 /* MFStyler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFStyler.m; sourceTree = "<group>"; };
D29DF14421E68728003B2FB9 /* MFSizeObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFSizeObject.m; sourceTree = "<group>"; };
@ -1138,8 +1107,6 @@
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>"; };
D2D2FCEF252B72AF0033EAAA /* MoleculeSectionFooterModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionFooterModel.swift; sourceTree = "<group>"; };
D2D2FCF2252B72CF0033EAAA /* MoleculeSectionFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionFooter.swift; sourceTree = "<group>"; };
D2D39579252FDBB300047B11 /* ModalSectionListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalSectionListTemplate.swift; sourceTree = "<group>"; };
@ -1171,11 +1138,6 @@
D2ED27E9254B0CE600A1C293 /* ActionAlertModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionAlertModel.swift; sourceTree = "<group>"; };
D2ED27EA254B0CE700A1C293 /* AlertModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertModel.swift; sourceTree = "<group>"; };
D2ED27F3254B0E0200A1C293 /* AlertObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertObject.swift; sourceTree = "<group>"; };
D2ED2808254B0EB700A1C293 /* MVMCoreTopAlertViewProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreTopAlertViewProtocol.h; sourceTree = "<group>"; };
D2ED2809254B0EB700A1C293 /* MVMCoreTopAlertDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreTopAlertDelegateProtocol.h; sourceTree = "<group>"; };
D2ED280A254B0EB700A1C293 /* MVMCoreTopAlertObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreTopAlertObject.h; sourceTree = "<group>"; };
D2ED280B254B0EB800A1C293 /* MVMCoreTopAlertObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreTopAlertObject.m; sourceTree = "<group>"; };
D2ED2814254B0EE400A1C293 /* MVMCoreGlobalTopAlertDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreGlobalTopAlertDelegateProtocol.h; 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>"; };
@ -1507,8 +1469,6 @@
94C01508242155FE005811A9 /* Actions */ = {
isa = PBXGroup;
children = (
94C0150924215643005811A9 /* ActionTopAlertModel.swift */,
AF1C336A28859C73006B1001 /* ActionTopAlertHandler.swift */,
94C0150B2421564A005811A9 /* ActionCollapseNotificationModel.swift */,
AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */,
D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */,
@ -2154,23 +2114,8 @@
isa = PBXGroup;
children = (
AFA4932129E5EF2E001A9663 /* NotificationHandler.swift */,
D2ED2814254B0EE400A1C293 /* MVMCoreGlobalTopAlertDelegateProtocol.h */,
D2ED2809254B0EB700A1C293 /* MVMCoreTopAlertDelegateProtocol.h */,
D2ED280A254B0EB700A1C293 /* MVMCoreTopAlertObject.h */,
D2ED280B254B0EB800A1C293 /* MVMCoreTopAlertObject.m */,
D2ED2808254B0EB700A1C293 /* MVMCoreTopAlertViewProtocol.h */,
D20C7008250BF99B0095B21C /* TopNotificationModel.swift */,
D20C700A250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift */,
D29DF12021E6851E003B2FB9 /* MVMCoreUITopAlertView.h */,
D29DF12421E6851E003B2FB9 /* MVMCoreUITopAlertView.m */,
D29DF12321E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.h */,
D29DF12821E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m */,
D29DF11F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.h */,
D29DF12521E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m */,
D29DF12221E6851E003B2FB9 /* MVMCoreUITopAlertShortView.h */,
D29DF12621E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m */,
D29DF12721E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.h */,
D29DF12121E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m */,
);
path = Notification;
sourceTree = "<group>";
@ -2478,11 +2423,9 @@
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>";
@ -2558,7 +2501,6 @@
D29DF25921E6A22D003B2FB9 /* MFButtonProtocol.h in Headers */,
D29DF28421E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.h in Headers */,
D29DF2CE21E7C104003B2FB9 /* MFLoadingViewController.h in Headers */,
D29DF12A21E6851E003B2FB9 /* MVMCoreUITopAlertView.h in Headers */,
D29DF27521E79E81003B2FB9 /* MVMCoreUILoggingHandler.h in Headers */,
D29DF2B321E7B76D003B2FB9 /* MFLoadingSpinner.h in Headers */,
D20492A424329A2800A5EED6 /* MVMCoreUIPagingProtocol.h in Headers */,
@ -2567,22 +2509,14 @@
D29DF26E21E6AA0B003B2FB9 /* FLAnimatedImage.h in Headers */,
D29DF11621E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.h in Headers */,
01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */,
D29DF13121E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.h in Headers */,
D29DF2CA21E7BFC8003B2FB9 /* MFSizeThreshold.h in Headers */,
D29DF28021E7AA51003B2FB9 /* MVMCoreUIDetailViewProtocol.h in Headers */,
D29DF2EE21ECEADF003B2FB9 /* MFFonts.h in Headers */,
D29DF12D21E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.h in Headers */,
D2C5001821F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h in Headers */,
D2ED2811254B0EB800A1C293 /* MVMCoreTopAlertObject.h in Headers */,
D2ED280F254B0EB800A1C293 /* MVMCoreTopAlertViewProtocol.h in Headers */,
D2ED2810254B0EB800A1C293 /* MVMCoreTopAlertDelegateProtocol.h in Headers */,
D2ED2815254B0EE400A1C293 /* MVMCoreGlobalTopAlertDelegateProtocol.h in Headers */,
D29DF26F21E6AA0B003B2FB9 /* FLAnimatedImageView.h in Headers */,
D29DF2A121E7AF4E003B2FB9 /* MVMCoreUIUtility.h in Headers */,
D29DF2C821E7BFC1003B2FB9 /* MFSizeObject.h in Headers */,
D29DF2E121E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h in Headers */,
D29DF12921E6851E003B2FB9 /* MVMCoreUITopAlertMainView.h in Headers */,
D29DF12C21E6851E003B2FB9 /* MVMCoreUITopAlertShortView.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -2745,7 +2679,6 @@
D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */,
0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */,
EA7E67742758310500ABF773 /* EnableFormFieldEffectModel.swift in Sources */,
D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */,
942C378C2412F4FA0066E45E /* ModalMoleculeListTemplate.swift in Sources */,
BB47A588241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift in Sources */,
012A88C8238DB02000FE3DA1 /* MoleculeDelegateProtocol.swift in Sources */,
@ -2786,11 +2719,9 @@
0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */,
D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */,
D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */,
D29DF12E21E6851E003B2FB9 /* MVMCoreUITopAlertView.m in Sources */,
AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */,
D23A8FEB26122F69007E14CE /* VisibleBehaviorForVideoModel.swift in Sources */,
BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */,
D2CAC7D3251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift in Sources */,
AA07EA932510A451009A2AE3 /* Star.swift in Sources */,
D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */,
D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */,
@ -2828,7 +2759,6 @@
0A0FEC7425D42A5E00AF2548 /* BaseItemPickerEntryField.swift in Sources */,
D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */,
D23A8FF82612308D007E14CE /* PageBehaviorProtocolRequirer.swift in Sources */,
D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */,
94C2D9A723872DA90006CF46 /* LabelAttributeColorModel.swift in Sources */,
943820842432382400B43AF3 /* WebView.swift in Sources */,
0103B84E23D7E33A009C315C /* HeadlineBodyToggleModel.swift in Sources */,
@ -2877,7 +2807,6 @@
01509D952327ED1900EF99AA /* HeadlineBodyLinkToggle.swift in Sources */,
AA104ADA244734DB004D2810 /* HeadersH1LandingPageHeader.swift in Sources */,
31BE15CB23D8924D00452370 /* CheckboxLabelModel.swift in Sources */,
D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */,
94F6516D2437954100631BF9 /* Tabs.swift in Sources */,
5248BFEC23F12E350059236A /* ListThreeColumnPlanDataDivider.swift in Sources */,
0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */,
@ -2972,7 +2901,6 @@
D2E2A99623D8CF85000B42E6 /* HeadlineBodyLinkToggleModel.swift in Sources */,
C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */,
32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */,
AF1C336B28859C73006B1001 /* ActionTopAlertHandler.swift in Sources */,
011D958524042432000E3791 /* RulesProtocol.swift in Sources */,
4457904E27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift in Sources */,
D23118B325124E18001C8440 /* Notification.swift in Sources */,
@ -3045,7 +2973,6 @@
D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */,
D2D90B442404789000DD6EC9 /* MoleculeContainerProtocol.swift in Sources */,
0A7ECC5F243CEB1200C828E8 /* ColorViewWithLabel.swift in Sources */,
94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */,
BB3BC12F2550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinks.swift in Sources */,
012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */,
D2092355244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift in Sources */,
@ -3084,7 +3011,6 @@
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 */,
01F2C20427C81F9700DC3D36 /* SubNavInteractor.swift.swift in Sources */,
@ -3144,7 +3070,6 @@
EAA0CFB1275E823A00D65EB0 /* HideFormFieldEffectModel.swift in Sources */,
D23A8FFB26123189007E14CE /* PageBehaviorModelProtocol.swift in Sources */,
52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */,
D2ED2812254B0EB800A1C293 /* MVMCoreTopAlertObject.m in Sources */,
0AA4D2E125CAEC72008DB32D /* AccessibilityModelProtocol.swift in Sources */,
EA05EFA9278DDE2C00828819 /* ClearFormFieldEffectModel.swift in Sources */,
C003506123AA94CD00B6AC29 /* Button.swift in Sources */,
@ -3159,7 +3084,6 @@
D21B7F73243BAC6800051ABF /* CollectionItemModelProtocol.swift in Sources */,
AA104B1A24474A66004D2810 /* HeadersH2Buttons.swift in Sources */,
C7192E7D23C301750050C2A0 /* HeadLineBodyCaretLinkImage.swift in Sources */,
D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */,
D2D2FCF0252B72AF0033EAAA /* MoleculeSectionFooterModel.swift in Sources */,
BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */,
D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */,

View File

@ -9,11 +9,22 @@
import Foundation
import MVMCore
/// Collapse the current top notification.
/// Notifications that conform are collapsable and can collapse.
public protocol CollapsableNotificationProtocol {
/// Collapses the notification.
func collapse()
}
/// Collapses the current notification if it can collapse, otherwise dismisses it.
open class ActionCollapseNotificationHandler: MVMCoreActionHandlerProtocol {
required public init() {}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
CoreUIObject.sharedInstance()?.globalTopAlertDelegate?.getTopAlertView?().collapseNotification?()
guard let notification = await NotificationHandler.shared().getCurrentNotification() else { return }
guard let notification = notification.0 as? CollapsableNotificationProtocol else {
NotificationHandler.shared().hideTopAlertView()
return
}
notification.collapse()
}
}

View File

@ -9,15 +9,11 @@
import Foundation
import MVMCore
/// Collapse the current top notification.
/// Dismiss the current top notification.
open class ActionDismissNotificationHandler: MVMCoreActionHandlerProtocol {
required public init() {}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
await withCheckedContinuation { continuation in
CoreUIObject.sharedInstance()?.globalTopAlertDelegate?.getTopAlertView?().hideAlertView?(true, completionHandler: { finished in
continuation.resume()
}) ?? continuation.resume()
}
NotificationHandler.shared().hideTopAlertView()
}
}

View File

@ -1,33 +0,0 @@
//
// ActionTopAlertHandler.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 7/18/22.
// Copyright © 2022 Verizon Wireless. All rights reserved.
//
import Foundation
import MVMCore
/// Creates and shows an alert using the ResponseInfo of a Page found in the cache.
open class ActionTopAlertHandler: MVMCoreActionHandlerProtocol {
required public init() {}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let model = model as? ActionTopAlertModel else { return }
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
MVMCoreCache.shared()?.fetchJSON(forPageType: model.pageType, queue: nil, waitUntilFinished: true, completionHandler: { json in
guard let responseInfo = json?.optionalDictionaryForKey(KeyResponseInfo) else {
continuation.resume(throwing: ModelRegistry.Error.decoderOther(message: "Alert Page \(model.pageType) missing ResponseInfo"))
return
}
let topAlertObject = MVMCoreTopAlertObject(responseInfo: responseInfo)!
topAlertObject.delegate = (delegateObject as? MVMCoreUIDelegateObject)?.topAlertDelegate
topAlertObject.pageType = model.pageType
NotificationHandler.shared().showTopAlert(with: topAlertObject)
continuation.resume()
})
}
}
}

View File

@ -1,24 +0,0 @@
//
// ActionTopAlertModel.swift
// MVMCore
//
// Created by Suresh, Kamlesh on 12/16/19.
// Copyright © 2019 myverizon. All rights reserved.
//
import Foundation
public struct ActionTopAlertModel: ActionModelProtocol {
public static var identifier: String = "topAlert"
public var actionType: String = ActionTopAlertModel.identifier
public var pageType: String
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
public init(pageType: String, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.pageType = pageType
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
}

View File

@ -15,6 +15,6 @@ open class ActionTopNotificationHandler: MVMCoreActionHandlerProtocol {
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let model = model as? ActionTopNotificationModel else { return }
NotificationHandler.shared().showTopNotification(with: model.topNotification)
try await NotificationHandler.shared().showNotification(for: model.topNotification, delegateObject: delegateObject as? MVMCoreUIDelegateObject)
}
}

View File

@ -103,7 +103,7 @@ import Foundation
performBlockOperation { [weak self] (operation) in
let strongSelf = self
MVMCoreDispatchUtility.performBlock(onMainThread: {
MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded()
strongSelf?.superview?.superview?.layoutIfNeeded()
let animation = {
strongSelf?.topView.isHidden = false
strongSelf?.bottomView.isHidden = true
@ -111,7 +111,7 @@ import Foundation
}
let completion: (Bool) -> Void = { (finished) in
strongSelf?.topView.button.isUserInteractionEnabled = true
MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded()
strongSelf?.superview?.superview?.layoutIfNeeded()
UIAccessibility.post(notification: .layoutChanged, argument: strongSelf?.getAccessibilityLayoutChangedArgument())
operation.markAsFinished()
}
@ -132,7 +132,7 @@ import Foundation
performBlockOperation { [weak self] (operation) in
let strongSelf = self
MVMCoreDispatchUtility.performBlock(onMainThread: {
MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded()
strongSelf?.superview?.superview?.layoutIfNeeded()
strongSelf?.topView.button.isUserInteractionEnabled = false
let animation = {
strongSelf?.topView.isHidden = !topViewShowing
@ -140,7 +140,7 @@ import Foundation
strongSelf?.verticalStack.layoutIfNeeded()
}
let completion: (Bool) -> Void = { (finished) in
MVMCoreUITopAlertView.sharedGlobal()?.superview?.layoutIfNeeded()
strongSelf?.superview?.superview?.layoutIfNeeded()
UIAccessibility.post(notification: .layoutChanged, argument: strongSelf?.getAccessibilityLayoutChangedArgument())
strongSelf?.autoCollapse()
operation.markAsFinished()
@ -187,3 +187,9 @@ extension CollapsableNotification: AccessibilityProtocol {
}
}
}
extension CollapsableNotification: CollapsableNotificationProtocol {
public func collapse() {
collapse(animated: true)
}
}

View File

@ -60,7 +60,7 @@ import Foundation
isAccessibilityElement = true
accessibilityLabel = label.text
accessibilityTraits = (button.isUserInteractionEnabled && button.actionModel != nil) ? .button : .none
MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: self)
NotificationView.amendAccesibilityLabel(for: self)
}
@objc func pressed(_ sender: Notification) {

View File

@ -1,53 +0,0 @@
//
// MVMCoreUITopAlertExpandableView+Extension.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/15/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
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
}
if let accessibilityIdentifier = model.accessibilityIdentifier {
self.accessibilityIdentifier = accessibilityIdentifier
}
}
}
extension MVMCoreUITopAlertExpandableView: StatusBarUI {
func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle) {
if shortView?.label?.text?.count ?? 0 > 0 {
let color = backgroundColor ?? UIColor.mvmGreen
var greyScale: CGFloat = 0
if shortView?.label?.textColor.getWhite(&greyScale, alpha: nil) ?? false {
return (color, greyScale > 0.5 ? .lightContent : .default)
} else {
return (color, .default)
}
} else {
return (.white, .default)
}
}
}

View File

@ -1,30 +0,0 @@
//
// MVMCoreUITopAlertMainView+Extension.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 9/15/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
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)
}
if let accessibilityIdentifier = model.accessibilityIdentifier {
self.accessibilityIdentifier = accessibilityIdentifier
}
setupCloseButton(model.closeButton != nil, animationDelegate: MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate)
setup(withMessage: model.headline.text, subMessage: model.body?.text, color: model.headline.textColor?.uiColor ?? .white, actionMap: actionMap, additionalData: nil)
}
}

View File

@ -74,9 +74,17 @@ import Foundation
}
open func updateAccessibility() {
MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: headline)
MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: body)
MVMCoreUITopAlertBaseView.amendAccesibilityLabel(for: button)
NotificationView.amendAccesibilityLabel(for: headline)
NotificationView.amendAccesibilityLabel(for: body)
NotificationView.amendAccesibilityLabel(for: button)
}
/// Formats the accessibilityLabel so voice over users know it's in the notification.
static public func amendAccesibilityLabel(for view: UIView) {
guard let amendment = MVMCoreUIUtility.hardcodedString(withKey: "top_alert_notification"),
let accessibilityLabel = view.accessibilityLabel,
!accessibilityLabel.hasPrefix(amendment) else { return }
view.accessibilityLabel = "\(amendment) - \(accessibilityLabel)"
}
}

View File

@ -178,8 +178,10 @@ public extension MVMCoreUISplitViewController {
navigationController.isDisplayed(viewController: viewController),
let model = navigationController.getNavigationModel(from: viewController) else { return }
setNavigationBar(for: viewController, navigationController: navigationController, navigationItemModel: model)
guard !(topAlertView?.overridingStatusBar() ?? false) else { return }
setStatusBarForCurrentViewController()
Task {
guard (await NotificationHandler.shared().getCurrentNotification()?.0 as? StatusBarUI) == nil else { return }
setStatusBarForCurrentViewController()
}
}
// MARK: - Status Bar

View File

@ -12,7 +12,6 @@
#import <MVMCoreUI/MVMCoreUIPanelProtocol.h>
#import <MVMCoreUI/MVMCoreUIPanelButtonProtocol.h>
@class MVMCoreUITopAlertView;
@class MFViewController;
@class NavigationController;
@protocol TabBarProtocol;
@ -42,7 +41,7 @@ typedef NS_ENUM(NSInteger, MFNumberOfDrawers) {
@property (nullable, weak, nonatomic, readonly) NavigationController *navigationController;
// Reference to the top alert view
@property (nullable, weak, nonatomic) MVMCoreUITopAlertView *topAlertView;
@property (nullable, weak, nonatomic) UIView *topAlertView;
// Reference to the status bar view
@property (nullable, weak, nonatomic) UIView *statusBarView;
@ -65,10 +64,10 @@ typedef NS_ENUM(NSInteger, MFNumberOfDrawers) {
- (nullable instancetype)initWithLeftPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel;
// Returns a split controller with the mvm styling. Also sets the appropriate handlers.
+ (nullable instancetype)setup:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel;
+ (nullable instancetype)setup:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel topAlertView:(nullable UIView*)topAlertView;
// Returns a split controller with the mvm styling. Also sets the appropriate handlers. Also sets up the default load screen
+ (nullable instancetype)setupAsMainController:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel;
+ (nullable instancetype)setupAsMainController:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel topAlertView:(nullable UIView*)topAlertView;
#pragma mark - Panel Functions

View File

@ -20,8 +20,8 @@
#import "MVMCoreUISession.h"
#import "MVMCoreUIConstants.h"
#import "MVMCoreUICommonViewsUtility.h"
#import "MVMCoreUITopAlertView.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
@import MVMCore.MVMCoreViewProtocol;
@interface MVMCoreUISplitViewController ()
@ -80,14 +80,15 @@ CGFloat const PanelAnimationDuration = 0.2;
return [MVMCoreActionUtility initializerClassCheck:[MVMCoreUISession sharedGlobal].splitViewController classToVerify:self];
}
+ (nullable instancetype)setup:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel topAlertView:(nonnull UIView <MVMCoreTopAlertViewProtocol>*)topAlertView {
+ (nullable instancetype)setup:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel topAlertView:(nullable UIView*)topAlertView {
MVMCoreUISplitViewController *splitViewController = [[self alloc] initWithLeftPanel:leftPanel rightPanel:rightPanel];
splitViewController.topAlertView = topAlertView;
[MVMCoreUISession sharedGlobal].splitViewController = splitViewController;
return splitViewController;
}
+ (nullable instancetype)setupAsMainController:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel {
MVMCoreUISplitViewController *splitViewController = [self setup:leftPanel rightPanel:rightPanel];
+ (nullable instancetype)setupAsMainController:(nullable UIViewController <MVMCoreUIPanelProtocol> *)leftPanel rightPanel:(nullable UIViewController <MVMCoreUIPanelProtocol> *)rightPanel topAlertView:(nullable UIView*)topAlertView {
MVMCoreUISplitViewController *splitViewController = [self setup:leftPanel rightPanel:rightPanel topAlertView:topAlertView];
[[MVMCoreUISession sharedGlobal] setupAsStandardLoadViewDelegate:splitViewController];
return splitViewController;
}
@ -904,14 +905,9 @@ CGFloat const PanelAnimationDuration = 0.2;
self.statusBarView = statusBarView;
// Top Alert
MVMCoreUITopAlertView *topAlertView = nil;
if ([[CoreUIObject sharedInstance].globalTopAlertDelegate respondsToSelector:@selector(getTopAlertView)]) {
topAlertView = (MVMCoreUITopAlertView *)[[CoreUIObject sharedInstance].globalTopAlertDelegate getTopAlertView];
if (topAlertView) {
self.topAlertView = topAlertView;
[self.view addSubview:topAlertView];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[topAlertView]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:@{@"topAlertView":topAlertView}]];
}
if (self.topAlertView) {
[self.view addSubview:self.topAlertView];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[topAlertView]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:@{@"topAlertView":self.topAlertView}]];
}
// The main view.
@ -947,7 +943,8 @@ CGFloat const PanelAnimationDuration = 0.2;
bottomProgressHeight.active = YES;
self.bottomProgressBarHeightConstraint = bottomProgressHeight;
if (topAlertView) {
if (self.topAlertView) {
UIView *topAlertView = self.topAlertView;
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[statusBarView]-0-[topAlertView]-0-[mainView]-0-[progressView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(statusBarView,topAlertView, mainView, progressView)]];
} else {
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[statusBarView]-0-[mainView]-0-[progressView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(statusBarView,mainView, progressView)]];
@ -1043,7 +1040,8 @@ CGFloat const PanelAnimationDuration = 0.2;
/*self.leftPanel.needToUpdateUI = YES;
[self.leftPanel.view setNeedsLayout];
[self.leftPanel.view layoutIfNeeded];*/
[self.topAlertView updateView:size.width];
if ([self.topAlertView conformsToProtocol:@protocol(MVMCoreViewProtocol)])
[((UIView <MVMCoreViewProtocol> *)self.topAlertView) updateView:size.width];
}];
}

View File

@ -21,19 +21,6 @@ FOUNDATION_EXPORT const unsigned char MVMCoreUIVersionString[];
#import <MVMCoreUI/MVMCoreUIViewControllerMappingObject.h>
#import <MVMCoreUI/MVMCoreUIViewConstrainingProtocol.h>
#pragma mark - TopAlert
#import <MVMCoreUI/MVMCoreGlobalTopAlertDelegateProtocol.h>
#import <MVMCoreUI/MVMCoreTopAlertObject.h>
#import <MVMCoreUI/MVMCoreTopAlertAnimationDelegateProtocol.h>
#import <MVMCoreUI/MVMCoreTopAlertDelegateProtocol.h>
#import <MVMCoreUI/MVMCoreTopAlertViewProtocol.h>
#import <MVMCoreUI/MVMCoreTopAlertOperation.h>
#import <MVMCoreUI/MVMCoreUITopAlertView.h>
#import <MVMCoreUI/MVMCoreUITopAlertBaseView.h>
#import <MVMCoreUI/MVMCoreUITopAlertMainView.h>
#import <MVMCoreUI/MVMCoreUITopAlertShortView.h>
#import <MVMCoreUI/MVMCoreUITopAlertExpandableView.h>
#pragma mark - Categories
#import <MVMCoreUI/NSLayoutConstraint+MFConvenience.h>
#import <MVMCoreUI/UIColor+MFConvenience.h>

View File

@ -1,21 +0,0 @@
//
// MVMCoreGlobalTopAlertDelegateProtocol.h
// MVMCore
//
// Created by Pfeil, Scott Robert on 1/2/18.
// Copyright © 2018 myverizon. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <MVMCoreUI/MVMCoreTopAlertViewProtocol.h>
@class MVMCoreTopAlertObject;
@protocol MVMCoreGlobalTopAlertDelegateProtocol <NSObject>
- (NSOperationQueuePriority)priorityForTopAlertByObject:(nonnull MVMCoreTopAlertObject *)object;
@optional
- (nonnull UIView <MVMCoreTopAlertViewProtocol>*)getTopAlertView;
@end

View File

@ -1,23 +0,0 @@
//
// MVMCoreTopAlertDelegateProtocol.h
// mobilefirst
//
// Created by Scott Pfeil on 6/4/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import <Foundation/Foundation.h>
@class MVMCoreTopAlertObject;
@protocol MVMCoreTopAlertDelegateProtocol
@optional
- (void)topAlertViewShown:(nonnull id)topAlert topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject;
- (void)topAlertViewDismissed:(nonnull id)topAlert;
// Called when the top alert is pressed. Determines if we should load the option the default way or not.
- (BOOL)shouldLoadTopAlertAction:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData;
@end

View File

@ -1,64 +0,0 @@
//
// MVMCoreTopAlertObject.h
// mobilefirst
//
// Created by Scott Pfeil on 5/24/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <MVMCoreUI/MVMCoreTopAlertDelegateProtocol.h>
extern NSUInteger const TopAlertDismissTime;
@interface MVMCoreTopAlertObject : NSObject
@property (nonatomic) BOOL persistent;
@property (nullable, nonatomic) NSString *type;
@property (nonatomic) NSOperationQueuePriority queuePriority;
// The text
@property (nullable, strong, nonatomic) NSString *title;
@property (nullable, strong, nonatomic) NSString *message;
@property (nullable, strong, nonatomic) NSString *topMessage;
// For the button.
@property (nullable, strong, nonatomic) NSDictionary *buttonMap;
@property (nullable, strong, nonatomic) NSDictionary *additionalData;
@property (nonatomic) BOOL useCloseButton;
// For non action map driven button.
@property (nullable, strong, nonatomic) NSString *buttonTitle;
@property (nullable, copy, nonatomic) void (^userActionHandler)(id _Nonnull sender);
@property (nullable, weak, nonatomic) NSObject <MVMCoreTopAlertDelegateProtocol> *delegate;
// This is used to ensure legacy style stays intact with new changes
@property (nonatomic) BOOL useNewStyle;
// The page type used for the top alert
@property (nullable, strong, nonatomic) NSString *pageType;
// image name or url used imageviews
@property (nullable, strong, nonatomic) NSString *imageNameOrURL;
@property (nullable, strong, nonatomic) NSString *aboveTextImageString;
// If 0, uses default 5 seconds.
@property (nonatomic) NSInteger topAlertDismissTime;
// Server can set color.
@property (nullable, strong, nonatomic) UIColor *backgroundColor;
@property (nullable, strong, nonatomic) UIColor *textColor;
// The full top alert json. Currently only used for molecular.
@property (nullable, strong, nonatomic) NSDictionary *json;
- (nullable instancetype)initWithResponseInfo:(nullable NSDictionary *)responseInfo;
- (nullable instancetype)initWithType:(nullable NSString *)type message:(nullable NSString *)message;
- (nullable instancetype)initWithType:(nullable NSString *)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData;
- (nullable instancetype)initWithType:(nullable NSString *)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler;
@end

View File

@ -1,93 +0,0 @@
//
// MVMCoreTopAlertObject.m
// mobilefirst
//
// Created by Scott Pfeil on 5/24/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import "MVMCoreTopAlertObject.h"
@import MVMCore.NSDictionary_MFConvenience;
@import MVMCore.MVMCoreGetterUtility;
@import MVMCore.MVMCoreJSONConstants;
NSUInteger const TopAlertDismissTime = 5;
@implementation MVMCoreTopAlertObject
- (nullable instancetype)initWithResponseInfo:(nullable NSDictionary *)responseInfo {
if (self = [super init]) {
self.type = [responseInfo string:KeyType];
self.persistent = [[responseInfo stringForKey:KeyMessageStyle] isEqualToString:ValueMessageStyleTopPersistent];
self.title = [responseInfo string:KeyMessage];
self.message = [responseInfo string:KeyUserMessage];
self.buttonMap = [responseInfo dict:KeyButtonMap];
self.topMessage = [responseInfo string:KeyTopMessage];
self.imageNameOrURL = [responseInfo string:@"topAlertImageUrl"];
self.aboveTextImageString = [responseInfo string:@"topAlertImageURLAboveText"];
NSString *color = [responseInfo string:@"topAlertColor"];
if (color) {
self.backgroundColor = [MVMCoreGetterUtility getColorForHexString:color];
}
color = [responseInfo string:@"messageColor"];
if (color) {
self.textColor = [MVMCoreGetterUtility getColorForHexString:color];
}
// The default is yes if not sent by server (for legacy to work as is)
NSNumber *closeButton = [responseInfo optionalNumberForKey:KeyCloseButton];
if (closeButton != nil) {
self.useCloseButton = [closeButton boolValue];
} else {
self.useCloseButton = YES;
}
self.useNewStyle = [responseInfo boolForKey:@"newTopAlertStyle"];
// Server driven dismiss time.
NSNumber *topAlertTime = [responseInfo optionalNumberForKey:@"topAlertTime"];
if (topAlertTime != nil) {
self.topAlertDismissTime = [topAlertTime integerValue];
}
}
return self;
}
- (nullable instancetype)initWithType:(nullable NSString *)type message:(nullable NSString *)message {
return [self initWithType:type message:nil subMessage:message persistent:NO actionMap:nil additionalData:nil];
}
- (nullable instancetype)initWithType:(nullable NSString *)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData {
if (self = [super init]) {
self.type = type;
self.persistent = persistent;
self.title = message;
self.message = subMessage;
self.buttonMap = actionMap;
self.additionalData = additionalData;
}
return self;
}
- (nullable instancetype)initWithType:(nullable NSString *)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler {
if (self = [super init]) {
self.type = type;
self.persistent = persistent;
self.title = message;
self.message = subMessage;
self.buttonTitle = buttonTitle;
self.userActionHandler = userActionHandler;
}
return self;
}
@end

View File

@ -1,31 +0,0 @@
//
// MVMCoreTopAlertViewProtocol.h
// MVMCore
//
// Created by Pfeil, Scott Robert on 1/2/18.
// Copyright © 2018 myverizon. All rights reserved.
//
#import <MVMCoreUI/MVMCoreTopAlertAnimationDelegateProtocol.h>
@class MVMCoreTopAlertObject;
@protocol MVMCoreTopAlertViewProtocol
@optional
/// Show based on the object
- (nonnull NSOperation *)showWithTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
/// Removes the notification
- (void)hideAlertView:(BOOL)forceful completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
/// Collapses the notification if it has a short top message. Otherwise removes notification.
- (void)collapseNotification;
/// Updates the existing top alert with the new object
- (void)updateTopAlertWith:(nullable MVMCoreTopAlertObject *)topAlertObject;
/// Returns if the top alert is currently utilizing the status bar.
- (BOOL)overridingStatusBar;
@end

View File

@ -1,35 +0,0 @@
//
// MVMCoreUITopAlertBaseView.h
// mobilefirst
//
// Created by Scott Pfeil on 9/6/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <MVMCoreUI/MVMCoreTopAlertAnimationDelegateProtocol.h>
@import MVMCore.MVMCoreViewProtocol;
@class Button;
@interface MVMCoreUITopAlertBaseView : UIView<MVMCoreViewProtocol>
// Adds a top alert action.
+ (void)addActionToButton:(nonnull Button *)button actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData;
// Returns a top alert styled label.
+ (nonnull UILabel *)topAlertLabel;
// Returns a string for the passed in messages.
+ (nullable NSAttributedString *)getStringForMessage:(nullable NSString *)message subMessage:(nullable NSString *)subMessage color:(nullable UIColor *)color;
// Adds a close button.
- (nonnull Button *)addCloseButtonWithAnimationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate;
// Handles making various parts accessible.
- (void)handleAccessibility;
/// Adds the top alert accessibility prefix to the view.
+ (void)amendAccesibilityLabelForView:(nonnull UIView *)view;
@end

View File

@ -1,93 +0,0 @@
//
// MVMCoreUITopAlertBaseView.m
// mobilefirst
//
// Created by Scott Pfeil on 9/6/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import "MVMCoreUITopAlertBaseView.h"
#import "MVMCoreUISplitViewController.h"
@import MVMCore.MVMCoreLoadObject;
@import MVMCore.MVMCoreRequestParameters;
#import "MVMCoreUITopAlertView.h"
@import MVMCore.MVMCoreJSONConstants;
@import MVMCore.NSDictionary_MFConvenience;
#import "UIColor+MFConvenience.h"
#import "MVMCoreUICommonViewsUtility.h"
#import "MFStyler.h"
#import "MVMCoreUISession.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
#import "MVMCoreTopAlertDelegateProtocol.h"
@implementation MVMCoreUITopAlertBaseView
+ (void)addActionToButton:(nonnull Button *)button actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData {
[button addActionBlockWithEvent:UIControlEventTouchUpInside :^(Button * _Nonnull button) {
// Check with the current screen if we should perform the action (not if it's the same page type)
BOOL performAction = YES;
UIViewController *topViewController = [MVMCoreUISession sharedGlobal].splitViewController.getCurrentDetailViewController;
if ([topViewController respondsToSelector:@selector(shouldLoadTopAlertAction:additionalData:)]) {
performAction = [((NSObject <MVMCoreTopAlertDelegateProtocol>*)topViewController) shouldLoadTopAlertAction:actionMap additionalData:additionalData];
}
if (performAction) {
[[MVMCoreUIActionHandler sharedActionHandler] handleActionWithDictionary:actionMap additionalData:additionalData delegateObject:[MVMCoreUIDelegateObject createWithDelegateForAll:[MVMCoreUISession sharedGlobal].topAlertView]];
}
}];
}
+ (UILabel *)topAlertLabel {
UILabel *label = [MVMCoreUICommonViewsUtility label];
[label setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
label.textColor = [UIColor whiteColor];
label.font = [MFStyler fontB1];
label.lineBreakMode = NSLineBreakByTruncatingTail;
return label;
}
+ (nullable NSAttributedString *)getStringForMessage:(nullable NSString *)message subMessage:(nullable NSString *)subMessage color:(nullable UIColor *)color {
UIColor *textColor = color ?: [UIColor whiteColor];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] init];
if (message.length > 0) {
[string appendAttributedString:[MFStyler styleGetAttributedString:message font:[MFStyler fontB1] color:textColor]];
}
if (subMessage.length > 0) {
NSString *subMessageToAppend = nil;
if (string.length > 0) {
subMessageToAppend = [NSString stringWithFormat:@"\n%@",subMessage];
} else {
subMessageToAppend = subMessage;
}
[string appendAttributedString:[MFStyler styleGetAttributedString:subMessageToAppend font:[MFStyler fontB2] color:textColor]];
}
return string;
}
- (nonnull Button *)addCloseButtonWithAnimationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate {
Button *closeButton = [MVMCoreUICommonViewsUtility addCloseButtonTo:self action:^(Button * _Nonnull button) {
[[MVMCoreUIActionHandler sharedActionHandler] handleActionWithDictionary:@{KeyActionType: @"dismissNotification"} additionalData:nil delegateObject:nil];
} centeredVertically:YES];
[closeButton.heightAnchor constraintEqualToConstant:16.0].active = YES;
[closeButton.widthAnchor constraintEqualToConstant:16.0].active = YES;
[MVMCoreUITopAlertBaseView amendAccesibilityLabelForView:closeButton];
return closeButton;
}
- (void)updateView:(CGFloat)size {}
- (void)handleAccessibility {}
+ (void)amendAccesibilityLabelForView:(nonnull UIView *)view {
NSString *amendment = [MVMCoreUIUtility hardcodedStringWithKey:@"top_alert_notification"];
NSString *accessibilityLabel = view.accessibilityLabel;
if (accessibilityLabel && ![accessibilityLabel hasPrefix:amendment]) {
view.accessibilityLabel = [NSString stringWithFormat:@"%@ - %@", amendment, accessibilityLabel];
}
}
@end

View File

@ -1,57 +0,0 @@
//
// MVMCoreUITopAlertExpandableView.h
// mobilefirst
//
// Created by Scott Pfeil on 8/18/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <MVMCoreUI/MVMCoreUITopAlertBaseView.h>
#import <MVMCoreUI/MVMCoreTopAlertAnimationDelegateProtocol.h>
@class PrimaryButton;
@class MVMCoreUITopAlertShortView;
@class MVMCoreUITopAlertMainView;
@class MVMCoreTopAlertObject;
@interface MVMCoreUITopAlertExpandableView : MVMCoreUITopAlertBaseView
@property (nullable, weak, nonatomic) MVMCoreUITopAlertShortView *shortView;
@property (nullable, weak, nonatomic) MVMCoreUITopAlertMainView *buttonView;
// Setting this will hide the short view when the notification is collapsed.
@property (nonatomic) BOOL onlyShowTopMessageWhenCollapsed;
// A flag for if the top alert expandable view should collapse automatically after being expanded
@property (nonatomic) BOOL collapseAutomaticallyAfterExpanded;
@property (nonatomic) NSInteger collapseTime;
// Standard
- (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate viewToLayout:(nonnull UIView *)viewTolayout;
// Used primarily for when button presses will expand or collapse. (Short view button will need to be set manually)
- (nullable instancetype)initWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate viewToLayout:(nonnull UIView *)viewTolayout;
- (nullable instancetype)initWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate viewToLayout:(nonnull UIView *)viewTolayout;
// Used when button uses standard action map.
- (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;
// Setters for making buttons expand and collapse the cell.
- (void)setButtonPressToExpand;
- (void)setButtonPressToCollapse;
- (void)setShortViewPressToCollapse;
- (void)setShortViewPressToExpand;
// Animates
- (void)expand:(BOOL)animated;
- (void)collapse;
@end

View File

@ -1,431 +0,0 @@
//
// MVMCoreUITopAlertExpandableView.m
// mobilefirst
//
// Created by Scott Pfeil on 8/18/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import "MVMCoreUITopAlertExpandableView.h"
#import "MVMCoreUITopAlertShortView.h"
#import "MVMCoreUITopAlertMainView.h"
@import MVMCore.MVMCoreDispatchUtility;
#import <MVMCoreUI/MVMCoreTopAlertObject.h>
@import MVMCore.MVMCoreBlockOperation;
@import MVMCore.MVMCoreNavigationHandler;
#import "MFStyler.h"
#import "NSLayoutConstraint+MFConvenience.h"
#import "MVMCoreUIUtility.h"
#import "MVMCoreUITopAlertView.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
@interface MVMCoreUITopAlertExpandableView ()
@property (nonatomic) BOOL expanded;
@property (nullable, weak, nonatomic) id <MVMCoreTopAlertAnimationDelegateProtocol> animationDelegate;
@property (nullable, weak, nonatomic) UIView *viewToLayout;
@property (nullable, strong, nonatomic) NSLayoutConstraint *topLabelConstraintBottom;
@property (nullable, strong, nonatomic) NSLayoutConstraint *topConstraint;
@property (nullable, strong, nonatomic) NSLayoutConstraint *shortViewHeight;
// Sets up the short view.
- (void)setupTopMessage:(nullable NSString *)topMessage;
// Sets up the button view.
- (void)setupTopAlertWithButton:(MVMCoreUITopAlertMainView *)topAlertWithButton;
// Sets up the whole view without setting button action.
- (void)setupViewWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle;
// Sets up the whole view while setting button action.
- (void)setupViewWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData;
@end
@implementation MVMCoreUITopAlertExpandableView
- (void)handleAccessibility {
if (self.shortView.label.text.length > 0 && !self.expanded) {
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.shortView.label);
} else if (self.buttonView.label.text.length > 0) {
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.buttonView.label);
} else {
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
}
}
#pragma mark - Setup View
- (void)updateView:(CGFloat)size {
[super updateView:size];
[self.shortView updateView:size];
[self.buttonView updateView:size];
}
- (nullable instancetype)init {
if (self = [super init]) {
self.translatesAutoresizingMaskIntoConstraints = NO;
self.clipsToBounds = YES;
self.expanded = NO;
}
return self;
}
- (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate viewToLayout:(nonnull UIView *)viewTolayout {
if (self = [self init]) {
self.animationDelegate = animationDelegate;
self.viewToLayout = viewTolayout;
if (topAlertObject.useNewStyle) {
// New style, collapses automatically after x time if there is a top message.
self.collapseTime = topAlertObject.topAlertDismissTime;
if (topAlertObject.persistent && topAlertObject.topMessage) {
_collapseAutomaticallyAfterExpanded = YES;
}
// Setup the views.
[self setupTopMessage:topAlertObject.topMessage];
MVMCoreUITopAlertMainView *topAlertWithButton = [[MVMCoreUITopAlertMainView alloc] initWithTopAlertObject:topAlertObject animationDelegate:animationDelegate];
[self setupTopAlertWithButton:topAlertWithButton];
// Sets the color
self.shortView.label.textColor = topAlertObject.textColor ?: [[MVMCoreUITopAlertView sharedGlobal] getContentColorForType:topAlertObject.type];
self.backgroundColor = topAlertObject.backgroundColor ?: [[MVMCoreUITopAlertView sharedGlobal] getBackgroundColorForType:topAlertObject.type];
if (topAlertWithButton.label.text.length > 0) {
[self expand:NO];
}
} else {
// Old style, has no top alert and main view is limited.
self.backgroundColor = [[MVMCoreUITopAlertView sharedGlobal] getBackgroundColorForType:topAlertObject.type];
UIColor *contentColor = [[MVMCoreUITopAlertView sharedGlobal] getContentColorForType:topAlertObject.type];
[self setupTopMessage:nil];
self.shortView.label.textColor = contentColor;
MVMCoreUITopAlertMainView *topAlertWithButton = [[MVMCoreUITopAlertMainView alloc] initWithColor:self.backgroundColor contentColor:contentColor message:topAlertObject.message subMessage:nil closeButton:YES animationDelegate:animationDelegate];
[self setupTopAlertWithButton:topAlertWithButton];
[self expand:NO];
}
}
return self;
}
- (nullable instancetype)initWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate viewToLayout:(nonnull UIView *)viewTolayout {
if (self = [self init]) {
self.animationDelegate = animationDelegate;
self.viewToLayout = viewTolayout;
[self setupViewWithTopMessage:topMessage message:message subMessage:nil contentColor:contentColor buttonTitle:buttonTitle];
}
return self;
}
- (nullable instancetype)initWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate viewToLayout:(nonnull UIView *)viewTolayout {
if (self = [self init]) {
self.animationDelegate = animationDelegate;
self.viewToLayout = viewTolayout;
[self setupViewWithTopMessage:topMessage message:message subMessage:subMessage contentColor:contentColor buttonTitle:buttonTitle];
}
return self;
}
- (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 {
if (self = [self init]) {
self.animationDelegate = animationDelegate;
self.viewToLayout = viewTolayout;
[self setupViewWithTopMessage:topMessage message:message subMessage:subMessage contentColor:contentColor actionMap:actionMap additionalData:additionalData];
}
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];
shortView.label.font = [MFStyler fontB2];
[self addSubview:shortView];
self.shortView = shortView;
[NSLayoutConstraint constraintPinSubview:shortView pinTop:YES topConstant:0 pinBottom:NO bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0];
self.topLabelConstraintBottom = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:shortView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
self.topLabelConstraintBottom.active = YES;
self.shortViewHeight = [NSLayoutConstraint constraintWithItem:shortView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:0];
[self setTopMessage:topMessage];
}
- (void)setupTopAlertWithButton:(MVMCoreUITopAlertMainView *)topAlertWithButton {
topAlertWithButton.label.alpha = 0;
topAlertWithButton.button.alpha = 0;
topAlertWithButton.backgroundColor = [UIColor clearColor];
[self insertSubview:topAlertWithButton belowSubview:self.shortView];
self.buttonView = topAlertWithButton;
self.topConstraint = [NSLayoutConstraint constraintWithItem:topAlertWithButton attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.shortView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
[NSLayoutConstraint constraintPinSubview:topAlertWithButton pinTop:NO topConstant:0 pinBottom:YES bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0];
}
- (void)setupViewWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor buttonTitle:(nullable NSString *)buttonTitle {
[self setupTopMessage:topMessage];
self.shortView.label.textColor = contentColor;
MVMCoreUITopAlertMainView *topAlertWithButton = [[MVMCoreUITopAlertMainView alloc] initWithColor:[UIColor clearColor] contentColor:contentColor message:message subMessage:subMessage buttonTitle:buttonTitle userActionHandler:nil];
[self setupTopAlertWithButton:topAlertWithButton];
}
- (void)setupViewWithTopMessage:(nullable NSString *)topMessage message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage contentColor:(nonnull UIColor *)contentColor actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData {
[self setupTopMessage:topMessage];
self.shortView.label.textColor = contentColor;
MVMCoreUITopAlertMainView *topAlertWithButton = [[MVMCoreUITopAlertMainView alloc] initWithColor:[UIColor clearColor] contentColor:contentColor message:message subMessage:subMessage actionMap:actionMap additionalData:additionalData];
[self setupTopAlertWithButton:topAlertWithButton];
}
#pragma mark - Setters
- (void)setTopMessage:(nullable NSString *)topMessage {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
self.shortView.label.text = topMessage;
self.shortView.label.accessibilityLabel = topMessage;
[MVMCoreUITopAlertBaseView amendAccesibilityLabelForView:self.shortView.label];
if (topMessage && (!self.onlyShowTopMessageWhenCollapsed || !self.expanded)) {
self.shortViewHeight.active = NO;
} else if (!topMessage || (self.onlyShowTopMessageWhenCollapsed && self.expanded)) {
self.shortViewHeight.active = YES;
}
}];
}
- (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 {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[self setTopMessage:topMessage];
[self.buttonView setupWithMessage:message subMessage:subMessage color:contentColor actionMap:actionMap additionalData: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 {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[self setTopMessage:topMessage];
[self.buttonView setupWithMessage:message subMessage:subMessage color:contentColor buttonTitle:buttonTitle userActionHandler:userActionHandler];
}];
}
- (void)setButtonPressToExpand {
__weak typeof(self) weakSelf = self;
[self.buttonView.button addActionBlockWithEvent:UIControlEventTouchUpInside :^(Button * _Nonnull button) {
if (weakSelf) {
[weakSelf expand:YES];
}
}];
}
- (void)setButtonPressToCollapse {
__weak typeof(self) weakSelf = self;
[self.buttonView.button addActionBlockWithEvent:UIControlEventTouchUpInside :^(Button * _Nonnull button) {
if (weakSelf) {
[weakSelf collapse];
}
}];
}
- (void)setShortViewPressToExpand {
__weak typeof(self) weakSelf = self;
self.shortView.label.accessibilityTraits = UIAccessibilityTraitButton;
[self.shortView.button addActionBlockWithEvent:UIControlEventTouchUpInside :^(Button * _Nonnull button) {
if (weakSelf) {
[weakSelf expand:YES];
}
}];
}
- (void)setShortViewPressToCollapse {
__weak typeof(self) weakSelf = self;
self.shortView.label.accessibilityTraits = UIAccessibilityTraitButton;
[self.shortView.button addActionBlockWithEvent:UIControlEventTouchUpInside :^(Button * _Nonnull button) {
if (weakSelf) {
[weakSelf collapse];
}
}];
}
- (void)setOnlyShowTopMessageWhenCollapsed:(BOOL)onlyShowTopMessageWhenCollapsed {
_onlyShowTopMessageWhenCollapsed = onlyShowTopMessageWhenCollapsed;
if (onlyShowTopMessageWhenCollapsed && self.expanded) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
self.shortView.label.alpha = 0;
self.shortViewHeight.active = YES;
}];
} else if (!onlyShowTopMessageWhenCollapsed && self.expanded) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
self.shortView.label.alpha = 1;
self.shortViewHeight.active = NO;
}];
}
}
- (void)setCollapseAutomaticallyAfterExpanded:(BOOL)collapseAutomaticallyAfterExpanded {
_collapseAutomaticallyAfterExpanded = collapseAutomaticallyAfterExpanded;
if (collapseAutomaticallyAfterExpanded) {
[self autoCollapse];
}
}
#pragma mark - Expand/Collapse
- (void)expand:(BOOL)animated {
if (!self.expanded) {
__weak typeof(self) weakSelf = self;
MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[weakSelf performExpansion:animated onCompletion:^{
[operation markAsFinished];
}];
}];
}];
[[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;
NSInteger dismissTime;
if (self.collapseTime > 0) {
dismissTime = self.collapseTime;
} else {
dismissTime = TopAlertDismissTime;
}
dispatch_time_t dispatchTime = dispatch_time(DISPATCH_TIME_NOW, dismissTime * NSEC_PER_SEC);
dispatch_after(dispatchTime, dispatch_get_main_queue(), ^(void){
typeof(self) strongSelf = weakSelf;
if (strongSelf && strongSelf.expanded && strongSelf.collapseAutomaticallyAfterExpanded) {
// If accessibility focused, delay collapse.
if ([MVMCoreUIUtility viewContainsAccessiblityFocus:strongSelf]) {
[[NSNotificationCenter defaultCenter] addObserver:strongSelf selector:@selector(accessibilityFocusChanged:) name:UIAccessibilityElementFocusedNotification object:nil];
} else {
[strongSelf collapse];
}
}
});
}
}
- (void)collapse {
if (self.expanded) {
__weak typeof(self) weakSelf = self;
MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[weakSelf performCollapseAnimationThen:^{
[operation markAsFinished];
}];
}];
}];
[[MVMCoreNavigationHandler sharedNavigationHandler] addNavigationOperation:operation];
}
}
- (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 (notification.userInfo[UIAccessibilityFocusedElementKey] && ![MVMCoreUIUtility viewContainsAccessiblityFocus:self]) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIAccessibilityElementFocusedNotification object:nil];
[self collapse];
}
}
@end

View File

@ -1,57 +0,0 @@
//
// MVMCoreUITopAlertMainView.h
// mobilefirst
//
// Created by Scott Pfeil on 8/17/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <MVMCoreUI/MVMCoreUITopAlertBaseView.h>
#import <MVMCoreUI/MVMCoreTopAlertAnimationDelegateProtocol.h>
@class PillButton;
@class MVMCoreTopAlertObject;
@interface MVMCoreUITopAlertMainView : MVMCoreUITopAlertBaseView
@property (nullable, nonatomic, weak) UILabel *label;
@property (nullable, nonatomic, weak) PillButton *button;
@property (nullable, nonatomic, strong) NSLayoutConstraint *height;
// Standard
- (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate;
- (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData closeButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate;
// inits with images
- (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;
- (void)setupWithMessage:(nullable NSString *)message subMessage:(nullable NSString *)subMessage color:(nullable UIColor *)color buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler;
// Setters for button.
- (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.
- (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nonnull UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData;
// Legacy init: inits with a label and possible icon and close button. No main button.
- (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nonnull UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage closeButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate;
// Legacy init: inits with a label and button, no close button or icon. If passing in a block to use for the button, the top alert delegate button functions will not be called.
- (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nonnull UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler;
@end

View File

@ -1,343 +0,0 @@
//
// MVMCoreUITopAlertMainView.m
// mobilefirst
//
// Created by Scott Pfeil on 8/17/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import "MVMCoreUITopAlertMainView.h"
#import "MFStyler.h"
#import "NSLayoutConstraint+MFConvenience.h"
@import MVMCore.NSDictionary_MFConvenience;
@import MVMCore.MVMCoreDispatchUtility;
#import <MVMCoreUI/MVMCoreTopAlertObject.h>
#import "UIColor+MFConvenience.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
@import MVMCore.MVMCoreJSONConstants;
#import "MVMCoreUICommonViewsUtility.h"
#import "MVMCoreUITopAlertView.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
@interface MVMCoreUITopAlertMainView ()
@property (nullable, strong, nonatomic) NSLayoutConstraint *labelRightConstraint;
@property (nullable, weak, nonatomic) UIView *centerView;
@property (nullable, weak, nonatomic) LoadImageView *iconView;
@property (nullable, weak, nonatomic) LoadImageView *topIconView;
@property (nullable, weak, nonatomic) Button *closeButton;
@property (nullable, strong, nonatomic) NSString *message;
@property (nullable, strong, nonatomic) NSString *subMessage;
@property (nullable, strong, nonatomic) UIColor *contentColor;
@end
@implementation MVMCoreUITopAlertMainView
- (void)handleAccessibility {
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.label);
}
#pragma mark - Setup View
- (void)updateView:(CGFloat)size {
[super updateView:size];
self.label.attributedText = [MVMCoreUITopAlertBaseView getStringForMessage:self.message subMessage:self.subMessage color:self.contentColor];
self.label.accessibilityLabel = self.label.text;
[MVMCoreUITopAlertBaseView amendAccesibilityLabelForView:self.label];
[self.button updateView:size];
}
- (nullable instancetype)init {
if (self = [super init]) {
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;
}
return self;
}
- (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate {
if (self = [self init]) {
UIColor *contentColor = topAlertObject.textColor ?: [[MVMCoreUITopAlertView sharedGlobal] getContentColorForType:topAlertObject.type];
self.backgroundColor = topAlertObject.backgroundColor ?: [[MVMCoreUITopAlertView sharedGlobal] getBackgroundColorForType:topAlertObject.type];
[self setupViewWithLabelAndImage:topAlertObject.imageNameOrURL topImage:topAlertObject.aboveTextImageString];
[self setupCloseButton:topAlertObject.useCloseButton animationDelegate:animationDelegate];
[self setupWithMessage:topAlertObject.title subMessage:topAlertObject.message color:contentColor actionMap:topAlertObject.buttonMap additionalData:topAlertObject.additionalData];
}
return self;
}
- (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nullable UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData closeButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate {
// Handles all scenarios.
if (self = [self init]) {
self.backgroundColor = color;
[self setupViewWithLabelAndImage:nil topImage:nil];
[self setupCloseButton:closeButton animationDelegate:animationDelegate];
[self setupWithMessage:message subMessage:subMessage color:contentColor actionMap:actionMap additionalData:additionalData];
}
return self;
}
#pragma mark - inits with images
- (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 {
// Handles all scenarios.
if (self = [self init]) {
self.backgroundColor = color;
[self setupViewWithLabelAndImage:imageURL topImage:nil];
[self setupCloseButton:closeButton animationDelegate:animationDelegate];
[self setupWithMessage:message subMessage:subMessage color:contentColor actionMap:actionMap additionalData:additionalData];
}
return self;
}
- (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 {
// No main button.
if (self = [self init]) {
self.backgroundColor = color;
[self setupViewWithLabelAndImage:imageURL topImage:nil];
[self setupCloseButton:closeButton animationDelegate:animationDelegate];
[self setupWithMessage:message subMessage:subMessage color:contentColor buttonTitle:nil userActionHandler:NULL];
}
return self;
}
#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];
[self addSubview:centerView];
self.centerView = centerView;
[centerView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor].active = YES;
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|->=0-[centerView]->=0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(centerView)]];
// Add the label.
UILabel *label = self.label;
if (label) {
[label removeFromSuperview];
} else {
label = [MVMCoreUITopAlertBaseView topAlertLabel];
self.label = label;
}
[centerView addSubview:label];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[label]-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(label)]];
[label setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
// Add Top Image
if (self.topIconView) {
[self.topIconView removeFromSuperview];
}
if (topImageString) {
LoadImageView *imageView = [[LoadImageView alloc] initWithPinnedEdges:UIRectEdgeLeft | UIRectEdgeTop | UIRectEdgeBottom];
imageView.imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.addSizeConstraintsForAspectRatio = YES;
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[centerView addSubview:imageView];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-out-[imageView]-between-[label]-out-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:@{@"out":@(PaddingTwo),@"between":@(PaddingOne)} views:NSDictionaryOfVariableBindings(imageView,label)]];
[imageView.leftAnchor constraintEqualToAnchor:label.leftAnchor].active = YES;
[imageView.rightAnchor constraintEqualToAnchor:label.rightAnchor].active = YES;
[imageView loadImageWithName:topImageString height:@(12)];
} else {
[NSLayoutConstraint constraintPinSubview:label pinTop:YES topConstant:PaddingTwo pinBottom:YES bottomConstant:PaddingTwo pinLeft:NO leftConstant:0 pinRight:NO rightConstant:0];
}
// Add main image.
if (self.iconView) {
[self.iconView removeFromSuperview];
}
CGFloat horizontalPadding = [MFStyler defaultHorizontalPaddingForApplicationWidth];
if (imageURL) {
LoadImageView *imageView = [[LoadImageView alloc] init];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[imageView setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
[self addSubview:imageView];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|->=space-[imageView]->=space-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:@{@"space":@(PaddingFive)} views:NSDictionaryOfVariableBindings(imageView)]];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-space-[imageView]-space-[centerView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:@{@"space":@(horizontalPadding)} views:NSDictionaryOfVariableBindings(imageView,centerView)]];
[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES;
self.iconView = imageView;
[imageView loadImageWithName:imageURL width:@(32)];
} else {
[NSLayoutConstraint constraintWithItem:centerView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:horizontalPadding].active = YES;
}
}
- (void)setupCloseButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate {
if (closeButton && !self.closeButton) {
self.closeButton = [self addCloseButtonWithAnimationDelegate:animationDelegate];
[self.closeButton setTintColor:self.contentColor ?: [UIColor whiteColor]];
} else if (!closeButton && self.closeButton) {
[self.closeButton removeFromSuperview];
self.closeButton = nil;
}
}
- (void)setupWithButton:(BOOL)showButton {
// Setup the button.
CGFloat horizontalPadding = [MFStyler defaultHorizontalPaddingForApplicationWidth];
if (showButton) {
if (!self.button) {
// remove label right constraint
self.labelRightConstraint.active = NO;
// Sets up to use a button action. Always uses the top view controller
PillButton *button = [[PillButton alloc] initAsPrimaryButton:false makeTiny:true];
[button setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
[button setContentHuggingPriority:800 forAxis:UILayoutConstraintAxisHorizontal];
button.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:button];
[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.centerView attribute:NSLayoutAttributeRight multiplier:1 constant:PaddingThree].active = YES;
[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeRight multiplier:1 constant:(self.closeButton ? PaddingTen : horizontalPadding)].active = YES;
self.button = button;
}
} else {
// remove button.
if (self.button) {
[self.button removeFromSuperview];
self.button = nil;
}
if (!self.labelRightConstraint) {
self.labelRightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.centerView attribute:NSLayoutAttributeRight multiplier:1 constant:(self.closeButton ? PaddingTen : horizontalPadding)];
}
self.labelRightConstraint.active = YES;
}
}
#pragma mark - Setters
- (void)setupWithMessage:(nullable NSString *)message subMessage:(nullable NSString *)subMessage color:(nullable UIColor *)color actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData {
self.message = message;
self.subMessage = subMessage;
self.contentColor = color;
[MVMCoreDispatchUtility performBlockOnMainThread:^{
// Sets the string
self.label.attributedText = [MVMCoreUITopAlertBaseView getStringForMessage:message subMessage:subMessage color:color];
self.label.accessibilityLabel = self.label.text;
[MVMCoreUITopAlertBaseView amendAccesibilityLabelForView:self.label];
// Sets the button
[self setupButtonWithActionMap:actionMap additionalData:additionalData];
if (color) {
self.button.layer.borderColor = color.CGColor;
[self.button setTitleColor:color forState:UIControlStateNormal];
[self.closeButton setTintColor:color];
}
}];
}
- (void)setupWithMessage:(nullable NSString *)message subMessage:(nullable NSString *)subMessage color:(nullable UIColor *)color buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler {
self.message = message;
self.subMessage = subMessage;
self.contentColor = color;
[MVMCoreDispatchUtility performBlockOnMainThread:^{
// Sets the string
self.label.attributedText = [MVMCoreUITopAlertBaseView getStringForMessage:message subMessage:subMessage color:color];
self.label.accessibilityLabel = self.label.text;
[MVMCoreUITopAlertBaseView amendAccesibilityLabelForView:self.label];
// Sets the color
if (color) {
self.button.layer.borderColor = color.CGColor;
[self.button setTitleColor:color forState:UIControlStateNormal];
[self.closeButton setTintColor:color];
}
// Sets the button
[self setupButtonWithButtonTitle:buttonTitle userActionHandler:userActionHandler];
}];
}
- (void)setupButtonWithActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
BOOL showButton = actionMap != nil;
[self setupWithButton:showButton];
if (showButton) {
[self.button setTitle:[actionMap stringForKey:KeyTitle] forState:UIControlStateNormal];
self.button.accessibilityLabel = [self.button titleForState:UIControlStateNormal];
[MVMCoreUITopAlertBaseView amendAccesibilityLabelForView:self.button];
[MVMCoreUITopAlertBaseView addActionToButton:self.button actionMap:actionMap additionalData:additionalData];
}
}];
}
- (void)setupButtonWithButtonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
BOOL showButton = buttonTitle.length > 0;
[self setupWithButton:showButton];
[self.button setTitle:buttonTitle forState:UIControlStateNormal];
self.button.accessibilityLabel = [self.button titleForState:UIControlStateNormal];
[MVMCoreUITopAlertBaseView amendAccesibilityLabelForView:self.button];
if (showButton && userActionHandler) {
[self.button addActionBlockWithEvent:UIControlEventTouchUpInside :userActionHandler];
}
}];
}
#pragma mark - legacy inits
- (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nonnull UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData {
// No icon or close button.
if (self = [self init]) {
self.backgroundColor = color;
[self setupViewWithLabelAndImage:nil topImage:nil];
[self setupWithMessage:message subMessage:subMessage color:contentColor actionMap:actionMap additionalData:additionalData];
}
return self;
}
- (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nonnull UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage closeButton:(BOOL)closeButton animationDelegate:(nullable id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate {
// No main button.
if (self = [self init]) {
self.backgroundColor = color;
[self setupViewWithLabelAndImage:nil topImage:nil];
[self setupWithMessage:message subMessage:subMessage color:contentColor buttonTitle:nil userActionHandler:NULL];
[self setupCloseButton:closeButton animationDelegate:animationDelegate];
}
return self;
}
- (nullable instancetype)initWithColor:(nonnull UIColor *)color contentColor:(nonnull UIColor *)contentColor message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler {
// No icon or close button. Custom button action.
if (self = [self init]) {
self.backgroundColor = color;
[self setupViewWithLabelAndImage:nil topImage:nil];
[self setupWithMessage:message subMessage:subMessage color:contentColor buttonTitle:buttonTitle userActionHandler:userActionHandler];
}
return self;
}
@end

View File

@ -1,22 +0,0 @@
//
// MVMCoreUITopAlertShortView.h
// mobilefirst
//
// Created by Scott Pfeil on 7/20/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <MVMCoreUI/MVMCoreUITopAlertBaseView.h>
@class MVMCoreTopAlertObject;
@class Button;
@interface MVMCoreUITopAlertShortView : MVMCoreUITopAlertBaseView
@property (nullable, weak, nonatomic) UILabel *label;
@property (nullable, weak, nonatomic) Button *button;
- (nullable instancetype)initWithColor:(nonnull UIColor *)color message:(nullable NSString *)message actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData topAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject;
@end

View File

@ -1,67 +0,0 @@
//
// MVMCoreUITopAlertShortView.m
// mobilefirst
//
// Created by Scott Pfeil on 7/20/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import "MVMCoreUITopAlertShortView.h"
#import "MFStyler.h"
#import "NSLayoutConstraint+MFConvenience.h"
#import "MVMCoreUIConstants.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
@interface MVMCoreUITopAlertShortView ()
@end
@implementation MVMCoreUITopAlertShortView
- (void)updateView:(CGFloat)size {
[super updateView:size];
self.label.font = [MFStyler fontB1];
}
- (nullable instancetype)initWithColor:(nonnull UIColor *)color message:(nullable NSString *)message actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData topAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject {
if (self = [super init]) {
self.backgroundColor = color;
self.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *label = [MVMCoreUITopAlertBaseView topAlertLabel];
label.numberOfLines = 1;
label.textAlignment = NSTextAlignmentCenter;
label.text = message;
[self addSubview:label];
[label setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
[NSLayoutConstraint constraintPinSubview:label pinTop:YES topConstant:0 pinBottom:YES bottomConstant:4 pinLeft:YES leftConstant:PaddingThree pinRight:YES rightConstant:PaddingThree];
self.label = label;
// Sets up to use a button action.
Button *button = [Button buttonWithType:UIButtonTypeCustom];
button.backgroundColor = [UIColor clearColor];
button.translatesAutoresizingMaskIntoConstraints = NO;
[button setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical];
[button setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
[self addSubview:button];
[NSLayoutConstraint constraintPinSubview:button pinTop:YES topConstant:0 pinBottom:YES bottomConstant:-5 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0];
if (actionMap) {
[MVMCoreUITopAlertBaseView addActionToButton:button actionMap:actionMap additionalData:additionalData];
}
self.button = button;
// Listen for status bar touches.
[[NSNotificationCenter defaultCenter] addObserverForName:NotificationStatusBarTouched object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
[button sendActionsForControlEvents:UIControlEventAllEvents];
}];
}
return self;
}
- (void)handleAccessibility {
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.label);
}
@end

View File

@ -14,68 +14,118 @@ protocol StatusBarUI {
func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle)
}
public extension MVMCoreUITopAlertView {
public class NotificationContainerView: UIView {
/// Shows the top alert with the json.
@objc func showTopAlert(with json: [AnyHashable: Any]) {
TopNotificationHandler.shared().showTopNotification(with: json)
public var currentModel: TopNotificationModel?
public var currentNotificationView: UIView?
lazy private var height = heightAnchor.constraint(equalToConstant: 0)
public init() {
super.init(frame: .zero)
setupView()
}
/// Updates the current top alert molecule with the new object
@objc func updateMolecule(with topAlertObject: MVMCoreTopAlertObject) {
guard topAlertObject.type == self.topAlertObject?.type else { return }
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
func updateAccessibilityForTopAlert(_ view: UIView) {
// Update accessibility with top alert
var accessibilityArgument: Any? = view
if let view = view as? AccessibilityProtocol {
accessibilityArgument = view.getAccessibilityLayoutChangedArgument()
}
UIAccessibility.post(notification: .layoutChanged, argument: accessibilityArgument)
}
// accessibilityFocusChanged; No longer seeing this function, needs a testing.
}
extension NotificationContainerView: NotificationTransitionDelegateProtocol {
@MainActor
public func show(notification: UIView) async {
currentNotificationView?.removeFromSuperview()
addSubview(notification)
NSLayoutConstraint.constraintPinSubview(toSuperview: notification)
currentNotificationView = notification
if let conformer = notification as? MVMCoreViewProtocol {
conformer.updateView(bounds.width)
}
if let conformer = notification as? StatusBarUI {
let statusBarUI = conformer.getStatusBarUI()
MVMCoreUISplitViewController.main()?.setStatusBarBackgroundColor(statusBarUI.color, style: statusBarUI.style)
}
superview?.layoutIfNeeded()
await withCheckedContinuation { continuation in
UIView.animate(withDuration: 0.5) {
self.height.isActive = false
self.superview?.layoutIfNeeded()
} completion: { finished in
self.superview?.layoutIfNeeded()
self.updateAccessibilityForTopAlert(notification)
continuation.resume()
}
}
}
@MainActor
public func hide(notification: UIView) async {
// accessibility - below line added to notify VI user through voiceover user when the top alert is closed
UIAccessibility.post(notification: .screenChanged, argument: MVMCoreUIUtility.hardcodedString(withKey: "AccTopAlertClosed"))
await withCheckedContinuation { continuation in
UIView.animate(withDuration: 0.5) {
self.height.isActive = true
self.superview?.layoutIfNeeded()
} completion: { finished in
UIAccessibility.post(notification: .layoutChanged, argument: nil)
if let _ = self.currentNotificationView as? StatusBarUI {
MVMCoreUISplitViewController.main()?.setStatusBarForCurrentViewController()
}
self.currentNotificationView?.removeFromSuperview()
self.currentNotificationView = nil
continuation.resume()
}
}
}
@MainActor
public func update(with model: TopNotificationModel) {
guard let currentModel = currentModel,
currentModel.type == model.type else { return }
let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self)
guard let newJson = topAlertObject.json,
let newModel = TopNotificationHandler.shared().decodeTopNotification(with: newJson, delegateObject: delegateObject),
let newModelName = ModelRegistry.getMoleculeClass(newModel.molecule)?.nameForReuse(with: newModel.molecule, delegateObject),
let currentJson = self.topAlertObject?.json,
let currentModel = TopNotificationHandler.shared().decodeTopNotification(with: currentJson, delegateObject: delegateObject),
let currentModelName = ModelRegistry.getMoleculeClass(currentModel.molecule)?.nameForReuse(with: currentModel.molecule, delegateObject),
newModelName == currentModelName,
let molecule = currentAlert as? MoleculeViewProtocol else {
guard let molecule = currentNotificationView as? MoleculeViewProtocol,
currentModel.molecule.moleculeName == model.molecule.moleculeName else {
// Log that we couldn't update.
if let errorObject = MVMCoreErrorObject(title: nil, message: nil, messageToLog: nil, code: ErrorCode.parsingJSON.rawValue, domain: ErrorDomainNative, location: "TopNotification update \(String(describing: topAlertObject.type))") {
if let errorObject = MVMCoreErrorObject(title: nil, message: nil, messageToLog: nil, code: ErrorCode.parsingJSON.rawValue, domain: ErrorDomainNative, location: "TopNotification update \(String(describing: model.type))") {
MVMCoreUILoggingHandler.addError(toLog: errorObject)
}
return
}
MVMCoreDispatchUtility.performBlock(onMainThread: {
// Update molecule
molecule.reset()
molecule.set(with: newModel.molecule, delegateObject, nil)
(molecule as? MVMCoreViewProtocol)?.updateView(self.bounds.width)
// Update status bar.
guard let statusBarDelegate = molecule as? StatusBarUI else { return }
let statusBarUI = statusBarDelegate.getStatusBarUI()
MVMCoreUISplitViewController.main()?.setStatusBarBackgroundColor(statusBarUI.color, style: statusBarUI.style)
})
}
/// 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 = ModelRegistry.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.addError(toLog: errorObject)
}
return nil
}
// Update molecule
molecule.reset()
molecule.set(with: model.molecule, delegateObject, nil)
(molecule as? MVMCoreViewProtocol)?.updateView(self.bounds.width)
// Update status bar.
guard let statusBarDelegate = molecule as? StatusBarUI else { return }
let statusBarUI = statusBarDelegate.getStatusBarUI()
MVMCoreUISplitViewController.main()?.setStatusBarBackgroundColor(statusBarUI.color, style: statusBarUI.style)
}
}
extension MVMCoreUITopAlertView: ActionDelegateProtocol {}
extension NotificationContainerView: MVMCoreViewProtocol {
public func updateView(_ size: CGFloat) {
(currentNotificationView as? MVMCoreViewProtocol)?.updateView(size)
}
public func setupView() {
clipsToBounds = false
height.isActive = true
}
}

View File

@ -1,46 +0,0 @@
//
// MVMCoreUITopAlertView.h
// myverizon
//
// Created by Chris Yang on 2/3/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import <UIKit/UIKit.h>
@import MVMCore.MVMCoreLoadDelegateProtocol;
@import MVMCore.MVMCoreActionDelegateProtocol;
@import MVMCore.MVMCorePresentationDelegateProtocol;
@import MVMCore.MVMCoreViewProtocol;
#import <MVMCoreUI/MVMCoreTopAlertAnimationDelegateProtocol.h>
#import <MVMCoreUI/MVMCoreTopAlertViewProtocol.h>
#import <MVMCoreUI/ButtonDelegateProtocol.h>
@class MVMCoreTopAlertObject;
@interface MVMCoreUITopAlertView : UIView <MVMCoreViewProtocol, MVMCoreTopAlertViewProtocol, MVMCoreLoadDelegateProtocol, MVMCoreActionDelegateProtocol, MVMCorePresentationDelegateProtocol, ButtonDelegateProtocol>
// Delegate for the top alert view
@property (nonatomic, nullable, weak) id <MVMCoreTopAlertAnimationDelegateProtocol> animationDelegate;
// Current top alert object
@property (strong, nullable, nonatomic) MVMCoreTopAlertObject *topAlertObject;
/// Current top alert view.
@property (weak, nullable, nonatomic, readonly) UIView *currentAlert;
// Returns the top alert view
+ (nullable instancetype)sharedGlobal;
// Returns a TopAlertView with the mvm styling. Also sets the property in the session.
+ (nullable instancetype)setupTopAlertView;
// Can be subclassed for custom views.
- (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;
/// Get the content color based on the type
- (nonnull UIColor *)getContentColorForType:(nullable NSString *)type;
@end

View File

@ -1,291 +0,0 @@
//
// MVMCoreUITopAlertView.m
// myverizon
//
// Created by Chris Yang on 2/3/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import "MVMCoreUITopAlertView.h"
#import "MVMCoreUICommonViewsUtility.h"
#import "UIColor+MFConvenience.h"
#import "MVMCoreUITopAlertShortView.h"
#import "MVMCoreUITopAlertMainView.h"
#import "MVMCoreUITopAlertExpandableView.h"
#import "MVMCoreUISplitViewController.h"
#import "NSLayoutConstraint+MFConvenience.h"
#import "MVMCoreUISession.h"
#import "MVMCoreUIUtility.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
#import <MVMCoreUI/MVMCoreTopAlertObject.h>
@import MVMCore.MVMCoreLoadHandler;
@import MVMCore.MVMCoreNavigationHandler;
@import MVMCore.MVMCoreBlockOperation;
@import MVMCore.NSDictionary_MFConvenience;
@import MVMCore.MVMCoreRequestParameters;
@import MVMCore.MVMCoreJSONConstants;
@import MVMCore.MVMCoreDispatchUtility;
NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
@interface MVMCoreUITopAlertView ()
@property (weak, nullable, nonatomic, readwrite) UIView *currentAlert;
@property (strong, nonatomic) NSLayoutConstraint *height;
@property (weak, nonatomic) MVMCoreUITopAlertExpandableView *topAlertClearspotView;
@property (strong, nonatomic) NSString *time;
/// Used if we delayed the collapse due to accessibility.
@property (copy, nonatomic) void (^ hideCompletionHandler)(BOOL finished);
@property (nonatomic) BOOL currentAlertOverridingStatusBar;
@end
@implementation MVMCoreUITopAlertView
- (instancetype)init {
self = [super init];
if (self) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupView];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self setupView];
}
return self;
}
+ (nullable instancetype)sharedGlobal {
return [MVMCoreUISession sharedGlobal].topAlertView;
}
+ (nullable instancetype)setupTopAlertView {
MVMCoreUITopAlertView *topAlertView = [[self alloc] init];
topAlertView.translatesAutoresizingMaskIntoConstraints = NO;
[MVMCoreUISession sharedGlobal].topAlertView = topAlertView;
return topAlertView;
}
- (void)setupView {
if (self.height) { return; }
self.clipsToBounds = YES;
self.height = [self.heightAnchor constraintEqualToConstant:0];
self.height.active = YES;
}
- (void)updateView:(CGFloat)size {
if ([self.currentAlert respondsToSelector:@selector(updateView:)]) {
[((UIView <MVMCoreViewProtocol>*)(self.currentAlert)) updateView:size];
}
}
- (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;
}
}
- (nonnull UIColor *)getBackgroundColorForType:(nullable NSString *)type {
if ([type isEqualToString:ValueTypeError]) {
return [UIColor mvmOrange];
} else {
return [UIColor mvmGreen];
}
}
- (nonnull UIColor *)getContentColorForType:(nullable NSString *)type {
if ([type isEqualToString:ValueTypeError]) {
return [UIColor blackColor];
} else {
return [UIColor whiteColor];
}
}
- (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);
}
}
- (nonnull NSOperation *)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) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
if (weakSelf.currentAlert.superview) {
[weakSelf.currentAlert removeFromSuperview];
}
[weakSelf addSubview:view];
[NSLayoutConstraint constraintPinSubviewToSuperview:view];
weakSelf.currentAlert = view;
[weakSelf.animationDelegate topAlertViewBeginAnimation];
[weakSelf.superview layoutIfNeeded];
[UIView animateWithDuration:.5 animations:^{
weakSelf.height.active = NO;
[weakSelf.superview layoutIfNeeded];
} completion:^(BOOL finished) {
[weakSelf.superview layoutIfNeeded];
[weakSelf.animationDelegate topAlertViewFinishAnimation];
[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);
}];
}];
}];
}];
[[MVMCoreNavigationHandler sharedNavigationHandler] addNavigationOperation:operation];
return operation;
}
/// If the voice over user leaves top alert focus, hide.
- (void)accessibilityFocusChanged:(NSNotification *)notification {
if (notification.userInfo[UIAccessibilityFocusedElementKey] && ![MVMCoreUIUtility viewContainsAccessiblityFocus:self]) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIAccessibilityElementFocusedNotification object:nil];
[self hideAlertView:YES completionHandler:self.hideCompletionHandler];
self.hideCompletionHandler = nil;
}
}
#pragma mark - MVMCoreTopAlertViewProtocol
- (nonnull NSOperation *)showWithTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id <MVMCoreTopAlertAnimationDelegateProtocol>)animationDelegate completionHandler:(void (^ __nullable)(BOOL finished))completionHandler {
self.animationDelegate = animationDelegate;
__block NSOperation *operation = nil;
[MVMCoreDispatchUtility performSyncBlockOnMainThread:^{
self.topAlertObject = topAlertObject;
self.topAlertClearspotView = nil;
UIColor *statusBarColor = nil;
UIStatusBarStyle statusBarStyle = UIStatusBarStyleDefault;
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) {
self.currentAlertOverridingStatusBar = YES;
[[MVMCoreUISplitViewController mainSplitViewController] setStatusBarBackgroundColor:statusBarColor style:statusBarStyle];
}
operation = [self showAlertView:view topAlertObject:topAlertObject completionHandler:completionHandler];
}];
return operation;
}
- (void)hideAlertView:(BOOL)forceful completionHandler:(void (^ __nullable)(BOOL finished))completionHandler {
// If accessible and focused, do not collapse until unfocused.
if (!forceful && [MVMCoreUIUtility viewContainsAccessiblityFocus:self]) {
self.hideCompletionHandler = completionHandler;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(accessibilityFocusChanged:) name:UIAccessibilityElementFocusedNotification object:nil];
return;
}
__weak typeof(self) weakSelf = self;
MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[weakSelf.animationDelegate topAlertWillDismiss];
[weakSelf.animationDelegate topAlertViewBeginAnimation];
// accessibility - below line added to notify VI user through voiceover user when the top alert is closed
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, [MVMCoreUIUtility hardcodedStringWithKey:@"AccTopAlertClosed"]);
[UIView animateWithDuration:.5 animations:^{
weakSelf.height.active = YES;
[weakSelf.superview layoutIfNeeded];
} completion:^(BOOL finished) {
[weakSelf.animationDelegate topAlertViewFinishAnimation];
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
UIView *view = weakSelf.currentAlert;
if (view.superview) {
[view removeFromSuperview];
}
[MVMCoreDispatchUtility performBlockInBackground:^{
if ([weakSelf.topAlertObject.delegate respondsToSelector:@selector(topAlertViewDismissed:)]) {
[weakSelf.topAlertObject.delegate topAlertViewDismissed:view];
}
[operation markAsFinished];
if (completionHandler) {
completionHandler(finished);
}
[weakSelf.animationDelegate topAlertDismissed];
weakSelf.topAlertObject = nil;
if (weakSelf.currentAlertOverridingStatusBar) {
weakSelf.currentAlertOverridingStatusBar = NO;
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[[MVMCoreUISplitViewController mainSplitViewController] setStatusBarForCurrentViewController];
}];
}
}];
}];
}];
}];
[[MVMCoreNavigationHandler sharedNavigationHandler] addNavigationOperation:operation];
}
- (void)updateTopAlertWith:(MVMCoreTopAlertObject *)topAlertObject {
[self updateMoleculeWith:topAlertObject];
}
- (void)collapseNotification {
if (self.currentAlert) {
if ([self.currentAlert isKindOfClass:[MVMCoreUITopAlertExpandableView class]] && ((MVMCoreUITopAlertExpandableView *)self.currentAlert).shortView.label.text.length > 0) {
// We have a short message, collapse to show short message.
[((MVMCoreUITopAlertExpandableView *)self.currentAlert) collapse];
} else {
// Top alert is not collapsable, remove it instead.
[self hideAlertView:NO completionHandler:NULL];
}
}
}
- (BOOL)overridingStatusBar {
return self.currentAlertOverridingStatusBar;
}
@end

View File

@ -8,6 +8,7 @@
import MVMCore
import Dispatch
import Combine
public protocol NotificationTransitionDelegateProtocol {
@MainActor
@ -22,9 +23,9 @@ public protocol NotificationTransitionDelegateProtocol {
public class NotificationOperation: MVMCoreOperation {
private let notification: UIView
public let notification: UIView
private var notificationModel: TopNotificationModel
public var notificationModel: TopNotificationModel
/// The delegate that manages transitioning the notification.
private let transitionDelegate: NotificationTransitionDelegateProtocol
@ -71,27 +72,27 @@ public class NotificationOperation: MVMCoreOperation {
}
}
private actor Properties {
public actor Properties {
private var isDisplayed: Bool = false
private var isAnimating: Bool = false
func set(displayed: Bool) {
fileprivate func set(displayed: Bool) {
isDisplayed = displayed
}
func getIsDisplayed() -> Bool {
public func getIsDisplayed() -> Bool {
return isDisplayed
}
func set(animating: Bool) {
fileprivate func set(animating: Bool) {
isAnimating = animating
}
func getIsAnimating() -> Bool {
public func getIsAnimating() -> Bool {
return isAnimating
}
}
private var properties = Properties()
public var properties = Properties()
// A flag for tracking if the operation needs to be re-added because it was cancelled for a higher priority notification.
public var reAddAfterCancel = false
@ -106,7 +107,8 @@ public class NotificationOperation: MVMCoreOperation {
public override func main() {
guard !checkAndHandleForCancellation() else { return }
add {
add { [weak self] in
guard let self = self else { return }
await self.showNotification()
guard !self.isCancelled else {
// Cancelled, dismiss immediately.
@ -124,7 +126,8 @@ public class NotificationOperation: MVMCoreOperation {
Task {
guard await properties.getIsDisplayed(),
await !properties.getIsAnimating() else { return }
add {
add { [weak self] in
guard let self = self else { return }
await self.hideNotification()
guard !self.isCancelled,
!self.notificationModel.persistent else { return }
@ -138,12 +141,8 @@ public class NotificationOperation: MVMCoreOperation {
Task {
guard await properties.getIsDisplayed(),
await !properties.getIsAnimating() else { return }
transitionOperation = MVMCoreBlockOperation(block: { [weak self] blockOperation in
transitionOperation = MVMCoreBlockOperation(block: { blockOperation in
guard !blockOperation.checkAndHandleForCancellation() else { return }
guard let self = self else {
blockOperation.markAsFinished()
return
}
Task {
await transition()
blockOperation.markAsFinished()
@ -168,6 +167,14 @@ public class NotificationOperation: MVMCoreOperation {
guard let self = self,
!self.isFinished,
!self.checkAndHandleForCancellation() else { return }
/*
// If accessible and focused, do not collapse until unfocused.
if (!forceful && [MVMCoreUIUtility viewContainsAccessiblityFocus:self]) {
self.hideCompletionHandler = completionHandler;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(accessibilityFocusChanged:) name:UIAccessibilityElementFocusedNotification object:nil];
return;
}
*/
self.stop()
})
timerSource?.setCancelHandler(handler: { [weak self] in
@ -200,6 +207,7 @@ public class NotificationOperation: MVMCoreOperation {
await transitionDelegate.show(notification: notification)
await properties.set(displayed: true)
await properties.set(animating: false)
NotificationHandler.shared().onNotificationShown.send((notification, notificationModel))
}
@MainActor
@ -208,6 +216,7 @@ public class NotificationOperation: MVMCoreOperation {
await transitionDelegate.hide(notification: notification)
await properties.set(displayed: false)
await properties.set(animating: false)
NotificationHandler.shared().onNotificationDismissed.send((notification, notificationModel))
}
/// Updates the notification with the new model.
@ -240,12 +249,23 @@ public class NotificationHandler {
/// The operation queue of top notification operations.
private var queue = OperationQueue()
public var transitionDelegate: NotificationTransitionDelegateProtocol
private var delegateObject: MVMCoreUIDelegateObject?
/// Publishes when a notification is shown.
public let onNotificationShown = PassthroughSubject<(UIView, TopNotificationModel), Never>()
/// Publishes when a notification is dismissed.
public let onNotificationDismissed = PassthroughSubject<(UIView, TopNotificationModel), Never>()
/// Returns the handler stored in the CoreUIObject
public static func shared() -> Self {
return MVMCoreActionUtility.fatalClassCheck(object: CoreUIObject.sharedInstance()?.topNotificationHandler)
}
public init() {
public init(with transitionDelegate: NotificationTransitionDelegateProtocol) {
self.transitionDelegate = transitionDelegate
registerWithNotificationCenter()
registerForPageChanges()
}
@ -262,14 +282,8 @@ public class NotificationHandler {
MVMCoreNavigationHandler.shared()?.addDelegate(self)
}
private func getDelegateObject() -> MVMCoreUIDelegateObject? {
// TODO: Top alert view is current delegate. Should move to current view controller eventually?
guard let alertView = MVMCoreUISplitViewController.main()?.topAlertView else { return nil }
return MVMCoreUIDelegateObject.create(withDelegateForAll: alertView)
}
/// Checks for new top alert json
@objc private func responseJSONUpdated(notification: Notification) {
@objc private func responseJSONUpdated(notification: Notification) async {
guard let loadObject = (notification.userInfo?[String(describing: MVMCoreLoadObject.self)] as? MVMCoreLoadObject) else { return }
// Dismiss any top alerts that server wants us to dismiss/
@ -279,29 +293,31 @@ public class NotificationHandler {
// Show any new top alert.
guard let responseJSON = loadObject.responseJSON,
let json = responseJSON.optionalDictionaryForKey(KeyTopAlert) else { return }
showTopNotification(with: json)
let json = responseJSON.optionalDictionaryForKey(KeyTopAlert) else { return }
Task {
await showNotification(for: json, delegateObject: delegateObject)
}
}
/// Decodes the json into a TopNotificationModel
public func decodeTopNotification(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) -> TopNotificationModel? {
public func showNotification(for json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) async {
do {
return try TopNotificationModel.decode(json: json, delegateObject: delegateObject)
let model = try TopNotificationModel.decode(json: json, delegateObject: delegateObject)
try await showNotification(for: model, delegateObject: delegateObject)
} catch {
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") {
MVMCoreUILoggingHandler.addError(toLog: errorObject)
}
return nil
}
}
// MARK: - Operation Handling
private func add(operation: MVMCoreTopAlertOperation) {
/// Adds the operation to the queue.
private func add(operation: NotificationOperation) {
operation.completionBlock = { [weak self] in
// If the alert was cancelled to show another with higher priority, re-add to the operation when cancelled to the queue.
if operation.reAddAfterCancel {
let newOperation: MVMCoreTopAlertOperation = operation.copy() as! MVMCoreTopAlertOperation
let newOperation: NotificationOperation = operation.copy() as! NotificationOperation
newOperation.reAddAfterCancel = false
self?.add(operation: newOperation)
}
@ -309,20 +325,19 @@ public class NotificationHandler {
}
let currentPageType = (MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol)?.pageType
operation.updateDisplayable(byPageType: currentPageType)
operation.updateDisplayable(by: currentPageType)
queue.addOperation(operation)
reevaluteQueue()
}
/// Checks for existing top alert object of same type and updates it. Only happens for molecular top alerts. Returns true if we updated.
private func checkAndUpdateExisting(with topAlertObject: MVMCoreTopAlertObject) -> Bool {
for case let operation as MVMCoreTopAlertOperation in queue.operations {
guard topAlertObject.json != nil,
operation.topAlertObject.type == topAlertObject.type else { continue }
operation.update(with: topAlertObject)
private func checkAndUpdateExisting(with model: TopNotificationModel) -> Bool {
for case let operation as NotificationOperation in queue.operations {
guard operation.notificationModel.type == model.type else { continue }
operation.update(with: model)
let pageType = (MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol)?.pageType
operation.updateDisplayable(byPageType: pageType)
operation.updateDisplayable(by: pageType)
reevaluteQueue()
return true
}
@ -331,9 +346,9 @@ public class NotificationHandler {
/// Re-evaluates the queue operations
private func reevaluteQueue() {
var highestReadyOperation: MVMCoreTopAlertOperation?
var executingOperation: MVMCoreTopAlertOperation?
for case let operation as MVMCoreTopAlertOperation in queue.operations {
var highestReadyOperation: NotificationOperation?
var executingOperation: NotificationOperation?
for case let operation as NotificationOperation in queue.operations {
guard !operation.isCancelled,
!operation.isFinished else { continue }
if operation.isReady,
@ -348,7 +363,7 @@ public class NotificationHandler {
// Cancel the executing operation if it is no longer ready to run. Re-add for later if it is persistent.
guard currentOperation.isReady else {
currentOperation.reAddAfterCancel = currentOperation.topAlertObject.persistent
currentOperation.reAddAfterCancel = currentOperation.notificationModel.persistent
currentOperation.cancel()
return
}
@ -356,7 +371,7 @@ public class NotificationHandler {
// If the highest priority operation is not executing, and the executing operation is persistent, cancel it.
if let newOperation = highestReadyOperation,
currentOperation != newOperation,
currentOperation.topAlertObject.persistent {
currentOperation.notificationModel.persistent {
currentOperation.reAddAfterCancel = true
currentOperation.cancel()
}
@ -373,37 +388,25 @@ public class NotificationHandler {
public func hasPersistentTopAlert(of type: String) -> Bool {
return queue.operations.first(where: { operation in
guard operation.isExecuting,
let operation = operation as? MVMCoreTopAlertOperation else { return false }
return operation.topAlertObject.persistent && operation.topAlertObject.type == type
}) as? MVMCoreTopAlertOperation != nil
let operation = operation as? NotificationOperation else { return false }
return operation.notificationModel.persistent && operation.notificationModel.type == type
}) as? NotificationOperation != nil
}
/// Shows the top alert with the json.
func showTopNotification(with json: [AnyHashable: Any]) {
guard let model = decodeTopNotification(with: json, delegateObject: getDelegateObject()) else { return }
showTopNotification(with: model)
}
/// Shows the top notification with the model.
func showTopNotification(with model: TopNotificationModel) {
let object = model.createTopAlertObject()
guard !checkAndUpdateExisting(with: object),
let operation = MVMCoreTopAlertOperation(topAlertObject: object) else { return }
/// Creates the view and queues up the notification.
public func showNotification(for model: TopNotificationModel, delegateObject: MVMCoreUIDelegateObject? = nil) async throws {
guard !checkAndUpdateExisting(with: model) else { return }
let view = try await createMolecule(for: model, delegateObject: delegateObject)
let operation = NotificationOperation(with: view, notificationModel: model, transitionDelegate: transitionDelegate)
NotificationHandler.shared().add(operation: operation)
}
/// Show the top alert with the legacy object.
public func showTopAlert(with topAlertObject: MVMCoreTopAlertObject) {
let alertOperation = MVMCoreTopAlertOperation(topAlertObject: topAlertObject)!
add(operation: alertOperation)
}
/// Cancel the current top alert view.
public func hideTopAlertView() {
guard let currentOperation = queue.operations.first(where: { operation in
return operation.isExecuting
}) as? MVMCoreTopAlertOperation else { return }
currentOperation.topAlertObject.persistent = false
}) as? NotificationOperation else { return }
currentOperation.notificationModel.persistent = false
currentOperation.reAddAfterCancel = false
currentOperation.cancel()
}
@ -411,8 +414,8 @@ public class NotificationHandler {
/// Cancel all operations of this type.
public func hideTopAlertView(of type: String) {
for operation in queue.operations {
guard let operation = operation as? MVMCoreTopAlertOperation,
operation.topAlertObject.type == type else { continue }
guard let operation = operation as? NotificationOperation,
operation.notificationModel.type == type else { continue }
operation.reAddAfterCancel = false
operation.cancel()
}
@ -421,19 +424,19 @@ public class NotificationHandler {
/// Cancel all persistent operations of this type.
public func hidePersistentTopAlertView(of type: String) {
for operation in queue.operations {
guard let operation = operation as? MVMCoreTopAlertOperation,
operation.topAlertObject.persistent,
operation.topAlertObject.type == type else { continue }
guard let operation = operation as? NotificationOperation,
operation.notificationModel.persistent,
operation.notificationModel.type == type else { continue }
operation.reAddAfterCancel = false
operation.cancel()
}
}
/// Finds an cancels top alerts associated with the object.
public func removeTopAlert(for object: MVMCoreTopAlertObject) {
public func removeTopAlert(for object: TopNotificationModel) {
for operation in queue.operations {
guard let operation = operation as? MVMCoreTopAlertOperation,
operation.topAlertObject === object else { return }
guard let operation = operation as? NotificationOperation,
operation.notificationModel.id == object.id else { return }
operation.reAddAfterCancel = false
operation.cancel()
}
@ -442,6 +445,27 @@ public class NotificationHandler {
public func removeAllTopAlerts() {
queue.cancelAllOperations()
}
public func getCurrentNotification() async -> (UIView, TopNotificationModel)? {
for operation in queue.operations {
guard operation.isExecuting,
let operation = operation as? NotificationOperation,
await operation.properties.getIsDisplayed() else { continue }
return (operation.notification, operation.notificationModel)
}
return nil
}
/// Creates and returns the molecule view.
@MainActor
private func createMolecule(for model: TopNotificationModel, delegateObject: MVMCoreUIDelegateObject? = nil) throws -> UIView {
do {
guard let molecule = ModelRegistry.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil) else {
throw ModelRegistry.Error.decoderOther(message: "Molecule not mapped")
}
return molecule
}
}
}
extension NotificationHandler: MVMCorePresentationDelegateProtocol {
@ -452,11 +476,11 @@ extension NotificationHandler: MVMCorePresentationDelegateProtocol {
guard viewController == MVMCoreUISplitViewController.main()?.getCurrentViewController() else { return }
let pageType = (viewController as? MVMCoreViewControllerProtocol)?.pageType
queue.operations.compactMap {
$0 as? MVMCoreTopAlertOperation
$0 as? NotificationOperation
}.sorted {
$0.queuePriority.rawValue > $1.queuePriority.rawValue
}.forEach {
$0.updateDisplayable(byPageType: pageType)
$0.updateDisplayable(by: pageType)
}
reevaluteQueue()
}
@ -464,7 +488,15 @@ extension NotificationHandler: MVMCorePresentationDelegateProtocol {
extension NotificationOperation {
/// Updates if the operation is displayable based on the page type.
func updateDisplayable(by pageType: String) {
isDisplayable = notificationModel.pages?.contains(pageType) ?? true
func updateDisplayable(by pageType: String?) {
guard let pages = notificationModel.pages else {
isDisplayable = true
return
}
guard let pageType = pageType else {
isDisplayable = false
return
}
isDisplayable = pages.contains(pageType)
}
}

View File

@ -7,8 +7,9 @@
//
import Foundation
import MVMCore
open class TopNotificationModel: Codable {
open class TopNotificationModel: Codable, Identifiable {
public var type: String
public var priority = Operation.QueuePriority.normal
public var molecule: MoleculeModelProtocol
@ -16,7 +17,8 @@ open class TopNotificationModel: Codable {
public var dismissTime = 5
public var pages: [String]?
public var analyticsData: JSONValueDictionary?
public var id: String
private enum CodingKeys: String, CodingKey {
case type
case priority
@ -25,6 +27,7 @@ open class TopNotificationModel: Codable {
case dismissTime
case pages
case analyticsData
case id
}
//--------------------------------------------------
@ -55,17 +58,6 @@ open class TopNotificationModel: Codable {
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)
@ -77,7 +69,7 @@ open class TopNotificationModel: Codable {
// 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) {
public init(with type: String, molecule: MoleculeModelProtocol, priority: Operation.QueuePriority = .normal, persistent: Bool = false, dismissTime: Int = 5, pages: [String]? = nil, analyticsData: JSONValueDictionary? = nil, id: String = UUID().uuidString) {
self.type = type
self.molecule = molecule
self.priority = priority
@ -85,6 +77,7 @@ open class TopNotificationModel: Codable {
self.dismissTime = dismissTime
self.pages = pages
self.analyticsData = analyticsData
self.id = id
}
//--------------------------------------------------
@ -95,6 +88,7 @@ open class TopNotificationModel: Codable {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
type = try typeContainer.decode(String.self, forKey: .type)
molecule = try typeContainer.decodeModel(codingKey: .molecule)
id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
if let priorityPercent = try typeContainer.decodeIfPresent(Float.self, forKey: .priority) {
setPriority(with: priorityPercent)
}
@ -117,5 +111,6 @@ open class TopNotificationModel: Codable {
try container.encode(dismissTime, forKey: .dismissTime)
try container.encodeIfPresent(pages, forKey: .pages)
try container.encodeIfPresent(analyticsData, forKey: .analyticsData)
try container.encode(id, forKey: .id)
}
}

View File

@ -232,7 +232,6 @@ open class CoreUIModelMapping: ModelMapping {
open override class func registerActions() {
super.registerActions()
ModelRegistry.register(handler: ActionAlertHandler.self, for: ActionAlertModel.self)
ModelRegistry.register(handler: ActionTopAlertHandler.self, for: ActionTopAlertModel.self)
ModelRegistry.register(handler: ActionCollapseNotificationHandler.self, for: ActionCollapseNotificationModel.self)
ModelRegistry.register(handler: ActionDismissNotificationHandler.self, for: ActionDismissNotificationModel.self)
ModelRegistry.register(handler: ActionOpenPanelHandler.self, for: ActionOpenPanelModel.self)

View File

@ -17,14 +17,16 @@ import MVMCore
CoreUIModelMapping.registerObjects()
loadHandler = MVMCoreLoadHandler()
cache = MVMCoreCache()
session = MVMCoreUISession()
Task { @MainActor in
self.sessionHandler = MVMCoreSessionTimeHandler()
let topAlertView = NotificationContainerView()
MVMCoreUISession.sharedGlobal()?.topAlertView = topAlertView
self.topNotificationHandler = NotificationHandler(with: topAlertView)
}
actionHandler = MVMCoreUIActionHandler()
session = MVMCoreUISession()
viewControllerMapping = MVMCoreUIViewControllerMappingObject()
loggingDelegate = MVMCoreUILoggingHandler()
alertHandler = AlertHandler()
topNotificationHandler = NotificationHandler()
}
}

View File

@ -18,7 +18,6 @@ open class MVMCoreUIDelegateObject: DelegateObject {
public weak var observingTextFieldDelegate: ObservingTextFieldDelegate?
public weak var moleculeDelegate: MoleculeDelegateProtocol?
public weak var alertDelegate: (AlertDelegateProtocol & NSObjectProtocol)?
public weak var topAlertDelegate: (MVMCoreTopAlertDelegateProtocol & NSObjectProtocol)?
open override func setAll(withDelegate delegate: Any) {
super.setAll(withDelegate: delegate)
@ -29,7 +28,6 @@ open class MVMCoreUIDelegateObject: DelegateObject {
observingTextFieldDelegate = delegate as? ObservingTextFieldDelegate
moleculeDelegate = delegate as? MoleculeDelegateProtocol
alertDelegate = delegate as? (AlertDelegateProtocol & NSObjectProtocol)
topAlertDelegate = delegate as? (MVMCoreTopAlertDelegateProtocol & NSObjectProtocol)
}
class func delegateObject(from controller: MVMCoreViewControllerProtocol?) -> MVMCoreUIDelegateObject? {

View File

@ -7,8 +7,6 @@
//
@import MVMCore.MVMCoreLoggingHandler;
@class MFViewController;
@class MVMCoreTopAlertObject;
NS_ASSUME_NONNULL_BEGIN
@ -21,9 +19,6 @@ 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

View File

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

View File

@ -8,7 +8,6 @@
@import UIKit.UIView;
@import MVMCore.MVMCoreSessionObject;
@class MVMCoreUITopAlertView;
@class MVMCoreUISplitViewController;
@class MFViewController;
@class MFLoadingViewController;
@ -18,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface MVMCoreUISession : MVMCoreSessionObject
@property (weak, nonatomic, nullable) MVMCoreUITopAlertView *topAlertView;
@property (weak, nonatomic, nullable) UIView *topAlertView;
@property (weak, nonatomic, nullable) MVMCoreUISplitViewController *splitViewController;
@property (weak, nonatomic, nullable) NavigationController *navigationController;
@property (weak, nonatomic, nullable) MFLoadingViewController *loadingViewController;
@ -32,9 +31,6 @@ NS_ASSUME_NONNULL_BEGIN
/// indicates if the app launched successfully
@property (assign, nonatomic) BOOL launchAppLoadedSuccessfully;
/// Allows a global overload of the title view of navigation item.
- (nullable UIView *)titleViewForController:(nonnull MFViewController *)controller;
/// Sets up the session as delegate for standard load view controller. Pass the view controller that will be used to present and will be disabled when load view is presented.
- (void)setupAsStandardLoadViewDelegate:(nonnull UIViewController *)mainViewController;

View File

@ -19,10 +19,6 @@
@implementation MVMCoreUISession
- (nullable UIView *)titleViewForController:(nonnull MFViewController *)controller {
return nil;
}
- (void)setupAsStandardLoadViewDelegate:(nonnull UIViewController *)mainViewController {
self.mainViewController = mainViewController;
[MVMCoreObject sharedInstance].loadingProtocol = self;